summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/cubeb_sink.cpp25
-rw-r--r--src/audio_core/sdl2_sink.cpp4
-rw-r--r--src/audio_core/time_stretch.cpp68
-rw-r--r--src/audio_core/time_stretch.h34
-rw-r--r--src/common/atomic_ops.h95
-rw-r--r--src/common/bit_field.h1
-rw-r--r--src/common/bit_util.h7
-rw-r--r--src/common/dynamic_library.cpp1
-rw-r--r--src/common/fiber.cpp5
-rw-r--r--src/common/fs/file.cpp1
-rw-r--r--src/common/fs/file.h2
-rw-r--r--src/common/fs/fs_types.h1
-rw-r--r--src/common/fs/fs_util.h1
-rw-r--r--src/common/hex_util.h1
-rw-r--r--src/common/host_memory.cpp6
-rw-r--r--src/common/intrusive_red_black_tree.h390
-rw-r--r--src/common/logging/backend.cpp4
-rw-r--r--src/common/logging/backend.h1
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/filter.h1
-rw-r--r--src/common/logging/text_formatter.cpp2
-rw-r--r--src/common/logging/text_formatter.h1
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/math_util.h50
-rw-r--r--src/common/memory_detect.cpp2
-rw-r--r--src/common/nvidia_flags.cpp1
-rw-r--r--src/common/page_table.h1
-rw-r--r--src/common/parent_of_member.h1
-rw-r--r--src/common/ring_buffer.h1
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/settings_input.h1
-rw-r--r--src/common/string_util.cpp2
-rw-r--r--src/common/telemetry.cpp61
-rw-r--r--src/common/telemetry.h6
-rw-r--r--src/common/thread.h2
-rw-r--r--src/common/threadsafe_queue.h4
-rw-r--r--src/common/tree.h625
-rw-r--r--src/common/uint128.h2
-rw-r--r--src/common/uuid.h1
-rw-r--r--src/common/virtual_buffer.h1
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/cpu_detect.cpp123
-rw-r--r--src/common/x64/cpu_detect.h79
-rw-r--r--src/common/x64/native_clock.cpp61
-rw-r--r--src/common/x64/native_clock.h2
-rw-r--r--src/core/CMakeLists.txt54
-rw-r--r--src/core/arm/arm_interface.cpp228
-rw-r--r--src/core/arm/arm_interface.h21
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp148
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp202
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h18
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h2
-rw-r--r--src/core/arm/symbols.cpp190
-rw-r--r--src/core/arm/symbols.h27
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/file_sys/directory.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp66
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/frontend/applets/mii_edit.cpp18
-rw-r--r--src/core/frontend/applets/mii_edit.h23
-rw-r--r--src/core/frontend/emu_window.h11
-rw-r--r--src/core/hid/emulated_console.cpp17
-rw-r--r--src/core/hid/emulated_console.h1
-rw-r--r--src/core/hid/emulated_controller.cpp377
-rw-r--r--src/core/hid/emulated_controller.h3
-rw-r--r--src/core/hid/emulated_devices.cpp78
-rw-r--r--src/core/hid/emulated_devices.h35
-rw-r--r--src/core/hid/hid_types.h5
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp6
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h3
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp16
-rw-r--r--src/core/hle/kernel/hle_ipc.h17
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp101
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.h5
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp6
-rw-r--r--src/core/hle/kernel/k_auto_object.h7
-rw-r--r--src/core/hle/kernel/k_class_token.h2
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp15
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp3
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h34
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp1
-rw-r--r--src/core/hle/kernel/k_page_buffer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_buffer.h28
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp530
-rw-r--r--src/core/hle/kernel/k_page_table.h65
-rw-r--r--src/core/hle/kernel/k_port.cpp7
-rw-r--r--src/core/hle/kernel/k_process.cpp196
-rw-r--r--src/core/hle/kernel/k_process.h21
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp9
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h3
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.h8
-rw-r--r--src/core/hle/kernel/k_server_session.cpp18
-rw-r--r--src/core/hle/kernel/k_slab_heap.h230
-rw-r--r--src/core/hle/kernel/k_spin_lock.cpp39
-rw-r--r--src/core/hle/kernel/k_spin_lock.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp49
-rw-r--r--src/core/hle/kernel/k_thread.h22
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp68
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h111
-rw-r--r--src/core/hle/kernel/kernel.cpp166
-rw-r--r--src/core/hle/kernel/kernel.h49
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.h7
-rw-r--r--src/core/hle/kernel/service_thread.cpp5
-rw-r--r--src/core/hle/kernel/slab_helpers.h2
-rw-r--r--src/core/hle/kernel/svc.cpp16
-rw-r--r--src/core/hle/kernel/svc_types.h2
-rw-r--r--src/core/hle/kernel/time_manager.cpp4
-rw-r--r--src/core/hle/service/acc/acc.cpp1
-rw-r--r--src/core/hle/service/acc/profile_manager.h1
-rw-r--r--src/core/hle/service/am/am.cpp13
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp139
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.h45
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h83
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp474
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h35
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard_types.h72
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp15
-rw-r--r--src/core/hle/service/am/applets/applets.h9
-rw-r--r--src/core/hle/service/audio/audout_u.cpp7
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h1
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h4
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp307
-rw-r--r--src/core/hle/service/hid/controllers/npad.h152
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h3
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h2
-rw-r--r--src/core/hle/service/hid/errors.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp160
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/hid/hidbus.cpp531
-rw-r--r--src/core/hle/service/hid/hidbus.h131
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp72
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h179
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp286
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h254
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h39
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp52
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h39
-rw-r--r--src/core/hle/service/jit/jit.cpp332
-rw-r--r--src/core/hle/service/jit/jit.h20
-rw-r--r--src/core/hle/service/jit/jit_context.cpp424
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/kernel_helpers.cpp11
-rw-r--r--src/core/hle/service/ldr/ldr.cpp112
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp3
-rw-r--r--src/core/hle/service/mii/mii_manager.h303
-rw-r--r--src/core/hle/service/mii/raw_data.h2
-rw-r--r--src/core/hle/service/mii/types.h307
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nfp/nfp.h2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h11
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp14
-rw-r--r--src/core/hle/service/nvflinger/binder.h42
-rw-r--r--src/core/hle/service/nvflinger/buffer_item.h46
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp59
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h28
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp206
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h154
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp231
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h37
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp117
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h79
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_defs.h21
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp923
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h83
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h39
-rw-r--r--src/core/hle/service/nvflinger/buffer_transform_flags.h25
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp133
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h60
-rw-r--r--src/core/hle/service/nvflinger/consumer_listener.h26
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp18
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h76
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.cpp36
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.h37
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp85
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h26
-rw-r--r--src/core/hle/service/nvflinger/parcel.h172
-rw-r--r--src/core/hle/service/nvflinger/pixel_format.h21
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h16
-rw-r--r--src/core/hle/service/nvflinger/status.h28
-rw-r--r--src/core/hle/service/nvflinger/ui/fence.h32
-rw-r--r--src/core/hle/service/nvflinger/ui/graphic_buffer.h100
-rw-r--r--src/core/hle/service/nvflinger/window.h53
-rw-r--r--src/core/hle/service/service.cpp13
-rw-r--r--src/core/hle/service/service.h23
-rw-r--r--src/core/hle/service/sm/sm.cpp12
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp11
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp205
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h1
-rw-r--r--src/core/hle/service/sockets/sockets.h1
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp50
-rw-r--r--src/core/hle/service/vi/display/vi_display.h29
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h57
-rw-r--r--src/core/hle/service/vi/vi.cpp691
-rw-r--r--src/core/hle/service/vi/vi.h10
-rw-r--r--src/core/hle/service/vi/vi_m.cpp9
-rw-r--r--src/core/hle/service/vi/vi_m.h7
-rw-r--r--src/core/hle/service/vi/vi_s.cpp9
-rw-r--r--src/core/hle/service/vi/vi_s.h7
-rw-r--r--src/core/hle/service/vi/vi_u.cpp9
-rw-r--r--src/core/hle/service/vi/vi_u.h7
-rw-r--r--src/core/network/network.cpp4
-rw-r--r--src/core/network/sockets.h3
-rw-r--r--src/core/perf_stats.cpp10
-rw-r--r--src/core/reporter.cpp1
-rw-r--r--src/core/tools/freezer.cpp18
-rw-r--r--src/input_common/drivers/gc_adapter.cpp16
-rw-r--r--src/input_common/drivers/gc_adapter.h3
-rw-r--r--src/input_common/drivers/sdl_driver.cpp45
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/udp_client.cpp16
-rw-r--r--src/input_common/drivers/udp_client.h2
-rw-r--r--src/input_common/helpers/udp_protocol.h10
-rw-r--r--src/input_common/input_engine.cpp47
-rw-r--r--src/input_common/input_engine.h5
-rw-r--r--src/input_common/main.cpp30
-rw-r--r--src/input_common/main.h3
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp3
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/reg_alloc.cpp3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp1
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp25
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp19
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.h1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp61
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_select.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp62
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h9
-rw-r--r--src/shader_recompiler/exception.h1
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.cpp2
-rw-r--r--src/shader_recompiler/frontend/ir/condition.h1
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.cpp2
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp1
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/instruction.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/location.h3
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp5
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp14
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp580
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py92
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp3
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.h5
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp81
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp1
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp6
-rw-r--r--src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp3
-rw-r--r--src/shader_recompiler/ir_opt/passes.h3
-rw-r--r--src/shader_recompiler/ir_opt/rescaling_pass.cpp30
-rw-r--r--src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp1
-rw-r--r--src/shader_recompiler/program_header.h5
-rw-r--r--src/shader_recompiler/runtime_info.h1
-rw-r--r--src/shader_recompiler/shader_info.h3
-rw-r--r--src/video_core/buffer_cache/buffer_base.h3
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h71
-rw-r--r--src/video_core/cdma_pusher.cpp2
-rw-r--r--src/video_core/cdma_pusher.h1
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp36
-rw-r--r--src/video_core/command_classes/codecs/codec.h2
-rw-r--r--src/video_core/command_classes/codecs/vp8.cpp1
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h1
-rw-r--r--src/video_core/command_classes/host1x.h2
-rw-r--r--src/video_core/dma_pusher.cpp1
-rw-r--r--src/video_core/engines/fermi_2d.h1
-rw-r--r--src/video_core/engines/kepler_compute.cpp1
-rw-r--r--src/video_core/engines/kepler_compute.h1
-rw-r--r--src/video_core/engines/kepler_memory.cpp2
-rw-r--r--src/video_core/engines/kepler_memory.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp114
-rw-r--r--src/video_core/engines/maxwell_3d.h56
-rw-r--r--src/video_core/engines/maxwell_dma.cpp20
-rw-r--r--src/video_core/engines/maxwell_dma.h4
-rw-r--r--src/video_core/fence_manager.h2
-rw-r--r--src/video_core/framebuffer_config.h29
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/gpu_thread.cpp2
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt2
-rw-r--r--src/video_core/host_shaders/convert_s8d24_to_abgr8.frag23
-rw-r--r--src/video_core/host_shaders/opengl_convert_s8d24.comp18
-rw-r--r--src/video_core/memory_manager.cpp1
-rw-r--r--src/video_core/query_cache.h2
-rw-r--r--src/video_core/renderer_base.cpp5
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h14
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp46
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h8
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp19
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h4
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h1
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.h1
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h16
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp18
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h8
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp24
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp16
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h6
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp3
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h1
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.cpp4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp3
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_util.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp69
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h1
-rw-r--r--src/video_core/shader_cache.cpp2
-rw-r--r--src/video_core/shader_environment.cpp1
-rw-r--r--src/video_core/shader_notify.cpp1
-rw-r--r--src/video_core/shader_notify.h1
-rw-r--r--src/video_core/surface.cpp8
-rw-r--r--src/video_core/surface.h10
-rw-r--r--src/video_core/texture_cache/descriptor_table.h1
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/video_core/texture_cache/formatter.h4
-rw-r--r--src/video_core/texture_cache/image_base.h7
-rw-r--r--src/video_core/texture_cache/render_targets.h1
-rw-r--r--src/video_core/texture_cache/slot_vector.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h82
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/video_core/texture_cache/util.h2
-rw-r--r--src/video_core/textures/astc.cpp1
-rw-r--r--src/video_core/textures/astc.h3
-rw-r--r--src/video_core/textures/decoders.cpp2
-rw-r--r--src/video_core/textures/texture.cpp1
-rw-r--r--src/video_core/video_core.cpp1
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.h14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp71
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h12
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h1
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp15
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h5
-rw-r--r--src/web_service/web_backend.cpp5
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/aboutdialog.ui2
-rw-r--r--src/yuzu/applets/qt_controller.cpp1
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp1
-rw-r--r--src/yuzu/applets/qt_profile_select.h2
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp8
-rw-r--r--src/yuzu/applets/qt_web_browser.h1
-rw-r--r--src/yuzu/bootmanager.cpp9
-rw-r--r--src/yuzu/bootmanager.h1
-rw-r--r--src/yuzu/compatdb.ui2
-rw-r--r--src/yuzu/configuration/config.cpp38
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp1
-rw-r--r--src/yuzu/configuration/configuration_shared.h1
-rw-r--r--src/yuzu/configuration/configure_audio.cpp2
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp4
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui5
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp8
-rw-r--r--src/yuzu/configuration/configure_debug.ui93
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp10
-rw-r--r--src/yuzu/configuration/configure_general.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp1
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp7
-rw-r--r--src/yuzu/configuration/configure_input.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui14
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp43
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp5
-rw-r--r--src/yuzu/configuration/configure_motion_touch.h1
-rw-r--r--src/yuzu/configuration/configure_network.cpp1
-rw-r--r--src/yuzu/configuration/configure_network.h1
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp6
-rw-r--r--src/yuzu/configuration/configure_per_game.h1
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp7
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp424
-rw-r--r--src/yuzu/configuration/configure_ringcon.h85
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui278
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.h1
-rw-r--r--src/yuzu/configuration/configure_tas.h2
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp1
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp6
-rw-r--r--src/yuzu/configuration/input_profiles.h1
-rw-r--r--src/yuzu/debugger/wait_tree.cpp2
-rw-r--r--src/yuzu/debugger/wait_tree.h1
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/game_list_p.h5
-rw-r--r--src/yuzu/game_list_worker.cpp1
-rw-r--r--src/yuzu/game_list_worker.h4
-rw-r--r--src/yuzu/hotkeys.cpp1
-rw-r--r--src/yuzu/install_dialog.cpp1
-rw-r--r--src/yuzu/loading_screen.cpp7
-rw-r--r--src/yuzu/main.cpp82
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/uisettings.cpp8
-rw-r--r--src/yuzu/uisettings.h3
-rw-r--r--src/yuzu/util/controller_navigation.cpp2
-rw-r--r--src/yuzu/util/overlay_dialog.h1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/config.cpp11
-rw-r--r--src/yuzu_cmd/config.h5
-rw-r--r--src/yuzu_cmd/default_ini.h12
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp27
524 files changed, 13340 insertions, 4782 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1bdf70b76..9182dbfd4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -65,12 +65,14 @@ if (MSVC)
65 /we4305 # 'context': truncation from 'type1' to 'type2' 65 /we4305 # 'context': truncation from 'type1' to 'type2'
66 /we4388 # 'expression': signed/unsigned mismatch 66 /we4388 # 'expression': signed/unsigned mismatch
67 /we4389 # 'operator': signed/unsigned mismatch 67 /we4389 # 'operator': signed/unsigned mismatch
68 /we4505 # 'function': unreferenced local function has been removed
68 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect 69 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
69 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 70 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
70 /we4555 # Expression has no effect; expected expression with side-effect 71 /we4555 # Expression has no effect; expected expression with side-effect
71 /we4715 # 'function': not all control paths return a value 72 /we4715 # 'function': not all control paths return a value
72 /we4834 # Discarding return value of function with 'nodiscard' attribute 73 /we4834 # Discarding return value of function with 'nodiscard' attribute
73 /we5038 # data member 'member1' will be initialized after data member 'member2' 74 /we5038 # data member 'member1' will be initialized after data member 'member2'
75 /we5245 # 'function': unreferenced function with internal linkage has been removed
74 ) 76 )
75 77
76 if (ARCHITECTURE_x86_64) 78 if (ARCHITECTURE_x86_64)
@@ -103,12 +105,6 @@ else()
103 -Wno-unused-parameter 105 -Wno-unused-parameter
104 ) 106 )
105 107
106 # TODO: Remove when we update to a GCC compiler that enables this
107 # by default (i.e. GCC 10 or newer).
108 if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
109 add_compile_options(-fconcepts)
110 endif()
111
112 if (ARCHITECTURE_x86_64) 108 if (ARCHITECTURE_x86_64)
113 add_compile_options("-mcx16") 109 add_compile_options("-mcx16")
114 endif() 110 endif()
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 090dd19b1..e553b8203 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -36,8 +36,6 @@ add_library(audio_core STATIC
36 splitter_context.h 36 splitter_context.h
37 stream.cpp 37 stream.cpp
38 stream.h 38 stream.h
39 time_stretch.cpp
40 time_stretch.h
41 voice_context.cpp 39 voice_context.cpp
42 voice_context.h 40 voice_context.h
43 41
@@ -63,7 +61,6 @@ if (NOT MSVC)
63endif() 61endif()
64 62
65target_link_libraries(audio_core PUBLIC common core) 63target_link_libraries(audio_core PUBLIC common core)
66target_link_libraries(audio_core PRIVATE SoundTouch)
67 64
68if(ENABLE_CUBEB) 65if(ENABLE_CUBEB)
69 target_link_libraries(audio_core PRIVATE cubeb) 66 target_link_libraries(audio_core PRIVATE cubeb)
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 93c35e785..13de3087c 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/cubeb_sink.h" 8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13#include "common/ring_buffer.h" 12#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
23public: 22public:
24 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 23 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
25 const std::string& name) 24 const std::string& name)
26 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, 25 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
27 num_channels} {
28 26
29 cubeb_stream_params params{}; 27 cubeb_stream_params params{};
30 params.rate = sample_rate; 28 params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
131 Common::RingBuffer<s16, 0x10000> queue; 129 Common::RingBuffer<s16, 0x10000> queue;
132 std::array<s16, 2> last_frame{}; 130 std::array<s16, 2> last_frame{};
133 std::atomic<bool> should_flush{}; 131 std::atomic<bool> should_flush{};
134 TimeStretcher time_stretch;
135 132
136 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 133 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
137 void* output_buffer, long num_frames); 134 void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
205 202
206 const std::size_t num_channels = impl->GetNumChannels(); 203 const std::size_t num_channels = impl->GetNumChannels();
207 const std::size_t samples_to_write = num_channels * num_frames; 204 const std::size_t samples_to_write = num_channels * num_frames;
208 std::size_t samples_written; 205 const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
209
210 /*
211 if (Settings::values.enable_audio_stretching.GetValue()) {
212 const std::vector<s16> in{impl->queue.Pop()};
213 const std::size_t num_in{in.size() / num_channels};
214 s16* const out{reinterpret_cast<s16*>(buffer)};
215 const std::size_t out_frames =
216 impl->time_stretch.Process(in.data(), num_in, out, num_frames);
217 samples_written = out_frames * num_channels;
218
219 if (impl->should_flush) {
220 impl->time_stretch.Flush();
221 impl->should_flush = false;
222 }
223 } else {
224 samples_written = impl->queue.Pop(buffer, samples_to_write);
225 }*/
226 samples_written = impl->queue.Pop(buffer, samples_to_write);
227 206
228 if (samples_written >= num_channels) { 207 if (samples_written >= num_channels) {
229 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 208 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 62d3716a6..2d14ce2cb 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/sdl2_sink.h" 8#include "audio_core/sdl2_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13//#include "common/settings.h" 12//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
27class SDLSinkStream final : public SinkStream { 26class SDLSinkStream final : public SinkStream {
28public: 27public:
29 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) 28 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
30 : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { 29 : num_channels{std::min(num_channels_, 6u)} {
31 30
32 SDL_AudioSpec spec; 31 SDL_AudioSpec spec;
33 spec.freq = sample_rate; 32 spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
116 SDL_AudioDeviceID dev = 0; 115 SDL_AudioDeviceID dev = 0;
117 u32 num_channels{}; 116 u32 num_channels{};
118 std::atomic<bool> should_flush{}; 117 std::atomic<bool> should_flush{};
119 TimeStretcher time_stretch;
120}; 118};
121 119
122SDLSink::SDLSink(std::string_view target_device_name) { 120SDLSink::SDLSink(std::string_view target_device_name) {
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
deleted file mode 100644
index 726591fce..000000000
--- a/src/audio_core/time_stretch.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cmath>
7#include <cstddef>
8#include "audio_core/time_stretch.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 m_sound_touch.setChannels(channel_count);
15 m_sound_touch.setSampleRate(sample_rate);
16 m_sound_touch.setPitch(1.0);
17 m_sound_touch.setTempo(1.0);
18}
19
20void TimeStretcher::Clear() {
21 m_sound_touch.clear();
22}
23
24void TimeStretcher::Flush() {
25 m_sound_touch.flush();
26}
27
28std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
29 std::size_t num_out) {
30 const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
31
32 // We were given actual_samples number of samples, and num_samples were requested from us.
33 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
34
35 const double max_latency = 0.25; // seconds
36 const double max_backlog = m_sample_rate * max_latency;
37 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
38 if (backlog_fullness > 4.0) {
39 // Too many samples in backlog: Don't push anymore on
40 num_in = 0;
41 }
42
43 // We ideally want the backlog to be about 50% full.
44 // This gives some headroom both ways to prevent underflow and overflow.
45 // We tweak current_ratio to encourage this.
46 constexpr double tweak_time_scale = 0.05; // seconds
47 const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
48 current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
49
50 // This low-pass filter smoothes out variance in the calculated stretch ratio.
51 // The time-scale determines how responsive this filter is.
52 constexpr double lpf_time_scale = 0.712; // seconds
53 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
54 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
55
56 // Place a lower limit of 5% speed. When a game boots up, there will be
57 // many silence samples. These do not need to be timestretched.
58 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
59 m_sound_touch.setTempo(m_stretch_ratio);
60
61 LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
62 backlog_fullness);
63
64 m_sound_touch.putSamples(in, static_cast<u32>(num_in));
65 return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
66}
67
68} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
deleted file mode 100644
index bb2270b96..000000000
--- a/src/audio_core/time_stretch.h
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <SoundTouch.h>
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class TimeStretcher {
14public:
15 TimeStretcher(u32 sample_rate, u32 channel_count);
16
17 /// @param in Input sample buffer
18 /// @param num_in Number of input frames in `in`
19 /// @param out Output sample buffer
20 /// @param num_out Desired number of output frames in `out`
21 /// @returns Actual number of frames written to `out`
22 std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
23
24 void Clear();
25
26 void Flush();
27
28private:
29 u32 m_sample_rate;
30 soundtouch::SoundTouch m_sound_touch;
31 double m_stretch_ratio = 1.0;
32};
33
34} // namespace AudioCore
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index 2b1f515e8..69fde8421 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -4,13 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
8#include <memory>
9
10#include "common/common_types.h" 7#include "common/common_types.h"
11 8
12#if _MSC_VER 9#if _MSC_VER
13#include <intrin.h> 10#include <intrin.h>
11#else
12#include <cstring>
14#endif 13#endif
15 14
16namespace Common { 15namespace Common {
@@ -47,6 +46,50 @@ namespace Common {
47 reinterpret_cast<__int64*>(expected.data())) != 0; 46 reinterpret_cast<__int64*>(expected.data())) != 0;
48} 47}
49 48
49[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
50 u8& actual) {
51 actual =
52 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
53 return actual == expected;
54}
55
56[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
57 u16& actual) {
58 actual =
59 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
60 return actual == expected;
61}
62
63[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
64 u32& actual) {
65 actual =
66 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
67 return actual == expected;
68}
69
70[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
71 u64& actual) {
72 actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
73 expected);
74 return actual == expected;
75}
76
77[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
78 u128& actual) {
79 const bool result =
80 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
81 value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
82 actual = expected;
83 return result;
84}
85
86[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
87 u128 result{};
88 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
89 result[0], reinterpret_cast<__int64*>(result.data()));
90 return result;
91}
92
50#else 93#else
51 94
52[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { 95[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -73,6 +116,52 @@ namespace Common {
73 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); 116 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
74} 117}
75 118
119[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
120 u8& actual) {
121 actual = __sync_val_compare_and_swap(pointer, expected, value);
122 return actual == expected;
123}
124
125[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
126 u16& actual) {
127 actual = __sync_val_compare_and_swap(pointer, expected, value);
128 return actual == expected;
129}
130
131[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
132 u32& actual) {
133 actual = __sync_val_compare_and_swap(pointer, expected, value);
134 return actual == expected;
135}
136
137[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
138 u64& actual) {
139 actual = __sync_val_compare_and_swap(pointer, expected, value);
140 return actual == expected;
141}
142
143[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
144 u128& actual) {
145 unsigned __int128 value_a;
146 unsigned __int128 expected_a;
147 unsigned __int128 actual_a;
148 std::memcpy(&value_a, value.data(), sizeof(u128));
149 std::memcpy(&expected_a, expected.data(), sizeof(u128));
150 actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
151 std::memcpy(actual.data(), &actual_a, sizeof(u128));
152 return actual_a == expected_a;
153}
154
155[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
156 unsigned __int128 zeros_a = 0;
157 unsigned __int128 result_a =
158 __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
159
160 u128 result;
161 std::memcpy(result.data(), &result_a, sizeof(u128));
162 return result;
163}
164
76#endif 165#endif
77 166
78} // namespace Common 167} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 0f0661172..7f8620e7d 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -33,7 +33,6 @@
33#include <cstddef> 33#include <cstddef>
34#include <limits> 34#include <limits>
35#include <type_traits> 35#include <type_traits>
36#include "common/common_funcs.h"
37#include "common/swap.h" 36#include "common/swap.h"
38 37
39/* 38/*
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index f50d3308a..f37538e06 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -57,4 +57,11 @@ requires std::is_integral_v<T>
57 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 57 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
58} 58}
59 59
60template <size_t bit_index, typename T>
61requires std::is_integral_v<T>
62[[nodiscard]] constexpr bool Bit(const T value) {
63 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
64 return ((value >> bit_index) & T(1)) == T(1);
65}
66
60} // namespace Common 67} // namespace Common
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7f0a10521..11003e1d6 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2+ 2// Licensed under GPLv2+
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6#include <string> 5#include <string>
7#include <utility> 6#include <utility>
8 7
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 81b212e4b..177a74deb 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -2,9 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/fiber.h" 8#include "common/fiber.h"
7#include "common/spin_lock.h"
8#include "common/virtual_buffer.h" 9#include "common/virtual_buffer.h"
9 10
10#include <boost/context/detail/fcontext.hpp> 11#include <boost/context/detail/fcontext.hpp>
@@ -19,7 +20,7 @@ struct Fiber::FiberImpl {
19 VirtualBuffer<u8> stack; 20 VirtualBuffer<u8> stack;
20 VirtualBuffer<u8> rewind_stack; 21 VirtualBuffer<u8> rewind_stack;
21 22
22 SpinLock guard{}; 23 std::mutex guard;
23 std::function<void(void*)> entry_point; 24 std::function<void(void*)> entry_point;
24 std::function<void(void*)> rewind_point; 25 std::function<void(void*)> rewind_point;
25 void* rewind_parameter{}; 26 void* rewind_parameter{};
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 274f57659..5d71275ef 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -4,7 +4,6 @@
4 4
5#include "common/fs/file.h" 5#include "common/fs/file.h"
6#include "common/fs/fs.h" 6#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9 8
10#ifdef _WIN32 9#ifdef _WIN32
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index a4f7944cd..8a2cab0af 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -6,10 +6,8 @@
6 6
7#include <cstdio> 7#include <cstdio>
8#include <filesystem> 8#include <filesystem>
9#include <fstream>
10#include <span> 9#include <span>
11#include <type_traits> 10#include <type_traits>
12#include <vector>
13 11
14#include "common/concepts.h" 12#include "common/concepts.h"
15#include "common/fs/fs_types.h" 13#include "common/fs/fs_types.h"
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h
index 089980aee..f5853f624 100644
--- a/src/common/fs/fs_types.h
+++ b/src/common/fs/fs_types.h
@@ -7,7 +7,6 @@
7#include <functional> 7#include <functional>
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h"
11 10
12namespace Common::FS { 11namespace Common::FS {
13 12
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index 1620d38c9..392af89f7 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -8,7 +8,6 @@
8#include <filesystem> 8#include <filesystem>
9#include <span> 9#include <span>
10#include <string> 10#include <string>
11#include <string_view>
12 11
13#include "common/common_types.h" 12#include "common/common_types.h"
14 13
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 5e9b6ef8b..323c8fb33 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <type_traits>
11#include <vector> 10#include <vector>
12#include <fmt/format.h> 11#include <fmt/format.h>
13#include "common/common_types.h" 12#include "common/common_types.h"
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index c465cfc14..802943eb7 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -18,6 +18,7 @@
18#include <fcntl.h> 18#include <fcntl.h>
19#include <sys/mman.h> 19#include <sys/mman.h>
20#include <unistd.h> 20#include <unistd.h>
21#include "common/scope_exit.h"
21 22
22#endif // ^^^ Linux ^^^ 23#endif // ^^^ Linux ^^^
23 24
@@ -27,7 +28,6 @@
27#include "common/assert.h" 28#include "common/assert.h"
28#include "common/host_memory.h" 29#include "common/host_memory.h"
29#include "common/logging/log.h" 30#include "common/logging/log.h"
30#include "common/scope_exit.h"
31 31
32namespace Common { 32namespace Common {
33 33
@@ -149,7 +149,7 @@ public:
149 } 149 }
150 150
151 void Unmap(size_t virtual_offset, size_t length) { 151 void Unmap(size_t virtual_offset, size_t length) {
152 std::lock_guard lock{placeholder_mutex}; 152 std::scoped_lock lock{placeholder_mutex};
153 153
154 // Unmap until there are no more placeholders 154 // Unmap until there are no more placeholders
155 while (UnmapOnePlaceholder(virtual_offset, length)) { 155 while (UnmapOnePlaceholder(virtual_offset, length)) {
@@ -169,7 +169,7 @@ public:
169 } 169 }
170 const size_t virtual_end = virtual_offset + length; 170 const size_t virtual_end = virtual_offset + length;
171 171
172 std::lock_guard lock{placeholder_mutex}; 172 std::scoped_lock lock{placeholder_mutex};
173 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); 173 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
174 while (it != end) { 174 while (it != end) {
175 const size_t offset = std::max(it->lower(), virtual_offset); 175 const size_t offset = std::max(it->lower(), virtual_offset);
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 3173cc449..eaf5675e3 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h"
7#include "common/parent_of_member.h" 8#include "common/parent_of_member.h"
8#include "common/tree.h" 9#include "common/tree.h"
9 10
@@ -15,32 +16,33 @@ class IntrusiveRedBlackTreeImpl;
15 16
16} 17}
17 18
19#pragma pack(push, 4)
18struct IntrusiveRedBlackTreeNode { 20struct IntrusiveRedBlackTreeNode {
21 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode);
22
19public: 23public:
20 using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; 24 using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
21 25
22 constexpr IntrusiveRedBlackTreeNode() = default; 26private:
27 RBEntry m_entry;
23 28
24 void SetEntry(const EntryType& new_entry) { 29public:
25 entry = new_entry; 30 explicit IntrusiveRedBlackTreeNode() = default;
26 }
27 31
28 [[nodiscard]] EntryType& GetEntry() { 32 [[nodiscard]] constexpr RBEntry& GetRBEntry() {
29 return entry; 33 return m_entry;
30 } 34 }
31 35 [[nodiscard]] constexpr const RBEntry& GetRBEntry() const {
32 [[nodiscard]] const EntryType& GetEntry() const { 36 return m_entry;
33 return entry;
34 } 37 }
35 38
36private: 39 constexpr void SetRBEntry(const RBEntry& entry) {
37 EntryType entry{}; 40 m_entry = entry;
38 41 }
39 friend class impl::IntrusiveRedBlackTreeImpl;
40
41 template <class, class, class>
42 friend class IntrusiveRedBlackTree;
43}; 42};
43static_assert(sizeof(IntrusiveRedBlackTreeNode) ==
44 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
45#pragma pack(pop)
44 46
45template <class T, class Traits, class Comparator> 47template <class T, class Traits, class Comparator>
46class IntrusiveRedBlackTree; 48class IntrusiveRedBlackTree;
@@ -48,12 +50,17 @@ class IntrusiveRedBlackTree;
48namespace impl { 50namespace impl {
49 51
50class IntrusiveRedBlackTreeImpl { 52class IntrusiveRedBlackTreeImpl {
53 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl);
54
51private: 55private:
52 template <class, class, class> 56 template <class, class, class>
53 friend class ::Common::IntrusiveRedBlackTree; 57 friend class ::Common::IntrusiveRedBlackTree;
54 58
55 using RootType = RBHead<IntrusiveRedBlackTreeNode>; 59private:
56 RootType root; 60 using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
61
62private:
63 RootType m_root;
57 64
58public: 65public:
59 template <bool Const> 66 template <bool Const>
@@ -81,149 +88,150 @@ public:
81 IntrusiveRedBlackTreeImpl::reference>; 88 IntrusiveRedBlackTreeImpl::reference>;
82 89
83 private: 90 private:
84 pointer node; 91 pointer m_node;
85 92
86 public: 93 public:
87 explicit Iterator(pointer n) : node(n) {} 94 constexpr explicit Iterator(pointer n) : m_node(n) {}
88 95
89 bool operator==(const Iterator& rhs) const { 96 constexpr bool operator==(const Iterator& rhs) const {
90 return this->node == rhs.node; 97 return m_node == rhs.m_node;
91 } 98 }
92 99
93 bool operator!=(const Iterator& rhs) const { 100 constexpr bool operator!=(const Iterator& rhs) const {
94 return !(*this == rhs); 101 return !(*this == rhs);
95 } 102 }
96 103
97 pointer operator->() const { 104 constexpr pointer operator->() const {
98 return this->node; 105 return m_node;
99 } 106 }
100 107
101 reference operator*() const { 108 constexpr reference operator*() const {
102 return *this->node; 109 return *m_node;
103 } 110 }
104 111
105 Iterator& operator++() { 112 constexpr Iterator& operator++() {
106 this->node = GetNext(this->node); 113 m_node = GetNext(m_node);
107 return *this; 114 return *this;
108 } 115 }
109 116
110 Iterator& operator--() { 117 constexpr Iterator& operator--() {
111 this->node = GetPrev(this->node); 118 m_node = GetPrev(m_node);
112 return *this; 119 return *this;
113 } 120 }
114 121
115 Iterator operator++(int) { 122 constexpr Iterator operator++(int) {
116 const Iterator it{*this}; 123 const Iterator it{*this};
117 ++(*this); 124 ++(*this);
118 return it; 125 return it;
119 } 126 }
120 127
121 Iterator operator--(int) { 128 constexpr Iterator operator--(int) {
122 const Iterator it{*this}; 129 const Iterator it{*this};
123 --(*this); 130 --(*this);
124 return it; 131 return it;
125 } 132 }
126 133
127 operator Iterator<true>() const { 134 constexpr operator Iterator<true>() const {
128 return Iterator<true>(this->node); 135 return Iterator<true>(m_node);
129 } 136 }
130 }; 137 };
131 138
132private: 139private:
133 // Define accessors using RB_* functions. 140 constexpr bool EmptyImpl() const {
134 bool EmptyImpl() const { 141 return m_root.IsEmpty();
135 return root.IsEmpty();
136 } 142 }
137 143
138 IntrusiveRedBlackTreeNode* GetMinImpl() const { 144 constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const {
139 return RB_MIN(const_cast<RootType*>(&root)); 145 return freebsd::RB_MIN(const_cast<RootType&>(m_root));
140 } 146 }
141 147
142 IntrusiveRedBlackTreeNode* GetMaxImpl() const { 148 constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const {
143 return RB_MAX(const_cast<RootType*>(&root)); 149 return freebsd::RB_MAX(const_cast<RootType&>(m_root));
144 } 150 }
145 151
146 IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { 152 constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
147 return RB_REMOVE(&root, node); 153 return freebsd::RB_REMOVE(m_root, node);
148 } 154 }
149 155
150public: 156public:
151 static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { 157 static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
152 return RB_NEXT(node); 158 return freebsd::RB_NEXT(node);
153 } 159 }
154 160
155 static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { 161 static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
156 return RB_PREV(node); 162 return freebsd::RB_PREV(node);
157 } 163 }
158 164
159 static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { 165 static constexpr IntrusiveRedBlackTreeNode const* GetNext(
166 IntrusiveRedBlackTreeNode const* node) {
160 return static_cast<const IntrusiveRedBlackTreeNode*>( 167 return static_cast<const IntrusiveRedBlackTreeNode*>(
161 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); 168 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
162 } 169 }
163 170
164 static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { 171 static constexpr IntrusiveRedBlackTreeNode const* GetPrev(
172 IntrusiveRedBlackTreeNode const* node) {
165 return static_cast<const IntrusiveRedBlackTreeNode*>( 173 return static_cast<const IntrusiveRedBlackTreeNode*>(
166 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); 174 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
167 } 175 }
168 176
169public: 177public:
170 constexpr IntrusiveRedBlackTreeImpl() {} 178 constexpr IntrusiveRedBlackTreeImpl() = default;
171 179
172 // Iterator accessors. 180 // Iterator accessors.
173 iterator begin() { 181 constexpr iterator begin() {
174 return iterator(this->GetMinImpl()); 182 return iterator(this->GetMinImpl());
175 } 183 }
176 184
177 const_iterator begin() const { 185 constexpr const_iterator begin() const {
178 return const_iterator(this->GetMinImpl()); 186 return const_iterator(this->GetMinImpl());
179 } 187 }
180 188
181 iterator end() { 189 constexpr iterator end() {
182 return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); 190 return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
183 } 191 }
184 192
185 const_iterator end() const { 193 constexpr const_iterator end() const {
186 return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); 194 return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
187 } 195 }
188 196
189 const_iterator cbegin() const { 197 constexpr const_iterator cbegin() const {
190 return this->begin(); 198 return this->begin();
191 } 199 }
192 200
193 const_iterator cend() const { 201 constexpr const_iterator cend() const {
194 return this->end(); 202 return this->end();
195 } 203 }
196 204
197 iterator iterator_to(reference ref) { 205 constexpr iterator iterator_to(reference ref) {
198 return iterator(&ref); 206 return iterator(std::addressof(ref));
199 } 207 }
200 208
201 const_iterator iterator_to(const_reference ref) const { 209 constexpr const_iterator iterator_to(const_reference ref) const {
202 return const_iterator(&ref); 210 return const_iterator(std::addressof(ref));
203 } 211 }
204 212
205 // Content management. 213 // Content management.
206 bool empty() const { 214 constexpr bool empty() const {
207 return this->EmptyImpl(); 215 return this->EmptyImpl();
208 } 216 }
209 217
210 reference back() { 218 constexpr reference back() {
211 return *this->GetMaxImpl(); 219 return *this->GetMaxImpl();
212 } 220 }
213 221
214 const_reference back() const { 222 constexpr const_reference back() const {
215 return *this->GetMaxImpl(); 223 return *this->GetMaxImpl();
216 } 224 }
217 225
218 reference front() { 226 constexpr reference front() {
219 return *this->GetMinImpl(); 227 return *this->GetMinImpl();
220 } 228 }
221 229
222 const_reference front() const { 230 constexpr const_reference front() const {
223 return *this->GetMinImpl(); 231 return *this->GetMinImpl();
224 } 232 }
225 233
226 iterator erase(iterator it) { 234 constexpr iterator erase(iterator it) {
227 auto cur = std::addressof(*it); 235 auto cur = std::addressof(*it);
228 auto next = GetNext(cur); 236 auto next = GetNext(cur);
229 this->RemoveImpl(cur); 237 this->RemoveImpl(cur);
@@ -234,16 +242,16 @@ public:
234} // namespace impl 242} // namespace impl
235 243
236template <typename T> 244template <typename T>
237concept HasLightCompareType = requires { 245concept HasRedBlackKeyType = requires {
238 { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; 246 { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
239}; 247};
240 248
241namespace impl { 249namespace impl {
242 250
243 template <typename T, typename Default> 251 template <typename T, typename Default>
244 consteval auto* GetLightCompareType() { 252 consteval auto* GetRedBlackKeyType() {
245 if constexpr (HasLightCompareType<T>) { 253 if constexpr (HasRedBlackKeyType<T>) {
246 return static_cast<typename T::LightCompareType*>(nullptr); 254 return static_cast<typename T::RedBlackKeyType*>(nullptr);
247 } else { 255 } else {
248 return static_cast<Default*>(nullptr); 256 return static_cast<Default*>(nullptr);
249 } 257 }
@@ -252,16 +260,17 @@ namespace impl {
252} // namespace impl 260} // namespace impl
253 261
254template <typename T, typename Default> 262template <typename T, typename Default>
255using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; 263using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>;
256 264
257template <class T, class Traits, class Comparator> 265template <class T, class Traits, class Comparator>
258class IntrusiveRedBlackTree { 266class IntrusiveRedBlackTree {
267 YUZU_NON_COPYABLE(IntrusiveRedBlackTree);
259 268
260public: 269public:
261 using ImplType = impl::IntrusiveRedBlackTreeImpl; 270 using ImplType = impl::IntrusiveRedBlackTreeImpl;
262 271
263private: 272private:
264 ImplType impl{}; 273 ImplType m_impl;
265 274
266public: 275public:
267 template <bool Const> 276 template <bool Const>
@@ -277,9 +286,9 @@ public:
277 using iterator = Iterator<false>; 286 using iterator = Iterator<false>;
278 using const_iterator = Iterator<true>; 287 using const_iterator = Iterator<true>;
279 288
280 using light_value_type = LightCompareType<Comparator, value_type>; 289 using key_type = RedBlackKeyType<Comparator, value_type>;
281 using const_light_pointer = const light_value_type*; 290 using const_key_pointer = const key_type*;
282 using const_light_reference = const light_value_type&; 291 using const_key_reference = const key_type&;
283 292
284 template <bool Const> 293 template <bool Const>
285 class Iterator { 294 class Iterator {
@@ -298,183 +307,201 @@ public:
298 IntrusiveRedBlackTree::reference>; 307 IntrusiveRedBlackTree::reference>;
299 308
300 private: 309 private:
301 ImplIterator iterator; 310 ImplIterator m_impl;
302 311
303 private: 312 private:
304 explicit Iterator(ImplIterator it) : iterator(it) {} 313 constexpr explicit Iterator(ImplIterator it) : m_impl(it) {}
305 314
306 explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, 315 constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {}
307 ImplType::iterator>::type::pointer ptr)
308 : iterator(ptr) {}
309 316
310 ImplIterator GetImplIterator() const { 317 constexpr ImplIterator GetImplIterator() const {
311 return this->iterator; 318 return m_impl;
312 } 319 }
313 320
314 public: 321 public:
315 bool operator==(const Iterator& rhs) const { 322 constexpr bool operator==(const Iterator& rhs) const {
316 return this->iterator == rhs.iterator; 323 return m_impl == rhs.m_impl;
317 } 324 }
318 325
319 bool operator!=(const Iterator& rhs) const { 326 constexpr bool operator!=(const Iterator& rhs) const {
320 return !(*this == rhs); 327 return !(*this == rhs);
321 } 328 }
322 329
323 pointer operator->() const { 330 constexpr pointer operator->() const {
324 return Traits::GetParent(std::addressof(*this->iterator)); 331 return Traits::GetParent(std::addressof(*m_impl));
325 } 332 }
326 333
327 reference operator*() const { 334 constexpr reference operator*() const {
328 return *Traits::GetParent(std::addressof(*this->iterator)); 335 return *Traits::GetParent(std::addressof(*m_impl));
329 } 336 }
330 337
331 Iterator& operator++() { 338 constexpr Iterator& operator++() {
332 ++this->iterator; 339 ++m_impl;
333 return *this; 340 return *this;
334 } 341 }
335 342
336 Iterator& operator--() { 343 constexpr Iterator& operator--() {
337 --this->iterator; 344 --m_impl;
338 return *this; 345 return *this;
339 } 346 }
340 347
341 Iterator operator++(int) { 348 constexpr Iterator operator++(int) {
342 const Iterator it{*this}; 349 const Iterator it{*this};
343 ++this->iterator; 350 ++m_impl;
344 return it; 351 return it;
345 } 352 }
346 353
347 Iterator operator--(int) { 354 constexpr Iterator operator--(int) {
348 const Iterator it{*this}; 355 const Iterator it{*this};
349 --this->iterator; 356 --m_impl;
350 return it; 357 return it;
351 } 358 }
352 359
353 operator Iterator<true>() const { 360 constexpr operator Iterator<true>() const {
354 return Iterator<true>(this->iterator); 361 return Iterator<true>(m_impl);
355 } 362 }
356 }; 363 };
357 364
358private: 365private:
359 static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, 366 static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
360 const IntrusiveRedBlackTreeNode* rhs) { 367 const IntrusiveRedBlackTreeNode* rhs) {
361 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); 368 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
362 } 369 }
363 370
364 static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { 371 static constexpr int CompareKeyImpl(const_key_reference key,
365 return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); 372 const IntrusiveRedBlackTreeNode* rhs) {
373 return Comparator::Compare(key, *Traits::GetParent(rhs));
366 } 374 }
367 375
368 // Define accessors using RB_* functions. 376 // Define accessors using RB_* functions.
369 IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { 377 constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
370 return RB_INSERT(&impl.root, node, CompareImpl); 378 return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl);
371 } 379 }
372 380
373 IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { 381 constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const {
374 return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), 382 return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root),
375 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); 383 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
376 } 384 }
377 385
378 IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { 386 constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const {
379 return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), 387 return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root),
380 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); 388 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
381 } 389 }
382 390
383 IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { 391 constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const {
384 return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), 392 return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
385 static_cast<const void*>(lelm), LightCompareImpl); 393 CompareKeyImpl);
386 } 394 }
387 395
388 IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { 396 constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const {
389 return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), 397 return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
390 static_cast<const void*>(lelm), LightCompareImpl); 398 CompareKeyImpl);
399 }
400
401 constexpr IntrusiveRedBlackTreeNode* FindExistingImpl(
402 IntrusiveRedBlackTreeNode const* node) const {
403 return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root),
404 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
405 }
406
407 constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const {
408 return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
409 CompareKeyImpl);
391 } 410 }
392 411
393public: 412public:
394 constexpr IntrusiveRedBlackTree() = default; 413 constexpr IntrusiveRedBlackTree() = default;
395 414
396 // Iterator accessors. 415 // Iterator accessors.
397 iterator begin() { 416 constexpr iterator begin() {
398 return iterator(this->impl.begin()); 417 return iterator(m_impl.begin());
399 } 418 }
400 419
401 const_iterator begin() const { 420 constexpr const_iterator begin() const {
402 return const_iterator(this->impl.begin()); 421 return const_iterator(m_impl.begin());
403 } 422 }
404 423
405 iterator end() { 424 constexpr iterator end() {
406 return iterator(this->impl.end()); 425 return iterator(m_impl.end());
407 } 426 }
408 427
409 const_iterator end() const { 428 constexpr const_iterator end() const {
410 return const_iterator(this->impl.end()); 429 return const_iterator(m_impl.end());
411 } 430 }
412 431
413 const_iterator cbegin() const { 432 constexpr const_iterator cbegin() const {
414 return this->begin(); 433 return this->begin();
415 } 434 }
416 435
417 const_iterator cend() const { 436 constexpr const_iterator cend() const {
418 return this->end(); 437 return this->end();
419 } 438 }
420 439
421 iterator iterator_to(reference ref) { 440 constexpr iterator iterator_to(reference ref) {
422 return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); 441 return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
423 } 442 }
424 443
425 const_iterator iterator_to(const_reference ref) const { 444 constexpr const_iterator iterator_to(const_reference ref) const {
426 return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); 445 return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
427 } 446 }
428 447
429 // Content management. 448 // Content management.
430 bool empty() const { 449 constexpr bool empty() const {
431 return this->impl.empty(); 450 return m_impl.empty();
432 } 451 }
433 452
434 reference back() { 453 constexpr reference back() {
435 return *Traits::GetParent(std::addressof(this->impl.back())); 454 return *Traits::GetParent(std::addressof(m_impl.back()));
436 } 455 }
437 456
438 const_reference back() const { 457 constexpr const_reference back() const {
439 return *Traits::GetParent(std::addressof(this->impl.back())); 458 return *Traits::GetParent(std::addressof(m_impl.back()));
440 } 459 }
441 460
442 reference front() { 461 constexpr reference front() {
443 return *Traits::GetParent(std::addressof(this->impl.front())); 462 return *Traits::GetParent(std::addressof(m_impl.front()));
444 } 463 }
445 464
446 const_reference front() const { 465 constexpr const_reference front() const {
447 return *Traits::GetParent(std::addressof(this->impl.front())); 466 return *Traits::GetParent(std::addressof(m_impl.front()));
448 } 467 }
449 468
450 iterator erase(iterator it) { 469 constexpr iterator erase(iterator it) {
451 return iterator(this->impl.erase(it.GetImplIterator())); 470 return iterator(m_impl.erase(it.GetImplIterator()));
452 } 471 }
453 472
454 iterator insert(reference ref) { 473 constexpr iterator insert(reference ref) {
455 ImplType::pointer node = Traits::GetNode(std::addressof(ref)); 474 ImplType::pointer node = Traits::GetNode(std::addressof(ref));
456 this->InsertImpl(node); 475 this->InsertImpl(node);
457 return iterator(node); 476 return iterator(node);
458 } 477 }
459 478
460 iterator find(const_reference ref) const { 479 constexpr iterator find(const_reference ref) const {
461 return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); 480 return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
462 } 481 }
463 482
464 iterator nfind(const_reference ref) const { 483 constexpr iterator nfind(const_reference ref) const {
465 return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); 484 return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
466 } 485 }
467 486
468 iterator find_light(const_light_reference ref) const { 487 constexpr iterator find_key(const_key_reference ref) const {
469 return iterator(this->FindLightImpl(std::addressof(ref))); 488 return iterator(this->FindKeyImpl(ref));
489 }
490
491 constexpr iterator nfind_key(const_key_reference ref) const {
492 return iterator(this->NFindKeyImpl(ref));
493 }
494
495 constexpr iterator find_existing(const_reference ref) const {
496 return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref))));
470 } 497 }
471 498
472 iterator nfind_light(const_light_reference ref) const { 499 constexpr iterator find_existing_key(const_key_reference ref) const {
473 return iterator(this->NFindLightImpl(std::addressof(ref))); 500 return iterator(this->FindExistingKeyImpl(ref));
474 } 501 }
475}; 502};
476 503
477template <auto T, class Derived = impl::GetParentType<T>> 504template <auto T, class Derived = Common::impl::GetParentType<T>>
478class IntrusiveRedBlackTreeMemberTraits; 505class IntrusiveRedBlackTreeMemberTraits;
479 506
480template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 507template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -498,19 +525,16 @@ private:
498 return std::addressof(parent->*Member); 525 return std::addressof(parent->*Member);
499 } 526 }
500 527
501 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 528 static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
502 return GetParentPointer<Member, Derived>(node); 529 return Common::GetParentPointer<Member, Derived>(node);
503 } 530 }
504 531
505 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 532 static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
506 return GetParentPointer<Member, Derived>(node); 533 return Common::GetParentPointer<Member, Derived>(node);
507 } 534 }
508
509private:
510 static constexpr TypedStorage<Derived> DerivedStorage = {};
511}; 535};
512 536
513template <auto T, class Derived = impl::GetParentType<T>> 537template <auto T, class Derived = Common::impl::GetParentType<T>>
514class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; 538class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
515 539
516template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 540template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -521,11 +545,6 @@ public:
521 IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; 545 IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
522 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; 546 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
523 547
524 static constexpr bool IsValid() {
525 TypedStorage<Derived> DerivedStorage = {};
526 return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
527 }
528
529private: 548private:
530 template <class, class, class> 549 template <class, class, class>
531 friend class IntrusiveRedBlackTree; 550 friend class IntrusiveRedBlackTree;
@@ -540,30 +559,36 @@ private:
540 return std::addressof(parent->*Member); 559 return std::addressof(parent->*Member);
541 } 560 }
542 561
543 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 562 static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
544 return GetParentPointer<Member, Derived>(node); 563 return Common::GetParentPointer<Member, Derived>(node);
545 } 564 }
546 565
547 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 566 static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
548 return GetParentPointer<Member, Derived>(node); 567 return Common::GetParentPointer<Member, Derived>(node);
549 } 568 }
550}; 569};
551 570
552template <class Derived> 571template <class Derived>
553class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { 572class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
554public: 573public:
574 using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode;
575
555 constexpr Derived* GetPrev() { 576 constexpr Derived* GetPrev() {
556 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); 577 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
578 impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
557 } 579 }
558 constexpr const Derived* GetPrev() const { 580 constexpr const Derived* GetPrev() const {
559 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); 581 return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
582 impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
560 } 583 }
561 584
562 constexpr Derived* GetNext() { 585 constexpr Derived* GetNext() {
563 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); 586 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
587 impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
564 } 588 }
565 constexpr const Derived* GetNext() const { 589 constexpr const Derived* GetNext() const {
566 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); 590 return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
591 impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
567 } 592 }
568}; 593};
569 594
@@ -581,19 +606,22 @@ private:
581 friend class impl::IntrusiveRedBlackTreeImpl; 606 friend class impl::IntrusiveRedBlackTreeImpl;
582 607
583 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { 608 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
584 return static_cast<IntrusiveRedBlackTreeNode*>(parent); 609 return static_cast<IntrusiveRedBlackTreeNode*>(
610 static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
585 } 611 }
586 612
587 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { 613 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
588 return static_cast<const IntrusiveRedBlackTreeNode*>(parent); 614 return static_cast<const IntrusiveRedBlackTreeNode*>(
615 static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
589 } 616 }
590 617
591 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 618 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
592 return static_cast<Derived*>(node); 619 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
593 } 620 }
594 621
595 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 622 static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
596 return static_cast<const Derived*>(node); 623 return static_cast<const Derived*>(
624 static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
597 } 625 }
598}; 626};
599 627
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index f1c9ed6c4..b3793106d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -5,10 +5,8 @@
5#include <atomic> 5#include <atomic>
6#include <chrono> 6#include <chrono>
7#include <climits> 7#include <climits>
8#include <exception>
9#include <stop_token> 8#include <stop_token>
10#include <thread> 9#include <thread>
11#include <vector>
12 10
13#include <fmt/format.h> 11#include <fmt/format.h>
14 12
@@ -276,9 +274,9 @@ private:
276 ColorConsoleBackend color_console_backend{}; 274 ColorConsoleBackend color_console_backend{};
277 FileBackend file_backend; 275 FileBackend file_backend;
278 276
279 std::jthread backend_thread;
280 MPSCQueue<Entry, true> message_queue{}; 277 MPSCQueue<Entry, true> message_queue{};
281 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; 278 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
279 std::jthread backend_thread;
282}; 280};
283} // namespace 281} // namespace
284 282
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index bf785f402..a0e80fe3c 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <filesystem>
8#include "common/logging/filter.h" 7#include "common/logging/filter.h"
9 8
10namespace Common::Log { 9namespace Common::Log {
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4afc1369a..4acbff649 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
101 SUB(Service, GRC) \ 101 SUB(Service, GRC) \
102 SUB(Service, HID) \ 102 SUB(Service, HID) \
103 SUB(Service, IRS) \ 103 SUB(Service, IRS) \
104 SUB(Service, JIT) \
104 SUB(Service, LBL) \ 105 SUB(Service, LBL) \
105 SUB(Service, LDN) \ 106 SUB(Service, LDN) \
106 SUB(Service, LDR) \ 107 SUB(Service, LDR) \
@@ -119,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
119 SUB(Service, NPNS) \ 120 SUB(Service, NPNS) \
120 SUB(Service, NS) \ 121 SUB(Service, NS) \
121 SUB(Service, NVDRV) \ 122 SUB(Service, NVDRV) \
123 SUB(Service, NVFlinger) \
122 SUB(Service, OLSC) \ 124 SUB(Service, OLSC) \
123 SUB(Service, PCIE) \ 125 SUB(Service, PCIE) \
124 SUB(Service, PCTL) \ 126 SUB(Service, PCTL) \
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index 1a3074e04..29419f051 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <chrono> 8#include <chrono>
9#include <cstddef> 9#include <cstddef>
10#include <string_view>
11#include "common/logging/log.h" 10#include "common/logging/log.h"
12 11
13namespace Common::Log { 12namespace Common::Log {
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 10b2281db..b2cad58d8 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -10,12 +10,10 @@
10#endif 10#endif
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_funcs.h"
14#include "common/logging/filter.h" 13#include "common/logging/filter.h"
15#include "common/logging/log.h" 14#include "common/logging/log.h"
16#include "common/logging/log_entry.h" 15#include "common/logging/log_entry.h"
17#include "common/logging/text_formatter.h" 16#include "common/logging/text_formatter.h"
18#include "common/string_util.h"
19 17
20namespace Common::Log { 18namespace Common::Log {
21 19
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index 171e74cfe..92c0bf0c5 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8#include <string> 7#include <string>
9 8
10namespace Common::Log { 9namespace Common::Log {
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 2b6e4daa7..99c15fa96 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -69,6 +69,7 @@ enum class Class : u8 {
69 Service_GRC, ///< The game recording service 69 Service_GRC, ///< The game recording service
70 Service_HID, ///< The HID (Human interface device) service 70 Service_HID, ///< The HID (Human interface device) service
71 Service_IRS, ///< The IRS service 71 Service_IRS, ///< The IRS service
72 Service_JIT, ///< The JIT service
72 Service_LBL, ///< The LBL (LCD backlight) service 73 Service_LBL, ///< The LBL (LCD backlight) service
73 Service_LDN, ///< The LDN (Local domain network) service 74 Service_LDN, ///< The LDN (Local domain network) service
74 Service_LDR, ///< The loader service 75 Service_LDR, ///< The loader service
@@ -87,6 +88,7 @@ enum class Class : u8 {
87 Service_NPNS, ///< The NPNS service 88 Service_NPNS, ///< The NPNS service
88 Service_NS, ///< The NS services 89 Service_NS, ///< The NS services
89 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 90 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
91 Service_NVFlinger, ///< The NVFlinger service
90 Service_OLSC, ///< The OLSC service 92 Service_OLSC, ///< The OLSC service
91 Service_PCIE, ///< The PCIe service 93 Service_PCIE, ///< The PCIe service
92 Service_PCTL, ///< The PCTL (Parental control) service 94 Service_PCTL, ///< The PCTL (Parental control) service
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 510c4e56d..54485bf53 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <cstdlib> 8#include <cstdlib>
8#include <type_traits> 9#include <type_traits>
9 10
@@ -20,10 +21,32 @@ struct Rectangle {
20 21
21 constexpr Rectangle() = default; 22 constexpr Rectangle() = default;
22 23
24 constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
25
23 constexpr Rectangle(T left_, T top_, T right_, T bottom_) 26 constexpr Rectangle(T left_, T top_, T right_, T bottom_)
24 : left(left_), top(top_), right(right_), bottom(bottom_) {} 27 : left(left_), top(top_), right(right_), bottom(bottom_) {}
25 28
26 [[nodiscard]] T GetWidth() const { 29 [[nodiscard]] constexpr T Left() const {
30 return left;
31 }
32
33 [[nodiscard]] constexpr T Top() const {
34 return top;
35 }
36
37 [[nodiscard]] constexpr T Right() const {
38 return right;
39 }
40
41 [[nodiscard]] constexpr T Bottom() const {
42 return bottom;
43 }
44
45 [[nodiscard]] constexpr bool IsEmpty() const {
46 return (GetWidth() <= 0) || (GetHeight() <= 0);
47 }
48
49 [[nodiscard]] constexpr T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 50 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 51 return std::abs(right - left);
29 } else { 52 } else {
@@ -31,7 +54,7 @@ struct Rectangle {
31 } 54 }
32 } 55 }
33 56
34 [[nodiscard]] T GetHeight() const { 57 [[nodiscard]] constexpr T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 58 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 59 return std::abs(bottom - top);
37 } else { 60 } else {
@@ -39,18 +62,35 @@ struct Rectangle {
39 } 62 }
40 } 63 }
41 64
42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const { 65 [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 66 return Rectangle{left + x, top, right + x, bottom};
44 } 67 }
45 68
46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const { 69 [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 70 return Rectangle{left, top + y, right, bottom + y};
48 } 71 }
49 72
50 [[nodiscard]] Rectangle<T> Scale(const float s) const { 73 [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), 74 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
52 static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; 75 static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
53 } 76 }
77
78 [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
79 return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
80 (bottom == rhs.bottom);
81 }
82
83 [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
84 return !operator==(rhs);
85 }
86
87 [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
88 result->left = std::max(left, with.left);
89 result->top = std::max(top, with.top);
90 result->right = std::min(right, with.right);
91 result->bottom = std::min(bottom, with.bottom);
92 return !result->IsEmpty();
93 }
54}; 94};
55 95
56template <typename T> 96template <typename T>
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
index 8cff6ec37..7a75a5ff4 100644
--- a/src/common/memory_detect.cpp
+++ b/src/common/memory_detect.cpp
@@ -70,4 +70,4 @@ const MemoryInfo& GetMemInfo() {
70 return mem_info; 70 return mem_info;
71} 71}
72 72
73} // namespace Common \ No newline at end of file 73} // namespace Common
diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp
index d1afd1f1d..c732c233e 100644
--- a/src/common/nvidia_flags.cpp
+++ b/src/common/nvidia_flags.cpp
@@ -6,7 +6,6 @@
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8 8
9#include "common/fs/file.h"
10#include "common/fs/fs.h" 9#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 10#include "common/fs/path_util.h"
12#include "common/nvidia_flags.h" 11#include "common/nvidia_flags.h"
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 82d91e9f3..13f4b44c0 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <tuple>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "common/virtual_buffer.h" 10#include "common/virtual_buffer.h"
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h
index 58c70b0e7..ad075615b 100644
--- a/src/common/parent_of_member.h
+++ b/src/common/parent_of_member.h
@@ -7,7 +7,6 @@
7#include <type_traits> 7#include <type_traits>
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_types.h"
11 10
12namespace Common { 11namespace Common {
13namespace detail { 12namespace detail {
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 4a8d09806..db6aa6b95 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -12,7 +12,6 @@
12#include <new> 12#include <new>
13#include <type_traits> 13#include <type_traits>
14#include <vector> 14#include <vector>
15#include "common/common_types.h"
16 15
17namespace Common { 16namespace Common {
18 17
diff --git a/src/common/settings.h b/src/common/settings.h
index a37d83fb3..3b7be63b3 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
38 Auto = 0, 38 Auto = 0,
39 Accurate = 1, 39 Accurate = 1,
40 Unsafe = 2, 40 Unsafe = 2,
41 Paranoid = 3,
41}; 42};
42 43
43enum class FullscreenMode : u32 { 44enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
470 471
471 // Cpu 472 // Cpu
472 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 473 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
473 CPUAccuracy::Unsafe, "cpu_accuracy"}; 474 CPUAccuracy::Paranoid, "cpu_accuracy"};
474 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 475 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
475 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 476 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
476 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 477 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -589,6 +590,9 @@ struct Values {
589 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 590 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
590 std::vector<TouchFromButtonMap> touch_from_button_maps; 591 std::vector<TouchFromButtonMap> touch_from_button_maps;
591 592
593 BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"};
594 RingconRaw ringcon_analogs;
595
592 // Data Storage 596 // Data Storage
593 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 597 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
594 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 598 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 4ff37e186..6f42346bc 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -357,6 +357,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
360using RingconRaw = std::string;
360 361
361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 362constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 363constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 662171138..3695dae4d 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -5,11 +5,9 @@
5#include <algorithm> 5#include <algorithm>
6#include <cctype> 6#include <cctype>
7#include <codecvt> 7#include <codecvt>
8#include <cstdlib>
9#include <locale> 8#include <locale>
10#include <sstream> 9#include <sstream>
11 10
12#include "common/logging/log.h"
13#include "common/string_util.h" 11#include "common/string_util.h"
14 12
15#ifdef _WIN32 13#ifdef _WIN32
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 6241d08b3..67261c55b 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include "common/assert.h"
8#include "common/scm_rev.h" 7#include "common/scm_rev.h"
9#include "common/telemetry.h" 8#include "common/telemetry.h"
10 9
@@ -55,22 +54,50 @@ void AppendBuildInfo(FieldCollection& fc) {
55 54
56void AppendCPUInfo(FieldCollection& fc) { 55void AppendCPUInfo(FieldCollection& fc) {
57#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
58 fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); 57
59 fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); 58 const auto& caps = Common::GetCPUCaps();
60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); 59 const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); 60 fc.AddField(FieldType::UserSystem, field_name, field_value);
62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); 61 };
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); 62 add_field("CPU_Model", caps.cpu_string);
64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); 63 add_field("CPU_BrandString", caps.brand_string);
65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); 64
66 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); 65 add_field("CPU_Extension_x64_SSE", caps.sse);
67 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); 66 add_field("CPU_Extension_x64_SSE2", caps.sse2);
68 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); 67 add_field("CPU_Extension_x64_SSE3", caps.sse3);
69 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); 68 add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
70 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); 69 add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
71 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); 70 add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
72 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); 71
73 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); 72 add_field("CPU_Extension_x64_AVX", caps.avx);
73 add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
74 add_field("CPU_Extension_x64_AVX2", caps.avx2);
75
76 // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
77 add_field("CPU_Extension_x64_AVX512",
78 caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
79
80 add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
81 add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
82 add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
83 add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
84 add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
85 add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
86 add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
87
88 add_field("CPU_Extension_x64_AES", caps.aes);
89 add_field("CPU_Extension_x64_BMI1", caps.bmi1);
90 add_field("CPU_Extension_x64_BMI2", caps.bmi2);
91 add_field("CPU_Extension_x64_F16C", caps.f16c);
92 add_field("CPU_Extension_x64_FMA", caps.fma);
93 add_field("CPU_Extension_x64_FMA4", caps.fma4);
94 add_field("CPU_Extension_x64_GFNI", caps.gfni);
95 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
96 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
97 add_field("CPU_Extension_x64_MOVBE", caps.movbe);
98 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
99 add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
100 add_field("CPU_Extension_x64_SHA", caps.sha);
74#else 101#else
75 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); 102 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
76#endif 103#endif
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 4d632f7eb..f9a824a7d 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -55,8 +55,8 @@ class Field : public FieldInterface {
55public: 55public:
56 YUZU_NON_COPYABLE(Field); 56 YUZU_NON_COPYABLE(Field);
57 57
58 Field(FieldType type_, std::string name_, T value_) 58 Field(FieldType type_, std::string_view name_, T value_)
59 : name(std::move(name_)), type(type_), value(std::move(value_)) {} 59 : name(name_), type(type_), value(std::move(value_)) {}
60 60
61 ~Field() override = default; 61 ~Field() override = default;
62 62
@@ -123,7 +123,7 @@ public:
123 * @param value Value for the field to add. 123 * @param value Value for the field to add.
124 */ 124 */
125 template <typename T> 125 template <typename T>
126 void AddField(FieldType type, const char* name, T value) { 126 void AddField(FieldType type, std::string_view name, T value) {
127 return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); 127 return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
128 } 128 }
129 129
diff --git a/src/common/thread.h b/src/common/thread.h
index a8c17c71a..626609372 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -17,7 +17,7 @@ namespace Common {
17class Event { 17class Event {
18public: 18public:
19 void Set() { 19 void Set() {
20 std::lock_guard lk{mutex}; 20 std::scoped_lock lk{mutex};
21 if (!is_set) { 21 if (!is_set) {
22 is_set = true; 22 is_set = true;
23 condvar.notify_one(); 23 condvar.notify_one();
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 2c8c2b90e..7272ac6e8 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -52,7 +52,7 @@ public:
52 // line before cv.wait 52 // line before cv.wait
53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. 53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. 54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
55 std::lock_guard lock{cv_mutex}; 55 std::scoped_lock lock{cv_mutex};
56 cv.notify_one(); 56 cv.notify_one();
57 } 57 }
58 58
@@ -159,7 +159,7 @@ public:
159 159
160 template <typename Arg> 160 template <typename Arg>
161 void Push(Arg&& t) { 161 void Push(Arg&& t) {
162 std::lock_guard lock{write_lock}; 162 std::scoped_lock lock{write_lock};
163 spsc_queue.Push(t); 163 spsc_queue.Push(t);
164 } 164 }
165 165
diff --git a/src/common/tree.h b/src/common/tree.h
index 18faa4a48..28370e343 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -43,294 +43,265 @@
43 * The maximum height of a red-black tree is 2lg (n+1). 43 * The maximum height of a red-black tree is 2lg (n+1).
44 */ 44 */
45 45
46#include "common/assert.h" 46namespace Common::freebsd {
47 47
48namespace Common { 48enum class RBColor {
49 RB_BLACK = 0,
50 RB_RED = 1,
51};
52
53#pragma pack(push, 4)
49template <typename T> 54template <typename T>
50class RBHead { 55class RBEntry {
51public: 56public:
52 [[nodiscard]] T* Root() { 57 constexpr RBEntry() = default;
53 return rbh_root;
54 }
55 58
56 [[nodiscard]] const T* Root() const { 59 [[nodiscard]] constexpr T* Left() {
57 return rbh_root; 60 return m_rbe_left;
58 } 61 }
59 62 [[nodiscard]] constexpr const T* Left() const {
60 void SetRoot(T* root) { 63 return m_rbe_left;
61 rbh_root = root;
62 } 64 }
63 65
64 [[nodiscard]] bool IsEmpty() const { 66 constexpr void SetLeft(T* e) {
65 return Root() == nullptr; 67 m_rbe_left = e;
66 } 68 }
67 69
68private: 70 [[nodiscard]] constexpr T* Right() {
69 T* rbh_root = nullptr; 71 return m_rbe_right;
70};
71
72enum class EntryColor {
73 Black,
74 Red,
75};
76
77template <typename T>
78class RBEntry {
79public:
80 [[nodiscard]] T* Left() {
81 return rbe_left;
82 } 72 }
83 73 [[nodiscard]] constexpr const T* Right() const {
84 [[nodiscard]] const T* Left() const { 74 return m_rbe_right;
85 return rbe_left;
86 } 75 }
87 76
88 void SetLeft(T* left) { 77 constexpr void SetRight(T* e) {
89 rbe_left = left; 78 m_rbe_right = e;
90 } 79 }
91 80
92 [[nodiscard]] T* Right() { 81 [[nodiscard]] constexpr T* Parent() {
93 return rbe_right; 82 return m_rbe_parent;
94 } 83 }
95 84 [[nodiscard]] constexpr const T* Parent() const {
96 [[nodiscard]] const T* Right() const { 85 return m_rbe_parent;
97 return rbe_right;
98 } 86 }
99 87
100 void SetRight(T* right) { 88 constexpr void SetParent(T* e) {
101 rbe_right = right; 89 m_rbe_parent = e;
102 } 90 }
103 91
104 [[nodiscard]] T* Parent() { 92 [[nodiscard]] constexpr bool IsBlack() const {
105 return rbe_parent; 93 return m_rbe_color == RBColor::RB_BLACK;
106 } 94 }
107 95 [[nodiscard]] constexpr bool IsRed() const {
108 [[nodiscard]] const T* Parent() const { 96 return m_rbe_color == RBColor::RB_RED;
109 return rbe_parent;
110 } 97 }
111 98 [[nodiscard]] constexpr RBColor Color() const {
112 void SetParent(T* parent) { 99 return m_rbe_color;
113 rbe_parent = parent;
114 } 100 }
115 101
116 [[nodiscard]] bool IsBlack() const { 102 constexpr void SetColor(RBColor c) {
117 return rbe_color == EntryColor::Black; 103 m_rbe_color = c;
118 } 104 }
119 105
120 [[nodiscard]] bool IsRed() const { 106private:
121 return rbe_color == EntryColor::Red; 107 T* m_rbe_left{};
122 } 108 T* m_rbe_right{};
109 T* m_rbe_parent{};
110 RBColor m_rbe_color{RBColor::RB_BLACK};
111};
112#pragma pack(pop)
123 113
124 [[nodiscard]] EntryColor Color() const { 114template <typename T>
125 return rbe_color; 115struct CheckRBEntry {
126 } 116 static constexpr bool value = false;
117};
118template <typename T>
119struct CheckRBEntry<RBEntry<T>> {
120 static constexpr bool value = true;
121};
127 122
128 void SetColor(EntryColor color) { 123template <typename T>
129 rbe_color = color; 124concept IsRBEntry = CheckRBEntry<T>::value;
130 }
131 125
126template <typename T>
127concept HasRBEntry = requires(T& t, const T& ct) {
128 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
129 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
130};
131
132template <typename T>
133requires HasRBEntry<T>
134class RBHead {
132private: 135private:
133 T* rbe_left = nullptr; 136 T* m_rbh_root = nullptr;
134 T* rbe_right = nullptr; 137
135 T* rbe_parent = nullptr; 138public:
136 EntryColor rbe_color{}; 139 [[nodiscard]] constexpr T* Root() {
140 return m_rbh_root;
141 }
142 [[nodiscard]] constexpr const T* Root() const {
143 return m_rbh_root;
144 }
145 constexpr void SetRoot(T* root) {
146 m_rbh_root = root;
147 }
148
149 [[nodiscard]] constexpr bool IsEmpty() const {
150 return this->Root() == nullptr;
151 }
137}; 152};
138 153
139template <typename Node> 154template <typename T>
140[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { 155requires HasRBEntry<T>
141 return node->GetEntry(); 156[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
157 return t->GetRBEntry();
142} 158}
143 159template <typename T>
144template <typename Node> 160requires HasRBEntry<T>
145[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { 161[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
146 return node->GetEntry(); 162 return t->GetRBEntry();
147} 163}
148 164
149template <typename Node> 165template <typename T>
150[[nodiscard]] Node* RB_PARENT(Node* node) { 166requires HasRBEntry<T>
151 return RB_ENTRY(node).Parent(); 167[[nodiscard]] constexpr T* RB_LEFT(T* t) {
168 return RB_ENTRY(t).Left();
152} 169}
153 170template <typename T>
154template <typename Node> 171requires HasRBEntry<T>
155[[nodiscard]] const Node* RB_PARENT(const Node* node) { 172[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
156 return RB_ENTRY(node).Parent(); 173 return RB_ENTRY(t).Left();
157} 174}
158 175
159template <typename Node> 176template <typename T>
160void RB_SET_PARENT(Node* node, Node* parent) { 177requires HasRBEntry<T>
161 return RB_ENTRY(node).SetParent(parent); 178[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
179 return RB_ENTRY(t).Right();
162} 180}
163 181template <typename T>
164template <typename Node> 182requires HasRBEntry<T>
165[[nodiscard]] Node* RB_LEFT(Node* node) { 183[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
166 return RB_ENTRY(node).Left(); 184 return RB_ENTRY(t).Right();
167} 185}
168 186
169template <typename Node> 187template <typename T>
170[[nodiscard]] const Node* RB_LEFT(const Node* node) { 188requires HasRBEntry<T>
171 return RB_ENTRY(node).Left(); 189[[nodiscard]] constexpr T* RB_PARENT(T* t) {
190 return RB_ENTRY(t).Parent();
172} 191}
173 192template <typename T>
174template <typename Node> 193requires HasRBEntry<T>
175void RB_SET_LEFT(Node* node, Node* left) { 194[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
176 return RB_ENTRY(node).SetLeft(left); 195 return RB_ENTRY(t).Parent();
177} 196}
178 197
179template <typename Node> 198template <typename T>
180[[nodiscard]] Node* RB_RIGHT(Node* node) { 199requires HasRBEntry<T>
181 return RB_ENTRY(node).Right(); 200constexpr void RB_SET_LEFT(T* t, T* e) {
201 RB_ENTRY(t).SetLeft(e);
182} 202}
183 203template <typename T>
184template <typename Node> 204requires HasRBEntry<T>
185[[nodiscard]] const Node* RB_RIGHT(const Node* node) { 205constexpr void RB_SET_RIGHT(T* t, T* e) {
186 return RB_ENTRY(node).Right(); 206 RB_ENTRY(t).SetRight(e);
187} 207}
188 208template <typename T>
189template <typename Node> 209requires HasRBEntry<T>
190void RB_SET_RIGHT(Node* node, Node* right) { 210constexpr void RB_SET_PARENT(T* t, T* e) {
191 return RB_ENTRY(node).SetRight(right); 211 RB_ENTRY(t).SetParent(e);
192} 212}
193 213
194template <typename Node> 214template <typename T>
195[[nodiscard]] bool RB_IS_BLACK(const Node* node) { 215requires HasRBEntry<T>
196 return RB_ENTRY(node).IsBlack(); 216[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
217 return RB_ENTRY(t).IsBlack();
197} 218}
198 219template <typename T>
199template <typename Node> 220requires HasRBEntry<T>
200[[nodiscard]] bool RB_IS_RED(const Node* node) { 221[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
201 return RB_ENTRY(node).IsRed(); 222 return RB_ENTRY(t).IsRed();
202} 223}
203 224
204template <typename Node> 225template <typename T>
205[[nodiscard]] EntryColor RB_COLOR(const Node* node) { 226requires HasRBEntry<T>
206 return RB_ENTRY(node).Color(); 227[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
228 return RB_ENTRY(t).Color();
207} 229}
208 230
209template <typename Node> 231template <typename T>
210void RB_SET_COLOR(Node* node, EntryColor color) { 232requires HasRBEntry<T>
211 return RB_ENTRY(node).SetColor(color); 233constexpr void RB_SET_COLOR(T* t, RBColor c) {
234 RB_ENTRY(t).SetColor(c);
212} 235}
213 236
214template <typename Node> 237template <typename T>
215void RB_SET(Node* node, Node* parent) { 238requires HasRBEntry<T>
216 auto& entry = RB_ENTRY(node); 239constexpr void RB_SET(T* elm, T* parent) {
217 entry.SetParent(parent); 240 auto& rb_entry = RB_ENTRY(elm);
218 entry.SetLeft(nullptr); 241 rb_entry.SetParent(parent);
219 entry.SetRight(nullptr); 242 rb_entry.SetLeft(nullptr);
220 entry.SetColor(EntryColor::Red); 243 rb_entry.SetRight(nullptr);
244 rb_entry.SetColor(RBColor::RB_RED);
221} 245}
222 246
223template <typename Node> 247template <typename T>
224void RB_SET_BLACKRED(Node* black, Node* red) { 248requires HasRBEntry<T>
225 RB_SET_COLOR(black, EntryColor::Black); 249constexpr void RB_SET_BLACKRED(T* black, T* red) {
226 RB_SET_COLOR(red, EntryColor::Red); 250 RB_SET_COLOR(black, RBColor::RB_BLACK);
251 RB_SET_COLOR(red, RBColor::RB_RED);
227} 252}
228 253
229template <typename Node> 254template <typename T>
230void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { 255requires HasRBEntry<T>
256constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
231 tmp = RB_RIGHT(elm); 257 tmp = RB_RIGHT(elm);
232 RB_SET_RIGHT(elm, RB_LEFT(tmp)); 258 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
233 if (RB_RIGHT(elm) != nullptr) {
234 RB_SET_PARENT(RB_LEFT(tmp), elm); 259 RB_SET_PARENT(RB_LEFT(tmp), elm);
235 } 260 }
236 261
237 RB_SET_PARENT(tmp, RB_PARENT(elm)); 262 if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
238 if (RB_PARENT(tmp) != nullptr) {
239 if (elm == RB_LEFT(RB_PARENT(elm))) { 263 if (elm == RB_LEFT(RB_PARENT(elm))) {
240 RB_SET_LEFT(RB_PARENT(elm), tmp); 264 RB_SET_LEFT(RB_PARENT(elm), tmp);
241 } else { 265 } else {
242 RB_SET_RIGHT(RB_PARENT(elm), tmp); 266 RB_SET_RIGHT(RB_PARENT(elm), tmp);
243 } 267 }
244 } else { 268 } else {
245 head->SetRoot(tmp); 269 head.SetRoot(tmp);
246 } 270 }
247 271
248 RB_SET_LEFT(tmp, elm); 272 RB_SET_LEFT(tmp, elm);
249 RB_SET_PARENT(elm, tmp); 273 RB_SET_PARENT(elm, tmp);
250} 274}
251 275
252template <typename Node> 276template <typename T>
253void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { 277requires HasRBEntry<T>
278constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
254 tmp = RB_LEFT(elm); 279 tmp = RB_LEFT(elm);
255 RB_SET_LEFT(elm, RB_RIGHT(tmp)); 280 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
256 if (RB_LEFT(elm) != nullptr) {
257 RB_SET_PARENT(RB_RIGHT(tmp), elm); 281 RB_SET_PARENT(RB_RIGHT(tmp), elm);
258 } 282 }
259 283
260 RB_SET_PARENT(tmp, RB_PARENT(elm)); 284 if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
261 if (RB_PARENT(tmp) != nullptr) {
262 if (elm == RB_LEFT(RB_PARENT(elm))) { 285 if (elm == RB_LEFT(RB_PARENT(elm))) {
263 RB_SET_LEFT(RB_PARENT(elm), tmp); 286 RB_SET_LEFT(RB_PARENT(elm), tmp);
264 } else { 287 } else {
265 RB_SET_RIGHT(RB_PARENT(elm), tmp); 288 RB_SET_RIGHT(RB_PARENT(elm), tmp);
266 } 289 }
267 } else { 290 } else {
268 head->SetRoot(tmp); 291 head.SetRoot(tmp);
269 } 292 }
270 293
271 RB_SET_RIGHT(tmp, elm); 294 RB_SET_RIGHT(tmp, elm);
272 RB_SET_PARENT(elm, tmp); 295 RB_SET_PARENT(elm, tmp);
273} 296}
274 297
275template <typename Node> 298template <typename T>
276void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { 299requires HasRBEntry<T>
277 Node* parent = nullptr; 300constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
278 Node* tmp = nullptr; 301 T* tmp;
279 302 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
280 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
281 Node* gparent = RB_PARENT(parent);
282 if (parent == RB_LEFT(gparent)) {
283 tmp = RB_RIGHT(gparent);
284 if (tmp && RB_IS_RED(tmp)) {
285 RB_SET_COLOR(tmp, EntryColor::Black);
286 RB_SET_BLACKRED(parent, gparent);
287 elm = gparent;
288 continue;
289 }
290
291 if (RB_RIGHT(parent) == elm) {
292 RB_ROTATE_LEFT(head, parent, tmp);
293 tmp = parent;
294 parent = elm;
295 elm = tmp;
296 }
297
298 RB_SET_BLACKRED(parent, gparent);
299 RB_ROTATE_RIGHT(head, gparent, tmp);
300 } else {
301 tmp = RB_LEFT(gparent);
302 if (tmp && RB_IS_RED(tmp)) {
303 RB_SET_COLOR(tmp, EntryColor::Black);
304 RB_SET_BLACKRED(parent, gparent);
305 elm = gparent;
306 continue;
307 }
308
309 if (RB_LEFT(parent) == elm) {
310 RB_ROTATE_RIGHT(head, parent, tmp);
311 tmp = parent;
312 parent = elm;
313 elm = tmp;
314 }
315
316 RB_SET_BLACKRED(parent, gparent);
317 RB_ROTATE_LEFT(head, gparent, tmp);
318 }
319 }
320
321 RB_SET_COLOR(head->Root(), EntryColor::Black);
322}
323
324template <typename Node>
325void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
326 Node* tmp;
327 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) {
328 if (RB_LEFT(parent) == elm) { 303 if (RB_LEFT(parent) == elm) {
329 tmp = RB_RIGHT(parent); 304 tmp = RB_RIGHT(parent);
330 if (!tmp) {
331 ASSERT_MSG(false, "tmp is invalid!");
332 break;
333 }
334 if (RB_IS_RED(tmp)) { 305 if (RB_IS_RED(tmp)) {
335 RB_SET_BLACKRED(tmp, parent); 306 RB_SET_BLACKRED(tmp, parent);
336 RB_ROTATE_LEFT(head, parent, tmp); 307 RB_ROTATE_LEFT(head, parent, tmp);
@@ -339,29 +310,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
339 310
340 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && 311 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
341 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { 312 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
342 RB_SET_COLOR(tmp, EntryColor::Red); 313 RB_SET_COLOR(tmp, RBColor::RB_RED);
343 elm = parent; 314 elm = parent;
344 parent = RB_PARENT(elm); 315 parent = RB_PARENT(elm);
345 } else { 316 } else {
346 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { 317 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
347 Node* oleft; 318 T* oleft;
348 if ((oleft = RB_LEFT(tmp)) != nullptr) { 319 if ((oleft = RB_LEFT(tmp)) != nullptr) {
349 RB_SET_COLOR(oleft, EntryColor::Black); 320 RB_SET_COLOR(oleft, RBColor::RB_BLACK);
350 } 321 }
351 322
352 RB_SET_COLOR(tmp, EntryColor::Red); 323 RB_SET_COLOR(tmp, RBColor::RB_RED);
353 RB_ROTATE_RIGHT(head, tmp, oleft); 324 RB_ROTATE_RIGHT(head, tmp, oleft);
354 tmp = RB_RIGHT(parent); 325 tmp = RB_RIGHT(parent);
355 } 326 }
356 327
357 RB_SET_COLOR(tmp, RB_COLOR(parent)); 328 RB_SET_COLOR(tmp, RB_COLOR(parent));
358 RB_SET_COLOR(parent, EntryColor::Black); 329 RB_SET_COLOR(parent, RBColor::RB_BLACK);
359 if (RB_RIGHT(tmp)) { 330 if (RB_RIGHT(tmp)) {
360 RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); 331 RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK);
361 } 332 }
362 333
363 RB_ROTATE_LEFT(head, parent, tmp); 334 RB_ROTATE_LEFT(head, parent, tmp);
364 elm = head->Root(); 335 elm = head.Root();
365 break; 336 break;
366 } 337 }
367 } else { 338 } else {
@@ -372,68 +343,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
372 tmp = RB_LEFT(parent); 343 tmp = RB_LEFT(parent);
373 } 344 }
374 345
375 if (!tmp) {
376 ASSERT_MSG(false, "tmp is invalid!");
377 break;
378 }
379
380 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && 346 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
381 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { 347 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
382 RB_SET_COLOR(tmp, EntryColor::Red); 348 RB_SET_COLOR(tmp, RBColor::RB_RED);
383 elm = parent; 349 elm = parent;
384 parent = RB_PARENT(elm); 350 parent = RB_PARENT(elm);
385 } else { 351 } else {
386 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { 352 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
387 Node* oright; 353 T* oright;
388 if ((oright = RB_RIGHT(tmp)) != nullptr) { 354 if ((oright = RB_RIGHT(tmp)) != nullptr) {
389 RB_SET_COLOR(oright, EntryColor::Black); 355 RB_SET_COLOR(oright, RBColor::RB_BLACK);
390 } 356 }
391 357
392 RB_SET_COLOR(tmp, EntryColor::Red); 358 RB_SET_COLOR(tmp, RBColor::RB_RED);
393 RB_ROTATE_LEFT(head, tmp, oright); 359 RB_ROTATE_LEFT(head, tmp, oright);
394 tmp = RB_LEFT(parent); 360 tmp = RB_LEFT(parent);
395 } 361 }
396 362
397 RB_SET_COLOR(tmp, RB_COLOR(parent)); 363 RB_SET_COLOR(tmp, RB_COLOR(parent));
398 RB_SET_COLOR(parent, EntryColor::Black); 364 RB_SET_COLOR(parent, RBColor::RB_BLACK);
399 365
400 if (RB_LEFT(tmp)) { 366 if (RB_LEFT(tmp)) {
401 RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); 367 RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK);
402 } 368 }
403 369
404 RB_ROTATE_RIGHT(head, parent, tmp); 370 RB_ROTATE_RIGHT(head, parent, tmp);
405 elm = head->Root(); 371 elm = head.Root();
406 break; 372 break;
407 } 373 }
408 } 374 }
409 } 375 }
410 376
411 if (elm) { 377 if (elm) {
412 RB_SET_COLOR(elm, EntryColor::Black); 378 RB_SET_COLOR(elm, RBColor::RB_BLACK);
413 } 379 }
414} 380}
415 381
416template <typename Node> 382template <typename T>
417Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { 383requires HasRBEntry<T>
418 Node* child = nullptr; 384constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
419 Node* parent = nullptr; 385 T* child = nullptr;
420 Node* old = elm; 386 T* parent = nullptr;
421 EntryColor color{}; 387 T* old = elm;
422 388 RBColor color = RBColor::RB_BLACK;
423 const auto finalize = [&] {
424 if (color == EntryColor::Black) {
425 RB_REMOVE_COLOR(head, parent, child);
426 }
427
428 return old;
429 };
430 389
431 if (RB_LEFT(elm) == nullptr) { 390 if (RB_LEFT(elm) == nullptr) {
432 child = RB_RIGHT(elm); 391 child = RB_RIGHT(elm);
433 } else if (RB_RIGHT(elm) == nullptr) { 392 } else if (RB_RIGHT(elm) == nullptr) {
434 child = RB_LEFT(elm); 393 child = RB_LEFT(elm);
435 } else { 394 } else {
436 Node* left; 395 T* left;
437 elm = RB_RIGHT(elm); 396 elm = RB_RIGHT(elm);
438 while ((left = RB_LEFT(elm)) != nullptr) { 397 while ((left = RB_LEFT(elm)) != nullptr) {
439 elm = left; 398 elm = left;
@@ -446,6 +405,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
446 if (child) { 405 if (child) {
447 RB_SET_PARENT(child, parent); 406 RB_SET_PARENT(child, parent);
448 } 407 }
408
449 if (parent) { 409 if (parent) {
450 if (RB_LEFT(parent) == elm) { 410 if (RB_LEFT(parent) == elm) {
451 RB_SET_LEFT(parent, child); 411 RB_SET_LEFT(parent, child);
@@ -453,14 +413,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
453 RB_SET_RIGHT(parent, child); 413 RB_SET_RIGHT(parent, child);
454 } 414 }
455 } else { 415 } else {
456 head->SetRoot(child); 416 head.SetRoot(child);
457 } 417 }
458 418
459 if (RB_PARENT(elm) == old) { 419 if (RB_PARENT(elm) == old) {
460 parent = elm; 420 parent = elm;
461 } 421 }
462 422
463 elm->SetEntry(old->GetEntry()); 423 elm->SetRBEntry(old->GetRBEntry());
464 424
465 if (RB_PARENT(old)) { 425 if (RB_PARENT(old)) {
466 if (RB_LEFT(RB_PARENT(old)) == old) { 426 if (RB_LEFT(RB_PARENT(old)) == old) {
@@ -469,17 +429,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
469 RB_SET_RIGHT(RB_PARENT(old), elm); 429 RB_SET_RIGHT(RB_PARENT(old), elm);
470 } 430 }
471 } else { 431 } else {
472 head->SetRoot(elm); 432 head.SetRoot(elm);
473 } 433 }
434
474 RB_SET_PARENT(RB_LEFT(old), elm); 435 RB_SET_PARENT(RB_LEFT(old), elm);
436
475 if (RB_RIGHT(old)) { 437 if (RB_RIGHT(old)) {
476 RB_SET_PARENT(RB_RIGHT(old), elm); 438 RB_SET_PARENT(RB_RIGHT(old), elm);
477 } 439 }
440
478 if (parent) { 441 if (parent) {
479 left = parent; 442 left = parent;
480 } 443 }
481 444
482 return finalize(); 445 if (color == RBColor::RB_BLACK) {
446 RB_REMOVE_COLOR(head, parent, child);
447 }
448
449 return old;
483 } 450 }
484 451
485 parent = RB_PARENT(elm); 452 parent = RB_PARENT(elm);
@@ -495,17 +462,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
495 RB_SET_RIGHT(parent, child); 462 RB_SET_RIGHT(parent, child);
496 } 463 }
497 } else { 464 } else {
498 head->SetRoot(child); 465 head.SetRoot(child);
466 }
467
468 if (color == RBColor::RB_BLACK) {
469 RB_REMOVE_COLOR(head, parent, child);
470 }
471
472 return old;
473}
474
475template <typename T>
476requires HasRBEntry<T>
477constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
478 T *parent = nullptr, *tmp = nullptr;
479 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
480 T* gparent = RB_PARENT(parent);
481 if (parent == RB_LEFT(gparent)) {
482 tmp = RB_RIGHT(gparent);
483 if (tmp && RB_IS_RED(tmp)) {
484 RB_SET_COLOR(tmp, RBColor::RB_BLACK);
485 RB_SET_BLACKRED(parent, gparent);
486 elm = gparent;
487 continue;
488 }
489
490 if (RB_RIGHT(parent) == elm) {
491 RB_ROTATE_LEFT(head, parent, tmp);
492 tmp = parent;
493 parent = elm;
494 elm = tmp;
495 }
496
497 RB_SET_BLACKRED(parent, gparent);
498 RB_ROTATE_RIGHT(head, gparent, tmp);
499 } else {
500 tmp = RB_LEFT(gparent);
501 if (tmp && RB_IS_RED(tmp)) {
502 RB_SET_COLOR(tmp, RBColor::RB_BLACK);
503 RB_SET_BLACKRED(parent, gparent);
504 elm = gparent;
505 continue;
506 }
507
508 if (RB_LEFT(parent) == elm) {
509 RB_ROTATE_RIGHT(head, parent, tmp);
510 tmp = parent;
511 parent = elm;
512 elm = tmp;
513 }
514
515 RB_SET_BLACKRED(parent, gparent);
516 RB_ROTATE_LEFT(head, gparent, tmp);
517 }
499 } 518 }
500 519
501 return finalize(); 520 RB_SET_COLOR(head.Root(), RBColor::RB_BLACK);
502} 521}
503 522
504// Inserts a node into the RB tree 523template <typename T, typename Compare>
505template <typename Node, typename CompareFunction> 524requires HasRBEntry<T>
506Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 525constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
507 Node* parent = nullptr; 526 T* parent = nullptr;
508 Node* tmp = head->Root(); 527 T* tmp = head.Root();
509 int comp = 0; 528 int comp = 0;
510 529
511 while (tmp) { 530 while (tmp) {
@@ -529,17 +548,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
529 RB_SET_RIGHT(parent, elm); 548 RB_SET_RIGHT(parent, elm);
530 } 549 }
531 } else { 550 } else {
532 head->SetRoot(elm); 551 head.SetRoot(elm);
533 } 552 }
534 553
535 RB_INSERT_COLOR(head, elm); 554 RB_INSERT_COLOR(head, elm);
536 return nullptr; 555 return nullptr;
537} 556}
538 557
539// Finds the node with the same key as elm 558template <typename T, typename Compare>
540template <typename Node, typename CompareFunction> 559requires HasRBEntry<T>
541Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 560constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
542 Node* tmp = head->Root(); 561 T* tmp = head.Root();
543 562
544 while (tmp) { 563 while (tmp) {
545 const int comp = cmp(elm, tmp); 564 const int comp = cmp(elm, tmp);
@@ -555,11 +574,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
555 return nullptr; 574 return nullptr;
556} 575}
557 576
558// Finds the first node greater than or equal to the search key 577template <typename T, typename Compare>
559template <typename Node, typename CompareFunction> 578requires HasRBEntry<T>
560Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 579constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
561 Node* tmp = head->Root(); 580 T* tmp = head.Root();
562 Node* res = nullptr; 581 T* res = nullptr;
563 582
564 while (tmp) { 583 while (tmp) {
565 const int comp = cmp(elm, tmp); 584 const int comp = cmp(elm, tmp);
@@ -576,13 +595,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
576 return res; 595 return res;
577} 596}
578 597
579// Finds the node with the same key as lelm 598template <typename T, typename U, typename Compare>
580template <typename Node, typename CompareFunction> 599requires HasRBEntry<T>
581Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 600constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
582 Node* tmp = head->Root(); 601 T* tmp = head.Root();
583 602
584 while (tmp) { 603 while (tmp) {
585 const int comp = lcmp(lelm, tmp); 604 const int comp = cmp(key, tmp);
586 if (comp < 0) { 605 if (comp < 0) {
587 tmp = RB_LEFT(tmp); 606 tmp = RB_LEFT(tmp);
588 } else if (comp > 0) { 607 } else if (comp > 0) {
@@ -595,14 +614,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
595 return nullptr; 614 return nullptr;
596} 615}
597 616
598// Finds the first node greater than or equal to the search key 617template <typename T, typename U, typename Compare>
599template <typename Node, typename CompareFunction> 618requires HasRBEntry<T>
600Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 619constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
601 Node* tmp = head->Root(); 620 T* tmp = head.Root();
602 Node* res = nullptr; 621 T* res = nullptr;
603 622
604 while (tmp) { 623 while (tmp) {
605 const int comp = lcmp(lelm, tmp); 624 const int comp = cmp(key, tmp);
606 if (comp < 0) { 625 if (comp < 0) {
607 res = tmp; 626 res = tmp;
608 tmp = RB_LEFT(tmp); 627 tmp = RB_LEFT(tmp);
@@ -616,8 +635,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
616 return res; 635 return res;
617} 636}
618 637
619template <typename Node> 638template <typename T, typename Compare>
620Node* RB_NEXT(Node* elm) { 639requires HasRBEntry<T>
640constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
641 T* tmp = head.Root();
642
643 while (true) {
644 const int comp = cmp(elm, tmp);
645 if (comp < 0) {
646 tmp = RB_LEFT(tmp);
647 } else if (comp > 0) {
648 tmp = RB_RIGHT(tmp);
649 } else {
650 return tmp;
651 }
652 }
653}
654
655template <typename T, typename U, typename Compare>
656requires HasRBEntry<T>
657constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
658 T* tmp = head.Root();
659
660 while (true) {
661 const int comp = cmp(key, tmp);
662 if (comp < 0) {
663 tmp = RB_LEFT(tmp);
664 } else if (comp > 0) {
665 tmp = RB_RIGHT(tmp);
666 } else {
667 return tmp;
668 }
669 }
670}
671
672template <typename T>
673requires HasRBEntry<T>
674constexpr T* RB_NEXT(T* elm) {
621 if (RB_RIGHT(elm)) { 675 if (RB_RIGHT(elm)) {
622 elm = RB_RIGHT(elm); 676 elm = RB_RIGHT(elm);
623 while (RB_LEFT(elm)) { 677 while (RB_LEFT(elm)) {
@@ -636,8 +690,9 @@ Node* RB_NEXT(Node* elm) {
636 return elm; 690 return elm;
637} 691}
638 692
639template <typename Node> 693template <typename T>
640Node* RB_PREV(Node* elm) { 694requires HasRBEntry<T>
695constexpr T* RB_PREV(T* elm) {
641 if (RB_LEFT(elm)) { 696 if (RB_LEFT(elm)) {
642 elm = RB_LEFT(elm); 697 elm = RB_LEFT(elm);
643 while (RB_RIGHT(elm)) { 698 while (RB_RIGHT(elm)) {
@@ -656,30 +711,32 @@ Node* RB_PREV(Node* elm) {
656 return elm; 711 return elm;
657} 712}
658 713
659template <typename Node> 714template <typename T>
660Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { 715requires HasRBEntry<T>
661 Node* tmp = head->Root(); 716constexpr T* RB_MIN(RBHead<T>& head) {
662 Node* parent = nullptr; 717 T* tmp = head.Root();
718 T* parent = nullptr;
663 719
664 while (tmp) { 720 while (tmp) {
665 parent = tmp; 721 parent = tmp;
666 if (is_min) { 722 tmp = RB_LEFT(tmp);
667 tmp = RB_LEFT(tmp);
668 } else {
669 tmp = RB_RIGHT(tmp);
670 }
671 } 723 }
672 724
673 return parent; 725 return parent;
674} 726}
675 727
676template <typename Node> 728template <typename T>
677Node* RB_MIN(RBHead<Node>* head) { 729requires HasRBEntry<T>
678 return RB_MINMAX(head, true); 730constexpr T* RB_MAX(RBHead<T>& head) {
679} 731 T* tmp = head.Root();
732 T* parent = nullptr;
680 733
681template <typename Node> 734 while (tmp) {
682Node* RB_MAX(RBHead<Node>* head) { 735 parent = tmp;
683 return RB_MINMAX(head, false); 736 tmp = RB_RIGHT(tmp);
737 }
738
739 return parent;
684} 740}
685} // namespace Common 741
742} // namespace Common::freebsd
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 4780b2f9d..ad1b90414 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
8#include <utility> 7#include <utility>
9 8
10#ifdef _MSC_VER 9#ifdef _MSC_VER
@@ -13,6 +12,7 @@
13#pragma intrinsic(_umul128) 12#pragma intrinsic(_umul128)
14#pragma intrinsic(_udiv128) 13#pragma intrinsic(_udiv128)
15#else 14#else
15#include <cstring>
16#include <x86intrin.h> 16#include <x86intrin.h>
17#endif 17#endif
18 18
diff --git a/src/common/uuid.h b/src/common/uuid.h
index fe31e64e6..c450d9e20 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <string> 9#include <string>
10#include <string_view>
11 10
12#include "common/common_types.h" 11#include "common/common_types.h"
13 12
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index fb1a6f81f..cac4f4895 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <type_traits>
8#include <utility> 7#include <utility>
9 8
10namespace Common { 9namespace Common {
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 9acf7551e..f30d91692 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdint>
6
7#include "common/uint128.h" 5#include "common/uint128.h"
8#include "common/wall_clock.h" 6#include "common/wall_clock.h"
9 7
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index fbeacc7e2..f5296b32a 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -1,8 +1,11 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
2// Licensed under GPLv2 or any later version 2// Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
3// Refer to the license.txt file included.
4 3
4#include <array>
5#include <cstring> 5#include <cstring>
6#include <iterator>
7#include <string_view>
8#include "common/bit_util.h"
6#include "common/common_types.h" 9#include "common/common_types.h"
7#include "common/x64/cpu_detect.h" 10#include "common/x64/cpu_detect.h"
8 11
@@ -17,7 +20,7 @@
17// clang-format on 20// clang-format on
18#endif 21#endif
19 22
20static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { 23static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
21#if defined(__DragonFly__) || defined(__FreeBSD__) 24#if defined(__DragonFly__) || defined(__FreeBSD__)
22 // Despite the name, this is just do_cpuid() with ECX as second input. 25 // Despite the name, this is just do_cpuid() with ECX as second input.
23 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); 26 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
@@ -30,7 +33,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
30#endif 33#endif
31} 34}
32 35
33static inline void __cpuid(int info[4], int function_id) { 36static inline void __cpuid(int info[4], u32 function_id) {
34 return __cpuidex(info, function_id, 0); 37 return __cpuidex(info, function_id, 0);
35} 38}
36 39
@@ -45,6 +48,17 @@ static inline u64 _xgetbv(u32 index) {
45 48
46namespace Common { 49namespace Common {
47 50
51CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
52 if (brand_string == "GenuineIntel") {
53 return Manufacturer::Intel;
54 } else if (brand_string == "AuthenticAMD") {
55 return Manufacturer::AMD;
56 } else if (brand_string == "HygonGenuine") {
57 return Manufacturer::Hygon;
58 }
59 return Manufacturer::Unknown;
60}
61
48// Detects the various CPU features 62// Detects the various CPU features
49static CPUCaps Detect() { 63static CPUCaps Detect() {
50 CPUCaps caps = {}; 64 CPUCaps caps = {};
@@ -53,75 +67,74 @@ static CPUCaps Detect() {
53 // yuzu at all anyway 67 // yuzu at all anyway
54 68
55 int cpu_id[4]; 69 int cpu_id[4];
56 memset(caps.brand_string, 0, sizeof(caps.brand_string));
57 70
58 // Detect CPU's CPUID capabilities and grab CPU string 71 // Detect CPU's CPUID capabilities and grab manufacturer string
59 __cpuid(cpu_id, 0x00000000); 72 __cpuid(cpu_id, 0x00000000);
60 u32 max_std_fn = cpu_id[0]; // EAX 73 const u32 max_std_fn = cpu_id[0]; // EAX
61
62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
65 if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
66 caps.manufacturer = Manufacturer::Intel;
67 else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
68 caps.manufacturer = Manufacturer::AMD;
69 else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
70 caps.manufacturer = Manufacturer::Hygon;
71 else
72 caps.manufacturer = Manufacturer::Unknown;
73 74
74 __cpuid(cpu_id, 0x80000000); 75 std::memset(caps.brand_string, 0, std::size(caps.brand_string));
76 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
77 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
78 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
79
80 caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
75 81
76 u32 max_ex_fn = cpu_id[0]; 82 // Set reasonable default cpu string even if brand string not available
83 std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
77 84
78 // Set reasonable default brand string even if brand string not available 85 __cpuid(cpu_id, 0x80000000);
79 strcpy(caps.cpu_string, caps.brand_string); 86
87 const u32 max_ex_fn = cpu_id[0];
80 88
81 // Detect family and other miscellaneous features 89 // Detect family and other miscellaneous features
82 if (max_std_fn >= 1) { 90 if (max_std_fn >= 1) {
83 __cpuid(cpu_id, 0x00000001); 91 __cpuid(cpu_id, 0x00000001);
84 if ((cpu_id[3] >> 25) & 1) 92 caps.sse = Common::Bit<25>(cpu_id[3]);
85 caps.sse = true; 93 caps.sse2 = Common::Bit<26>(cpu_id[3]);
86 if ((cpu_id[3] >> 26) & 1) 94 caps.sse3 = Common::Bit<0>(cpu_id[2]);
87 caps.sse2 = true; 95 caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
88 if ((cpu_id[2]) & 1) 96 caps.ssse3 = Common::Bit<9>(cpu_id[2]);
89 caps.sse3 = true; 97 caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
90 if ((cpu_id[2] >> 9) & 1) 98 caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
91 caps.ssse3 = true; 99 caps.movbe = Common::Bit<22>(cpu_id[2]);
92 if ((cpu_id[2] >> 19) & 1) 100 caps.popcnt = Common::Bit<23>(cpu_id[2]);
93 caps.sse4_1 = true; 101 caps.aes = Common::Bit<25>(cpu_id[2]);
94 if ((cpu_id[2] >> 20) & 1) 102 caps.f16c = Common::Bit<29>(cpu_id[2]);
95 caps.sse4_2 = true;
96 if ((cpu_id[2] >> 25) & 1)
97 caps.aes = true;
98 103
99 // AVX support requires 3 separate checks: 104 // AVX support requires 3 separate checks:
100 // - Is the AVX bit set in CPUID? 105 // - Is the AVX bit set in CPUID?
101 // - Is the XSAVE bit set in CPUID? 106 // - Is the XSAVE bit set in CPUID?
102 // - XGETBV result has the XCR bit set. 107 // - XGETBV result has the XCR bit set.
103 if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { 108 if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
104 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { 109 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
105 caps.avx = true; 110 caps.avx = true;
106 if ((cpu_id[2] >> 12) & 1) 111 if (Common::Bit<12>(cpu_id[2]))
107 caps.fma = true; 112 caps.fma = true;
108 } 113 }
109 } 114 }
110 115
111 if (max_std_fn >= 7) { 116 if (max_std_fn >= 7) {
112 __cpuidex(cpu_id, 0x00000007, 0x00000000); 117 __cpuidex(cpu_id, 0x00000007, 0x00000000);
113 // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed 118 // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
114 if ((cpu_id[1] >> 5) & 1) 119 if (caps.avx) {
115 caps.avx2 = caps.avx; 120 caps.avx2 = Common::Bit<5>(cpu_id[1]);
116 if ((cpu_id[1] >> 3) & 1) 121 caps.avx512f = Common::Bit<16>(cpu_id[1]);
117 caps.bmi1 = true; 122 caps.avx512dq = Common::Bit<17>(cpu_id[1]);
118 if ((cpu_id[1] >> 8) & 1) 123 caps.avx512cd = Common::Bit<28>(cpu_id[1]);
119 caps.bmi2 = true; 124 caps.avx512bw = Common::Bit<30>(cpu_id[1]);
120 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) 125 caps.avx512vl = Common::Bit<31>(cpu_id[1]);
121 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && 126 caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
122 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { 127 caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
123 caps.avx512 = caps.avx2;
124 } 128 }
129
130 caps.bmi1 = Common::Bit<3>(cpu_id[1]);
131 caps.bmi2 = Common::Bit<8>(cpu_id[1]);
132 caps.sha = Common::Bit<29>(cpu_id[1]);
133
134 caps.gfni = Common::Bit<8>(cpu_id[2]);
135
136 __cpuidex(cpu_id, 0x00000007, 0x00000001);
137 caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
125 } 138 }
126 } 139 }
127 140
@@ -138,15 +151,13 @@ static CPUCaps Detect() {
138 if (max_ex_fn >= 0x80000001) { 151 if (max_ex_fn >= 0x80000001) {
139 // Check for more features 152 // Check for more features
140 __cpuid(cpu_id, 0x80000001); 153 __cpuid(cpu_id, 0x80000001);
141 if ((cpu_id[2] >> 16) & 1) 154 caps.lzcnt = Common::Bit<5>(cpu_id[2]);
142 caps.fma4 = true; 155 caps.fma4 = Common::Bit<16>(cpu_id[2]);
143 } 156 }
144 157
145 if (max_ex_fn >= 0x80000007) { 158 if (max_ex_fn >= 0x80000007) {
146 __cpuid(cpu_id, 0x80000007); 159 __cpuid(cpu_id, 0x80000007);
147 if (cpu_id[3] & (1 << 8)) { 160 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
148 caps.invariant_tsc = true;
149 }
150 } 161 }
151 162
152 if (max_std_fn >= 0x16) { 163 if (max_std_fn >= 0x16) {
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index e3b63302e..40c48b132 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -1,42 +1,65 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
2// Licensed under GPLv2 or any later version 2// Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7namespace Common { 6#include <string_view>
7#include "common/common_types.h"
8 8
9enum class Manufacturer : u32 { 9namespace Common {
10 Intel = 0,
11 AMD = 1,
12 Hygon = 2,
13 Unknown = 3,
14};
15 10
16/// x86/x64 CPU capabilities that may be detected by this module 11/// x86/x64 CPU capabilities that may be detected by this module
17struct CPUCaps { 12struct CPUCaps {
13
14 enum class Manufacturer : u8 {
15 Unknown = 0,
16 Intel = 1,
17 AMD = 2,
18 Hygon = 3,
19 };
20
21 static Manufacturer ParseManufacturer(std::string_view brand_string);
22
18 Manufacturer manufacturer; 23 Manufacturer manufacturer;
19 char cpu_string[0x21]; 24 char brand_string[13];
20 char brand_string[0x41]; 25
21 bool sse; 26 char cpu_string[48];
22 bool sse2; 27
23 bool sse3;
24 bool ssse3;
25 bool sse4_1;
26 bool sse4_2;
27 bool lzcnt;
28 bool avx;
29 bool avx2;
30 bool avx512;
31 bool bmi1;
32 bool bmi2;
33 bool fma;
34 bool fma4;
35 bool aes;
36 bool invariant_tsc;
37 u32 base_frequency; 28 u32 base_frequency;
38 u32 max_frequency; 29 u32 max_frequency;
39 u32 bus_frequency; 30 u32 bus_frequency;
31
32 bool sse : 1;
33 bool sse2 : 1;
34 bool sse3 : 1;
35 bool ssse3 : 1;
36 bool sse4_1 : 1;
37 bool sse4_2 : 1;
38
39 bool avx : 1;
40 bool avx_vnni : 1;
41 bool avx2 : 1;
42 bool avx512f : 1;
43 bool avx512dq : 1;
44 bool avx512cd : 1;
45 bool avx512bw : 1;
46 bool avx512vl : 1;
47 bool avx512vbmi : 1;
48 bool avx512bitalg : 1;
49
50 bool aes : 1;
51 bool bmi1 : 1;
52 bool bmi2 : 1;
53 bool f16c : 1;
54 bool fma : 1;
55 bool fma4 : 1;
56 bool gfni : 1;
57 bool invariant_tsc : 1;
58 bool lzcnt : 1;
59 bool movbe : 1;
60 bool pclmulqdq : 1;
61 bool popcnt : 1;
62 bool sha : 1;
40}; 63};
41 64
42/** 65/**
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 91b842829..7fd9d22f8 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -4,33 +4,55 @@
4 4
5#include <array> 5#include <array>
6#include <chrono> 6#include <chrono>
7#include <limits>
8#include <mutex>
9#include <thread> 7#include <thread>
10 8
11#include "common/atomic_ops.h" 9#include "common/atomic_ops.h"
12#include "common/uint128.h" 10#include "common/uint128.h"
13#include "common/x64/native_clock.h" 11#include "common/x64/native_clock.h"
14 12
13#ifdef _MSC_VER
14#include <intrin.h>
15#endif
16
15namespace Common { 17namespace Common {
16 18
19#ifdef _MSC_VER
20__forceinline static u64 FencedRDTSC() {
21 _mm_lfence();
22 _ReadWriteBarrier();
23 const u64 result = __rdtsc();
24 _mm_lfence();
25 _ReadWriteBarrier();
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 result;
31 asm volatile("lfence\n\t"
32 "rdtsc\n\t"
33 "shl $32, %%rdx\n\t"
34 "or %%rdx, %0\n\t"
35 "lfence"
36 : "=a"(result)
37 :
38 : "rdx", "memory", "cc");
39 return result;
40}
41#endif
42
17u64 EstimateRDTSCFrequency() { 43u64 EstimateRDTSCFrequency() {
18 // Discard the first result measuring the rdtsc. 44 // Discard the first result measuring the rdtsc.
19 _mm_mfence(); 45 FencedRDTSC();
20 __rdtsc();
21 std::this_thread::sleep_for(std::chrono::milliseconds{1}); 46 std::this_thread::sleep_for(std::chrono::milliseconds{1});
22 _mm_mfence(); 47 FencedRDTSC();
23 __rdtsc();
24 48
25 // Get the current time. 49 // Get the current time.
26 const auto start_time = std::chrono::steady_clock::now(); 50 const auto start_time = std::chrono::steady_clock::now();
27 _mm_mfence(); 51 const u64 tsc_start = FencedRDTSC();
28 const u64 tsc_start = __rdtsc();
29 // Wait for 200 milliseconds. 52 // Wait for 200 milliseconds.
30 std::this_thread::sleep_for(std::chrono::milliseconds{200}); 53 std::this_thread::sleep_for(std::chrono::milliseconds{200});
31 const auto end_time = std::chrono::steady_clock::now(); 54 const auto end_time = std::chrono::steady_clock::now();
32 _mm_mfence(); 55 const u64 tsc_end = FencedRDTSC();
33 const u64 tsc_end = __rdtsc();
34 // Calculate differences. 56 // Calculate differences.
35 const u64 timer_diff = static_cast<u64>( 57 const u64 timer_diff = static_cast<u64>(
36 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); 58 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
@@ -44,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
44 u64 rtsc_frequency_) 66 u64 rtsc_frequency_)
45 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ 67 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
46 rtsc_frequency_} { 68 rtsc_frequency_} {
47 _mm_mfence(); 69 time_point.inner.last_measure = FencedRDTSC();
48 time_point.inner.last_measure = __rdtsc();
49 time_point.inner.accumulated_ticks = 0U; 70 time_point.inner.accumulated_ticks = 0U;
50 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); 71 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
51 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); 72 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -57,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
57u64 NativeClock::GetRTSC() { 78u64 NativeClock::GetRTSC() {
58 TimePoint new_time_point{}; 79 TimePoint new_time_point{};
59 TimePoint current_time_point{}; 80 TimePoint current_time_point{};
81
82 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
60 do { 83 do {
61 current_time_point.pack = time_point.pack; 84 const u64 current_measure = FencedRDTSC();
62 _mm_mfence();
63 const u64 current_measure = __rdtsc();
64 u64 diff = current_measure - current_time_point.inner.last_measure; 85 u64 diff = current_measure - current_time_point.inner.last_measure;
65 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) 86 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
66 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure 87 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -68,7 +89,7 @@ u64 NativeClock::GetRTSC() {
68 : current_time_point.inner.last_measure; 89 : current_time_point.inner.last_measure;
69 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 90 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
70 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 91 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
71 current_time_point.pack)); 92 current_time_point.pack, current_time_point.pack));
72 /// The clock cannot be more precise than the guest timer, remove the lower bits 93 /// The clock cannot be more precise than the guest timer, remove the lower bits
73 return new_time_point.inner.accumulated_ticks & inaccuracy_mask; 94 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
74} 95}
@@ -77,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
77 if (!is_paused) { 98 if (!is_paused) {
78 TimePoint current_time_point{}; 99 TimePoint current_time_point{};
79 TimePoint new_time_point{}; 100 TimePoint new_time_point{};
101
102 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
80 do { 103 do {
81 current_time_point.pack = time_point.pack;
82 new_time_point.pack = current_time_point.pack; 104 new_time_point.pack = current_time_point.pack;
83 _mm_mfence(); 105 new_time_point.inner.last_measure = FencedRDTSC();
84 new_time_point.inner.last_measure = __rdtsc();
85 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 106 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
86 current_time_point.pack)); 107 current_time_point.pack, current_time_point.pack));
87 } 108 }
88} 109}
89 110
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 7cbd400d2..2c3082ea0 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
8
9#include "common/wall_clock.h" 7#include "common/wall_clock.h"
10 8
11namespace Common { 9namespace Common {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5db6a1b3a..62230bae0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -13,6 +13,8 @@ add_library(core STATIC
13 arm/dynarmic/arm_exclusive_monitor.h 13 arm/dynarmic/arm_exclusive_monitor.h
14 arm/exclusive_monitor.cpp 14 arm/exclusive_monitor.cpp
15 arm/exclusive_monitor.h 15 arm/exclusive_monitor.h
16 arm/symbols.cpp
17 arm/symbols.h
16 constants.cpp 18 constants.cpp
17 constants.h 19 constants.h
18 core.cpp 20 core.cpp
@@ -122,6 +124,8 @@ add_library(core STATIC
122 frontend/applets/error.h 124 frontend/applets/error.h
123 frontend/applets/general_frontend.cpp 125 frontend/applets/general_frontend.cpp
124 frontend/applets/general_frontend.h 126 frontend/applets/general_frontend.h
127 frontend/applets/mii_edit.cpp
128 frontend/applets/mii_edit.h
125 frontend/applets/profile_select.cpp 129 frontend/applets/profile_select.cpp
126 frontend/applets/profile_select.h 130 frontend/applets/profile_select.h
127 frontend/applets/software_keyboard.cpp 131 frontend/applets/software_keyboard.cpp
@@ -207,6 +211,8 @@ add_library(core STATIC
207 hle/kernel/k_memory_region.h 211 hle/kernel/k_memory_region.h
208 hle/kernel/k_memory_region_type.h 212 hle/kernel/k_memory_region_type.h
209 hle/kernel/k_page_bitmap.h 213 hle/kernel/k_page_bitmap.h
214 hle/kernel/k_page_buffer.cpp
215 hle/kernel/k_page_buffer.h
210 hle/kernel/k_page_heap.cpp 216 hle/kernel/k_page_heap.cpp
211 hle/kernel/k_page_heap.h 217 hle/kernel/k_page_heap.h
212 hle/kernel/k_page_linked_list.h 218 hle/kernel/k_page_linked_list.h
@@ -244,6 +250,8 @@ add_library(core STATIC
244 hle/kernel/k_system_control.h 250 hle/kernel/k_system_control.h
245 hle/kernel/k_thread.cpp 251 hle/kernel/k_thread.cpp
246 hle/kernel/k_thread.h 252 hle/kernel/k_thread.h
253 hle/kernel/k_thread_local_page.cpp
254 hle/kernel/k_thread_local_page.h
247 hle/kernel/k_thread_queue.cpp 255 hle/kernel/k_thread_queue.cpp
248 hle/kernel/k_thread_queue.h 256 hle/kernel/k_thread_queue.h
249 hle/kernel/k_trace.h 257 hle/kernel/k_trace.h
@@ -300,6 +308,9 @@ add_library(core STATIC
300 hle/service/am/applets/applet_error.h 308 hle/service/am/applets/applet_error.h
301 hle/service/am/applets/applet_general_backend.cpp 309 hle/service/am/applets/applet_general_backend.cpp
302 hle/service/am/applets/applet_general_backend.h 310 hle/service/am/applets/applet_general_backend.h
311 hle/service/am/applets/applet_mii_edit.cpp
312 hle/service/am/applets/applet_mii_edit.h
313 hle/service/am/applets/applet_mii_edit_types.h
303 hle/service/am/applets/applet_profile_select.cpp 314 hle/service/am/applets/applet_profile_select.cpp
304 hle/service/am/applets/applet_profile_select.h 315 hle/service/am/applets/applet_profile_select.h
305 hle/service/am/applets/applet_software_keyboard.cpp 316 hle/service/am/applets/applet_software_keyboard.cpp
@@ -423,6 +434,8 @@ add_library(core STATIC
423 hle/service/grc/grc.h 434 hle/service/grc/grc.h
424 hle/service/hid/hid.cpp 435 hle/service/hid/hid.cpp
425 hle/service/hid/hid.h 436 hle/service/hid/hid.h
437 hle/service/hid/hidbus.cpp
438 hle/service/hid/hidbus.h
426 hle/service/hid/irs.cpp 439 hle/service/hid/irs.cpp
427 hle/service/hid/irs.h 440 hle/service/hid/irs.h
428 hle/service/hid/ring_lifo.h 441 hle/service/hid/ring_lifo.h
@@ -449,6 +462,18 @@ add_library(core STATIC
449 hle/service/hid/controllers/touchscreen.h 462 hle/service/hid/controllers/touchscreen.h
450 hle/service/hid/controllers/xpad.cpp 463 hle/service/hid/controllers/xpad.cpp
451 hle/service/hid/controllers/xpad.h 464 hle/service/hid/controllers/xpad.h
465 hle/service/hid/hidbus/hidbus_base.cpp
466 hle/service/hid/hidbus/hidbus_base.h
467 hle/service/hid/hidbus/ringcon.cpp
468 hle/service/hid/hidbus/ringcon.h
469 hle/service/hid/hidbus/starlink.cpp
470 hle/service/hid/hidbus/starlink.h
471 hle/service/hid/hidbus/stubbed.cpp
472 hle/service/hid/hidbus/stubbed.h
473 hle/service/jit/jit_context.cpp
474 hle/service/jit/jit_context.h
475 hle/service/jit/jit.cpp
476 hle/service/jit/jit.h
452 hle/service/lbl/lbl.cpp 477 hle/service/lbl/lbl.cpp
453 hle/service/lbl/lbl.h 478 hle/service/lbl/lbl.h
454 hle/service/ldn/errors.h 479 hle/service/ldn/errors.h
@@ -526,10 +551,35 @@ add_library(core STATIC
526 hle/service/nvdrv/nvmemp.h 551 hle/service/nvdrv/nvmemp.h
527 hle/service/nvdrv/syncpoint_manager.cpp 552 hle/service/nvdrv/syncpoint_manager.cpp
528 hle/service/nvdrv/syncpoint_manager.h 553 hle/service/nvdrv/syncpoint_manager.h
529 hle/service/nvflinger/buffer_queue.cpp 554 hle/service/nvflinger/binder.h
530 hle/service/nvflinger/buffer_queue.h 555 hle/service/nvflinger/buffer_item.h
556 hle/service/nvflinger/buffer_item_consumer.cpp
557 hle/service/nvflinger/buffer_item_consumer.h
558 hle/service/nvflinger/buffer_queue_consumer.cpp
559 hle/service/nvflinger/buffer_queue_consumer.h
560 hle/service/nvflinger/buffer_queue_core.cpp
561 hle/service/nvflinger/buffer_queue_core.h
562 hle/service/nvflinger/buffer_queue_defs.h
563 hle/service/nvflinger/buffer_queue_producer.cpp
564 hle/service/nvflinger/buffer_queue_producer.h
565 hle/service/nvflinger/buffer_slot.h
566 hle/service/nvflinger/buffer_transform_flags.h
567 hle/service/nvflinger/consumer_base.cpp
568 hle/service/nvflinger/consumer_base.h
569 hle/service/nvflinger/consumer_listener.h
570 hle/service/nvflinger/graphic_buffer_producer.cpp
571 hle/service/nvflinger/graphic_buffer_producer.h
572 hle/service/nvflinger/hos_binder_driver_server.cpp
573 hle/service/nvflinger/hos_binder_driver_server.h
531 hle/service/nvflinger/nvflinger.cpp 574 hle/service/nvflinger/nvflinger.cpp
532 hle/service/nvflinger/nvflinger.h 575 hle/service/nvflinger/nvflinger.h
576 hle/service/nvflinger/parcel.h
577 hle/service/nvflinger/pixel_format.h
578 hle/service/nvflinger/producer_listener.h
579 hle/service/nvflinger/status.h
580 hle/service/nvflinger/ui/fence.h
581 hle/service/nvflinger/ui/graphic_buffer.h
582 hle/service/nvflinger/window.h
533 hle/service/olsc/olsc.cpp 583 hle/service/olsc/olsc.cpp
534 hle/service/olsc/olsc.h 584 hle/service/olsc/olsc.h
535 hle/service/pcie/pcie.cpp 585 hle/service/pcie/pcie.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..2a9390e26 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -8,226 +8,41 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/arm/symbols.h"
11#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/kernel/k_process.h"
12#include "core/loader/loader.h" 14#include "core/loader/loader.h"
13#include "core/memory.h" 15#include "core/memory.h"
14 16
15namespace Core { 17#include "core/arm/dynarmic/arm_dynarmic_32.h"
16namespace { 18#include "core/arm/dynarmic/arm_dynarmic_64.h"
17
18constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
19constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
20constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
21constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
22
23enum class ELFSymbolType : u8 {
24 None = 0,
25 Object = 1,
26 Function = 2,
27 Section = 3,
28 File = 4,
29 Common = 5,
30 TLS = 6,
31};
32
33enum class ELFSymbolBinding : u8 {
34 Local = 0,
35 Global = 1,
36 Weak = 2,
37};
38
39enum class ELFSymbolVisibility : u8 {
40 Default = 0,
41 Internal = 1,
42 Hidden = 2,
43 Protected = 3,
44};
45
46struct ELFSymbol {
47 u32 name_index;
48 union {
49 u8 info;
50
51 BitField<0, 4, ELFSymbolType> type;
52 BitField<4, 4, ELFSymbolBinding> binding;
53 };
54 ELFSymbolVisibility visibility;
55 u16 sh_index;
56 u64 value;
57 u64 size;
58};
59static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
60
61using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
62
63Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
64 const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
65
66 if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
67 memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
68 return {};
69 }
70
71 const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
72
73 VAddr string_table_offset{};
74 VAddr symbol_table_offset{};
75 u64 symbol_entry_size{};
76
77 VAddr dynamic_index = dynamic_offset;
78 while (true) {
79 const u64 tag = memory.Read64(dynamic_index);
80 const u64 value = memory.Read64(dynamic_index + 0x8);
81 dynamic_index += 0x10;
82
83 if (tag == ELF_DYNAMIC_TAG_NULL) {
84 break;
85 }
86
87 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
88 string_table_offset = value;
89 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
90 symbol_table_offset = value;
91 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
92 symbol_entry_size = value;
93 }
94 }
95
96 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
97 return {};
98 }
99 19
100 const auto string_table_address = text_offset + string_table_offset; 20namespace Core {
101 const auto symbol_table_address = text_offset + symbol_table_offset;
102
103 Symbols out;
104
105 VAddr symbol_index = symbol_table_address;
106 while (symbol_index < string_table_address) {
107 ELFSymbol symbol{};
108 memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
109
110 VAddr string_offset = string_table_address + symbol.name_index;
111 std::string name;
112 for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
113 name += static_cast<char>(c);
114 }
115
116 symbol_index += symbol_entry_size;
117 out.push_back({symbol, name});
118 }
119
120 return out;
121}
122
123std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
124 const auto iter =
125 std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
126 const auto& symbol = pair.first;
127 const auto end_address = symbol.value + symbol.size;
128 return func_address >= symbol.value && func_address < end_address;
129 });
130
131 if (iter == symbols.end()) {
132 return std::nullopt;
133 }
134
135 return iter->second;
136}
137
138} // Anonymous namespace
139 21
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 22constexpr u64 SEGMENT_BASE = 0x7100000000ull;
141 23
142std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( 24std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
143 System& system, const ThreadContext64& ctx) { 25 Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
144 std::vector<BacktraceEntry> out; 26 return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
145 auto& memory = system.Memory();
146
147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30];
149 while (true) {
150 out.push_back({
151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
159 break;
160 }
161
162 lr = memory.Read64(fp + 8) - 4;
163 fp = memory.Read64(fp);
164 }
165
166 std::map<VAddr, std::string> modules;
167 auto& loader{system.GetAppLoader()};
168 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
169 return {};
170 }
171
172 std::map<std::string, Symbols> symbols;
173 for (const auto& module : modules) {
174 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
175 }
176
177 for (auto& entry : out) {
178 VAddr base = 0;
179 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
180 const auto& module{*iter};
181 if (entry.original_address >= module.first) {
182 entry.module = module.second;
183 base = module.first;
184 break;
185 }
186 }
187
188 entry.offset = entry.original_address - base;
189 entry.address = SEGMENT_BASE + entry.offset;
190
191 if (entry.module.empty())
192 entry.module = "unknown";
193
194 const auto symbol_set = symbols.find(entry.module);
195 if (symbol_set != symbols.end()) {
196 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
197 if (symbol.has_value()) {
198 // TODO(DarkLordZach): Add demangling of symbol names.
199 entry.name = *symbol;
200 }
201 }
202 }
203
204 return out;
205} 27}
206 28
207std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { 29std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
208 std::vector<BacktraceEntry> out; 30 Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
209 auto& memory = system.Memory(); 31 return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
210 32}
211 auto fp = GetReg(29);
212 auto lr = GetReg(30);
213 while (true) {
214 out.push_back({"", 0, lr, 0, ""});
215 if (!fp) {
216 break;
217 }
218 lr = memory.Read64(fp + 8) - 4;
219 fp = memory.Read64(fp);
220 }
221 33
34void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
222 std::map<VAddr, std::string> modules; 35 std::map<VAddr, std::string> modules;
223 auto& loader{system.GetAppLoader()}; 36 auto& loader{system.GetAppLoader()};
224 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { 37 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
225 return {}; 38 return;
226 } 39 }
227 40
228 std::map<std::string, Symbols> symbols; 41 std::map<std::string, Symbols::Symbols> symbols;
229 for (const auto& module : modules) { 42 for (const auto& module : modules) {
230 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); 43 symbols.insert_or_assign(module.second,
44 Symbols::GetSymbols(module.first, system.Memory(),
45 system.CurrentProcess()->Is64BitProcess()));
231 } 46 }
232 47
233 for (auto& entry : out) { 48 for (auto& entry : out) {
@@ -244,24 +59,23 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
244 entry.offset = entry.original_address - base; 59 entry.offset = entry.original_address - base;
245 entry.address = SEGMENT_BASE + entry.offset; 60 entry.address = SEGMENT_BASE + entry.offset;
246 61
247 if (entry.module.empty()) 62 if (entry.module.empty()) {
248 entry.module = "unknown"; 63 entry.module = "unknown";
64 }
249 65
250 const auto symbol_set = symbols.find(entry.module); 66 const auto symbol_set = symbols.find(entry.module);
251 if (symbol_set != symbols.end()) { 67 if (symbol_set != symbols.end()) {
252 const auto symbol = GetSymbolName(symbol_set->second, entry.offset); 68 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
253 if (symbol.has_value()) { 69 if (symbol.has_value()) {
254 // TODO(DarkLordZach): Add demangling of symbol names. 70 // TODO(DarkLordZach): Add demangling of symbol names.
255 entry.name = *symbol; 71 entry.name = *symbol;
256 } 72 }
257 } 73 }
258 } 74 }
259
260 return out;
261} 75}
262 76
263void ARM_Interface::LogBacktrace() const { 77void ARM_Interface::LogBacktrace() const {
264 const VAddr sp = GetReg(13); 78 const VAddr sp = GetSP();
265 const VAddr pc = GetPC(); 79 const VAddr pc = GetPC();
266 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); 80 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
267 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", 81 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..bcec4b3b8 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -101,6 +101,12 @@ public:
101 virtual u64 GetPC() const = 0; 101 virtual u64 GetPC() const = 0;
102 102
103 /** 103 /**
104 * Get the current Stack Pointer
105 * @return Returns current SP
106 */
107 virtual u64 GetSP() const = 0;
108
109 /**
104 * Get an ARM register 110 * Get an ARM register
105 * @param index Register index 111 * @param index Register index
106 * @return Returns the value in the register 112 * @return Returns the value in the register
@@ -171,6 +177,9 @@ public:
171 /// Prepare core for thread reschedule (if needed to correctly handle state) 177 /// Prepare core for thread reschedule (if needed to correctly handle state)
172 virtual void PrepareReschedule() = 0; 178 virtual void PrepareReschedule() = 0;
173 179
180 /// Signal an interrupt and ask the core to halt as soon as possible.
181 virtual void SignalInterrupt() = 0;
182
174 struct BacktraceEntry { 183 struct BacktraceEntry {
175 std::string module; 184 std::string module;
176 u64 address; 185 u64 address;
@@ -180,16 +189,12 @@ public:
180 }; 189 };
181 190
182 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, 191 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
192 const ThreadContext32& ctx);
193 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
183 const ThreadContext64& ctx); 194 const ThreadContext64& ctx);
184 195
185 std::vector<BacktraceEntry> GetBacktrace() const; 196 virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
186 197
187 /// fp (= r29) points to the last frame record.
188 /// Note that this is the frame record for the *previous* frame, not the current one.
189 /// Note we need to subtract 4 from our last read to get the proper address
190 /// Frame records are two words long:
191 /// fp+0 : pointer to previous frame record
192 /// fp+8 : value of lr for frame
193 void LogBacktrace() const; 198 void LogBacktrace() const;
194 199
195protected: 200protected:
@@ -197,6 +202,8 @@ protected:
197 System& system; 202 System& system;
198 CPUInterrupts& interrupt_handlers; 203 CPUInterrupts& interrupt_handlers;
199 bool uses_wall_clock; 204 bool uses_wall_clock;
205
206 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
200}; 207};
201 208
202} // namespace Core 209} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 286976623..da5659046 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -25,6 +25,9 @@ namespace Core {
25 25
26using namespace Common::Literals; 26using namespace Common::Literals;
27 27
28constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
29constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
30
28class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 31class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
29public: 32public:
30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 33 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -70,11 +73,13 @@ public:
70 } 73 }
71 74
72 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 75 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
76 parent.LogBacktrace();
73 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 77 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
74 MemoryReadCode(pc)); 78 MemoryReadCode(pc));
75 } 79 }
76 80
77 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 81 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
82 parent.LogBacktrace();
78 LOG_CRITICAL(Core_ARM, 83 LOG_CRITICAL(Core_ARM,
79 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 84 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
80 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 85 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -82,15 +87,13 @@ public:
82 } 87 }
83 88
84 void CallSVC(u32 swi) override { 89 void CallSVC(u32 swi) override {
85 parent.svc_called = true;
86 parent.svc_swi = swi; 90 parent.svc_swi = swi;
87 parent.jit->HaltExecution(); 91 parent.jit.load()->HaltExecution(svc_call);
88 } 92 }
89 93
90 void AddTicks(u64 ticks) override { 94 void AddTicks(u64 ticks) override {
91 if (parent.uses_wall_clock) { 95 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
92 return; 96
93 }
94 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 97 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
95 // rough approximation of the amount of executed ticks in the system, it may be thrown off 98 // rough approximation of the amount of executed ticks in the system, it may be thrown off
96 // if not all cores are doing a similar amount of work. Instead of doing this, we should 99 // if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -106,12 +109,8 @@ public:
106 } 109 }
107 110
108 u64 GetTicksRemaining() override { 111 u64 GetTicksRemaining() override {
109 if (parent.uses_wall_clock) { 112 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
110 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 113
111 return minimum_run_cycles;
112 }
113 return 0U;
114 }
115 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 114 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
116 } 115 }
117 116
@@ -146,11 +145,19 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
146 145
147 // Timing 146 // Timing
148 config.wall_clock_cntpct = uses_wall_clock; 147 config.wall_clock_cntpct = uses_wall_clock;
148 config.enable_cycle_counting = !uses_wall_clock;
149 149
150 // Code cache size 150 // Code cache size
151 config.code_cache_size = 512_MiB; 151 config.code_cache_size = 512_MiB;
152 config.far_code_offset = 400_MiB; 152 config.far_code_offset = 400_MiB;
153 153
154 // null_jit
155 if (!page_table) {
156 // Don't waste too much memory on null_jit
157 config.code_cache_size = 8_MiB;
158 config.far_code_offset = 4_MiB;
159 }
160
154 // Safe optimizations 161 // Safe optimizations
155 if (Settings::values.cpu_debug_mode) { 162 if (Settings::values.cpu_debug_mode) {
156 if (!Settings::values.cpuopt_page_tables) { 163 if (!Settings::values.cpuopt_page_tables) {
@@ -186,35 +193,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (!Settings::values.cpuopt_recompile_exclusives) { 193 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false; 194 config.recompile_on_exclusive_fastmem_failure = false;
188 } 195 }
189 } 196 } else {
197 // Unsafe optimizations
198 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
199 config.unsafe_optimizations = true;
200 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
202 }
203 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
208 }
209 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
210 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
211 }
212 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
214 }
215 }
190 216
191 // Unsafe optimizations 217 // Curated optimizations
192 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 218 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
193 config.unsafe_optimizations = true; 219 config.unsafe_optimizations = true;
194 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
195 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 220 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
196 }
197 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
198 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
199 }
200 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 221 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
202 }
203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 222 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 223 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 } 224 }
209 }
210 225
211 // Curated optimizations 226 // Paranoia mode for debugging optimizations
212 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 227 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
213 config.unsafe_optimizations = true; 228 config.unsafe_optimizations = false;
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 229 config.optimizations = Dynarmic::no_optimizations;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 230 }
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
218 } 231 }
219 232
220 return std::make_unique<Dynarmic::A32::Jit>(config); 233 return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,20 +235,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
222 235
223void ARM_Dynarmic_32::Run() { 236void ARM_Dynarmic_32::Run() {
224 while (true) { 237 while (true) {
225 jit->Run(); 238 const auto hr = jit.load()->Run();
226 if (!svc_called) { 239 if (Has(hr, svc_call)) {
227 break; 240 Kernel::Svc::Call(system, svc_swi);
228 } 241 }
229 svc_called = false; 242 if (Has(hr, break_loop) || !uses_wall_clock) {
230 Kernel::Svc::Call(system, svc_swi);
231 if (shutdown) {
232 break; 243 break;
233 } 244 }
234 } 245 }
235} 246}
236 247
237void ARM_Dynarmic_32::Step() { 248void ARM_Dynarmic_32::Step() {
238 jit->Step(); 249 jit.load()->Step();
239} 250}
240 251
241ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 252ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -245,24 +256,28 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handl
245 cb(std::make_unique<DynarmicCallbacks32>(*this)), 256 cb(std::make_unique<DynarmicCallbacks32>(*this)),
246 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 257 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
247 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 258 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
248 jit(MakeJit(nullptr)) {} 259 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
249 260
250ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 261ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
251 262
252void ARM_Dynarmic_32::SetPC(u64 pc) { 263void ARM_Dynarmic_32::SetPC(u64 pc) {
253 jit->Regs()[15] = static_cast<u32>(pc); 264 jit.load()->Regs()[15] = static_cast<u32>(pc);
254} 265}
255 266
256u64 ARM_Dynarmic_32::GetPC() const { 267u64 ARM_Dynarmic_32::GetPC() const {
257 return jit->Regs()[15]; 268 return jit.load()->Regs()[15];
269}
270
271u64 ARM_Dynarmic_32::GetSP() const {
272 return jit.load()->Regs()[13];
258} 273}
259 274
260u64 ARM_Dynarmic_32::GetReg(int index) const { 275u64 ARM_Dynarmic_32::GetReg(int index) const {
261 return jit->Regs()[index]; 276 return jit.load()->Regs()[index];
262} 277}
263 278
264void ARM_Dynarmic_32::SetReg(int index, u64 value) { 279void ARM_Dynarmic_32::SetReg(int index, u64 value) {
265 jit->Regs()[index] = static_cast<u32>(value); 280 jit.load()->Regs()[index] = static_cast<u32>(value);
266} 281}
267 282
268u128 ARM_Dynarmic_32::GetVectorReg(int index) const { 283u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -272,11 +287,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
272void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} 287void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
273 288
274u32 ARM_Dynarmic_32::GetPSTATE() const { 289u32 ARM_Dynarmic_32::GetPSTATE() const {
275 return jit->Cpsr(); 290 return jit.load()->Cpsr();
276} 291}
277 292
278void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { 293void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
279 jit->SetCpsr(cpsr); 294 jit.load()->SetCpsr(cpsr);
280} 295}
281 296
282u64 ARM_Dynarmic_32::GetTlsAddress() const { 297u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -297,7 +312,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
297 312
298void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 313void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
299 Dynarmic::A32::Context context; 314 Dynarmic::A32::Context context;
300 jit->SaveContext(context); 315 jit.load()->SaveContext(context);
301 ctx.cpu_registers = context.Regs(); 316 ctx.cpu_registers = context.Regs();
302 ctx.extension_registers = context.ExtRegs(); 317 ctx.extension_registers = context.ExtRegs();
303 ctx.cpsr = context.Cpsr(); 318 ctx.cpsr = context.Cpsr();
@@ -310,24 +325,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
310 context.ExtRegs() = ctx.extension_registers; 325 context.ExtRegs() = ctx.extension_registers;
311 context.SetCpsr(ctx.cpsr); 326 context.SetCpsr(ctx.cpsr);
312 context.SetFpscr(ctx.fpscr); 327 context.SetFpscr(ctx.fpscr);
313 jit->LoadContext(context); 328 jit.load()->LoadContext(context);
314} 329}
315 330
316void ARM_Dynarmic_32::PrepareReschedule() { 331void ARM_Dynarmic_32::PrepareReschedule() {
317 jit->HaltExecution(); 332 jit.load()->HaltExecution(break_loop);
318 shutdown = true; 333}
334
335void ARM_Dynarmic_32::SignalInterrupt() {
336 jit.load()->HaltExecution(break_loop);
319} 337}
320 338
321void ARM_Dynarmic_32::ClearInstructionCache() { 339void ARM_Dynarmic_32::ClearInstructionCache() {
322 jit->ClearCache(); 340 jit.load()->ClearCache();
323} 341}
324 342
325void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { 343void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
326 jit->InvalidateCacheRange(static_cast<u32>(addr), size); 344 jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
327} 345}
328 346
329void ARM_Dynarmic_32::ClearExclusiveState() { 347void ARM_Dynarmic_32::ClearExclusiveState() {
330 jit->ClearExclusiveState(); 348 jit.load()->ClearExclusiveState();
331} 349}
332 350
333void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, 351void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -338,13 +356,29 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
338 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 356 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
339 auto iter = jit_cache.find(key); 357 auto iter = jit_cache.find(key);
340 if (iter != jit_cache.end()) { 358 if (iter != jit_cache.end()) {
341 jit = iter->second; 359 jit.store(iter->second.get());
342 LoadContext(ctx); 360 LoadContext(ctx);
343 return; 361 return;
344 } 362 }
345 jit = MakeJit(&page_table); 363 std::shared_ptr new_jit = MakeJit(&page_table);
364 jit.store(new_jit.get());
346 LoadContext(ctx); 365 LoadContext(ctx);
347 jit_cache.emplace(key, jit); 366 jit_cache.emplace(key, std::move(new_jit));
367}
368
369std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
370 u64 sp, u64 lr) {
371 // No way to get accurate stack traces in A32 yet
372 return {};
373}
374
375std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
376 System& system, const ThreadContext32& ctx) {
377 return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
378}
379
380std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
381 return GetBacktrace(system, GetReg(13), GetReg(14));
348} 382}
349 383
350} // namespace Core 384} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..1b628f94d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -34,6 +35,7 @@ public:
34 35
35 void SetPC(u64 pc) override; 36 void SetPC(u64 pc) override;
36 u64 GetPC() const override; 37 u64 GetPC() const override;
38 u64 GetSP() const override;
37 u64 GetReg(int index) const override; 39 u64 GetReg(int index) const override;
38 void SetReg(int index, u64 value) override; 40 void SetReg(int index, u64 value) override;
39 u128 GetVectorReg(int index) const override; 41 u128 GetVectorReg(int index) const override;
@@ -57,6 +59,7 @@ public:
57 void LoadContext(const ThreadContext64& ctx) override {} 59 void LoadContext(const ThreadContext64& ctx) override {}
58 60
59 void PrepareReschedule() override; 61 void PrepareReschedule() override;
62 void SignalInterrupt() override;
60 void ClearExclusiveState() override; 63 void ClearExclusiveState() override;
61 64
62 void ClearInstructionCache() override; 65 void ClearInstructionCache() override;
@@ -64,9 +67,16 @@ public:
64 void PageTableChanged(Common::PageTable& new_page_table, 67 void PageTableChanged(Common::PageTable& new_page_table,
65 std::size_t new_address_space_size_in_bits) override; 68 std::size_t new_address_space_size_in_bits) override;
66 69
70 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
71 const ThreadContext32& ctx);
72
73 std::vector<BacktraceEntry> GetBacktrace() const override;
74
67private: 75private:
68 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 76 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
69 77
78 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
79
70 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 80 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
71 using JitCacheType = 81 using JitCacheType =
72 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; 82 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +89,14 @@ private:
79 std::shared_ptr<DynarmicCP15> cp15; 89 std::shared_ptr<DynarmicCP15> cp15;
80 std::size_t core_index; 90 std::size_t core_index;
81 DynarmicExclusiveMonitor& exclusive_monitor; 91 DynarmicExclusiveMonitor& exclusive_monitor;
82 std::shared_ptr<Dynarmic::A32::Jit> jit; 92
93 std::shared_ptr<Dynarmic::A32::Jit> null_jit;
94
95 // A raw pointer here is fine; we never delete Jit instances.
96 std::atomic<Dynarmic::A32::Jit*> jit;
83 97
84 // SVC callback 98 // SVC callback
85 u32 svc_swi{}; 99 u32 svc_swi{};
86 bool svc_called{};
87
88 bool shutdown{};
89}; 100};
90 101
91} // namespace Core 102} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index d96226c41..871d9d10e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -26,6 +26,9 @@ namespace Core {
26using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
27using namespace Common::Literals; 27using namespace Common::Literals;
28 28
29constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
30constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
31
29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 32class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
30public: 33public:
31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 34 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -81,6 +84,7 @@ public:
81 } 84 }
82 85
83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 86 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
87 parent.LogBacktrace();
84 LOG_ERROR(Core_ARM, 88 LOG_ERROR(Core_ARM,
85 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 89 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
86 num_instructions, MemoryReadCode(pc)); 90 num_instructions, MemoryReadCode(pc));
@@ -93,17 +97,19 @@ public:
93 static constexpr u64 ICACHE_LINE_SIZE = 64; 97 static constexpr u64 ICACHE_LINE_SIZE = 64;
94 98
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); 99 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); 100 parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break; 101 break;
98 } 102 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: 103 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache(); 104 parent.system.InvalidateCpuInstructionCaches();
101 break; 105 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: 106 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default: 107 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); 108 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break; 109 break;
106 } 110 }
111
112 parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
107 } 113 }
108 114
109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 115 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -116,21 +122,19 @@ public:
116 return; 122 return;
117 case Dynarmic::A64::Exception::Breakpoint: 123 case Dynarmic::A64::Exception::Breakpoint:
118 default: 124 default:
125 parent.LogBacktrace();
119 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 126 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
120 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 127 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
121 } 128 }
122 } 129 }
123 130
124 void CallSVC(u32 swi) override { 131 void CallSVC(u32 swi) override {
125 parent.svc_called = true;
126 parent.svc_swi = swi; 132 parent.svc_swi = swi;
127 parent.jit->HaltExecution(); 133 parent.jit.load()->HaltExecution(svc_call);
128 } 134 }
129 135
130 void AddTicks(u64 ticks) override { 136 void AddTicks(u64 ticks) override {
131 if (parent.uses_wall_clock) { 137 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
132 return;
133 }
134 138
135 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 139 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
136 // rough approximation of the amount of executed ticks in the system, it may be thrown off 140 // rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -145,12 +149,8 @@ public:
145 } 149 }
146 150
147 u64 GetTicksRemaining() override { 151 u64 GetTicksRemaining() override {
148 if (parent.uses_wall_clock) { 152 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
149 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 153
150 return minimum_run_cycles;
151 }
152 return 0U;
153 }
154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
155 } 155 }
156 156
@@ -206,11 +206,19 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
206 206
207 // Timing 207 // Timing
208 config.wall_clock_cntpct = uses_wall_clock; 208 config.wall_clock_cntpct = uses_wall_clock;
209 config.enable_cycle_counting = !uses_wall_clock;
209 210
210 // Code cache size 211 // Code cache size
211 config.code_cache_size = 512_MiB; 212 config.code_cache_size = 512_MiB;
212 config.far_code_offset = 400_MiB; 213 config.far_code_offset = 400_MiB;
213 214
215 // null_jit
216 if (!page_table) {
217 // Don't waste too much memory on null_jit
218 config.code_cache_size = 8_MiB;
219 config.far_code_offset = 4_MiB;
220 }
221
214 // Safe optimizations 222 // Safe optimizations
215 if (Settings::values.cpu_debug_mode) { 223 if (Settings::values.cpu_debug_mode) {
216 if (!Settings::values.cpuopt_page_tables) { 224 if (!Settings::values.cpuopt_page_tables) {
@@ -246,35 +254,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
246 if (!Settings::values.cpuopt_recompile_exclusives) { 254 if (!Settings::values.cpuopt_recompile_exclusives) {
247 config.recompile_on_exclusive_fastmem_failure = false; 255 config.recompile_on_exclusive_fastmem_failure = false;
248 } 256 }
249 } 257 } else {
258 // Unsafe optimizations
259 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
260 config.unsafe_optimizations = true;
261 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
262 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
263 }
264 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
265 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
266 }
267 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
268 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
269 }
270 if (Settings::values.cpuopt_unsafe_fastmem_check) {
271 config.fastmem_address_space_bits = 64;
272 }
273 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
275 }
276 }
250 277
251 // Unsafe optimizations 278 // Curated optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 279 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
253 config.unsafe_optimizations = true; 280 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 281 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
256 }
257 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
259 }
260 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 282 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64; 283 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 284 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 } 285 }
269 }
270 286
271 // Curated optimizations 287 // Paranoia mode for debugging optimizations
272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 288 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
273 config.unsafe_optimizations = true; 289 config.unsafe_optimizations = false;
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 290 config.optimizations = Dynarmic::no_optimizations;
275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 291 }
276 config.fastmem_address_space_bits = 64;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
278 } 292 }
279 293
280 return std::make_shared<Dynarmic::A64::Jit>(config); 294 return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -282,20 +296,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
282 296
283void ARM_Dynarmic_64::Run() { 297void ARM_Dynarmic_64::Run() {
284 while (true) { 298 while (true) {
285 jit->Run(); 299 const auto hr = jit.load()->Run();
286 if (!svc_called) { 300 if (Has(hr, svc_call)) {
287 break; 301 Kernel::Svc::Call(system, svc_swi);
288 } 302 }
289 svc_called = false; 303 if (Has(hr, break_loop) || !uses_wall_clock) {
290 Kernel::Svc::Call(system, svc_swi);
291 if (shutdown) {
292 break; 304 break;
293 } 305 }
294 } 306 }
295} 307}
296 308
297void ARM_Dynarmic_64::Step() { 309void ARM_Dynarmic_64::Step() {
298 jit->Step(); 310 jit.load()->Step();
299} 311}
300 312
301ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 313ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -304,40 +316,44 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handl
304 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, 316 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
305 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 317 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
306 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 318 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
307 jit(MakeJit(nullptr, 48)) {} 319 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
308 320
309ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 321ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
310 322
311void ARM_Dynarmic_64::SetPC(u64 pc) { 323void ARM_Dynarmic_64::SetPC(u64 pc) {
312 jit->SetPC(pc); 324 jit.load()->SetPC(pc);
313} 325}
314 326
315u64 ARM_Dynarmic_64::GetPC() const { 327u64 ARM_Dynarmic_64::GetPC() const {
316 return jit->GetPC(); 328 return jit.load()->GetPC();
329}
330
331u64 ARM_Dynarmic_64::GetSP() const {
332 return jit.load()->GetSP();
317} 333}
318 334
319u64 ARM_Dynarmic_64::GetReg(int index) const { 335u64 ARM_Dynarmic_64::GetReg(int index) const {
320 return jit->GetRegister(index); 336 return jit.load()->GetRegister(index);
321} 337}
322 338
323void ARM_Dynarmic_64::SetReg(int index, u64 value) { 339void ARM_Dynarmic_64::SetReg(int index, u64 value) {
324 jit->SetRegister(index, value); 340 jit.load()->SetRegister(index, value);
325} 341}
326 342
327u128 ARM_Dynarmic_64::GetVectorReg(int index) const { 343u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
328 return jit->GetVector(index); 344 return jit.load()->GetVector(index);
329} 345}
330 346
331void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { 347void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
332 jit->SetVector(index, value); 348 jit.load()->SetVector(index, value);
333} 349}
334 350
335u32 ARM_Dynarmic_64::GetPSTATE() const { 351u32 ARM_Dynarmic_64::GetPSTATE() const {
336 return jit->GetPstate(); 352 return jit.load()->GetPstate();
337} 353}
338 354
339void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { 355void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
340 jit->SetPstate(pstate); 356 jit.load()->SetPstate(pstate);
341} 357}
342 358
343u64 ARM_Dynarmic_64::GetTlsAddress() const { 359u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -357,42 +373,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
357} 373}
358 374
359void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 375void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
360 ctx.cpu_registers = jit->GetRegisters(); 376 Dynarmic::A64::Jit* j = jit.load();
361 ctx.sp = jit->GetSP(); 377 ctx.cpu_registers = j->GetRegisters();
362 ctx.pc = jit->GetPC(); 378 ctx.sp = j->GetSP();
363 ctx.pstate = jit->GetPstate(); 379 ctx.pc = j->GetPC();
364 ctx.vector_registers = jit->GetVectors(); 380 ctx.pstate = j->GetPstate();
365 ctx.fpcr = jit->GetFpcr(); 381 ctx.vector_registers = j->GetVectors();
366 ctx.fpsr = jit->GetFpsr(); 382 ctx.fpcr = j->GetFpcr();
383 ctx.fpsr = j->GetFpsr();
367 ctx.tpidr = cb->tpidr_el0; 384 ctx.tpidr = cb->tpidr_el0;
368} 385}
369 386
370void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { 387void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
371 jit->SetRegisters(ctx.cpu_registers); 388 Dynarmic::A64::Jit* j = jit.load();
372 jit->SetSP(ctx.sp); 389 j->SetRegisters(ctx.cpu_registers);
373 jit->SetPC(ctx.pc); 390 j->SetSP(ctx.sp);
374 jit->SetPstate(ctx.pstate); 391 j->SetPC(ctx.pc);
375 jit->SetVectors(ctx.vector_registers); 392 j->SetPstate(ctx.pstate);
376 jit->SetFpcr(ctx.fpcr); 393 j->SetVectors(ctx.vector_registers);
377 jit->SetFpsr(ctx.fpsr); 394 j->SetFpcr(ctx.fpcr);
395 j->SetFpsr(ctx.fpsr);
378 SetTPIDR_EL0(ctx.tpidr); 396 SetTPIDR_EL0(ctx.tpidr);
379} 397}
380 398
381void ARM_Dynarmic_64::PrepareReschedule() { 399void ARM_Dynarmic_64::PrepareReschedule() {
382 jit->HaltExecution(); 400 jit.load()->HaltExecution(break_loop);
383 shutdown = true; 401}
402
403void ARM_Dynarmic_64::SignalInterrupt() {
404 jit.load()->HaltExecution(break_loop);
384} 405}
385 406
386void ARM_Dynarmic_64::ClearInstructionCache() { 407void ARM_Dynarmic_64::ClearInstructionCache() {
387 jit->ClearCache(); 408 jit.load()->ClearCache();
388} 409}
389 410
390void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { 411void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
391 jit->InvalidateCacheRange(addr, size); 412 jit.load()->InvalidateCacheRange(addr, size);
392} 413}
393 414
394void ARM_Dynarmic_64::ClearExclusiveState() { 415void ARM_Dynarmic_64::ClearExclusiveState() {
395 jit->ClearExclusiveState(); 416 jit.load()->ClearExclusiveState();
396} 417}
397 418
398void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, 419void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -403,13 +424,48 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
403 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 424 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
404 auto iter = jit_cache.find(key); 425 auto iter = jit_cache.find(key);
405 if (iter != jit_cache.end()) { 426 if (iter != jit_cache.end()) {
406 jit = iter->second; 427 jit.store(iter->second.get());
407 LoadContext(ctx); 428 LoadContext(ctx);
408 return; 429 return;
409 } 430 }
410 jit = MakeJit(&page_table, new_address_space_size_in_bits); 431 std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
432 jit.store(new_jit.get());
411 LoadContext(ctx); 433 LoadContext(ctx);
412 jit_cache.emplace(key, jit); 434 jit_cache.emplace(key, std::move(new_jit));
435}
436
437std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
438 u64 fp, u64 lr) {
439 std::vector<BacktraceEntry> out;
440 auto& memory = system.Memory();
441
442 // fp (= r29) points to the last frame record.
443 // Note that this is the frame record for the *previous* frame, not the current one.
444 // Note we need to subtract 4 from our last read to get the proper address
445 // Frame records are two words long:
446 // fp+0 : pointer to previous frame record
447 // fp+8 : value of lr for frame
448 while (true) {
449 out.push_back({"", 0, lr, 0, ""});
450 if (!fp) {
451 break;
452 }
453 lr = memory.Read64(fp + 8) - 4;
454 fp = memory.Read64(fp);
455 }
456
457 SymbolicateBacktrace(system, out);
458
459 return out;
460}
461
462std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
463 System& system, const ThreadContext64& ctx) {
464 return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
465}
466
467std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
468 return GetBacktrace(system, GetReg(29), GetReg(30));
413} 469}
414 470
415} // namespace Core 471} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..78773e293 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -32,6 +33,7 @@ public:
32 33
33 void SetPC(u64 pc) override; 34 void SetPC(u64 pc) override;
34 u64 GetPC() const override; 35 u64 GetPC() const override;
36 u64 GetSP() const override;
35 u64 GetReg(int index) const override; 37 u64 GetReg(int index) const override;
36 void SetReg(int index, u64 value) override; 38 void SetReg(int index, u64 value) override;
37 u128 GetVectorReg(int index) const override; 39 u128 GetVectorReg(int index) const override;
@@ -51,6 +53,7 @@ public:
51 void LoadContext(const ThreadContext64& ctx) override; 53 void LoadContext(const ThreadContext64& ctx) override;
52 54
53 void PrepareReschedule() override; 55 void PrepareReschedule() override;
56 void SignalInterrupt() override;
54 void ClearExclusiveState() override; 57 void ClearExclusiveState() override;
55 58
56 void ClearInstructionCache() override; 59 void ClearInstructionCache() override;
@@ -58,10 +61,17 @@ public:
58 void PageTableChanged(Common::PageTable& new_page_table, 61 void PageTableChanged(Common::PageTable& new_page_table,
59 std::size_t new_address_space_size_in_bits) override; 62 std::size_t new_address_space_size_in_bits) override;
60 63
64 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
65 const ThreadContext64& ctx);
66
67 std::vector<BacktraceEntry> GetBacktrace() const override;
68
61private: 69private:
62 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 70 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
63 std::size_t address_space_bits) const; 71 std::size_t address_space_bits) const;
64 72
73 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
74
65 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 75 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
66 using JitCacheType = 76 using JitCacheType =
67 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; 77 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +83,13 @@ private:
73 std::size_t core_index; 83 std::size_t core_index;
74 DynarmicExclusiveMonitor& exclusive_monitor; 84 DynarmicExclusiveMonitor& exclusive_monitor;
75 85
76 std::shared_ptr<Dynarmic::A64::Jit> jit; 86 std::shared_ptr<Dynarmic::A64::Jit> null_jit;
87
88 // A raw pointer here is fine; we never delete Jit instances.
89 std::atomic<Dynarmic::A64::Jit*> jit;
77 90
78 // SVC callback 91 // SVC callback
79 u32 svc_swi{}; 92 u32 svc_swi{};
80 bool svc_called{};
81
82 bool shutdown{};
83}; 93};
84 94
85} // namespace Core 95} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index 5a15b43ef..b82c77f76 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <unordered_map>
8
9#include <dynarmic/interface/exclusive_monitor.h> 7#include <dynarmic/interface/exclusive_monitor.h>
10 8
11#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 000000000..26c44f0c7
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,190 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/bit_field.h"
6#include "common/common_funcs.h"
7#include "core/arm/symbols.h"
8#include "core/core.h"
9#include "core/memory.h"
10
11namespace Core {
12namespace {
13
14constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
15constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
16constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
17constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
18
19enum class ELFSymbolType : u8 {
20 None = 0,
21 Object = 1,
22 Function = 2,
23 Section = 3,
24 File = 4,
25 Common = 5,
26 TLS = 6,
27};
28
29enum class ELFSymbolBinding : u8 {
30 Local = 0,
31 Global = 1,
32 Weak = 2,
33};
34
35enum class ELFSymbolVisibility : u8 {
36 Default = 0,
37 Internal = 1,
38 Hidden = 2,
39 Protected = 3,
40};
41
42struct ELF64Symbol {
43 u32 name_index;
44 union {
45 u8 info;
46
47 BitField<0, 4, ELFSymbolType> type;
48 BitField<4, 4, ELFSymbolBinding> binding;
49 };
50 ELFSymbolVisibility visibility;
51 u16 sh_index;
52 u64 value;
53 u64 size;
54};
55static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
56
57struct ELF32Symbol {
58 u32 name_index;
59 u32 value;
60 u32 size;
61 union {
62 u8 info;
63
64 BitField<0, 4, ELFSymbolType> type;
65 BitField<4, 4, ELFSymbolBinding> binding;
66 };
67 ELFSymbolVisibility visibility;
68 u16 sh_index;
69};
70static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
71
72} // Anonymous namespace
73
74namespace Symbols {
75
76template <typename Word, typename ELFSymbol, typename ByteReader>
77static Symbols GetSymbols(ByteReader ReadBytes) {
78 const auto Read8{[&](u64 index) {
79 u8 ret;
80 ReadBytes(&ret, index, sizeof(u8));
81 return ret;
82 }};
83
84 const auto Read32{[&](u64 index) {
85 u32 ret;
86 ReadBytes(&ret, index, sizeof(u32));
87 return ret;
88 }};
89
90 const auto ReadWord{[&](u64 index) {
91 Word ret;
92 ReadBytes(&ret, index, sizeof(Word));
93 return ret;
94 }};
95
96 const u32 mod_offset = Read32(4);
97
98 if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
99 return {};
100 }
101
102 VAddr string_table_offset{};
103 VAddr symbol_table_offset{};
104 u64 symbol_entry_size{};
105
106 const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
107
108 VAddr dynamic_index = dynamic_offset;
109 while (true) {
110 const Word tag = ReadWord(dynamic_index);
111 const Word value = ReadWord(dynamic_index + sizeof(Word));
112 dynamic_index += 2 * sizeof(Word);
113
114 if (tag == ELF_DYNAMIC_TAG_NULL) {
115 break;
116 }
117
118 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
119 string_table_offset = value;
120 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
121 symbol_table_offset = value;
122 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
123 symbol_entry_size = value;
124 }
125 }
126
127 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
128 return {};
129 }
130
131 Symbols out;
132
133 VAddr symbol_index = symbol_table_offset;
134 while (symbol_index < string_table_offset) {
135 ELFSymbol symbol{};
136 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
137
138 VAddr string_offset = string_table_offset + symbol.name_index;
139 std::string name;
140 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
141 name += static_cast<char>(c);
142 }
143
144 symbol_index += symbol_entry_size;
145 out[name] = std::make_pair(symbol.value, symbol.size);
146 }
147
148 return out;
149}
150
151Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 const auto ReadBytes{
153 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
154
155 if (is_64) {
156 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
157 } else {
158 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
159 }
160}
161
162Symbols GetSymbols(std::span<const u8> data, bool is_64) {
163 const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
164 std::memcpy(ptr, data.data() + offset, size);
165 }};
166
167 if (is_64) {
168 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
169 } else {
170 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
171 }
172}
173
174std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
175 const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
176 const auto& [name, sym_info] = pair;
177 const auto& [start_address, size] = sym_info;
178 const auto end_address = start_address + size;
179 return addr >= start_address && addr < end_address;
180 });
181
182 if (iter == symbols.cend()) {
183 return std::nullopt;
184 }
185
186 return iter->first;
187}
188
189} // namespace Symbols
190} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 000000000..99e6a9f8e
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,27 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <optional>
9#include <span>
10#include <string>
11#include <utility>
12
13#include "common/common_types.h"
14
15namespace Core::Memory {
16class Memory;
17} // namespace Core::Memory
18
19namespace Core::Symbols {
20
21using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
22
23Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
24Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
25std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
26
27} // namespace Core::Symbols
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b0cfee3ee..b5e2bcae2 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -38,7 +38,6 @@
38#include "core/hle/service/apm/apm_controller.h" 38#include "core/hle/service/apm/apm_controller.h"
39#include "core/hle/service/filesystem/filesystem.h" 39#include "core/hle/service/filesystem/filesystem.h"
40#include "core/hle/service/glue/glue_manager.h" 40#include "core/hle/service/glue/glue_manager.h"
41#include "core/hle/service/hid/hid.h"
42#include "core/hle/service/service.h" 41#include "core/hle/service/service.h"
43#include "core/hle/service/sm/sm.h" 42#include "core/hle/service/sm/sm.h"
44#include "core/hle/service/time/time_manager.h" 43#include "core/hle/service/time/time_manager.h"
@@ -326,7 +325,9 @@ struct System::Impl {
326 is_powered_on = false; 325 is_powered_on = false;
327 exit_lock = false; 326 exit_lock = false;
328 327
329 gpu_core->NotifyShutdown(); 328 if (gpu_core != nullptr) {
329 gpu_core->NotifyShutdown();
330 }
330 331
331 services.reset(); 332 services.reset();
332 service_manager.reset(); 333 service_manager.reset();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 888828fd0..28b63be43 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -8,13 +8,13 @@
8#include <chrono> 8#include <chrono>
9#include <functional> 9#include <functional>
10#include <memory> 10#include <memory>
11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
13#include <thread> 14#include <thread>
14#include <vector> 15#include <vector>
15 16
16#include "common/common_types.h" 17#include "common/common_types.h"
17#include "common/spin_lock.h"
18#include "common/thread.h" 18#include "common/thread.h"
19#include "common/wall_clock.h" 19#include "common/wall_clock.h"
20 20
@@ -149,8 +149,8 @@ private:
149 std::shared_ptr<EventType> ev_lost; 149 std::shared_ptr<EventType> ev_lost;
150 Common::Event event{}; 150 Common::Event event{};
151 Common::Event pause_event{}; 151 Common::Event pause_event{};
152 Common::SpinLock basic_lock{}; 152 std::mutex basic_lock;
153 Common::SpinLock advance_lock{}; 153 std::mutex advance_lock;
154 std::unique_ptr<std::thread> timer_thread; 154 std::unique_ptr<std::thread> timer_thread;
155 std::atomic<bool> paused{}; 155 std::atomic<bool> paused{};
156 std::atomic<bool> paused_set{}; 156 std::atomic<bool> paused_set{};
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 21c7aefc8..9a3b62117 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <iterator>
9#include "common/common_funcs.h" 8#include "common/common_funcs.h"
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index f19ac4607..73e724f3d 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -10,7 +10,10 @@
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#ifndef _WIN32
13#include "common/string_util.h" 14#include "common/string_util.h"
15#endif
16
14#include "core/core.h" 17#include "core/core.h"
15#include "core/file_sys/common_funcs.h" 18#include "core/file_sys/common_funcs.h"
16#include "core/file_sys/content_archive.h" 19#include "core/file_sys/content_archive.h"
@@ -145,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
145 148
146 // LayeredExeFS 149 // LayeredExeFS
147 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 150 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
151 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
152
153 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
148 if (load_dir != nullptr && load_dir->GetSize() > 0) { 154 if (load_dir != nullptr && load_dir->GetSize() > 0) {
149 auto patch_dirs = load_dir->GetSubdirectories(); 155 const auto load_patch_dirs = load_dir->GetSubdirectories();
150 std::sort( 156 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
151 patch_dirs.begin(), patch_dirs.end(), 157 }
152 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
153
154 std::vector<VirtualDir> layers;
155 layers.reserve(patch_dirs.size() + 1);
156 for (const auto& subdir : patch_dirs) {
157 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
158 continue;
159 158
160 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); 159 std::sort(patch_dirs.begin(), patch_dirs.end(),
161 if (exefs_dir != nullptr) 160 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
162 layers.push_back(std::move(exefs_dir));
163 }
164 layers.push_back(exefs);
165 161
166 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); 162 std::vector<VirtualDir> layers;
167 if (layered != nullptr) { 163 layers.reserve(patch_dirs.size() + 1);
168 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); 164 for (const auto& subdir : patch_dirs) {
169 exefs = std::move(layered); 165 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
170 } 166 continue;
167
168 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
169 if (exefs_dir != nullptr)
170 layers.push_back(std::move(exefs_dir));
171 }
172 layers.push_back(exefs);
173
174 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
175 if (layered != nullptr) {
176 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
177 exefs = std::move(layered);
171 } 178 }
172 179
173 if (Settings::values.dump_exefs) { 180 if (Settings::values.dump_exefs) {
@@ -533,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
533 540
534 // SDMC mod directory (RomFS LayeredFS) 541 // SDMC mod directory (RomFS LayeredFS)
535 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 542 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
536 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && 543 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
537 IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { 544 std::string types;
538 const auto mod_disabled = 545 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
539 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); 546 AppendCommaIfNotEmpty(types, "LayeredExeFS");
540 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); 547 }
548 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
549 AppendCommaIfNotEmpty(types, "LayeredFS");
550 }
551
552 if (!types.empty()) {
553 const auto mod_disabled =
554 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
555 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
556 }
541 } 557 }
542 558
543 // DLC 559 // DLC
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 1eee916be..32435e123 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -6,7 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
9#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/swap.h" 13#include "common/swap.h"
12#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs_types.h"
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 7a646b5f1..4ada4a69b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
387 continue; 387 continue;
388 388
389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) { 389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
390 if (!FollowsNcaIdFormat(nca_dir->GetName())) 390 if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
391 continue; 391 continue;
392 }
392 393
393 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); 394 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
394 } 395 }
395 396
396 for (const auto& nca_file : d2_dir->GetFiles()) { 397 for (const auto& nca_file : d2_dir->GetFiles()) {
397 if (!FollowsNcaIdFormat(nca_file->GetName())) 398 if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
398 continue; 399 continue;
400 }
399 401
400 ids.push_back( 402 ids.push_back(
401 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); 403 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp
new file mode 100644
index 000000000..fadb5fb15
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.cpp
@@ -0,0 +1,18 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/mii_edit.h"
7
8namespace Core::Frontend {
9
10MiiEditApplet::~MiiEditApplet() = default;
11
12void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
13 LOG_WARNING(Service_AM, "(STUBBED) called");
14
15 callback();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h
new file mode 100644
index 000000000..cca0e931d
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.h
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9namespace Core::Frontend {
10
11class MiiEditApplet {
12public:
13 virtual ~MiiEditApplet();
14
15 virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
16};
17
18class DefaultMiiEditApplet final : public MiiEditApplet {
19public:
20 void ShowMiiEdit(const std::function<void()>& callback) const override;
21};
22
23} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index e413a520a..b3bffecb2 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -42,11 +42,20 @@ public:
42 context.MakeCurrent(); 42 context.MakeCurrent();
43 } 43 }
44 ~Scoped() { 44 ~Scoped() {
45 context.DoneCurrent(); 45 if (active) {
46 context.DoneCurrent();
47 }
48 }
49
50 /// In the event that context was destroyed before the Scoped is destroyed, this provides a
51 /// mechanism to prevent calling a destroyed object's method during the deconstructor
52 void Cancel() {
53 active = false;
46 } 54 }
47 55
48 private: 56 private:
49 GraphicsContext& context; 57 GraphicsContext& context;
58 bool active{true};
50 }; 59 };
51 60
52 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 61 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index eef0ff493..de565048b 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
132} 132}
133 133
134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { 134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
135 std::lock_guard lock{mutex}; 135 std::unique_lock lock{mutex};
136 auto& raw_status = console.motion_values.raw_status; 136 auto& raw_status = console.motion_values.raw_status;
137 auto& emulated = console.motion_values.emulated; 137 auto& emulated = console.motion_values.emulated;
138 138
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
151 emulated.UpdateOrientation(raw_status.delta_timestamp); 151 emulated.UpdateOrientation(raw_status.delta_timestamp);
152 152
153 if (is_configuring) { 153 if (is_configuring) {
154 lock.unlock();
154 TriggerOnChange(ConsoleTriggerType::Motion); 155 TriggerOnChange(ConsoleTriggerType::Motion);
155 return; 156 return;
156 } 157 }
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
166 // Find what is this value 167 // Find what is this value
167 motion.verticalization_error = 0.0f; 168 motion.verticalization_error = 0.0f;
168 169
170 lock.unlock();
169 TriggerOnChange(ConsoleTriggerType::Motion); 171 TriggerOnChange(ConsoleTriggerType::Motion);
170} 172}
171 173
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
173 if (index >= console.touch_values.size()) { 175 if (index >= console.touch_values.size()) {
174 return; 176 return;
175 } 177 }
176 std::lock_guard lock{mutex}; 178 std::unique_lock lock{mutex};
177 179
178 console.touch_values[index] = TransformToTouch(callback); 180 console.touch_values[index] = TransformToTouch(callback);
179 181
180 if (is_configuring) { 182 if (is_configuring) {
183 lock.unlock();
181 TriggerOnChange(ConsoleTriggerType::Touch); 184 TriggerOnChange(ConsoleTriggerType::Touch);
182 return; 185 return;
183 } 186 }
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
189 .pressed = console.touch_values[index].pressed.value, 192 .pressed = console.touch_values[index].pressed.value,
190 }; 193 };
191 194
195 lock.unlock();
192 TriggerOnChange(ConsoleTriggerType::Touch); 196 TriggerOnChange(ConsoleTriggerType::Touch);
193} 197}
194 198
195ConsoleMotionValues EmulatedConsole::GetMotionValues() const { 199ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
200 std::scoped_lock lock{mutex};
196 return console.motion_values; 201 return console.motion_values;
197} 202}
198 203
199TouchValues EmulatedConsole::GetTouchValues() const { 204TouchValues EmulatedConsole::GetTouchValues() const {
205 std::scoped_lock lock{mutex};
200 return console.touch_values; 206 return console.touch_values;
201} 207}
202 208
203ConsoleMotion EmulatedConsole::GetMotion() const { 209ConsoleMotion EmulatedConsole::GetMotion() const {
210 std::scoped_lock lock{mutex};
204 return console.motion_state; 211 return console.motion_state;
205} 212}
206 213
207TouchFingerState EmulatedConsole::GetTouch() const { 214TouchFingerState EmulatedConsole::GetTouch() const {
215 std::scoped_lock lock{mutex};
208 return console.touch_state; 216 return console.touch_state;
209} 217}
210 218
211void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { 219void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
220 std::scoped_lock lock{callback_mutex};
212 for (const auto& poller_pair : callback_list) { 221 for (const auto& poller_pair : callback_list) {
213 const ConsoleUpdateCallback& poller = poller_pair.second; 222 const ConsoleUpdateCallback& poller = poller_pair.second;
214 if (poller.on_change) { 223 if (poller.on_change) {
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
218} 227}
219 228
220int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { 229int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
221 std::lock_guard lock{mutex}; 230 std::scoped_lock lock{callback_mutex};
222 callback_list.insert_or_assign(last_callback_key, update_callback); 231 callback_list.insert_or_assign(last_callback_key, update_callback);
223 return last_callback_key++; 232 return last_callback_key++;
224} 233}
225 234
226void EmulatedConsole::DeleteCallback(int key) { 235void EmulatedConsole::DeleteCallback(int key) {
227 std::lock_guard lock{mutex}; 236 std::scoped_lock lock{callback_mutex};
228 const auto& iterator = callback_list.find(key); 237 const auto& iterator = callback_list.find(key);
229 if (iterator == callback_list.end()) { 238 if (iterator == callback_list.end()) {
230 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 239 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 5eb170823..53677bdc5 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -183,6 +183,7 @@ private:
183 TouchDevices touch_devices; 183 TouchDevices touch_devices;
184 184
185 mutable std::mutex mutex; 185 mutable std::mutex mutex;
186 mutable std::mutex callback_mutex;
186 std::unordered_map<int, ConsoleUpdateCallback> callback_list; 187 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
187 int last_callback_key = 0; 188 int last_callback_key = 0;
188 189
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7e05666d6..c3f21066c 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() {
353} 353}
354 354
355void EmulatedController::EnableSystemButtons() { 355void EmulatedController::EnableSystemButtons() {
356 std::scoped_lock lock{mutex};
356 system_buttons_enabled = true; 357 system_buttons_enabled = true;
357} 358}
358 359
359void EmulatedController::DisableSystemButtons() { 360void EmulatedController::DisableSystemButtons() {
361 std::scoped_lock lock{mutex};
360 system_buttons_enabled = false; 362 system_buttons_enabled = false;
361} 363}
362 364
363void EmulatedController::ResetSystemButtons() { 365void EmulatedController::ResetSystemButtons() {
366 std::scoped_lock lock{mutex};
364 controller.home_button_state.home.Assign(false); 367 controller.home_button_state.home.Assign(false);
365 controller.capture_button_state.capture.Assign(false); 368 controller.capture_button_state.capture.Assign(false);
366} 369}
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
494 if (index >= controller.button_values.size()) { 497 if (index >= controller.button_values.size()) {
495 return; 498 return;
496 } 499 }
497 { 500 std::unique_lock lock{mutex};
498 std::lock_guard lock{mutex}; 501 bool value_changed = false;
499 bool value_changed = false; 502 const auto new_status = TransformToButton(callback);
500 const auto new_status = TransformToButton(callback); 503 auto& current_status = controller.button_values[index];
501 auto& current_status = controller.button_values[index];
502 504
503 // Only read button values that have the same uuid or are pressed once 505 // Only read button values that have the same uuid or are pressed once
504 if (current_status.uuid != uuid) { 506 if (current_status.uuid != uuid) {
505 if (!new_status.value) { 507 if (!new_status.value) {
506 return; 508 return;
507 }
508 } 509 }
510 }
509 511
510 current_status.toggle = new_status.toggle; 512 current_status.toggle = new_status.toggle;
511 current_status.uuid = uuid; 513 current_status.uuid = uuid;
512
513 // Update button status with current
514 if (!current_status.toggle) {
515 current_status.locked = false;
516 if (current_status.value != new_status.value) {
517 current_status.value = new_status.value;
518 value_changed = true;
519 }
520 } else {
521 // Toggle button and lock status
522 if (new_status.value && !current_status.locked) {
523 current_status.locked = true;
524 current_status.value = !current_status.value;
525 value_changed = true;
526 }
527 514
528 // Unlock button ready for next press 515 // Update button status with current
529 if (!new_status.value && current_status.locked) { 516 if (!current_status.toggle) {
530 current_status.locked = false; 517 current_status.locked = false;
531 } 518 if (current_status.value != new_status.value) {
519 current_status.value = new_status.value;
520 value_changed = true;
532 } 521 }
533 522 } else {
534 if (!value_changed) { 523 // Toggle button and lock status
535 return; 524 if (new_status.value && !current_status.locked) {
525 current_status.locked = true;
526 current_status.value = !current_status.value;
527 value_changed = true;
536 } 528 }
537 529
538 if (is_configuring) { 530 // Unlock button ready for next press
539 controller.npad_button_state.raw = NpadButton::None; 531 if (!new_status.value && current_status.locked) {
540 controller.debug_pad_button_state.raw = 0; 532 current_status.locked = false;
541 TriggerOnChange(ControllerTriggerType::Button, false);
542 return;
543 } 533 }
534 }
544 535
545 switch (index) { 536 if (!value_changed) {
546 case Settings::NativeButton::A: 537 return;
547 controller.npad_button_state.a.Assign(current_status.value); 538 }
548 controller.debug_pad_button_state.a.Assign(current_status.value); 539
549 break; 540 if (is_configuring) {
550 case Settings::NativeButton::B: 541 controller.npad_button_state.raw = NpadButton::None;
551 controller.npad_button_state.b.Assign(current_status.value); 542 controller.debug_pad_button_state.raw = 0;
552 controller.debug_pad_button_state.b.Assign(current_status.value); 543 lock.unlock();
553 break; 544 TriggerOnChange(ControllerTriggerType::Button, false);
554 case Settings::NativeButton::X: 545 return;
555 controller.npad_button_state.x.Assign(current_status.value); 546 }
556 controller.debug_pad_button_state.x.Assign(current_status.value); 547
557 break; 548 switch (index) {
558 case Settings::NativeButton::Y: 549 case Settings::NativeButton::A:
559 controller.npad_button_state.y.Assign(current_status.value); 550 controller.npad_button_state.a.Assign(current_status.value);
560 controller.debug_pad_button_state.y.Assign(current_status.value); 551 controller.debug_pad_button_state.a.Assign(current_status.value);
561 break; 552 break;
562 case Settings::NativeButton::LStick: 553 case Settings::NativeButton::B:
563 controller.npad_button_state.stick_l.Assign(current_status.value); 554 controller.npad_button_state.b.Assign(current_status.value);
564 break; 555 controller.debug_pad_button_state.b.Assign(current_status.value);
565 case Settings::NativeButton::RStick: 556 break;
566 controller.npad_button_state.stick_r.Assign(current_status.value); 557 case Settings::NativeButton::X:
567 break; 558 controller.npad_button_state.x.Assign(current_status.value);
568 case Settings::NativeButton::L: 559 controller.debug_pad_button_state.x.Assign(current_status.value);
569 controller.npad_button_state.l.Assign(current_status.value); 560 break;
570 controller.debug_pad_button_state.l.Assign(current_status.value); 561 case Settings::NativeButton::Y:
571 break; 562 controller.npad_button_state.y.Assign(current_status.value);
572 case Settings::NativeButton::R: 563 controller.debug_pad_button_state.y.Assign(current_status.value);
573 controller.npad_button_state.r.Assign(current_status.value); 564 break;
574 controller.debug_pad_button_state.r.Assign(current_status.value); 565 case Settings::NativeButton::LStick:
575 break; 566 controller.npad_button_state.stick_l.Assign(current_status.value);
576 case Settings::NativeButton::ZL: 567 break;
577 controller.npad_button_state.zl.Assign(current_status.value); 568 case Settings::NativeButton::RStick:
578 controller.debug_pad_button_state.zl.Assign(current_status.value); 569 controller.npad_button_state.stick_r.Assign(current_status.value);
579 break; 570 break;
580 case Settings::NativeButton::ZR: 571 case Settings::NativeButton::L:
581 controller.npad_button_state.zr.Assign(current_status.value); 572 controller.npad_button_state.l.Assign(current_status.value);
582 controller.debug_pad_button_state.zr.Assign(current_status.value); 573 controller.debug_pad_button_state.l.Assign(current_status.value);
583 break; 574 break;
584 case Settings::NativeButton::Plus: 575 case Settings::NativeButton::R:
585 controller.npad_button_state.plus.Assign(current_status.value); 576 controller.npad_button_state.r.Assign(current_status.value);
586 controller.debug_pad_button_state.plus.Assign(current_status.value); 577 controller.debug_pad_button_state.r.Assign(current_status.value);
587 break; 578 break;
588 case Settings::NativeButton::Minus: 579 case Settings::NativeButton::ZL:
589 controller.npad_button_state.minus.Assign(current_status.value); 580 controller.npad_button_state.zl.Assign(current_status.value);
590 controller.debug_pad_button_state.minus.Assign(current_status.value); 581 controller.debug_pad_button_state.zl.Assign(current_status.value);
591 break; 582 break;
592 case Settings::NativeButton::DLeft: 583 case Settings::NativeButton::ZR:
593 controller.npad_button_state.left.Assign(current_status.value); 584 controller.npad_button_state.zr.Assign(current_status.value);
594 controller.debug_pad_button_state.d_left.Assign(current_status.value); 585 controller.debug_pad_button_state.zr.Assign(current_status.value);
595 break; 586 break;
596 case Settings::NativeButton::DUp: 587 case Settings::NativeButton::Plus:
597 controller.npad_button_state.up.Assign(current_status.value); 588 controller.npad_button_state.plus.Assign(current_status.value);
598 controller.debug_pad_button_state.d_up.Assign(current_status.value); 589 controller.debug_pad_button_state.plus.Assign(current_status.value);
599 break; 590 break;
600 case Settings::NativeButton::DRight: 591 case Settings::NativeButton::Minus:
601 controller.npad_button_state.right.Assign(current_status.value); 592 controller.npad_button_state.minus.Assign(current_status.value);
602 controller.debug_pad_button_state.d_right.Assign(current_status.value); 593 controller.debug_pad_button_state.minus.Assign(current_status.value);
603 break; 594 break;
604 case Settings::NativeButton::DDown: 595 case Settings::NativeButton::DLeft:
605 controller.npad_button_state.down.Assign(current_status.value); 596 controller.npad_button_state.left.Assign(current_status.value);
606 controller.debug_pad_button_state.d_down.Assign(current_status.value); 597 controller.debug_pad_button_state.d_left.Assign(current_status.value);
607 break; 598 break;
608 case Settings::NativeButton::SL: 599 case Settings::NativeButton::DUp:
609 controller.npad_button_state.left_sl.Assign(current_status.value); 600 controller.npad_button_state.up.Assign(current_status.value);
610 controller.npad_button_state.right_sl.Assign(current_status.value); 601 controller.debug_pad_button_state.d_up.Assign(current_status.value);
611 break; 602 break;
612 case Settings::NativeButton::SR: 603 case Settings::NativeButton::DRight:
613 controller.npad_button_state.left_sr.Assign(current_status.value); 604 controller.npad_button_state.right.Assign(current_status.value);
614 controller.npad_button_state.right_sr.Assign(current_status.value); 605 controller.debug_pad_button_state.d_right.Assign(current_status.value);
615 break; 606 break;
616 case Settings::NativeButton::Home: 607 case Settings::NativeButton::DDown:
617 if (!system_buttons_enabled) { 608 controller.npad_button_state.down.Assign(current_status.value);
618 break; 609 controller.debug_pad_button_state.d_down.Assign(current_status.value);
619 } 610 break;
620 controller.home_button_state.home.Assign(current_status.value); 611 case Settings::NativeButton::SL:
612 controller.npad_button_state.left_sl.Assign(current_status.value);
613 controller.npad_button_state.right_sl.Assign(current_status.value);
614 break;
615 case Settings::NativeButton::SR:
616 controller.npad_button_state.left_sr.Assign(current_status.value);
617 controller.npad_button_state.right_sr.Assign(current_status.value);
618 break;
619 case Settings::NativeButton::Home:
620 if (!system_buttons_enabled) {
621 break; 621 break;
622 case Settings::NativeButton::Screenshot: 622 }
623 if (!system_buttons_enabled) { 623 controller.home_button_state.home.Assign(current_status.value);
624 break; 624 break;
625 } 625 case Settings::NativeButton::Screenshot:
626 controller.capture_button_state.capture.Assign(current_status.value); 626 if (!system_buttons_enabled) {
627 break; 627 break;
628 } 628 }
629 controller.capture_button_state.capture.Assign(current_status.value);
630 break;
629 } 631 }
632
633 lock.unlock();
634
630 if (!is_connected) { 635 if (!is_connected) {
631 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { 636 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
632 Connect(); 637 Connect();
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
643 if (index >= controller.stick_values.size()) { 648 if (index >= controller.stick_values.size()) {
644 return; 649 return;
645 } 650 }
646 std::lock_guard lock{mutex}; 651 std::unique_lock lock{mutex};
647 const auto stick_value = TransformToStick(callback); 652 const auto stick_value = TransformToStick(callback);
648 653
649 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 654 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
659 if (is_configuring) { 664 if (is_configuring) {
660 controller.analog_stick_state.left = {}; 665 controller.analog_stick_state.left = {};
661 controller.analog_stick_state.right = {}; 666 controller.analog_stick_state.right = {};
667 lock.unlock();
662 TriggerOnChange(ControllerTriggerType::Stick, false); 668 TriggerOnChange(ControllerTriggerType::Stick, false);
663 return; 669 return;
664 } 670 }
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
685 break; 691 break;
686 } 692 }
687 693
694 lock.unlock();
688 TriggerOnChange(ControllerTriggerType::Stick, true); 695 TriggerOnChange(ControllerTriggerType::Stick, true);
689} 696}
690 697
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
693 if (index >= controller.trigger_values.size()) { 700 if (index >= controller.trigger_values.size()) {
694 return; 701 return;
695 } 702 }
696 std::lock_guard lock{mutex}; 703 std::unique_lock lock{mutex};
697 const auto trigger_value = TransformToTrigger(callback); 704 const auto trigger_value = TransformToTrigger(callback);
698 705
699 // Only read trigger values that have the same uuid or are pressed once 706 // Only read trigger values that have the same uuid or are pressed once
@@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
709 if (is_configuring) { 716 if (is_configuring) {
710 controller.gc_trigger_state.left = 0; 717 controller.gc_trigger_state.left = 0;
711 controller.gc_trigger_state.right = 0; 718 controller.gc_trigger_state.right = 0;
719 lock.unlock();
712 TriggerOnChange(ControllerTriggerType::Trigger, false); 720 TriggerOnChange(ControllerTriggerType::Trigger, false);
713 return; 721 return;
714 } 722 }
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
727 break; 735 break;
728 } 736 }
729 737
738 lock.unlock();
730 TriggerOnChange(ControllerTriggerType::Trigger, true); 739 TriggerOnChange(ControllerTriggerType::Trigger, true);
731} 740}
732 741
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
735 if (index >= controller.motion_values.size()) { 744 if (index >= controller.motion_values.size()) {
736 return; 745 return;
737 } 746 }
738 std::lock_guard lock{mutex}; 747 std::unique_lock lock{mutex};
739 auto& raw_status = controller.motion_values[index].raw_status; 748 auto& raw_status = controller.motion_values[index].raw_status;
740 auto& emulated = controller.motion_values[index].emulated; 749 auto& emulated = controller.motion_values[index].emulated;
741 750
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
756 force_update_motion = raw_status.force_update; 765 force_update_motion = raw_status.force_update;
757 766
758 if (is_configuring) { 767 if (is_configuring) {
768 lock.unlock();
759 TriggerOnChange(ControllerTriggerType::Motion, false); 769 TriggerOnChange(ControllerTriggerType::Motion, false);
760 return; 770 return;
761 } 771 }
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
767 motion.orientation = emulated.GetOrientation(); 777 motion.orientation = emulated.GetOrientation();
768 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 778 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
769 779
780 lock.unlock();
770 TriggerOnChange(ControllerTriggerType::Motion, true); 781 TriggerOnChange(ControllerTriggerType::Motion, true);
771} 782}
772 783
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
775 if (index >= controller.battery_values.size()) { 786 if (index >= controller.battery_values.size()) {
776 return; 787 return;
777 } 788 }
778 std::lock_guard lock{mutex}; 789 std::unique_lock lock{mutex};
779 controller.battery_values[index] = TransformToBattery(callback); 790 controller.battery_values[index] = TransformToBattery(callback);
780 791
781 if (is_configuring) { 792 if (is_configuring) {
793 lock.unlock();
782 TriggerOnChange(ControllerTriggerType::Battery, false); 794 TriggerOnChange(ControllerTriggerType::Battery, false);
783 return; 795 return;
784 } 796 }
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
835 }; 847 };
836 break; 848 break;
837 } 849 }
850
851 lock.unlock();
838 TriggerOnChange(ControllerTriggerType::Battery, true); 852 TriggerOnChange(ControllerTriggerType::Battery, true);
839} 853}
840 854
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
932} 946}
933 947
934bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { 948bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
949 std::scoped_lock lock{mutex};
935 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
936 switch (type) { 951 switch (type) {
937 case NpadStyleIndex::ProController: 952 case NpadStyleIndex::ProController:
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
947} 962}
948 963
949bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { 964bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
965 std::scoped_lock lock{mutex};
950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 966 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
951 switch (type) { 967 switch (type) {
952 case NpadStyleIndex::ProController: 968 case NpadStyleIndex::ProController:
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
982 LOG_ERROR(Service_HID, "Controller type {} is not supported", type); 998 LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
983 return; 999 return;
984 } 1000 }
985 {
986 std::lock_guard lock{mutex};
987 if (is_configuring) {
988 tmp_is_connected = true;
989 TriggerOnChange(ControllerTriggerType::Connected, false);
990 return;
991 }
992 1001
993 if (is_connected) { 1002 std::unique_lock lock{mutex};
994 return; 1003 if (is_configuring) {
995 } 1004 tmp_is_connected = true;
996 is_connected = true; 1005 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Connected, false);
1007 return;
1008 }
1009
1010 if (is_connected) {
1011 return;
997 } 1012 }
1013 is_connected = true;
1014
1015 lock.unlock();
998 TriggerOnChange(ControllerTriggerType::Connected, true); 1016 TriggerOnChange(ControllerTriggerType::Connected, true);
999} 1017}
1000 1018
1001void EmulatedController::Disconnect() { 1019void EmulatedController::Disconnect() {
1002 { 1020 std::unique_lock lock{mutex};
1003 std::lock_guard lock{mutex}; 1021 if (is_configuring) {
1004 if (is_configuring) { 1022 tmp_is_connected = false;
1005 tmp_is_connected = false; 1023 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Disconnected, false); 1024 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1007 return; 1025 return;
1008 } 1026 }
1009 1027
1010 if (!is_connected) { 1028 if (!is_connected) {
1011 return; 1029 return;
1012 }
1013 is_connected = false;
1014 } 1030 }
1031 is_connected = false;
1032
1033 lock.unlock();
1015 TriggerOnChange(ControllerTriggerType::Disconnected, true); 1034 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1016} 1035}
1017 1036
1018bool EmulatedController::IsConnected(bool get_temporary_value) const { 1037bool EmulatedController::IsConnected(bool get_temporary_value) const {
1038 std::scoped_lock lock{mutex};
1019 if (get_temporary_value && is_configuring) { 1039 if (get_temporary_value && is_configuring) {
1020 return tmp_is_connected; 1040 return tmp_is_connected;
1021 } 1041 }
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
1029} 1049}
1030 1050
1031NpadIdType EmulatedController::GetNpadIdType() const { 1051NpadIdType EmulatedController::GetNpadIdType() const {
1052 std::scoped_lock lock{mutex};
1032 return npad_id_type; 1053 return npad_id_type;
1033} 1054}
1034 1055
1035NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { 1056NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
1057 std::scoped_lock lock{mutex};
1036 if (get_temporary_value && is_configuring) { 1058 if (get_temporary_value && is_configuring) {
1037 return tmp_npad_type; 1059 return tmp_npad_type;
1038 } 1060 }
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1040} 1062}
1041 1063
1042void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1064void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1043 { 1065 std::unique_lock lock{mutex};
1044 std::lock_guard lock{mutex};
1045 1066
1046 if (is_configuring) { 1067 if (is_configuring) {
1047 if (tmp_npad_type == npad_type_) { 1068 if (tmp_npad_type == npad_type_) {
1048 return;
1049 }
1050 tmp_npad_type = npad_type_;
1051 TriggerOnChange(ControllerTriggerType::Type, false);
1052 return; 1069 return;
1053 } 1070 }
1071 tmp_npad_type = npad_type_;
1072 lock.unlock();
1073 TriggerOnChange(ControllerTriggerType::Type, false);
1074 return;
1075 }
1054 1076
1055 if (npad_type == npad_type_) { 1077 if (npad_type == npad_type_) {
1056 return; 1078 return;
1057 } 1079 }
1058 if (is_connected) { 1080 if (is_connected) {
1059 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1081 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1060 NpadIdTypeToIndex(npad_id_type)); 1082 NpadIdTypeToIndex(npad_id_type));
1061 }
1062 npad_type = npad_type_;
1063 } 1083 }
1084 npad_type = npad_type_;
1085
1086 lock.unlock();
1064 TriggerOnChange(ControllerTriggerType::Type, true); 1087 TriggerOnChange(ControllerTriggerType::Type, true);
1065} 1088}
1066 1089
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
1088} 1111}
1089 1112
1090ButtonValues EmulatedController::GetButtonsValues() const { 1113ButtonValues EmulatedController::GetButtonsValues() const {
1114 std::scoped_lock lock{mutex};
1091 return controller.button_values; 1115 return controller.button_values;
1092} 1116}
1093 1117
1094SticksValues EmulatedController::GetSticksValues() const { 1118SticksValues EmulatedController::GetSticksValues() const {
1119 std::scoped_lock lock{mutex};
1095 return controller.stick_values; 1120 return controller.stick_values;
1096} 1121}
1097 1122
1098TriggerValues EmulatedController::GetTriggersValues() const { 1123TriggerValues EmulatedController::GetTriggersValues() const {
1124 std::scoped_lock lock{mutex};
1099 return controller.trigger_values; 1125 return controller.trigger_values;
1100} 1126}
1101 1127
1102ControllerMotionValues EmulatedController::GetMotionValues() const { 1128ControllerMotionValues EmulatedController::GetMotionValues() const {
1129 std::scoped_lock lock{mutex};
1103 return controller.motion_values; 1130 return controller.motion_values;
1104} 1131}
1105 1132
1106ColorValues EmulatedController::GetColorsValues() const { 1133ColorValues EmulatedController::GetColorsValues() const {
1134 std::scoped_lock lock{mutex};
1107 return controller.color_values; 1135 return controller.color_values;
1108} 1136}
1109 1137
1110BatteryValues EmulatedController::GetBatteryValues() const { 1138BatteryValues EmulatedController::GetBatteryValues() const {
1139 std::scoped_lock lock{mutex};
1111 return controller.battery_values; 1140 return controller.battery_values;
1112} 1141}
1113 1142
1114HomeButtonState EmulatedController::GetHomeButtons() const { 1143HomeButtonState EmulatedController::GetHomeButtons() const {
1144 std::scoped_lock lock{mutex};
1115 if (is_configuring) { 1145 if (is_configuring) {
1116 return {}; 1146 return {};
1117 } 1147 }
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
1119} 1149}
1120 1150
1121CaptureButtonState EmulatedController::GetCaptureButtons() const { 1151CaptureButtonState EmulatedController::GetCaptureButtons() const {
1152 std::scoped_lock lock{mutex};
1122 if (is_configuring) { 1153 if (is_configuring) {
1123 return {}; 1154 return {};
1124 } 1155 }
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
1126} 1157}
1127 1158
1128NpadButtonState EmulatedController::GetNpadButtons() const { 1159NpadButtonState EmulatedController::GetNpadButtons() const {
1160 std::scoped_lock lock{mutex};
1129 if (is_configuring) { 1161 if (is_configuring) {
1130 return {}; 1162 return {};
1131 } 1163 }
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1133} 1165}
1134 1166
1135DebugPadButton EmulatedController::GetDebugPadButtons() const { 1167DebugPadButton EmulatedController::GetDebugPadButtons() const {
1168 std::scoped_lock lock{mutex};
1136 if (is_configuring) { 1169 if (is_configuring) {
1137 return {}; 1170 return {};
1138 } 1171 }
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1140} 1173}
1141 1174
1142AnalogSticks EmulatedController::GetSticks() const { 1175AnalogSticks EmulatedController::GetSticks() const {
1176 std::unique_lock lock{mutex};
1177
1143 if (is_configuring) { 1178 if (is_configuring) {
1144 return {}; 1179 return {};
1145 } 1180 }
1181
1146 // Some drivers like stick from buttons need constant refreshing 1182 // Some drivers like stick from buttons need constant refreshing
1147 for (auto& device : stick_devices) { 1183 for (auto& device : stick_devices) {
1148 if (!device) { 1184 if (!device) {
1149 continue; 1185 continue;
1150 } 1186 }
1187 lock.unlock();
1151 device->SoftUpdate(); 1188 device->SoftUpdate();
1189 lock.lock();
1152 } 1190 }
1191
1153 return controller.analog_stick_state; 1192 return controller.analog_stick_state;
1154} 1193}
1155 1194
1156NpadGcTriggerState EmulatedController::GetTriggers() const { 1195NpadGcTriggerState EmulatedController::GetTriggers() const {
1196 std::scoped_lock lock{mutex};
1157 if (is_configuring) { 1197 if (is_configuring) {
1158 return {}; 1198 return {};
1159 } 1199 }
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
1161} 1201}
1162 1202
1163MotionState EmulatedController::GetMotions() const { 1203MotionState EmulatedController::GetMotions() const {
1204 std::unique_lock lock{mutex};
1205
1206 // Some drivers like mouse motion need constant refreshing
1164 if (force_update_motion) { 1207 if (force_update_motion) {
1165 for (auto& device : motion_devices) { 1208 for (auto& device : motion_devices) {
1166 if (!device) { 1209 if (!device) {
1167 continue; 1210 continue;
1168 } 1211 }
1212 lock.unlock();
1169 device->ForceUpdate(); 1213 device->ForceUpdate();
1214 lock.lock();
1170 } 1215 }
1171 } 1216 }
1217
1172 return controller.motion_state; 1218 return controller.motion_state;
1173} 1219}
1174 1220
1175ControllerColors EmulatedController::GetColors() const { 1221ControllerColors EmulatedController::GetColors() const {
1222 std::scoped_lock lock{mutex};
1176 return controller.colors_state; 1223 return controller.colors_state;
1177} 1224}
1178 1225
1179BatteryLevelState EmulatedController::GetBattery() const { 1226BatteryLevelState EmulatedController::GetBattery() const {
1227 std::scoped_lock lock{mutex};
1180 return controller.battery_state; 1228 return controller.battery_state;
1181} 1229}
1182 1230
1183void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1231void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1232 std::scoped_lock lock{callback_mutex};
1184 for (const auto& poller_pair : callback_list) { 1233 for (const auto& poller_pair : callback_list) {
1185 const ControllerUpdateCallback& poller = poller_pair.second; 1234 const ControllerUpdateCallback& poller = poller_pair.second;
1186 if (!is_npad_service_update && poller.is_npad_service) { 1235 if (!is_npad_service_update && poller.is_npad_service) {
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
1193} 1242}
1194 1243
1195int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { 1244int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1196 std::lock_guard lock{mutex}; 1245 std::scoped_lock lock{callback_mutex};
1197 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 1246 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1198 return last_callback_key++; 1247 return last_callback_key++;
1199} 1248}
1200 1249
1201void EmulatedController::DeleteCallback(int key) { 1250void EmulatedController::DeleteCallback(int key) {
1202 std::lock_guard lock{mutex}; 1251 std::scoped_lock lock{callback_mutex};
1203 const auto& iterator = callback_list.find(key); 1252 const auto& iterator = callback_list.find(key);
1204 if (iterator == callback_list.end()) { 1253 if (iterator == callback_list.end()) {
1205 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 1254 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index aa52f9572..1e224685d 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -400,7 +400,7 @@ private:
400 */ 400 */
401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
402 402
403 NpadIdType npad_id_type; 403 const NpadIdType npad_id_type;
404 NpadStyleIndex npad_type{NpadStyleIndex::None}; 404 NpadStyleIndex npad_type{NpadStyleIndex::None};
405 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 405 NpadStyleTag supported_style_tag{NpadStyleSet::All};
406 bool is_connected{false}; 406 bool is_connected{false};
@@ -434,6 +434,7 @@ private:
434 StickDevices tas_stick_devices; 434 StickDevices tas_stick_devices;
435 435
436 mutable std::mutex mutex; 436 mutable std::mutex mutex;
437 mutable std::mutex callback_mutex;
437 std::unordered_map<int, ControllerUpdateCallback> callback_list; 438 std::unordered_map<int, ControllerUpdateCallback> callback_list;
438 int last_callback_key = 0; 439 int last_callback_key = 0;
439 440
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 708480f2d..2f84d2b52 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -15,6 +15,7 @@ EmulatedDevices::EmulatedDevices() = default;
15EmulatedDevices::~EmulatedDevices() = default; 15EmulatedDevices::~EmulatedDevices() = default;
16 16
17void EmulatedDevices::ReloadFromSettings() { 17void EmulatedDevices::ReloadFromSettings() {
18 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 19 ReloadInput();
19} 20}
20 21
@@ -66,6 +67,8 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 67 key_index++;
67 } 68 }
68 69
70 ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
71
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 72 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) { 73 if (!mouse_button_devices[index]) {
71 continue; 74 continue;
@@ -120,6 +123,13 @@ void EmulatedDevices::ReloadInput() {
120 }, 123 },
121 }); 124 });
122 } 125 }
126
127 if (ring_analog_device) {
128 ring_analog_device->SetCallback({
129 .on_change =
130 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
131 });
132 }
123} 133}
124 134
125void EmulatedDevices::UnloadInput() { 135void EmulatedDevices::UnloadInput() {
@@ -155,6 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) { 165 if (!is_configuring) {
156 return; 166 return;
157 } 167 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
158} 169}
159 170
160void EmulatedDevices::RestoreConfig() { 171void EmulatedDevices::RestoreConfig() {
@@ -164,12 +175,21 @@ void EmulatedDevices::RestoreConfig() {
164 ReloadFromSettings(); 175 ReloadFromSettings();
165} 176}
166 177
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) { 188 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) { 189 if (index >= device_status.keyboard_values.size()) {
170 return; 190 return;
171 } 191 }
172 std::lock_guard lock{mutex}; 192 std::unique_lock lock{mutex};
173 bool value_changed = false; 193 bool value_changed = false;
174 const auto new_status = TransformToButton(callback); 194 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index]; 195 auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +221,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
201 } 221 }
202 222
203 if (is_configuring) { 223 if (is_configuring) {
224 lock.unlock();
204 TriggerOnChange(DeviceTriggerType::Keyboard); 225 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return; 226 return;
206 } 227 }
@@ -208,6 +229,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex 229 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value); 230 UpdateKey(index, current_status.value);
210 231
232 lock.unlock();
211 TriggerOnChange(DeviceTriggerType::Keyboard); 233 TriggerOnChange(DeviceTriggerType::Keyboard);
212} 234}
213 235
@@ -227,7 +249,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
227 if (index >= device_status.keyboard_moddifier_values.size()) { 249 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return; 250 return;
229 } 251 }
230 std::lock_guard lock{mutex}; 252 std::unique_lock lock{mutex};
231 bool value_changed = false; 253 bool value_changed = false;
232 const auto new_status = TransformToButton(callback); 254 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index]; 255 auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +281,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
259 } 281 }
260 282
261 if (is_configuring) { 283 if (is_configuring) {
284 lock.unlock();
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 285 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return; 286 return;
264 } 287 }
@@ -289,6 +312,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
289 break; 312 break;
290 } 313 }
291 314
315 lock.unlock();
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 316 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293} 317}
294 318
@@ -297,7 +321,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
297 if (index >= device_status.mouse_button_values.size()) { 321 if (index >= device_status.mouse_button_values.size()) {
298 return; 322 return;
299 } 323 }
300 std::lock_guard lock{mutex}; 324 std::unique_lock lock{mutex};
301 bool value_changed = false; 325 bool value_changed = false;
302 const auto new_status = TransformToButton(callback); 326 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index]; 327 auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +353,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
329 } 353 }
330 354
331 if (is_configuring) { 355 if (is_configuring) {
356 lock.unlock();
332 TriggerOnChange(DeviceTriggerType::Mouse); 357 TriggerOnChange(DeviceTriggerType::Mouse);
333 return; 358 return;
334 } 359 }
@@ -351,6 +376,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
351 break; 376 break;
352 } 377 }
353 378
379 lock.unlock();
354 TriggerOnChange(DeviceTriggerType::Mouse); 380 TriggerOnChange(DeviceTriggerType::Mouse);
355} 381}
356 382
@@ -359,13 +385,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
359 if (index >= device_status.mouse_analog_values.size()) { 385 if (index >= device_status.mouse_analog_values.size()) {
360 return; 386 return;
361 } 387 }
362 std::lock_guard lock{mutex}; 388 std::unique_lock lock{mutex};
363 const auto analog_value = TransformToAnalog(callback); 389 const auto analog_value = TransformToAnalog(callback);
364 390
365 device_status.mouse_analog_values[index] = analog_value; 391 device_status.mouse_analog_values[index] = analog_value;
366 392
367 if (is_configuring) { 393 if (is_configuring) {
368 device_status.mouse_position_state = {}; 394 device_status.mouse_position_state = {};
395 lock.unlock();
369 TriggerOnChange(DeviceTriggerType::Mouse); 396 TriggerOnChange(DeviceTriggerType::Mouse);
370 return; 397 return;
371 } 398 }
@@ -379,17 +406,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
379 break; 406 break;
380 } 407 }
381 408
409 lock.unlock();
382 TriggerOnChange(DeviceTriggerType::Mouse); 410 TriggerOnChange(DeviceTriggerType::Mouse);
383} 411}
384 412
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { 413void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex}; 414 std::unique_lock lock{mutex};
387 const auto touch_value = TransformToTouch(callback); 415 const auto touch_value = TransformToTouch(callback);
388 416
389 device_status.mouse_stick_value = touch_value; 417 device_status.mouse_stick_value = touch_value;
390 418
391 if (is_configuring) { 419 if (is_configuring) {
392 device_status.mouse_position_state = {}; 420 device_status.mouse_position_state = {};
421 lock.unlock();
393 TriggerOnChange(DeviceTriggerType::Mouse); 422 TriggerOnChange(DeviceTriggerType::Mouse);
394 return; 423 return;
395 } 424 }
@@ -397,42 +426,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
397 device_status.mouse_position_state.x = touch_value.x.value; 426 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value; 427 device_status.mouse_position_state.y = touch_value.y.value;
399 428
429 lock.unlock();
400 TriggerOnChange(DeviceTriggerType::Mouse); 430 TriggerOnChange(DeviceTriggerType::Mouse);
401} 431}
402 432
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
403KeyboardValues EmulatedDevices::GetKeyboardValues() const { 450KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex};
404 return device_status.keyboard_values; 452 return device_status.keyboard_values;
405} 453}
406 454
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { 455KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
456 std::scoped_lock lock{mutex};
408 return device_status.keyboard_moddifier_values; 457 return device_status.keyboard_moddifier_values;
409} 458}
410 459
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { 460MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
461 std::scoped_lock lock{mutex};
412 return device_status.mouse_button_values; 462 return device_status.mouse_button_values;
413} 463}
414 464
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
415KeyboardKey EmulatedDevices::GetKeyboard() const { 469KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex};
416 return device_status.keyboard_state; 471 return device_status.keyboard_state;
417} 472}
418 473
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { 474KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
475 std::scoped_lock lock{mutex};
420 return device_status.keyboard_moddifier_state; 476 return device_status.keyboard_moddifier_state;
421} 477}
422 478
423MouseButton EmulatedDevices::GetMouseButtons() const { 479MouseButton EmulatedDevices::GetMouseButtons() const {
480 std::scoped_lock lock{mutex};
424 return device_status.mouse_button_state; 481 return device_status.mouse_button_state;
425} 482}
426 483
427MousePosition EmulatedDevices::GetMousePosition() const { 484MousePosition EmulatedDevices::GetMousePosition() const {
485 std::scoped_lock lock{mutex};
428 return device_status.mouse_position_state; 486 return device_status.mouse_position_state;
429} 487}
430 488
431AnalogStickState EmulatedDevices::GetMouseWheel() const { 489AnalogStickState EmulatedDevices::GetMouseWheel() const {
490 std::scoped_lock lock{mutex};
432 return device_status.mouse_wheel_state; 491 return device_status.mouse_wheel_state;
433} 492}
434 493
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex};
436 for (const auto& poller_pair : callback_list) { 500 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second; 501 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) { 502 if (poller.on_change) {
@@ -442,13 +506,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
442} 506}
443 507
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { 508int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex}; 509 std::scoped_lock lock{callback_mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 510 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++; 511 return last_callback_key++;
448} 512}
449 513
450void EmulatedDevices::DeleteCallback(int key) { 514void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex}; 515 std::scoped_lock lock{callback_mutex};
452 const auto& iterator = callback_list.find(key); 516 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) { 517 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 518 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 790d3b411..fb6451e7a 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,9 +26,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
29 30
30using MouseButtonParams = 31using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
32 34
33using KeyboardValues = 35using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +41,17 @@ using MouseButtonValues =
39using MouseAnalogValues = 41using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus; 43using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
42 45
43struct MousePosition { 46struct MousePosition {
44 f32 x; 47 f32 x;
45 f32 y; 48 f32 y;
46}; 49};
47 50
51struct RingSensorForce {
52 f32 force;
53};
54
48struct DeviceStatus { 55struct DeviceStatus {
49 // Data from input_common 56 // Data from input_common
50 KeyboardValues keyboard_values{}; 57 KeyboardValues keyboard_values{};
@@ -52,6 +59,7 @@ struct DeviceStatus {
52 MouseButtonValues mouse_button_values{}; 59 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{}; 60 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{}; 61 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
55 63
56 // Data for HID serices 64 // Data for HID serices
57 KeyboardKey keyboard_state{}; 65 KeyboardKey keyboard_state{};
@@ -59,12 +67,14 @@ struct DeviceStatus {
59 MouseButton mouse_button_state{}; 67 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{}; 68 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{}; 69 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
62}; 71};
63 72
64enum class DeviceTriggerType { 73enum class DeviceTriggerType {
65 Keyboard, 74 Keyboard,
66 KeyboardModdifier, 75 KeyboardModdifier,
67 Mouse, 76 Mouse,
77 RingController,
68}; 78};
69 79
70struct InterfaceUpdateCallback { 80struct InterfaceUpdateCallback {
@@ -110,6 +120,15 @@ public:
110 /// Reverts any mapped changes made that weren't saved 120 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig(); 121 void RestoreConfig();
112 122
123 // Returns the current mapped ring device
124 Common::ParamPackage GetRingParam() const;
125
126 /**
127 * Updates the current mapped ring device
128 * @param param ParamPackage with ring sensor data to be mapped
129 */
130 void SetRingParam(Common::ParamPackage param);
131
113 /// Returns the latest status of button input from the keyboard with parameters 132 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const; 133 KeyboardValues GetKeyboardValues() const;
115 134
@@ -119,6 +138,9 @@ public:
119 /// Returns the latest status of button input from the mouse with parameters 138 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const; 139 MouseButtonValues GetMouseButtonsValues() const;
121 140
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
122 /// Returns the latest status of button input from the keyboard 144 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const; 145 KeyboardKey GetKeyboard() const;
124 146
@@ -134,6 +156,9 @@ public:
134 /// Returns the latest mouse wheel change 156 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const; 157 AnalogStickState GetMouseWheel() const;
136 158
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
137 /** 162 /**
138 * Adds a callback to the list of events 163 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered 164 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +211,12 @@ private:
186 void SetMouseStick(const Common::Input::CallbackStatus& callback); 211 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187 212
188 /** 213 /**
214 * Updates the ring analog sensor status of the ring controller
215 * @param callback A CallbackStatus containing the force status
216 */
217 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
218
219 /**
189 * Triggers a callback that something has changed on the device status 220 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger 221 * @param type Input type of the event to trigger
191 */ 222 */
@@ -193,13 +224,17 @@ private:
193 224
194 bool is_configuring{false}; 225 bool is_configuring{false};
195 226
227 RingAnalogParams ring_params;
228
196 KeyboardDevices keyboard_devices; 229 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices; 230 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices; 231 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices; 232 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device; 233 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
201 235
202 mutable std::mutex mutex; 236 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list; 238 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0; 239 int last_callback_key = 0;
205 240
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 778b328b9..422a9af33 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -491,9 +491,10 @@ struct SixAxisSensorHandle {
491}; 491};
492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); 492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
493 493
494// These parameters seem related to how much gyro/accelerometer is used
494struct SixAxisSensorFusionParameters { 495struct SixAxisSensorFusionParameters {
495 f32 parameter1; 496 f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
496 f32 parameter2; 497 f32 parameter2{0.4f}; // Default 0.4
497}; 498};
498static_assert(sizeof(SixAxisSensorFusionParameters) == 8, 499static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
499 "SixAxisSensorFusionParameters is an invalid size"); 500 "SixAxisSensorFusionParameters is an invalid size");
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 026257115..3c4e45fcd 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -385,7 +385,7 @@ public:
385 T PopRaw(); 385 T PopRaw();
386 386
387 template <class T> 387 template <class T>
388 std::shared_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
389 ASSERT(context->Session()->IsDomain()); 389 ASSERT(context->Session()->IsDomain());
390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
391 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 8027bec00..7765e7848 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
148} // Anonymous namespace 148} // Anonymous namespace
149 149
150u64 KSystemControl::GenerateRandomU64() { 150u64 KSystemControl::GenerateRandomU64() {
151 static std::random_device device; 151 std::random_device device;
152 static std::mt19937 gen(device()); 152 std::mt19937 gen(device());
153 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 153 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
154 return distribution(gen); 154 return distribution(gen);
155} 155}
156 156
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 6f44b534f..47425a3a1 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -8,7 +8,6 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/spin_lock.h"
12#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
13#include "core/hle/kernel/k_priority_queue.h" 12#include "core/hle/kernel/k_priority_queue.h"
14#include "core/hle/kernel/k_scheduler_lock.h" 13#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +79,7 @@ private:
80 79
81 /// Lists all thread ids that aren't deleted/etc. 80 /// Lists all thread ids that aren't deleted/etc.
82 std::vector<KThread*> thread_list; 81 std::vector<KThread*> thread_list;
83 Common::SpinLock global_list_guard{}; 82 std::mutex global_list_guard;
84}; 83};
85 84
86} // namespace Kernel 85} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e19544c54..5828ac923 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -17,7 +17,6 @@
17#include "core/hle/kernel/k_auto_object.h" 17#include "core/hle/kernel/k_auto_object.h"
18#include "core/hle/kernel/k_handle_table.h" 18#include "core/hle/kernel/k_handle_table.h"
19#include "core/hle/kernel/k_process.h" 19#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/k_readable_event.h"
21#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
22#include "core/hle/kernel/k_thread.h" 21#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
@@ -25,8 +24,15 @@
25 24
26namespace Kernel { 25namespace Kernel {
27 26
28SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) 27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
29 : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} 28 ServiceThreadType thread_type)
29 : kernel{kernel_} {
30 if (thread_type == ServiceThreadType::CreateNew) {
31 service_thread = kernel.CreateServiceThread(service_name_);
32 } else {
33 service_thread = kernel.GetDefaultServiceThread();
34 }
35}
30 36
31SessionRequestHandler::~SessionRequestHandler() { 37SessionRequestHandler::~SessionRequestHandler() {
32 kernel.ReleaseServiceThread(service_thread); 38 kernel.ReleaseServiceThread(service_thread);
@@ -45,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
45 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); 51 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
46 return false; 52 return false;
47 } 53 }
48 return DomainHandler(object_id - 1) != nullptr; 54 return !DomainHandler(object_id - 1).expired();
49 } else { 55 } else {
50 return session_handler != nullptr; 56 return session_handler != nullptr;
51 } 57 }
@@ -55,7 +61,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) {
55 session->ClientConnected(shared_from_this()); 61 session->ClientConnected(shared_from_this());
56 62
57 // Ensure our server session is tracked globally. 63 // Ensure our server session is tracked globally.
58 kernel.RegisterServerSession(session); 64 kernel.RegisterServerObject(session);
59} 65}
60 66
61void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 67void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 754b41ff6..640146137 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -33,6 +33,11 @@ namespace Service {
33class ServiceFrameworkBase; 33class ServiceFrameworkBase;
34} 34}
35 35
36enum class ServiceThreadType {
37 Default,
38 CreateNew,
39};
40
36namespace Kernel { 41namespace Kernel {
37 42
38class Domain; 43class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
57 */ 62 */
58class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { 63class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
59public: 64public:
60 SessionRequestHandler(KernelCore& kernel, const char* service_name_); 65 SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
66 ServiceThreadType thread_type);
61 virtual ~SessionRequestHandler(); 67 virtual ~SessionRequestHandler();
62 68
63 /** 69 /**
@@ -94,6 +100,7 @@ protected:
94 std::weak_ptr<ServiceThread> service_thread; 100 std::weak_ptr<ServiceThread> service_thread;
95}; 101};
96 102
103using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
97using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; 104using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
98 105
99/** 106/**
@@ -139,7 +146,7 @@ public:
139 } 146 }
140 } 147 }
141 148
142 SessionRequestHandlerPtr DomainHandler(std::size_t index) const { 149 SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
143 ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); 150 ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
144 return domain_handlers.at(index); 151 return domain_handlers.at(index);
145 } 152 }
@@ -328,10 +335,10 @@ public:
328 335
329 template <typename T> 336 template <typename T>
330 std::shared_ptr<T> GetDomainHandler(std::size_t index) const { 337 std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
331 return std::static_pointer_cast<T>(manager->DomainHandler(index)); 338 return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
332 } 339 }
333 340
334 void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { 341 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
335 manager = std::move(manager_); 342 manager = std::move(manager_);
336 } 343 }
337 344
@@ -374,7 +381,7 @@ private:
374 u32 handles_offset{}; 381 u32 handles_offset{};
375 u32 domain_offset{}; 382 u32 domain_offset{};
376 383
377 std::shared_ptr<SessionRequestManager> manager; 384 std::weak_ptr<SessionRequestManager> manager;
378 385
379 KernelCore& kernel; 386 KernelCore& kernel;
380 Core::Memory::Memory& memory; 387 Core::Memory::Memory& memory;
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 36fc0944a..b0f773ee0 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -7,19 +7,23 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/device_memory.h"
10#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
11#include "core/hle/kernel/init/init_slab_setup.h" 12#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 13#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
14#include "core/hle/kernel/k_memory_layout.h" 15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_page_buffer.h"
16#include "core/hle/kernel/k_port.h" 18#include "core/hle/kernel/k_port.h"
17#include "core/hle/kernel/k_process.h" 19#include "core/hle/kernel/k_process.h"
18#include "core/hle/kernel/k_resource_limit.h" 20#include "core/hle/kernel/k_resource_limit.h"
19#include "core/hle/kernel/k_session.h" 21#include "core/hle/kernel/k_session.h"
20#include "core/hle/kernel/k_shared_memory.h" 22#include "core/hle/kernel/k_shared_memory.h"
23#include "core/hle/kernel/k_shared_memory_info.h"
21#include "core/hle/kernel/k_system_control.h" 24#include "core/hle/kernel/k_system_control.h"
22#include "core/hle/kernel/k_thread.h" 25#include "core/hle/kernel/k_thread.h"
26#include "core/hle/kernel/k_thread_local_page.h"
23#include "core/hle/kernel/k_transfer_memory.h" 27#include "core/hle/kernel/k_transfer_memory.h"
24 28
25namespace Kernel::Init { 29namespace Kernel::Init {
@@ -32,9 +36,13 @@ namespace Kernel::Init {
32 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ 36 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
33 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ 37 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
34 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ 38 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
39 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
35 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 40 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
36 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ 41 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
37 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 42 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
43 HANDLER(KThreadLocalPage, \
44 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
45 ##__VA_ARGS__) \
38 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) 46 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
39 47
40namespace { 48namespace {
@@ -50,38 +58,46 @@ enum KSlabType : u32 {
50// Constexpr counts. 58// Constexpr counts.
51constexpr size_t SlabCountKProcess = 80; 59constexpr size_t SlabCountKProcess = 80;
52constexpr size_t SlabCountKThread = 800; 60constexpr size_t SlabCountKThread = 800;
53constexpr size_t SlabCountKEvent = 700; 61constexpr size_t SlabCountKEvent = 900;
54constexpr size_t SlabCountKInterruptEvent = 100; 62constexpr size_t SlabCountKInterruptEvent = 100;
55constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. 63constexpr size_t SlabCountKPort = 384;
56constexpr size_t SlabCountKSharedMemory = 80; 64constexpr size_t SlabCountKSharedMemory = 80;
57constexpr size_t SlabCountKTransferMemory = 200; 65constexpr size_t SlabCountKTransferMemory = 200;
58constexpr size_t SlabCountKCodeMemory = 10; 66constexpr size_t SlabCountKCodeMemory = 10;
59constexpr size_t SlabCountKDeviceAddressSpace = 300; 67constexpr size_t SlabCountKDeviceAddressSpace = 300;
60constexpr size_t SlabCountKSession = 933; 68constexpr size_t SlabCountKSession = 1133;
61constexpr size_t SlabCountKLightSession = 100; 69constexpr size_t SlabCountKLightSession = 100;
62constexpr size_t SlabCountKObjectName = 7; 70constexpr size_t SlabCountKObjectName = 7;
63constexpr size_t SlabCountKResourceLimit = 5; 71constexpr size_t SlabCountKResourceLimit = 5;
64constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; 72constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
65constexpr size_t SlabCountKAlpha = 1; 73constexpr size_t SlabCountKIoPool = 1;
66constexpr size_t SlabCountKBeta = 6; 74constexpr size_t SlabCountKIoRegion = 6;
67 75
68constexpr size_t SlabCountExtraKThread = 160; 76constexpr size_t SlabCountExtraKThread = 160;
69 77
78/// Helper function to translate from the slab virtual address to the reserved location in physical
79/// memory.
80static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
81 slab_addr -= memory_layout.GetSlabRegionAddress();
82 return slab_addr + Core::DramMemoryMap::SlabHeapBase;
83}
84
70template <typename T> 85template <typename T>
71VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, 86VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
72 size_t num_objects) { 87 size_t num_objects) {
73 // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for
74 // kernel object type T with the backing kernel memory pointer once we emulate kernel memory.
75 88
76 const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); 89 const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
77 VAddr start = Common::AlignUp(address, alignof(T)); 90 VAddr start = Common::AlignUp(address, alignof(T));
78 91
79 // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with 92 // This should use the virtual memory address passed in, but currently, we do not setup the
80 // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free 93 // kernel virtual memory layout. Instead, we simply map these at a region of physical memory
81 // host memory. 94 // that we reserve for the slab heaps.
82 void* backing_kernel_memory{}; 95 // TODO(bunnei): Fix this once we support the kernel virtual memory layout.
83 96
84 if (size > 0) { 97 if (size > 0) {
98 void* backing_kernel_memory{
99 system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
100
85 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); 101 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
86 ASSERT(region != nullptr); 102 ASSERT(region != nullptr);
87 ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); 103 ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
@@ -91,6 +107,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
91 return start + size; 107 return start + size;
92} 108}
93 109
110size_t CalculateSlabHeapGapSize() {
111 constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
112 static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
113 return KernelSlabHeapGapSize;
114}
115
94} // namespace 116} // namespace
95 117
96KSlabResourceCounts KSlabResourceCounts::CreateDefault() { 118KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
@@ -109,8 +131,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
109 .num_KObjectName = SlabCountKObjectName, 131 .num_KObjectName = SlabCountKObjectName,
110 .num_KResourceLimit = SlabCountKResourceLimit, 132 .num_KResourceLimit = SlabCountKResourceLimit,
111 .num_KDebug = SlabCountKDebug, 133 .num_KDebug = SlabCountKDebug,
112 .num_KAlpha = SlabCountKAlpha, 134 .num_KIoPool = SlabCountKIoPool,
113 .num_KBeta = SlabCountKBeta, 135 .num_KIoRegion = SlabCountKIoRegion,
114 }; 136 };
115} 137}
116 138
@@ -136,11 +158,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
136#undef ADD_SLAB_SIZE 158#undef ADD_SLAB_SIZE
137 159
138 // Add the reserved size. 160 // Add the reserved size.
139 size += KernelSlabHeapGapsSize; 161 size += CalculateSlabHeapGapSize();
140 162
141 return size; 163 return size;
142} 164}
143 165
166void InitializeKPageBufferSlabHeap(Core::System& system) {
167 auto& kernel = system.Kernel();
168
169 const auto& counts = kernel.SlabResourceCounts();
170 const size_t num_pages =
171 counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
172 const size_t slab_size = num_pages * PageSize;
173
174 // Reserve memory from the system resource limit.
175 ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
176
177 // Allocate memory for the slab.
178 constexpr auto AllocateOption = KMemoryManager::EncodeOption(
179 KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
180 const PAddr slab_address =
181 kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
182 ASSERT(slab_address != 0);
183
184 // Initialize the slabheap.
185 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
186 slab_size);
187}
188
144void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { 189void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
145 auto& kernel = system.Kernel(); 190 auto& kernel = system.Kernel();
146 191
@@ -160,13 +205,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
160 } 205 }
161 206
162 // Create an array to represent the gaps between the slabs. 207 // Create an array to represent the gaps between the slabs.
163 const size_t total_gap_size = KernelSlabHeapGapsSize; 208 const size_t total_gap_size = CalculateSlabHeapGapSize();
164 std::array<size_t, slab_types.size()> slab_gaps; 209 std::array<size_t, slab_types.size()> slab_gaps;
165 for (size_t i = 0; i < slab_gaps.size(); i++) { 210 for (auto& slab_gap : slab_gaps) {
166 // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange 211 // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
167 // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we 212 // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
168 // will include it ourselves. 213 // will include it ourselves.
169 slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); 214 slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
170 } 215 }
171 216
172 // Sort the array, so that we can treat differences between values as offsets to the starts of 217 // Sort the array, so that we can treat differences between values as offsets to the starts of
@@ -177,13 +222,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
177 } 222 }
178 } 223 }
179 224
180 for (size_t i = 0; i < slab_types.size(); i++) { 225 // Track the gaps, so that we can free them to the unused slab tree.
226 VAddr gap_start = address;
227 size_t gap_size = 0;
228
229 for (size_t i = 0; i < slab_gaps.size(); i++) {
181 // Add the random gap to the address. 230 // Add the random gap to the address.
182 address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; 231 const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
232 address += cur_gap;
233 gap_size += cur_gap;
183 234
184#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ 235#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
185 case KSlabType_##NAME: \ 236 case KSlabType_##NAME: \
186 address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ 237 if (COUNT > 0) { \
238 address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
239 } \
187 break; 240 break;
188 241
189 // Initialize the slabheap. 242 // Initialize the slabheap.
@@ -192,7 +245,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
192 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) 245 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
193 // If we somehow get an invalid type, abort. 246 // If we somehow get an invalid type, abort.
194 default: 247 default:
195 UNREACHABLE(); 248 UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
249 }
250
251 // If we've hit the end of a gap, free it.
252 if (gap_start + gap_size != address) {
253 gap_start = address;
254 gap_size = 0;
196 } 255 }
197 } 256 }
198} 257}
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h
index a8f7e0918..f54b67d02 100644
--- a/src/core/hle/kernel/init/init_slab_setup.h
+++ b/src/core/hle/kernel/init/init_slab_setup.h
@@ -32,12 +32,13 @@ struct KSlabResourceCounts {
32 size_t num_KObjectName; 32 size_t num_KObjectName;
33 size_t num_KResourceLimit; 33 size_t num_KResourceLimit;
34 size_t num_KDebug; 34 size_t num_KDebug;
35 size_t num_KAlpha; 35 size_t num_KIoPool;
36 size_t num_KBeta; 36 size_t num_KIoRegion;
37}; 37};
38 38
39void InitializeSlabResourceCounts(KernelCore& kernel); 39void InitializeSlabResourceCounts(KernelCore& kernel);
40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); 40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
41void InitializeKPageBufferSlabHeap(Core::System& system);
41void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); 42void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
42 43
43} // namespace Kernel::Init 44} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1d1f5e5f8..8cdd0490f 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -115,7 +115,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
115 { 115 {
116 KScopedSchedulerLock sl(kernel); 116 KScopedSchedulerLock sl(kernel);
117 117
118 auto it = thread_tree.nfind_light({addr, -1}); 118 auto it = thread_tree.nfind_key({addr, -1});
119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
120 (it->GetAddressArbiterKey() == addr)) { 120 (it->GetAddressArbiterKey() == addr)) {
121 // End the thread's wait. 121 // End the thread's wait.
@@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
148 return ResultInvalidState; 148 return ResultInvalidState;
149 } 149 }
150 150
151 auto it = thread_tree.nfind_light({addr, -1}); 151 auto it = thread_tree.nfind_key({addr, -1});
152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
153 (it->GetAddressArbiterKey() == addr)) { 153 (it->GetAddressArbiterKey() == addr)) {
154 // End the thread's wait. 154 // End the thread's wait.
@@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
171 { 171 {
172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel); 172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel);
173 173
174 auto it = thread_tree.nfind_light({addr, -1}); 174 auto it = thread_tree.nfind_key({addr, -1});
175 // Determine the updated value. 175 // Determine the updated value.
176 s32 new_value{}; 176 s32 new_value{};
177 if (count <= 0) { 177 if (count <= 0) {
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 05779f2d5..423e8d8f5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -89,9 +89,7 @@ public:
89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { 89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
90 RegisterWithKernel(); 90 RegisterWithKernel();
91 } 91 }
92 virtual ~KAutoObject() { 92 virtual ~KAutoObject() = default;
93 UnregisterWithKernel();
94 }
95 93
96 static KAutoObject* Create(KAutoObject* ptr); 94 static KAutoObject* Create(KAutoObject* ptr);
97 95
@@ -163,11 +161,12 @@ public:
163 do { 161 do {
164 ASSERT(cur_ref_count > 0); 162 ASSERT(cur_ref_count > 0);
165 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, 163 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
166 std::memory_order_relaxed)); 164 std::memory_order_acq_rel));
167 165
168 // If ref count hits zero, destroy the object. 166 // If ref count hits zero, destroy the object.
169 if (cur_ref_count - 1 == 0) { 167 if (cur_ref_count - 1 == 0) {
170 this->Destroy(); 168 this->Destroy();
169 this->UnregisterWithKernel();
171 } 170 }
172 } 171 }
173 172
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index 980010150..93fc8786d 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8
9#include "common/bit_util.h" 7#include "common/bit_util.h"
10#include "common/common_types.h" 8#include "common/common_types.h"
11 9
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 0b225e8e0..09eaf004c 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -5,7 +5,6 @@
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/device_memory.h" 7#include "core/device_memory.h"
8#include "core/hle/kernel/k_auto_object.h"
9#include "core/hle/kernel/k_code_memory.h" 8#include "core/hle/kernel/k_code_memory.h"
10#include "core/hle/kernel/k_light_lock.h" 9#include "core/hle/kernel/k_light_lock.h"
11#include "core/hle/kernel/k_memory_block.h" 10#include "core/hle/kernel/k_memory_block.h"
@@ -29,15 +28,21 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
29 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->PageTable();
30 29
31 // Construct the page group. 30 // Construct the page group.
32 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); 31 m_page_group =
32 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
33 33
34 // Lock the memory. 34 // Lock the memory.
35 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
36 36
37 // Clear the memory. 37 // Clear the memory.
38 for (const auto& block : m_page_group.Nodes()) { 38 //
39 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); 39 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
40 } 40 // guest memory, and is not specifically required if the guest program is correctly
41 // written, so disable until this is further investigated.
42 //
43 // for (const auto& block : m_page_group.Nodes()) {
44 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
45 // }
41 46
42 // Set remaining tracking members. 47 // Set remaining tracking members.
43 m_address = addr; 48 m_address = addr;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index aadcc297a..aa29922d0 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_scheduler.h" 10#include "core/hle/kernel/k_scheduler.h"
11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
12#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/k_thread.h" 12#include "core/hle/kernel/k_thread.h"
14#include "core/hle/kernel/k_thread_queue.h" 13#include "core/hle/kernel/k_thread_queue.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
@@ -244,7 +243,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
244 { 243 {
245 KScopedSchedulerLock sl(kernel); 244 KScopedSchedulerLock sl(kernel);
246 245
247 auto it = thread_tree.nfind_light({cv_key, -1}); 246 auto it = thread_tree.nfind_key({cv_key, -1});
248 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 247 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
249 (it->GetConditionVariableKey() == cv_key)) { 248 (it->GetConditionVariableKey() == cv_key)) {
250 KThread* target_thread = std::addressof(*it); 249 KThread* target_thread = std::addressof(*it);
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index cf95f0852..db7512ee7 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -63,7 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
63 return true; 63 return true;
64} 64}
65 65
66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
67 KScopedDisableDispatch dd(kernel); 67 KScopedDisableDispatch dd(kernel);
68 KScopedSpinLock lk(m_lock); 68 KScopedSpinLock lk(m_lock);
69 69
@@ -75,7 +75,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
75 const auto linear_id = this->AllocateLinearId(); 75 const auto linear_id = this->AllocateLinearId();
76 const auto index = this->AllocateEntry(); 76 const auto index = this->AllocateEntry();
77 77
78 m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; 78 m_entry_infos[index].linear_id = linear_id;
79 m_objects[index] = obj; 79 m_objects[index] = obj;
80 80
81 obj->Open(); 81 obj->Open();
@@ -116,7 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
116 } 116 }
117} 117}
118 118
119void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 119void KHandleTable::Register(Handle handle, KAutoObject* obj) {
120 KScopedDisableDispatch dd(kernel); 120 KScopedDisableDispatch dd(kernel);
121 KScopedSpinLock lk(m_lock); 121 KScopedSpinLock lk(m_lock);
122 122
@@ -132,7 +132,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
132 // Set the entry. 132 // Set the entry.
133 ASSERT(m_objects[index] == nullptr); 133 ASSERT(m_objects[index] == nullptr);
134 134
135 m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type}; 135 m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
136 m_objects[index] = obj; 136 m_objects[index] = obj;
137 137
138 obj->Open(); 138 obj->Open();
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 87004a0f9..dd27689b6 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -42,7 +42,7 @@ public:
42 m_free_head_index = -1; 42 m_free_head_index = -1;
43 43
44 // Free all entries. 44 // Free all entries.
45 for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { 45 for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
46 m_objects[i] = nullptr; 46 m_objects[i] = nullptr;
47 m_entry_infos[i].next_free_index = i - 1; 47 m_entry_infos[i].next_free_index = i - 1;
48 m_free_head_index = i; 48 m_free_head_index = i;
@@ -104,17 +104,8 @@ public:
104 ResultCode Reserve(Handle* out_handle); 104 ResultCode Reserve(Handle* out_handle);
105 void Unreserve(Handle handle); 105 void Unreserve(Handle handle);
106 106
107 template <typename T> 107 ResultCode Add(Handle* out_handle, KAutoObject* obj);
108 ResultCode Add(Handle* out_handle, T* obj) { 108 void Register(Handle handle, KAutoObject* obj);
109 static_assert(std::is_base_of_v<KAutoObject, T>);
110 return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
111 }
112
113 template <typename T>
114 void Register(Handle handle, T* obj) {
115 static_assert(std::is_base_of_v<KAutoObject, T>);
116 return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
117 }
118 109
119 template <typename T> 110 template <typename T>
120 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { 111 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -160,9 +151,6 @@ public:
160 } 151 }
161 152
162private: 153private:
163 ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
164 void Register(Handle handle, KAutoObject* obj, u16 type);
165
166 s32 AllocateEntry() { 154 s32 AllocateEntry() {
167 ASSERT(m_count < m_table_size); 155 ASSERT(m_count < m_table_size);
168 156
@@ -179,7 +167,7 @@ private:
179 ASSERT(m_count > 0); 167 ASSERT(m_count > 0);
180 168
181 m_objects[index] = nullptr; 169 m_objects[index] = nullptr;
182 m_entry_infos[index].next_free_index = m_free_head_index; 170 m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
183 171
184 m_free_head_index = index; 172 m_free_head_index = index;
185 173
@@ -278,19 +266,13 @@ private:
278 } 266 }
279 267
280 union EntryInfo { 268 union EntryInfo {
281 struct { 269 u16 linear_id;
282 u16 linear_id; 270 s16 next_free_index;
283 u16 type;
284 } info;
285 s32 next_free_index;
286 271
287 constexpr u16 GetLinearId() const { 272 constexpr u16 GetLinearId() const {
288 return info.linear_id; 273 return linear_id;
289 }
290 constexpr u16 GetType() const {
291 return info.type;
292 } 274 }
293 constexpr s32 GetNextFreeIndex() const { 275 constexpr s16 GetNextFreeIndex() const {
294 return next_free_index; 276 return next_free_index;
295 } 277 }
296 }; 278 };
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index bcddb0d62..0858827b6 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -57,11 +57,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor
57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; 57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
58 58
59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; 59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
60constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; 60constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; 61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
62 62
63// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. 63// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
64constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; 64constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
65 65
66constexpr std::size_t KernelResourceSize = 66constexpr std::size_t KernelResourceSize =
67 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; 67 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index a2f18f643..17a1b76c7 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -15,7 +15,6 @@
15#include "core/hle/kernel/k_page_linked_list.h" 15#include "core/hle/kernel/k_page_linked_list.h"
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/svc_results.h" 17#include "core/hle/kernel/svc_results.h"
18#include "core/memory.h"
19 18
20namespace Kernel { 19namespace Kernel {
21 20
diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp
new file mode 100644
index 000000000..f7df4a9a8
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.cpp
@@ -0,0 +1,19 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/device_memory.h"
9#include "core/hle/kernel/k_page_buffer.h"
10#include "core/hle/kernel/memory_types.h"
11
12namespace Kernel {
13
14KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
15 ASSERT(Common::IsAligned(phys_addr, PageSize));
16 return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr));
17}
18
19} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h
new file mode 100644
index 000000000..1d11a4e27
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.h
@@ -0,0 +1,28 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/kernel/memory_types.h"
11#include "core/hle/kernel/slab_helpers.h"
12
13namespace Kernel {
14
15class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
16public:
17 KPageBuffer() = default;
18
19 static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
20
21private:
22 [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
23};
24
25static_assert(sizeof(KPageBuffer) == PageSize);
26static_assert(alignof(KPageBuffer) == PageSize);
27
28} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 0e2ae582a..869228322 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -89,6 +89,10 @@ public:
89 return ResultSuccess; 89 return ResultSuccess;
90 } 90 }
91 91
92 bool Empty() const {
93 return nodes.empty();
94 }
95
92private: 96private:
93 std::list<Node> nodes; 97 std::list<Node> nodes;
94}; 98};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index dfea0b6e2..47ea3c89c 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -285,72 +285,260 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
285 return ResultSuccess; 285 return ResultSuccess;
286} 286}
287 287
288ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 288ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
289 // Validate the mapping request.
290 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
291 ResultInvalidMemoryRegion);
292
293 // Lock the table.
289 KScopedLightLock lk(general_lock); 294 KScopedLightLock lk(general_lock);
290 295
291 const std::size_t num_pages{size / PageSize}; 296 // Verify that the source memory is normal heap.
297 KMemoryState src_state{};
298 KMemoryPermission src_perm{};
299 std::size_t num_src_allocator_blocks{};
300 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
301 src_address, size, KMemoryState::All, KMemoryState::Normal,
302 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
303 KMemoryAttribute::All, KMemoryAttribute::None));
292 304
293 KMemoryState state{}; 305 // Verify that the destination memory is unmapped.
294 KMemoryPermission perm{}; 306 std::size_t num_dst_allocator_blocks{};
295 CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, 307 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
296 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, 308 KMemoryState::Free, KMemoryPermission::None,
297 KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, 309 KMemoryPermission::None, KMemoryAttribute::None,
298 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 310 KMemoryAttribute::None));
299 311
300 if (IsRegionMapped(dst_addr, size)) { 312 // Map the code memory.
301 return ResultInvalidCurrentMemory; 313 {
314 // Determine the number of pages being operated on.
315 const std::size_t num_pages = size / PageSize;
316
317 // Create page groups for the memory being mapped.
318 KPageLinkedList pg;
319 AddRegionToPages(src_address, num_pages, pg);
320
321 // Reprotect the source as kernel-read/not mapped.
322 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
323 KMemoryPermission::NotMapped);
324 R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
325
326 // Ensure that we unprotect the source pages on failure.
327 auto unprot_guard = SCOPE_GUARD({
328 ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
329 .IsSuccess());
330 });
331
332 // Map the alias pages.
333 R_TRY(MapPages(dst_address, pg, new_perm));
334
335 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
336 // failure.
337 unprot_guard.Cancel();
338
339 // Apply the memory block updates.
340 block_manager->Update(src_address, num_pages, src_state, new_perm,
341 KMemoryAttribute::Locked);
342 block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
343 KMemoryAttribute::None);
302 } 344 }
303 345
304 KPageLinkedList page_linked_list; 346 return ResultSuccess;
305 AddRegionToPages(src_addr, num_pages, page_linked_list); 347}
348
349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
350 ICacheInvalidationStrategy icache_invalidation_strategy) {
351 // Validate the mapping request.
352 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
353 ResultInvalidMemoryRegion);
306 354
355 // Lock the table.
356 KScopedLightLock lk(general_lock);
357
358 // Verify that the source memory is locked normal heap.
359 std::size_t num_src_allocator_blocks{};
360 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
361 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
362 KMemoryPermission::None, KMemoryAttribute::All,
363 KMemoryAttribute::Locked));
364
365 // Verify that the destination memory is aliasable code.
366 std::size_t num_dst_allocator_blocks{};
367 R_TRY(this->CheckMemoryStateContiguous(
368 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
369 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
370 KMemoryAttribute::All, KMemoryAttribute::None));
371
372 // Determine whether any pages being unmapped are code.
373 bool any_code_pages = false;
307 { 374 {
308 auto block_guard = detail::ScopeExit( 375 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
309 [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); 376 while (true) {
377 // Get the memory info.
378 const KMemoryInfo info = it->GetMemoryInfo();
310 379
311 CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, 380 // Check if the memory has code flag.
312 OperationType::ChangePermissions)); 381 if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
313 CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); 382 any_code_pages = true;
383 break;
384 }
314 385
315 block_guard.Cancel(); 386 // Check if we're done.
387 if (dst_address + size - 1 <= info.GetLastAddress()) {
388 break;
389 }
390
391 // Advance.
392 ++it;
393 }
316 } 394 }
317 395
318 block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, 396 // Ensure that we maintain the instruction cache.
319 KMemoryAttribute::Locked); 397 bool reprotected_pages = false;
320 block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); 398 SCOPE_EXIT({
399 if (reprotected_pages && any_code_pages) {
400 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
401 system.InvalidateCpuInstructionCacheRange(dst_address, size);
402 } else {
403 system.InvalidateCpuInstructionCaches();
404 }
405 }
406 });
407
408 // Unmap.
409 {
410 // Determine the number of pages being operated on.
411 const std::size_t num_pages = size / PageSize;
412
413 // Unmap the aliased copy of the pages.
414 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
415
416 // Try to set the permissions for the source pages back to what they should be.
417 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
418 OperationType::ChangePermissions));
419
420 // Apply the memory block updates.
421 block_manager->Update(dst_address, num_pages, KMemoryState::None);
422 block_manager->Update(src_address, num_pages, KMemoryState::Normal,
423 KMemoryPermission::UserReadWrite);
424
425 // Note that we reprotected pages.
426 reprotected_pages = true;
427 }
321 428
322 return ResultSuccess; 429 return ResultSuccess;
323} 430}
324 431
325ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 432VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
326 KScopedLightLock lk(general_lock); 433 std::size_t num_pages, std::size_t alignment, std::size_t offset,
434 std::size_t guard_pages) {
435 VAddr address = 0;
436
437 if (num_pages <= region_num_pages) {
438 if (this->IsAslrEnabled()) {
439 // Try to directly find a free area up to 8 times.
440 for (std::size_t i = 0; i < 8; i++) {
441 const std::size_t random_offset =
442 KSystemControl::GenerateRandomRange(
443 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
444 alignment;
445 const VAddr candidate =
446 Common::AlignDown((region_start + random_offset), alignment) + offset;
447
448 KMemoryInfo info = this->QueryInfoImpl(candidate);
449
450 if (info.state != KMemoryState::Free) {
451 continue;
452 }
453 if (region_start > candidate) {
454 continue;
455 }
456 if (info.GetAddress() + guard_pages * PageSize > candidate) {
457 continue;
458 }
327 459
328 if (!size) { 460 const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
329 return ResultSuccess; 461 if (candidate_end > info.GetLastAddress()) {
462 continue;
463 }
464 if (candidate_end > region_start + region_num_pages * PageSize - 1) {
465 continue;
466 }
467
468 address = candidate;
469 break;
470 }
471 // Fall back to finding the first free area with a random offset.
472 if (address == 0) {
473 // NOTE: Nintendo does not account for guard pages here.
474 // This may theoretically cause an offset to be chosen that cannot be mapped. We
475 // will account for guard pages.
476 const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
477 0, region_num_pages - num_pages - guard_pages);
478 address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
479 region_num_pages - offset_pages, num_pages,
480 alignment, offset, guard_pages);
481 }
482 }
483
484 // Find the first free area.
485 if (address == 0) {
486 address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
487 alignment, offset, guard_pages);
488 }
330 } 489 }
331 490
332 const std::size_t num_pages{size / PageSize}; 491 return address;
492}
333 493
334 CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, 494ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
335 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, 495 ASSERT(this->IsLockedByCurrentThread());
336 KMemoryPermission::None, KMemoryAttribute::Mask,
337 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
338 496
339 KMemoryState state{}; 497 const size_t size = num_pages * PageSize;
340 CASCADE_CODE(CheckMemoryState(
341 &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
342 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
343 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
344 CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
345 KMemoryPermission::None, KMemoryAttribute::Mask,
346 KMemoryAttribute::None));
347 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
348 498
349 block_manager->Update(dst_addr, num_pages, KMemoryState::Free); 499 // We're making a new group, not adding to an existing one.
350 block_manager->Update(src_addr, num_pages, KMemoryState::Normal, 500 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
351 KMemoryPermission::UserReadWrite); 501
502 // Begin traversal.
503 Common::PageTable::TraversalContext context;
504 Common::PageTable::TraversalEntry next_entry;
505 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
506
507 // Prepare tracking variables.
508 PAddr cur_addr = next_entry.phys_addr;
509 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
510 size_t tot_size = cur_size;
511
512 // Iterate, adding to group as we go.
513 const auto& memory_layout = system.Kernel().MemoryLayout();
514 while (tot_size < size) {
515 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
516 ResultInvalidCurrentMemory);
517
518 if (next_entry.phys_addr != (cur_addr + cur_size)) {
519 const size_t cur_pages = cur_size / PageSize;
520
521 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
522 R_TRY(pg.AddBlock(cur_addr, cur_pages));
523
524 cur_addr = next_entry.phys_addr;
525 cur_size = next_entry.block_size;
526 } else {
527 cur_size += next_entry.block_size;
528 }
529
530 tot_size += next_entry.block_size;
531 }
532
533 // Ensure we add the right amount for the last block.
534 if (tot_size > size) {
535 cur_size -= (tot_size - size);
536 }
352 537
353 system.InvalidateCpuInstructionCacheRange(dst_addr, size); 538 // Add the last block.
539 const size_t cur_pages = cur_size / PageSize;
540 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
541 R_TRY(pg.AddBlock(cur_addr, cur_pages));
354 542
355 return ResultSuccess; 543 return ResultSuccess;
356} 544}
@@ -380,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
380 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 568 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
381 KMemoryAttribute::None); 569 KMemoryAttribute::None);
382 570
571 system.InvalidateCpuInstructionCaches();
572
383 return ResultSuccess; 573 return ResultSuccess;
384} 574}
385 575
@@ -986,6 +1176,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
986 return ResultSuccess; 1176 return ResultSuccess;
987} 1177}
988 1178
1179ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
1180 PAddr phys_addr, bool is_pa_valid, VAddr region_start,
1181 std::size_t region_num_pages, KMemoryState state,
1182 KMemoryPermission perm) {
1183 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
1184
1185 // Ensure this is a valid map request.
1186 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
1187 ResultInvalidCurrentMemory);
1188 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
1189
1190 // Lock the table.
1191 KScopedLightLock lk(general_lock);
1192
1193 // Find a random address to map at.
1194 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
1195 this->GetNumGuardPages());
1196 R_UNLESS(addr != 0, ResultOutOfMemory);
1197 ASSERT(Common::IsAligned(addr, alignment));
1198 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
1199 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
1200 KMemoryPermission::None, KMemoryPermission::None,
1201 KMemoryAttribute::None, KMemoryAttribute::None)
1202 .IsSuccess());
1203
1204 // Perform mapping operation.
1205 if (is_pa_valid) {
1206 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
1207 } else {
1208 UNIMPLEMENTED();
1209 }
1210
1211 // Update the blocks.
1212 block_manager->Update(addr, num_pages, state, perm);
1213
1214 // We successfully mapped the pages.
1215 *out_addr = addr;
1216 return ResultSuccess;
1217}
1218
989ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 1219ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
990 ASSERT(this->IsLockedByCurrentThread()); 1220 ASSERT(this->IsLockedByCurrentThread());
991 1221
@@ -1028,6 +1258,55 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
1028 return ResultSuccess; 1258 return ResultSuccess;
1029} 1259}
1030 1260
1261ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
1262 // Check that the unmap is in range.
1263 const std::size_t size = num_pages * PageSize;
1264 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1265
1266 // Lock the table.
1267 KScopedLightLock lk(general_lock);
1268
1269 // Check the memory state.
1270 std::size_t num_allocator_blocks{};
1271 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1272 KMemoryState::All, state, KMemoryPermission::None,
1273 KMemoryPermission::None, KMemoryAttribute::All,
1274 KMemoryAttribute::None));
1275
1276 // Perform the unmap.
1277 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
1278
1279 // Update the blocks.
1280 block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
1281
1282 return ResultSuccess;
1283}
1284
1285ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
1286 KMemoryState state_mask, KMemoryState state,
1287 KMemoryPermission perm_mask, KMemoryPermission perm,
1288 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1289 // Ensure that the page group isn't null.
1290 ASSERT(out != nullptr);
1291
1292 // Make sure that the region we're mapping is valid for the table.
1293 const size_t size = num_pages * PageSize;
1294 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1295
1296 // Lock the table.
1297 KScopedLightLock lk(general_lock);
1298
1299 // Check if state allows us to create the group.
1300 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
1301 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1302 attr_mask, attr));
1303
1304 // Create a new page group for the region.
1305 R_TRY(this->MakePageGroup(*out, address, num_pages));
1306
1307 return ResultSuccess;
1308}
1309
1031ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1310ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1032 Svc::MemoryPermission svc_perm) { 1311 Svc::MemoryPermission svc_perm) {
1033 const size_t num_pages = size / PageSize; 1312 const size_t num_pages = size / PageSize;
@@ -1410,57 +1689,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1410} 1689}
1411 1690
1412ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1691ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1413 KScopedLightLock lk(general_lock); 1692 return this->LockMemoryAndOpen(
1414 1693 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1415 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1694 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1416 1695 KMemoryAttribute::All, KMemoryAttribute::None,
1417 KMemoryPermission old_perm{}; 1696 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1418 1697 KMemoryPermission::KernelReadWrite),
1419 if (const ResultCode result{CheckMemoryState( 1698 KMemoryAttribute::Locked);
1420 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1421 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1422 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1423 result.IsError()) {
1424 return result;
1425 }
1426
1427 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1428
1429 block_manager->UpdateLock(
1430 addr, size / PageSize,
1431 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1432 block->ShareToDevice(permission);
1433 },
1434 new_perm);
1435
1436 return ResultSuccess;
1437} 1699}
1438 1700
1439ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1701ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1440 KScopedLightLock lk(general_lock); 1702 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1441 1703 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1442 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1704 KMemoryPermission::None, KMemoryAttribute::All,
1443 1705 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1444 KMemoryPermission old_perm{}; 1706 KMemoryAttribute::Locked, nullptr);
1445
1446 if (const ResultCode result{CheckMemoryState(
1447 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1448 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1449 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1450 result.IsError()) {
1451 return result;
1452 }
1453
1454 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1455
1456 block_manager->UpdateLock(
1457 addr, size / PageSize,
1458 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1459 block->UnshareToDevice(permission);
1460 },
1461 new_perm);
1462
1463 return ResultSuccess;
1464} 1707}
1465 1708
1466ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1709ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1796,4 +2039,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1796 return ResultSuccess; 2039 return ResultSuccess;
1797} 2040}
1798 2041
2042ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
2043 size_t size, KMemoryState state_mask, KMemoryState state,
2044 KMemoryPermission perm_mask, KMemoryPermission perm,
2045 KMemoryAttribute attr_mask, KMemoryAttribute attr,
2046 KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
2047 // Validate basic preconditions.
2048 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
2049 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
2050 KMemoryAttribute::None);
2051
2052 // Validate the lock request.
2053 const size_t num_pages = size / PageSize;
2054 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2055
2056 // Lock the table.
2057 KScopedLightLock lk(general_lock);
2058
2059 // Check that the output page group is empty, if it exists.
2060 if (out_pg) {
2061 ASSERT(out_pg->GetNumPages() == 0);
2062 }
2063
2064 // Check the state.
2065 KMemoryState old_state{};
2066 KMemoryPermission old_perm{};
2067 KMemoryAttribute old_attr{};
2068 size_t num_allocator_blocks{};
2069 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2070 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2071 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2072 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2073 attr_mask, attr));
2074
2075 // Get the physical address, if we're supposed to.
2076 if (out_paddr != nullptr) {
2077 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
2078 }
2079
2080 // Make the page group, if we're supposed to.
2081 if (out_pg != nullptr) {
2082 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2083 }
2084
2085 // Decide on new perm and attr.
2086 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2087 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
2088
2089 // Update permission, if we need to.
2090 if (new_perm != old_perm) {
2091 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2092 }
2093
2094 // Apply the memory block updates.
2095 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2096
2097 return ResultSuccess;
2098}
2099
2100ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
2101 KMemoryState state, KMemoryPermission perm_mask,
2102 KMemoryPermission perm, KMemoryAttribute attr_mask,
2103 KMemoryAttribute attr, KMemoryPermission new_perm,
2104 KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
2105 // Validate basic preconditions.
2106 ASSERT((attr_mask & lock_attr) == lock_attr);
2107 ASSERT((attr & lock_attr) == lock_attr);
2108
2109 // Validate the unlock request.
2110 const size_t num_pages = size / PageSize;
2111 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2112
2113 // Lock the table.
2114 KScopedLightLock lk(general_lock);
2115
2116 // Check the state.
2117 KMemoryState old_state{};
2118 KMemoryPermission old_perm{};
2119 KMemoryAttribute old_attr{};
2120 size_t num_allocator_blocks{};
2121 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2122 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2123 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2124 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2125 attr_mask, attr));
2126
2127 // Check the page group.
2128 if (pg != nullptr) {
2129 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
2130 }
2131
2132 // Decide on new perm and attr.
2133 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2134 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2135
2136 // Update permission, if we need to.
2137 if (new_perm != old_perm) {
2138 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2139 }
2140
2141 // Apply the memory block updates.
2142 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2143
2144 return ResultSuccess;
2145}
2146
1799} // namespace Kernel 2147} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 194177332..dd6022975 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -12,6 +12,7 @@
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 17#include "core/hle/result.h"
17 18
@@ -25,6 +26,8 @@ class KMemoryBlockManager;
25 26
26class KPageTable final { 27class KPageTable final {
27public: 28public:
29 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
30
28 YUZU_NON_COPYABLE(KPageTable); 31 YUZU_NON_COPYABLE(KPageTable);
29 YUZU_NON_MOVEABLE(KPageTable); 32 YUZU_NON_MOVEABLE(KPageTable);
30 33
@@ -36,8 +39,9 @@ public:
36 KMemoryManager::Pool pool); 39 KMemoryManager::Pool pool);
37 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, 40 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
38 KMemoryPermission perm); 41 KMemoryPermission perm);
39 ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 42 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
40 ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 43 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
44 ICacheInvalidationStrategy icache_invalidation_strategy);
41 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 45 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
42 VAddr src_addr); 46 VAddr src_addr);
43 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 47 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -46,7 +50,14 @@ public:
46 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 50 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
47 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 51 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
48 KMemoryPermission perm); 52 KMemoryPermission perm);
53 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
54 PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
55 return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
56 this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
57 state, perm);
58 }
49 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 59 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
60 ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
50 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, 61 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
51 Svc::MemoryPermission svc_perm); 62 Svc::MemoryPermission svc_perm);
52 KMemoryInfo QueryInfo(VAddr addr); 63 KMemoryInfo QueryInfo(VAddr addr);
@@ -64,6 +75,10 @@ public:
64 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 75 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
65 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 76 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
66 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 77 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
78 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
79 KMemoryState state_mask, KMemoryState state,
80 KMemoryPermission perm_mask, KMemoryPermission perm,
81 KMemoryAttribute attr_mask, KMemoryAttribute attr);
67 82
68 Common::PageTable& PageTableImpl() { 83 Common::PageTable& PageTableImpl() {
69 return page_table_impl; 84 return page_table_impl;
@@ -91,6 +106,9 @@ private:
91 ResultCode InitializeMemoryLayout(VAddr start, VAddr end); 106 ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
92 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, 107 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
93 KMemoryPermission perm); 108 KMemoryPermission perm);
109 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
110 PAddr phys_addr, bool is_pa_valid, VAddr region_start,
111 std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
94 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); 112 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
95 bool IsRegionMapped(VAddr address, u64 size); 113 bool IsRegionMapped(VAddr address, u64 size);
96 bool IsRegionContiguous(VAddr addr, u64 size) const; 114 bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -105,6 +123,9 @@ private:
105 VAddr GetRegionAddress(KMemoryState state) const; 123 VAddr GetRegionAddress(KMemoryState state) const;
106 std::size_t GetRegionSize(KMemoryState state) const; 124 std::size_t GetRegionSize(KMemoryState state) const;
107 125
126 VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
127 std::size_t alignment, std::size_t offset, std::size_t guard_pages);
128
108 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, 129 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
109 std::size_t size, KMemoryState state_mask, 130 std::size_t size, KMemoryState state_mask,
110 KMemoryState state, KMemoryPermission perm_mask, 131 KMemoryState state, KMemoryPermission perm_mask,
@@ -137,7 +158,7 @@ private:
137 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, 158 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
138 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); 159 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
139 } 160 }
140 ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, 161 ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
141 KMemoryState state, KMemoryPermission perm_mask, 162 KMemoryState state, KMemoryPermission perm_mask,
142 KMemoryPermission perm, KMemoryAttribute attr_mask, 163 KMemoryPermission perm, KMemoryAttribute attr_mask,
143 KMemoryAttribute attr, 164 KMemoryAttribute attr,
@@ -146,10 +167,37 @@ private:
146 attr_mask, attr, ignore_attr); 167 attr_mask, attr, ignore_attr);
147 } 168 }
148 169
170 ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
171 KMemoryState state_mask, KMemoryState state,
172 KMemoryPermission perm_mask, KMemoryPermission perm,
173 KMemoryAttribute attr_mask, KMemoryAttribute attr,
174 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
175 ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
176 KMemoryPermission perm_mask, KMemoryPermission perm,
177 KMemoryAttribute attr_mask, KMemoryAttribute attr,
178 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
179 const KPageLinkedList* pg);
180
181 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
182
149 bool IsLockedByCurrentThread() const { 183 bool IsLockedByCurrentThread() const {
150 return general_lock.IsLockedByCurrentThread(); 184 return general_lock.IsLockedByCurrentThread();
151 } 185 }
152 186
187 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
188 ASSERT(this->IsLockedByCurrentThread());
189
190 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
191 }
192
193 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
194 ASSERT(this->IsLockedByCurrentThread());
195
196 *out = GetPhysicalAddr(virt_addr);
197
198 return *out != 0;
199 }
200
153 mutable KLightLock general_lock; 201 mutable KLightLock general_lock;
154 mutable KLightLock map_physical_memory_lock; 202 mutable KLightLock map_physical_memory_lock;
155 203
@@ -210,7 +258,7 @@ public:
210 constexpr VAddr GetAliasCodeRegionSize() const { 258 constexpr VAddr GetAliasCodeRegionSize() const {
211 return alias_code_region_end - alias_code_region_start; 259 return alias_code_region_end - alias_code_region_start;
212 } 260 }
213 size_t GetNormalMemorySize() { 261 std::size_t GetNormalMemorySize() {
214 KScopedLightLock lk(general_lock); 262 KScopedLightLock lk(general_lock);
215 return GetHeapSize() + mapped_physical_memory_size; 263 return GetHeapSize() + mapped_physical_memory_size;
216 } 264 }
@@ -253,7 +301,9 @@ public:
253 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { 301 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
254 return !IsOutsideASLRRegion(address, size); 302 return !IsOutsideASLRRegion(address, size);
255 } 303 }
256 304 constexpr std::size_t GetNumGuardPages() const {
305 return IsKernel() ? 1 : 4;
306 }
257 PAddr GetPhysicalAddr(VAddr addr) const { 307 PAddr GetPhysicalAddr(VAddr addr) const {
258 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; 308 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
259 ASSERT(backing_addr); 309 ASSERT(backing_addr);
@@ -275,10 +325,6 @@ private:
275 return is_aslr_enabled; 325 return is_aslr_enabled;
276 } 326 }
277 327
278 constexpr std::size_t GetNumGuardPages() const {
279 return IsKernel() ? 1 : 4;
280 }
281
282 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { 328 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
283 return (address_space_start <= addr) && 329 return (address_space_start <= addr) &&
284 (num_pages <= (address_space_end - address_space_start) / PageSize) && 330 (num_pages <= (address_space_end - address_space_start) / PageSize) &&
@@ -311,6 +357,7 @@ private:
311 bool is_aslr_enabled{}; 357 bool is_aslr_enabled{};
312 358
313 u32 heap_fill_value{}; 359 u32 heap_fill_value{};
360 const KMemoryRegion* cached_physical_heap_region{};
314 361
315 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 362 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
316 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 363 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index a8ba09c4a..ceb98709f 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,7 +57,12 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
57 R_UNLESS(state == State::Normal, ResultPortClosed); 57 R_UNLESS(state == State::Normal, ResultPortClosed);
58 58
59 server.EnqueueSession(session); 59 server.EnqueueSession(session);
60 server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); 60
61 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
62 session_ptr->ClientConnected(server.AcceptSession());
63 } else {
64 UNREACHABLE();
65 }
61 66
62 return ResultSuccess; 67 return ResultSuccess;
63} 68}
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 9233261cd..490e31fc7 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -13,7 +13,6 @@
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "common/settings.h" 14#include "common/settings.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/device_memory.h"
17#include "core/file_sys/program_metadata.h" 16#include "core/file_sys/program_metadata.h"
18#include "core/hle/kernel/code_set.h" 17#include "core/hle/kernel/code_set.h"
19#include "core/hle/kernel/k_memory_block_manager.h" 18#include "core/hle/kernel/k_memory_block_manager.h"
@@ -24,7 +23,6 @@
24#include "core/hle/kernel/k_scoped_resource_reservation.h" 23#include "core/hle/kernel/k_scoped_resource_reservation.h"
25#include "core/hle/kernel/k_shared_memory.h" 24#include "core/hle/kernel/k_shared_memory.h"
26#include "core/hle/kernel/k_shared_memory_info.h" 25#include "core/hle/kernel/k_shared_memory_info.h"
27#include "core/hle/kernel/k_slab_heap.h"
28#include "core/hle/kernel/k_thread.h" 26#include "core/hle/kernel/k_thread.h"
29#include "core/hle/kernel/kernel.h" 27#include "core/hle/kernel/kernel.h"
30#include "core/hle/kernel/svc_results.h" 28#include "core/hle/kernel/svc_results.h"
@@ -70,58 +68,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
70} 68}
71} // Anonymous namespace 69} // Anonymous namespace
72 70
73// Represents a page used for thread-local storage.
74//
75// Each TLS page contains slots that may be used by processes and threads.
76// Every process and thread is created with a slot in some arbitrary page
77// (whichever page happens to have an available slot).
78class TLSPage {
79public:
80 static constexpr std::size_t num_slot_entries =
81 Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
82
83 explicit TLSPage(VAddr address) : base_address{address} {}
84
85 bool HasAvailableSlots() const {
86 return !is_slot_used.all();
87 }
88
89 VAddr GetBaseAddress() const {
90 return base_address;
91 }
92
93 std::optional<VAddr> ReserveSlot() {
94 for (std::size_t i = 0; i < is_slot_used.size(); i++) {
95 if (is_slot_used[i]) {
96 continue;
97 }
98
99 is_slot_used[i] = true;
100 return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
101 }
102
103 return std::nullopt;
104 }
105
106 void ReleaseSlot(VAddr address) {
107 // Ensure that all given addresses are consistent with how TLS pages
108 // are intended to be used when releasing slots.
109 ASSERT(IsWithinPage(address));
110 ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
111
112 const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
113 is_slot_used[index] = false;
114 }
115
116private:
117 bool IsWithinPage(VAddr address) const {
118 return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
119 }
120
121 VAddr base_address;
122 std::bitset<num_slot_entries> is_slot_used;
123};
124
125ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, 71ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
126 ProcessType type, KResourceLimit* res_limit) { 72 ProcessType type, KResourceLimit* res_limit) {
127 auto& kernel = system.Kernel(); 73 auto& kernel = system.Kernel();
@@ -404,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
404 } 350 }
405 351
406 // Create TLS region 352 // Create TLS region
407 tls_region_address = CreateTLSRegion(); 353 R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
408 memory_reservation.Commit(); 354 memory_reservation.Commit();
409 355
410 return handle_table.Initialize(capabilities.GetHandleTableSize()); 356 return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -444,7 +390,7 @@ void KProcess::PrepareForTermination() {
444 390
445 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); 391 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
446 392
447 FreeTLSRegion(tls_region_address); 393 this->DeleteThreadLocalRegion(tls_region_address);
448 tls_region_address = 0; 394 tls_region_address = 0;
449 395
450 if (resource_limit) { 396 if (resource_limit) {
@@ -456,9 +402,6 @@ void KProcess::PrepareForTermination() {
456} 402}
457 403
458void KProcess::Finalize() { 404void KProcess::Finalize() {
459 // Finalize the handle table and close any open handles.
460 handle_table.Finalize();
461
462 // Free all shared memory infos. 405 // Free all shared memory infos.
463 { 406 {
464 auto it = shared_memory_list.begin(); 407 auto it = shared_memory_list.begin();
@@ -483,67 +426,110 @@ void KProcess::Finalize() {
483 resource_limit = nullptr; 426 resource_limit = nullptr;
484 } 427 }
485 428
429 // Finalize the page table.
430 page_table.reset();
431
486 // Perform inherited finalization. 432 // Perform inherited finalization.
487 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); 433 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
488} 434}
489 435
490/** 436ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
491 * Attempts to find a TLS page that contains a free slot for 437 KThreadLocalPage* tlp = nullptr;
492 * use by a thread. 438 VAddr tlr = 0;
493 *
494 * @returns If a page with an available slot is found, then an iterator
495 * pointing to the page is returned. Otherwise the end iterator
496 * is returned instead.
497 */
498static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
499 return std::find_if(tls_pages.begin(), tls_pages.end(),
500 [](const auto& page) { return page.HasAvailableSlots(); });
501}
502 439
503VAddr KProcess::CreateTLSRegion() { 440 // See if we can get a region from a partially used TLP.
504 KScopedSchedulerLock lock(kernel); 441 {
505 if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; 442 KScopedSchedulerLock sl{kernel};
506 tls_page_iter != tls_pages.cend()) {
507 return *tls_page_iter->ReserveSlot();
508 }
509 443
510 Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; 444 if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
511 ASSERT(tls_page_ptr); 445 tlr = it->Reserve();
446 ASSERT(tlr != 0);
512 447
513 const VAddr start{page_table->GetKernelMapRegionStart()}; 448 if (it->IsAllUsed()) {
514 const VAddr size{page_table->GetKernelMapRegionEnd() - start}; 449 tlp = std::addressof(*it);
515 const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; 450 partially_used_tlp_tree.erase(it);
516 const VAddr tls_page_addr{page_table 451 fully_used_tlp_tree.insert(*tlp);
517 ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, 452 }
518 KMemoryState::ThreadLocal,
519 KMemoryPermission::UserReadWrite,
520 tls_map_addr)
521 .ValueOr(0)};
522 453
523 ASSERT(tls_page_addr); 454 *out = tlr;
455 return ResultSuccess;
456 }
457 }
458
459 // Allocate a new page.
460 tlp = KThreadLocalPage::Allocate(kernel);
461 R_UNLESS(tlp != nullptr, ResultOutOfMemory);
462 auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
463
464 // Initialize the new page.
465 R_TRY(tlp->Initialize(kernel, this));
524 466
525 std::memset(tls_page_ptr, 0, PageSize); 467 // Reserve a TLR.
526 tls_pages.emplace_back(tls_page_addr); 468 tlr = tlp->Reserve();
469 ASSERT(tlr != 0);
527 470
528 const auto reserve_result{tls_pages.back().ReserveSlot()}; 471 // Insert into our tree.
529 ASSERT(reserve_result.has_value()); 472 {
473 KScopedSchedulerLock sl{kernel};
474 if (tlp->IsAllUsed()) {
475 fully_used_tlp_tree.insert(*tlp);
476 } else {
477 partially_used_tlp_tree.insert(*tlp);
478 }
479 }
530 480
531 return *reserve_result; 481 // We succeeded!
482 tlp_guard.Cancel();
483 *out = tlr;
484 return ResultSuccess;
532} 485}
533 486
534void KProcess::FreeTLSRegion(VAddr tls_address) { 487ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
535 KScopedSchedulerLock lock(kernel); 488 KThreadLocalPage* page_to_free = nullptr;
536 const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
537 auto iter =
538 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
539 return page.GetBaseAddress() == aligned_address;
540 });
541 489
542 // Something has gone very wrong if we're freeing a region 490 // Release the region.
543 // with no actual page available. 491 {
544 ASSERT(iter != tls_pages.cend()); 492 KScopedSchedulerLock sl{kernel};
493
494 // Try to find the page in the partially used list.
495 auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
496 if (it == partially_used_tlp_tree.end()) {
497 // If we don't find it, it has to be in the fully used list.
498 it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
499 R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
500
501 // Release the region.
502 it->Release(addr);
503
504 // Move the page out of the fully used list.
505 KThreadLocalPage* tlp = std::addressof(*it);
506 fully_used_tlp_tree.erase(it);
507 if (tlp->IsAllFree()) {
508 page_to_free = tlp;
509 } else {
510 partially_used_tlp_tree.insert(*tlp);
511 }
512 } else {
513 // Release the region.
514 it->Release(addr);
515
516 // Handle the all-free case.
517 KThreadLocalPage* tlp = std::addressof(*it);
518 if (tlp->IsAllFree()) {
519 partially_used_tlp_tree.erase(it);
520 page_to_free = tlp;
521 }
522 }
523 }
524
525 // If we should free the page it was in, do so.
526 if (page_to_free != nullptr) {
527 page_to_free->Finalize();
545 528
546 iter->ReleaseSlot(tls_address); 529 KThreadLocalPage::Free(kernel, page_to_free);
530 }
531
532 return ResultSuccess;
547} 533}
548 534
549void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 535void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index cf1b67428..9f171e3da 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -8,13 +8,13 @@
8#include <cstddef> 8#include <cstddef>
9#include <list> 9#include <list>
10#include <string> 10#include <string>
11#include <vector>
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "core/hle/kernel/k_address_arbiter.h" 12#include "core/hle/kernel/k_address_arbiter.h"
14#include "core/hle/kernel/k_auto_object.h" 13#include "core/hle/kernel/k_auto_object.h"
15#include "core/hle/kernel/k_condition_variable.h" 14#include "core/hle/kernel/k_condition_variable.h"
16#include "core/hle/kernel/k_handle_table.h" 15#include "core/hle/kernel/k_handle_table.h"
17#include "core/hle/kernel/k_synchronization_object.h" 16#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/kernel/k_thread_local_page.h"
18#include "core/hle/kernel/k_worker_task.h" 18#include "core/hle/kernel/k_worker_task.h"
19#include "core/hle/kernel/process_capability.h" 19#include "core/hle/kernel/process_capability.h"
20#include "core/hle/kernel/slab_helpers.h" 20#include "core/hle/kernel/slab_helpers.h"
@@ -362,10 +362,10 @@ public:
362 // Thread-local storage management 362 // Thread-local storage management
363 363
364 // Marks the next available region as used and returns the address of the slot. 364 // Marks the next available region as used and returns the address of the slot.
365 [[nodiscard]] VAddr CreateTLSRegion(); 365 [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
366 366
367 // Frees a used TLS slot identified by the given address 367 // Frees a used TLS slot identified by the given address
368 void FreeTLSRegion(VAddr tls_address); 368 ResultCode DeleteThreadLocalRegion(VAddr addr);
369 369
370private: 370private:
371 void PinThread(s32 core_id, KThread* thread) { 371 void PinThread(s32 core_id, KThread* thread) {
@@ -413,13 +413,6 @@ private:
413 /// The ideal CPU core for this process, threads are scheduled on this core by default. 413 /// The ideal CPU core for this process, threads are scheduled on this core by default.
414 u8 ideal_core = 0; 414 u8 ideal_core = 0;
415 415
416 /// The Thread Local Storage area is allocated as processes create threads,
417 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
418 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
419 /// page as a bitmask.
420 /// This vector will grow as more pages are allocated for new threads.
421 std::vector<TLSPage> tls_pages;
422
423 /// Contains the parsed process capability descriptors. 416 /// Contains the parsed process capability descriptors.
424 ProcessCapabilities capabilities; 417 ProcessCapabilities capabilities;
425 418
@@ -429,7 +422,7 @@ private:
429 bool is_64bit_process = true; 422 bool is_64bit_process = true;
430 423
431 /// Total running time for the process in ticks. 424 /// Total running time for the process in ticks.
432 u64 total_process_running_time_ticks = 0; 425 std::atomic<u64> total_process_running_time_ticks = 0;
433 426
434 /// Per-process handle table for storing created object handles in. 427 /// Per-process handle table for storing created object handles in.
435 KHandleTable handle_table; 428 KHandleTable handle_table;
@@ -482,6 +475,12 @@ private:
482 KThread* exception_thread{}; 475 KThread* exception_thread{};
483 476
484 KLightLock state_lock; 477 KLightLock state_lock;
478
479 using TLPTree =
480 Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
481 using TLPIterator = TLPTree::iterator;
482 TLPTree fully_used_tlp_tree;
483 TLPTree partially_used_tlp_tree;
485}; 484};
486 485
487} // namespace Kernel 486} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c96520828..526eb4b70 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -22,7 +22,6 @@
22#include "core/hle/kernel/k_thread.h" 22#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/kernel.h" 23#include "core/hle/kernel/kernel.h"
24#include "core/hle/kernel/physical_core.h" 24#include "core/hle/kernel/physical_core.h"
25#include "core/hle/kernel/time_manager.h"
26 25
27namespace Kernel { 26namespace Kernel {
28 27
@@ -706,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
706 prev_thread = nullptr; 705 prev_thread = nullptr;
707 } 706 }
708 707
709 thread->context_guard.Unlock(); 708 thread->context_guard.unlock();
710} 709}
711 710
712void KScheduler::Reload(KThread* thread) { 711void KScheduler::Reload(KThread* thread) {
@@ -795,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
795 do { 794 do {
796 auto next_thread = current_thread.load(); 795 auto next_thread = current_thread.load();
797 if (next_thread != nullptr) { 796 if (next_thread != nullptr) {
798 const auto locked = next_thread->context_guard.TryLock(); 797 const auto locked = next_thread->context_guard.try_lock();
799 if (state.needs_scheduling.load()) { 798 if (state.needs_scheduling.load()) {
800 next_thread->context_guard.Unlock(); 799 next_thread->context_guard.unlock();
801 break; 800 break;
802 } 801 }
803 if (next_thread->GetActiveCore() != core_id) { 802 if (next_thread->GetActiveCore() != core_id) {
804 next_thread->context_guard.Unlock(); 803 next_thread->context_guard.unlock();
805 break; 804 break;
806 } 805 }
807 if (!locked) { 806 if (!locked) {
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 93c47f1b1..016e0a818 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/hle/kernel/k_spin_lock.h" 9#include "core/hle/kernel/k_spin_lock.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -75,7 +76,7 @@ private:
75 KernelCore& kernel; 76 KernelCore& kernel;
76 KAlignedSpinLock spin_lock{}; 77 KAlignedSpinLock spin_lock{};
77 s32 lock_count{}; 78 s32 lock_count{};
78 KThread* owner_thread{}; 79 std::atomic<KThread*> owner_thread{};
79}; 80};
80 81
81} // namespace Kernel 82} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index 433fc98e1..e66c0c992 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -62,6 +62,12 @@ void KServerPort::Destroy() {
62 62
63 // Close our reference to our parent. 63 // Close our reference to our parent.
64 parent->Close(); 64 parent->Close();
65
66 // Release host emulation members.
67 session_handler.reset();
68
69 // Ensure that the global list tracking server objects does not hold on to a reference.
70 kernel.UnregisterServerObject(this);
65} 71}
66 72
67bool KServerPort::IsSignaled() const { 73bool KServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 6302d5e61..2185736be 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -30,11 +30,11 @@ public:
30 30
31 /// Whether or not this server port has an HLE handler available. 31 /// Whether or not this server port has an HLE handler available.
32 bool HasSessionRequestHandler() const { 32 bool HasSessionRequestHandler() const {
33 return session_handler != nullptr; 33 return !session_handler.expired();
34 } 34 }
35 35
36 /// Gets the HLE handler for this port. 36 /// Gets the HLE handler for this port.
37 SessionRequestHandlerPtr GetSessionRequestHandler() const { 37 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
38 return session_handler; 38 return session_handler;
39 } 39 }
40 40
@@ -42,7 +42,7 @@ public:
42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
43 * will inherit a reference to this handler. 43 * will inherit a reference to this handler.
44 */ 44 */
45 void SetSessionHandler(SessionRequestHandlerPtr&& handler) { 45 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
46 session_handler = std::move(handler); 46 session_handler = std::move(handler);
47 } 47 }
48 48
@@ -66,7 +66,7 @@ private:
66 void CleanupSessions(); 66 void CleanupSessions();
67 67
68 SessionList session_list; 68 SessionList session_list;
69 SessionRequestHandlerPtr session_handler; 69 SessionRequestHandlerWeakPtr session_handler;
70 KPort* parent{}; 70 KPort* parent{};
71}; 71};
72 72
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 4d94eb9cf..7ac2ef254 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -27,10 +27,7 @@ namespace Kernel {
27 27
28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} 28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
29 29
30KServerSession::~KServerSession() { 30KServerSession::~KServerSession() = default;
31 // Ensure that the global list tracking server sessions does not hold on to a reference.
32 kernel.UnregisterServerSession(this);
33}
34 31
35void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 32void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
36 std::shared_ptr<SessionRequestManager> manager_) { 33 std::shared_ptr<SessionRequestManager> manager_) {
@@ -49,6 +46,12 @@ void KServerSession::Destroy() {
49 parent->OnServerClosed(); 46 parent->OnServerClosed();
50 47
51 parent->Close(); 48 parent->Close();
49
50 // Release host emulation members.
51 manager.reset();
52
53 // Ensure that the global list tracking server objects does not hold on to a reference.
54 kernel.UnregisterServerObject(this);
52} 55}
53 56
54void KServerSession::OnClientClosed() { 57void KServerSession::OnClientClosed() {
@@ -98,7 +101,12 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
98 UNREACHABLE(); 101 UNREACHABLE();
99 return ResultSuccess; // Ignore error if asserts are off 102 return ResultSuccess; // Ignore error if asserts are off
100 } 103 }
101 return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context); 104 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
105 return strong_ptr->HandleSyncRequest(*this, context);
106 } else {
107 UNREACHABLE();
108 return ResultSuccess;
109 }
102 110
103 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { 111 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
104 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); 112 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index 05c0bec9c..5690cc757 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -16,39 +16,34 @@ class KernelCore;
16 16
17namespace impl { 17namespace impl {
18 18
19class KSlabHeapImpl final { 19class KSlabHeapImpl {
20public:
21 YUZU_NON_COPYABLE(KSlabHeapImpl); 20 YUZU_NON_COPYABLE(KSlabHeapImpl);
22 YUZU_NON_MOVEABLE(KSlabHeapImpl); 21 YUZU_NON_MOVEABLE(KSlabHeapImpl);
23 22
23public:
24 struct Node { 24 struct Node {
25 Node* next{}; 25 Node* next{};
26 }; 26 };
27 27
28public:
28 constexpr KSlabHeapImpl() = default; 29 constexpr KSlabHeapImpl() = default;
29 constexpr ~KSlabHeapImpl() = default;
30 30
31 void Initialize(std::size_t size) { 31 void Initialize() {
32 ASSERT(head == nullptr); 32 ASSERT(m_head == nullptr);
33 obj_size = size;
34 }
35
36 constexpr std::size_t GetObjectSize() const {
37 return obj_size;
38 } 33 }
39 34
40 Node* GetHead() const { 35 Node* GetHead() const {
41 return head; 36 return m_head;
42 } 37 }
43 38
44 void* Allocate() { 39 void* Allocate() {
45 Node* ret = head.load(); 40 Node* ret = m_head.load();
46 41
47 do { 42 do {
48 if (ret == nullptr) { 43 if (ret == nullptr) {
49 break; 44 break;
50 } 45 }
51 } while (!head.compare_exchange_weak(ret, ret->next)); 46 } while (!m_head.compare_exchange_weak(ret, ret->next));
52 47
53 return ret; 48 return ret;
54 } 49 }
@@ -56,170 +51,157 @@ public:
56 void Free(void* obj) { 51 void Free(void* obj) {
57 Node* node = static_cast<Node*>(obj); 52 Node* node = static_cast<Node*>(obj);
58 53
59 Node* cur_head = head.load(); 54 Node* cur_head = m_head.load();
60 do { 55 do {
61 node->next = cur_head; 56 node->next = cur_head;
62 } while (!head.compare_exchange_weak(cur_head, node)); 57 } while (!m_head.compare_exchange_weak(cur_head, node));
63 } 58 }
64 59
65private: 60private:
66 std::atomic<Node*> head{}; 61 std::atomic<Node*> m_head{};
67 std::size_t obj_size{};
68}; 62};
69 63
70} // namespace impl 64} // namespace impl
71 65
72class KSlabHeapBase { 66template <bool SupportDynamicExpansion>
73public: 67class KSlabHeapBase : protected impl::KSlabHeapImpl {
74 YUZU_NON_COPYABLE(KSlabHeapBase); 68 YUZU_NON_COPYABLE(KSlabHeapBase);
75 YUZU_NON_MOVEABLE(KSlabHeapBase); 69 YUZU_NON_MOVEABLE(KSlabHeapBase);
76 70
77 constexpr KSlabHeapBase() = default; 71private:
78 constexpr ~KSlabHeapBase() = default; 72 size_t m_obj_size{};
73 uintptr_t m_peak{};
74 uintptr_t m_start{};
75 uintptr_t m_end{};
79 76
80 constexpr bool Contains(uintptr_t addr) const { 77private:
81 return start <= addr && addr < end; 78 void UpdatePeakImpl(uintptr_t obj) {
82 } 79 static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
80 std::atomic_ref<uintptr_t> peak_ref(m_peak);
83 81
84 constexpr std::size_t GetSlabHeapSize() const { 82 const uintptr_t alloc_peak = obj + this->GetObjectSize();
85 return (end - start) / GetObjectSize(); 83 uintptr_t cur_peak = m_peak;
84 do {
85 if (alloc_peak <= cur_peak) {
86 break;
87 }
88 } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
86 } 89 }
87 90
88 constexpr std::size_t GetObjectSize() const { 91public:
89 return impl.GetObjectSize(); 92 constexpr KSlabHeapBase() = default;
90 }
91 93
92 constexpr uintptr_t GetSlabHeapAddress() const { 94 bool Contains(uintptr_t address) const {
93 return start; 95 return m_start <= address && address < m_end;
94 } 96 }
95 97
96 std::size_t GetObjectIndexImpl(const void* obj) const { 98 void Initialize(size_t obj_size, void* memory, size_t memory_size) {
97 return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); 99 // Ensure we don't initialize a slab using null memory.
100 ASSERT(memory != nullptr);
101
102 // Set our object size.
103 m_obj_size = obj_size;
104
105 // Initialize the base allocator.
106 KSlabHeapImpl::Initialize();
107
108 // Set our tracking variables.
109 const size_t num_obj = (memory_size / obj_size);
110 m_start = reinterpret_cast<uintptr_t>(memory);
111 m_end = m_start + num_obj * obj_size;
112 m_peak = m_start;
113
114 // Free the objects.
115 u8* cur = reinterpret_cast<u8*>(m_end);
116
117 for (size_t i = 0; i < num_obj; i++) {
118 cur -= obj_size;
119 KSlabHeapImpl::Free(cur);
120 }
98 } 121 }
99 122
100 std::size_t GetPeakIndex() const { 123 size_t GetSlabHeapSize() const {
101 return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); 124 return (m_end - m_start) / this->GetObjectSize();
102 } 125 }
103 126
104 void* AllocateImpl() { 127 size_t GetObjectSize() const {
105 return impl.Allocate(); 128 return m_obj_size;
106 } 129 }
107 130
108 void FreeImpl(void* obj) { 131 void* Allocate() {
109 // Don't allow freeing an object that wasn't allocated from this heap 132 void* obj = KSlabHeapImpl::Allocate();
110 ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
111 133
112 impl.Free(obj); 134 return obj;
113 } 135 }
114 136
115 void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { 137 void Free(void* obj) {
116 // Ensure we don't initialize a slab using null memory 138 // Don't allow freeing an object that wasn't allocated from this heap.
117 ASSERT(memory != nullptr); 139 const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
118 140 ASSERT(contained);
119 // Initialize the base allocator 141 KSlabHeapImpl::Free(obj);
120 impl.Initialize(obj_size); 142 }
121 143
122 // Set our tracking variables 144 size_t GetObjectIndex(const void* obj) const {
123 const std::size_t num_obj = (memory_size / obj_size); 145 if constexpr (SupportDynamicExpansion) {
124 start = reinterpret_cast<uintptr_t>(memory); 146 if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
125 end = start + num_obj * obj_size; 147 return std::numeric_limits<size_t>::max();
126 peak = start; 148 }
149 }
127 150
128 // Free the objects 151 return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
129 u8* cur = reinterpret_cast<u8*>(end); 152 }
130 153
131 for (std::size_t i{}; i < num_obj; i++) { 154 size_t GetPeakIndex() const {
132 cur -= obj_size; 155 return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
133 impl.Free(cur);
134 }
135 } 156 }
136 157
137private: 158 uintptr_t GetSlabHeapAddress() const {
138 using Impl = impl::KSlabHeapImpl; 159 return m_start;
160 }
139 161
140 Impl impl; 162 size_t GetNumRemaining() const {
141 uintptr_t peak{}; 163 // Only calculate the number of remaining objects under debug configuration.
142 uintptr_t start{}; 164 return 0;
143 uintptr_t end{}; 165 }
144}; 166};
145 167
146template <typename T> 168template <typename T>
147class KSlabHeap final : public KSlabHeapBase { 169class KSlabHeap final : public KSlabHeapBase<false> {
148public: 170private:
149 enum class AllocationType { 171 using BaseHeap = KSlabHeapBase<false>;
150 Host,
151 Guest,
152 };
153 172
154 explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) 173public:
155 : KSlabHeapBase(), allocation_type{allocation_type_} {} 174 constexpr KSlabHeap() = default;
156 175
157 void Initialize(void* memory, std::size_t memory_size) { 176 void Initialize(void* memory, size_t memory_size) {
158 if (allocation_type == AllocationType::Guest) { 177 BaseHeap::Initialize(sizeof(T), memory, memory_size);
159 InitializeImpl(sizeof(T), memory, memory_size);
160 }
161 } 178 }
162 179
163 T* Allocate() { 180 T* Allocate() {
164 switch (allocation_type) { 181 T* obj = static_cast<T*>(BaseHeap::Allocate());
165 case AllocationType::Host:
166 // Fallback for cases where we do not yet support allocating guest memory from the slab
167 // heap, such as for kernel memory regions.
168 return new T;
169
170 case AllocationType::Guest:
171 T* obj = static_cast<T*>(AllocateImpl());
172 if (obj != nullptr) {
173 new (obj) T();
174 }
175 return obj;
176 }
177 182
178 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); 183 if (obj != nullptr) [[likely]] {
179 return nullptr; 184 std::construct_at(obj);
185 }
186 return obj;
180 } 187 }
181 188
182 T* AllocateWithKernel(KernelCore& kernel) { 189 T* Allocate(KernelCore& kernel) {
183 switch (allocation_type) { 190 T* obj = static_cast<T*>(BaseHeap::Allocate());
184 case AllocationType::Host:
185 // Fallback for cases where we do not yet support allocating guest memory from the slab
186 // heap, such as for kernel memory regions.
187 return new T(kernel);
188 191
189 case AllocationType::Guest: 192 if (obj != nullptr) [[likely]] {
190 T* obj = static_cast<T*>(AllocateImpl()); 193 std::construct_at(obj, kernel);
191 if (obj != nullptr) {
192 new (obj) T(kernel);
193 }
194 return obj;
195 } 194 }
196 195 return obj;
197 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
198 return nullptr;
199 } 196 }
200 197
201 void Free(T* obj) { 198 void Free(T* obj) {
202 switch (allocation_type) { 199 BaseHeap::Free(obj);
203 case AllocationType::Host:
204 // Fallback for cases where we do not yet support allocating guest memory from the slab
205 // heap, such as for kernel memory regions.
206 delete obj;
207 return;
208
209 case AllocationType::Guest:
210 FreeImpl(obj);
211 return;
212 }
213
214 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
215 } 200 }
216 201
217 constexpr std::size_t GetObjectIndex(const T* obj) const { 202 size_t GetObjectIndex(const T* obj) const {
218 return GetObjectIndexImpl(obj); 203 return BaseHeap::GetObjectIndex(obj);
219 } 204 }
220
221private:
222 const AllocationType allocation_type;
223}; 205};
224 206
225} // namespace Kernel 207} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 4412aa4bb..527ff0f9f 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -4,51 +4,18 @@
4 4
5#include "core/hle/kernel/k_spin_lock.h" 5#include "core/hle/kernel/k_spin_lock.h"
6 6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // namespace
34
35namespace Kernel { 7namespace Kernel {
36 8
37void KSpinLock::Lock() { 9void KSpinLock::Lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) { 10 lck.lock();
39 ThreadPause();
40 }
41} 11}
42 12
43void KSpinLock::Unlock() { 13void KSpinLock::Unlock() {
44 lck.clear(std::memory_order_release); 14 lck.unlock();
45} 15}
46 16
47bool KSpinLock::TryLock() { 17bool KSpinLock::TryLock() {
48 if (lck.test_and_set(std::memory_order_acquire)) { 18 return lck.try_lock();
49 return false;
50 }
51 return true;
52} 19}
53 20
54} // namespace Kernel 21} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 4d87d006a..7868b25a5 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <mutex>
8 8
9#include "core/hle/kernel/k_scoped_lock.h" 9#include "core/hle/kernel/k_scoped_lock.h"
10 10
@@ -25,7 +25,7 @@ public:
25 [[nodiscard]] bool TryLock(); 25 [[nodiscard]] bool TryLock();
26 26
27private: 27private:
28 std::atomic_flag lck = ATOMIC_FLAG_INIT; 28 std::mutex lck;
29}; 29};
30 30
31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. 31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index de3ffe0c7..af71987e8 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -14,9 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/fiber.h" 15#include "common/fiber.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/scope_exit.h"
18#include "common/settings.h" 17#include "common/settings.h"
19#include "common/thread_queue_list.h"
20#include "core/core.h" 18#include "core/core.h"
21#include "core/cpu_manager.h" 19#include "core/cpu_manager.h"
22#include "core/hardware_properties.h" 20#include "core/hardware_properties.h"
@@ -33,7 +31,6 @@
33#include "core/hle/kernel/k_worker_task_manager.h" 31#include "core/hle/kernel/k_worker_task_manager.h"
34#include "core/hle/kernel/kernel.h" 32#include "core/hle/kernel/kernel.h"
35#include "core/hle/kernel/svc_results.h" 33#include "core/hle/kernel/svc_results.h"
36#include "core/hle/kernel/time_manager.h"
37#include "core/hle/result.h" 34#include "core/hle/result.h"
38#include "core/memory.h" 35#include "core/memory.h"
39 36
@@ -210,7 +207,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
210 if (owner != nullptr) { 207 if (owner != nullptr) {
211 // Setup the TLS, if needed. 208 // Setup the TLS, if needed.
212 if (type == ThreadType::User) { 209 if (type == ThreadType::User) {
213 tls_address = owner->CreateTLSRegion(); 210 R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
214 } 211 }
215 212
216 parent = owner; 213 parent = owner;
@@ -305,7 +302,7 @@ void KThread::Finalize() {
305 302
306 // If the thread has a local region, delete it. 303 // If the thread has a local region, delete it.
307 if (tls_address != 0) { 304 if (tls_address != 0) {
308 parent->FreeTLSRegion(tls_address); 305 ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
309 } 306 }
310 307
311 // Release any waiters. 308 // Release any waiters.
@@ -326,6 +323,9 @@ void KThread::Finalize() {
326 } 323 }
327 } 324 }
328 325
326 // Release host emulation members.
327 host_context.reset();
328
329 // Perform inherited finalization. 329 // Perform inherited finalization.
330 KSynchronizationObject::Finalize(); 330 KSynchronizationObject::Finalize();
331} 331}
@@ -723,10 +723,10 @@ void KThread::UpdateState() {
723 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 723 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
724 724
725 // Set our suspend flags in state. 725 // Set our suspend flags in state.
726 const auto old_state = thread_state; 726 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
727 const auto new_state = 727 const auto new_state =
728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); 728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
729 thread_state = new_state; 729 thread_state.store(new_state, std::memory_order_relaxed);
730 730
731 // Note the state change in scheduler. 731 // Note the state change in scheduler.
732 if (new_state != old_state) { 732 if (new_state != old_state) {
@@ -738,8 +738,8 @@ void KThread::Continue() {
738 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 738 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
739 739
740 // Clear our suspend flags in state. 740 // Clear our suspend flags in state.
741 const auto old_state = thread_state; 741 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
742 thread_state = old_state & ThreadState::Mask; 742 thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
743 743
744 // Note the state change in scheduler. 744 // Note the state change in scheduler.
745 KScheduler::OnThreadStateChanged(kernel, this, old_state); 745 KScheduler::OnThreadStateChanged(kernel, this, old_state);
@@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
1079 return; 1079 return;
1080 } 1080 }
1081 1081
1082 // Block until we can grab the lock. 1082 // Block until we are no longer waiting.
1083 KScopedSpinLock lk{dummy_wait_lock}; 1083 std::unique_lock lk(dummy_wait_lock);
1084} 1084 dummy_wait_cv.wait(
1085 1085 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1086void KThread::IfDummyThreadBeginWait() {
1087 if (!IsDummyThread()) {
1088 return;
1089 }
1090
1091 // Ensure the thread will block when IfDummyThreadTryWait is called.
1092 dummy_wait_lock.Lock();
1093} 1086}
1094 1087
1095void KThread::IfDummyThreadEndWait() { 1088void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
1097 return; 1090 return;
1098 } 1091 }
1099 1092
1100 // Ensure the thread will no longer block. 1093 // Wake up the waiting thread.
1101 dummy_wait_lock.Unlock(); 1094 dummy_wait_cv.notify_one();
1102} 1095}
1103 1096
1104void KThread::BeginWait(KThreadQueue* queue) { 1097void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
1107 1100
1108 // Set our wait queue. 1101 // Set our wait queue.
1109 wait_queue = queue; 1102 wait_queue = queue;
1110
1111 // Special case for dummy threads to ensure they block.
1112 IfDummyThreadBeginWait();
1113} 1103}
1114 1104
1115void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { 1105void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
1158 SetMutexWaitAddressForDebugging({}); 1148 SetMutexWaitAddressForDebugging({});
1159 SetWaitReasonForDebugging({}); 1149 SetWaitReasonForDebugging({});
1160 1150
1161 const ThreadState old_state = thread_state; 1151 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
1162 thread_state = 1152 thread_state.store(
1163 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); 1153 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
1164 if (thread_state != old_state) { 1154 std::memory_order_relaxed);
1155 if (thread_state.load(std::memory_order_relaxed) != old_state) {
1165 KScheduler::OnThreadStateChanged(kernel, this, old_state); 1156 KScheduler::OnThreadStateChanged(kernel, this, old_state);
1166 } 1157 }
1167} 1158}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d058db62c..4892fdf76 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -5,6 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
9#include <condition_variable>
10#include <mutex>
8#include <span> 11#include <span>
9#include <string> 12#include <string>
10#include <utility> 13#include <utility>
@@ -14,6 +17,7 @@
14 17
15#include "common/common_types.h" 18#include "common/common_types.h"
16#include "common/intrusive_red_black_tree.h" 19#include "common/intrusive_red_black_tree.h"
20#include "common/spin_lock.h"
17#include "core/arm/arm_interface.h" 21#include "core/arm/arm_interface.h"
18#include "core/hle/kernel/k_affinity_mask.h" 22#include "core/hle/kernel/k_affinity_mask.h"
19#include "core/hle/kernel/k_light_lock.h" 23#include "core/hle/kernel/k_light_lock.h"
@@ -255,11 +259,11 @@ public:
255 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); 259 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
256 260
257 [[nodiscard]] ThreadState GetState() const { 261 [[nodiscard]] ThreadState GetState() const {
258 return thread_state & ThreadState::Mask; 262 return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
259 } 263 }
260 264
261 [[nodiscard]] ThreadState GetRawState() const { 265 [[nodiscard]] ThreadState GetRawState() const {
262 return thread_state; 266 return thread_state.load(std::memory_order_relaxed);
263 } 267 }
264 268
265 void SetState(ThreadState state); 269 void SetState(ThreadState state);
@@ -641,7 +645,6 @@ public:
641 // blocking as needed. 645 // blocking as needed.
642 646
643 void IfDummyThreadTryWait(); 647 void IfDummyThreadTryWait();
644 void IfDummyThreadBeginWait();
645 void IfDummyThreadEndWait(); 648 void IfDummyThreadEndWait();
646 649
647private: 650private:
@@ -656,7 +659,7 @@ private:
656 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); 659 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
657 660
658 struct ConditionVariableComparator { 661 struct ConditionVariableComparator {
659 struct LightCompareType { 662 struct RedBlackKeyType {
660 u64 cv_key{}; 663 u64 cv_key{};
661 s32 priority{}; 664 s32 priority{};
662 665
@@ -672,8 +675,8 @@ private:
672 template <typename T> 675 template <typename T>
673 requires( 676 requires(
674 std::same_as<T, KThread> || 677 std::same_as<T, KThread> ||
675 std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, 678 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
676 const KThread& rhs) { 679 const KThread& rhs) {
677 const u64 l_key = lhs.GetConditionVariableKey(); 680 const u64 l_key = lhs.GetConditionVariableKey();
678 const u64 r_key = rhs.GetConditionVariableKey(); 681 const u64 r_key = rhs.GetConditionVariableKey();
679 682
@@ -751,7 +754,7 @@ private:
751 KAffinityMask original_physical_affinity_mask{}; 754 KAffinityMask original_physical_affinity_mask{};
752 s32 original_physical_ideal_core_id{}; 755 s32 original_physical_ideal_core_id{};
753 s32 num_core_migration_disables{}; 756 s32 num_core_migration_disables{};
754 ThreadState thread_state{}; 757 std::atomic<ThreadState> thread_state{};
755 std::atomic<bool> termination_requested{}; 758 std::atomic<bool> termination_requested{};
756 bool wait_cancelled{}; 759 bool wait_cancelled{};
757 bool cancellable{}; 760 bool cancellable{};
@@ -761,13 +764,14 @@ private:
761 s8 priority_inheritance_count{}; 764 s8 priority_inheritance_count{};
762 bool resource_limit_release_hint{}; 765 bool resource_limit_release_hint{};
763 StackParameters stack_parameters{}; 766 StackParameters stack_parameters{};
764 KSpinLock context_guard{}; 767 Common::SpinLock context_guard{};
765 KSpinLock dummy_wait_lock{};
766 768
767 // For emulation 769 // For emulation
768 std::shared_ptr<Common::Fiber> host_context{}; 770 std::shared_ptr<Common::Fiber> host_context{};
769 bool is_single_core{}; 771 bool is_single_core{};
770 ThreadType thread_type{}; 772 ThreadType thread_type{};
773 std::mutex dummy_wait_lock;
774 std::condition_variable dummy_wait_cv;
771 775
772 // For debugging 776 // For debugging
773 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 777 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
new file mode 100644
index 000000000..3fb277eba
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -0,0 +1,68 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/scope_exit.h"
6#include "core/core.h"
7
8#include "core/hle/kernel/k_memory_block.h"
9#include "core/hle/kernel/k_page_buffer.h"
10#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h"
12#include "core/hle/kernel/k_thread_local_page.h"
13#include "core/hle/kernel/kernel.h"
14
15namespace Kernel {
16
17ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
18 // Set that this process owns us.
19 m_owner = process;
20 m_kernel = &kernel;
21
22 // Allocate a new page.
23 KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
24 R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
25 auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
26
27 // Map the address in.
28 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
29 R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
30 KMemoryState::ThreadLocal,
31 KMemoryPermission::UserReadWrite));
32
33 // We succeeded.
34 page_buf_guard.Cancel();
35
36 return ResultSuccess;
37}
38
39ResultCode KThreadLocalPage::Finalize() {
40 // Get the physical address of the page.
41 const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
42 ASSERT(phys_addr);
43
44 // Unmap the page.
45 R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
46
47 // Free the page.
48 KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
49
50 return ResultSuccess;
51}
52
53VAddr KThreadLocalPage::Reserve() {
54 for (size_t i = 0; i < m_is_region_free.size(); i++) {
55 if (m_is_region_free[i]) {
56 m_is_region_free[i] = false;
57 return this->GetRegionAddress(i);
58 }
59 }
60
61 return 0;
62}
63
64void KThreadLocalPage::Release(VAddr addr) {
65 m_is_region_free[this->GetRegionIndex(addr)] = true;
66}
67
68} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
new file mode 100644
index 000000000..74b565a71
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -0,0 +1,111 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/intrusive_red_black_tree.h"
14#include "core/hle/kernel/memory_types.h"
15#include "core/hle/kernel/slab_helpers.h"
16#include "core/hle/result.h"
17
18namespace Kernel {
19
20class KernelCore;
21class KProcess;
22
23class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
24 public KSlabAllocated<KThreadLocalPage> {
25public:
26 static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
27 static_assert(RegionsPerPage > 0);
28
29public:
30 constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
31 m_is_region_free.fill(true);
32 }
33
34 constexpr VAddr GetAddress() const {
35 return m_virt_addr;
36 }
37
38 ResultCode Initialize(KernelCore& kernel, KProcess* process);
39 ResultCode Finalize();
40
41 VAddr Reserve();
42 void Release(VAddr addr);
43
44 bool IsAllUsed() const {
45 return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
46 [](bool is_free) { return !is_free; });
47 }
48
49 bool IsAllFree() const {
50 return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
51 [](bool is_free) { return is_free; });
52 }
53
54 bool IsAnyUsed() const {
55 return !this->IsAllFree();
56 }
57
58 bool IsAnyFree() const {
59 return !this->IsAllUsed();
60 }
61
62public:
63 using RedBlackKeyType = VAddr;
64
65 static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
66 return v;
67 }
68 static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
69 return v.GetAddress();
70 }
71
72 template <typename T>
73 requires(std::same_as<T, KThreadLocalPage> ||
74 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
75 const KThreadLocalPage&
76 rhs) {
77 const VAddr lval = GetRedBlackKey(lhs);
78 const VAddr rval = GetRedBlackKey(rhs);
79
80 if (lval < rval) {
81 return -1;
82 } else if (lval == rval) {
83 return 0;
84 } else {
85 return 1;
86 }
87 }
88
89private:
90 constexpr VAddr GetRegionAddress(size_t i) const {
91 return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
92 }
93
94 constexpr bool Contains(VAddr addr) const {
95 return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
96 }
97
98 constexpr size_t GetRegionIndex(VAddr addr) const {
99 ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
100 ASSERT(this->Contains(addr));
101 return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
102 }
103
104private:
105 VAddr m_virt_addr{};
106 KProcess* m_owner{};
107 KernelCore* m_kernel{};
108 std::array<bool, RegionsPerPage> m_is_region_free{};
109};
110
111} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 71bd466cf..5984afd7e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -22,9 +22,7 @@
22#include "core/arm/exclusive_monitor.h" 22#include "core/arm/exclusive_monitor.h"
23#include "core/core.h" 23#include "core/core.h"
24#include "core/core_timing.h" 24#include "core/core_timing.h"
25#include "core/core_timing_util.h"
26#include "core/cpu_manager.h" 25#include "core/cpu_manager.h"
27#include "core/device_memory.h"
28#include "core/hardware_properties.h" 26#include "core/hardware_properties.h"
29#include "core/hle/kernel/init/init_slab_setup.h" 27#include "core/hle/kernel/init/init_slab_setup.h"
30#include "core/hle/kernel/k_client_port.h" 28#include "core/hle/kernel/k_client_port.h"
@@ -35,7 +33,6 @@
35#include "core/hle/kernel/k_resource_limit.h" 33#include "core/hle/kernel/k_resource_limit.h"
36#include "core/hle/kernel/k_scheduler.h" 34#include "core/hle/kernel/k_scheduler.h"
37#include "core/hle/kernel/k_shared_memory.h" 35#include "core/hle/kernel/k_shared_memory.h"
38#include "core/hle/kernel/k_slab_heap.h"
39#include "core/hle/kernel/k_thread.h" 36#include "core/hle/kernel/k_thread.h"
40#include "core/hle/kernel/k_worker_task_manager.h" 37#include "core/hle/kernel/k_worker_task_manager.h"
41#include "core/hle/kernel/kernel.h" 38#include "core/hle/kernel/kernel.h"
@@ -52,7 +49,7 @@ namespace Kernel {
52 49
53struct KernelCore::Impl { 50struct KernelCore::Impl {
54 explicit Impl(Core::System& system_, KernelCore& kernel_) 51 explicit Impl(Core::System& system_, KernelCore& kernel_)
55 : time_manager{system_}, object_list_container{kernel_}, 52 : time_manager{system_},
56 service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} 53 service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
57 54
58 void SetMulticore(bool is_multi) { 55 void SetMulticore(bool is_multi) {
@@ -60,9 +57,11 @@ struct KernelCore::Impl {
60 } 57 }
61 58
62 void Initialize(KernelCore& kernel) { 59 void Initialize(KernelCore& kernel) {
60 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
63 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
64 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
65 global_handle_table->Initialize(KHandleTable::MaxTableSize); 63 global_handle_table->Initialize(KHandleTable::MaxTableSize);
64 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
66 65
67 is_phantom_mode_for_singlecore = false; 66 is_phantom_mode_for_singlecore = false;
68 67
@@ -76,7 +75,7 @@ struct KernelCore::Impl {
76 // Initialize kernel memory and resources. 75 // Initialize kernel memory and resources.
77 InitializeSystemResourceLimit(kernel, system.CoreTiming()); 76 InitializeSystemResourceLimit(kernel, system.CoreTiming());
78 InitializeMemoryLayout(); 77 InitializeMemoryLayout();
79 InitializePageSlab(); 78 Init::InitializeKPageBufferSlabHeap(system);
80 InitializeSchedulers(); 79 InitializeSchedulers();
81 InitializeSuspendThreads(); 80 InitializeSuspendThreads();
82 InitializePreemption(kernel); 81 InitializePreemption(kernel);
@@ -86,7 +85,7 @@ struct KernelCore::Impl {
86 85
87 void InitializeCores() { 86 void InitializeCores() {
88 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 87 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
89 cores[core_id].Initialize(current_process->Is64BitProcess()); 88 cores[core_id].Initialize((*current_process).Is64BitProcess());
90 system.Memory().SetCurrentPageTable(*current_process, core_id); 89 system.Memory().SetCurrentPageTable(*current_process, core_id);
91 } 90 }
92 } 91 }
@@ -97,30 +96,17 @@ struct KernelCore::Impl {
97 96
98 process_list.clear(); 97 process_list.clear();
99 98
100 // Close all open server ports. 99 // Close all open server sessions and ports.
101 std::unordered_set<KServerPort*> server_ports_; 100 std::unordered_set<KAutoObject*> server_objects_;
102 { 101 {
103 std::lock_guard lk(server_ports_lock); 102 std::scoped_lock lk(server_objects_lock);
104 server_ports_ = server_ports; 103 server_objects_ = server_objects;
105 server_ports.clear(); 104 server_objects.clear();
106 } 105 }
107 for (auto* server_port : server_ports_) { 106 for (auto* server_object : server_objects_) {
108 server_port->Close(); 107 server_object->Close();
109 }
110 // Close all open server sessions.
111 std::unordered_set<KServerSession*> server_sessions_;
112 {
113 std::lock_guard lk(server_sessions_lock);
114 server_sessions_ = server_sessions;
115 server_sessions.clear();
116 }
117 for (auto* server_session : server_sessions_) {
118 server_session->Close();
119 } 108 }
120 109
121 // Ensure that the object list container is finalized and properly shutdown.
122 object_list_container.Finalize();
123
124 // Ensures all service threads gracefully shutdown. 110 // Ensures all service threads gracefully shutdown.
125 ClearServiceThreads(); 111 ClearServiceThreads();
126 112
@@ -154,6 +140,7 @@ struct KernelCore::Impl {
154 CleanupObject(font_shared_mem); 140 CleanupObject(font_shared_mem);
155 CleanupObject(irs_shared_mem); 141 CleanupObject(irs_shared_mem);
156 CleanupObject(time_shared_mem); 142 CleanupObject(time_shared_mem);
143 CleanupObject(hidbus_shared_mem);
157 CleanupObject(system_resource_limit); 144 CleanupObject(system_resource_limit);
158 145
159 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 146 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -171,7 +158,7 @@ struct KernelCore::Impl {
171 158
172 // Close kernel objects that were not freed on shutdown 159 // Close kernel objects that were not freed on shutdown
173 { 160 {
174 std::lock_guard lk(registered_in_use_objects_lock); 161 std::scoped_lock lk{registered_in_use_objects_lock};
175 if (registered_in_use_objects.size()) { 162 if (registered_in_use_objects.size()) {
176 for (auto& object : registered_in_use_objects) { 163 for (auto& object : registered_in_use_objects) {
177 object->Close(); 164 object->Close();
@@ -182,23 +169,27 @@ struct KernelCore::Impl {
182 169
183 // Shutdown all processes. 170 // Shutdown all processes.
184 if (current_process) { 171 if (current_process) {
185 current_process->Finalize(); 172 (*current_process).Finalize();
186 // current_process->Close(); 173 // current_process->Close();
187 // TODO: The current process should be destroyed based on accurate ref counting after 174 // TODO: The current process should be destroyed based on accurate ref counting after
188 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. 175 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
189 current_process->Destroy(); 176 (*current_process).Destroy();
190 current_process = nullptr; 177 current_process = nullptr;
191 } 178 }
192 179
193 // Track kernel objects that were not freed on shutdown 180 // Track kernel objects that were not freed on shutdown
194 { 181 {
195 std::lock_guard lk(registered_objects_lock); 182 std::scoped_lock lk{registered_objects_lock};
196 if (registered_objects.size()) { 183 if (registered_objects.size()) {
197 LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", 184 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
198 registered_objects.size()); 185 registered_objects.size());
199 registered_objects.clear(); 186 registered_objects.clear();
200 } 187 }
201 } 188 }
189
190 // Ensure that the object list container is finalized and properly shutdown.
191 global_object_list_container->Finalize();
192 global_object_list_container.reset();
202 } 193 }
203 194
204 void InitializePhysicalCores() { 195 void InitializePhysicalCores() {
@@ -291,15 +282,16 @@ struct KernelCore::Impl {
291 282
292 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 283 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
293 KThread* GetHostDummyThread() { 284 KThread* GetHostDummyThread() {
294 auto make_thread = [this]() { 285 auto initialize = [this](KThread* thread) {
295 KThread* thread = KThread::Create(system.Kernel());
296 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 286 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
297 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 287 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
298 return thread; 288 return thread;
299 }; 289 };
300 290
301 thread_local KThread* saved_thread = make_thread(); 291 thread_local auto raw_thread = KThread(system.Kernel());
302 return saved_thread; 292 thread_local auto thread = initialize(&raw_thread);
293
294 return thread;
303 } 295 }
304 296
305 /// Registers a CPU core thread by allocating a host thread ID for it 297 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -631,16 +623,20 @@ struct KernelCore::Impl {
631 constexpr std::size_t font_size{0x1100000}; 623 constexpr std::size_t font_size{0x1100000};
632 constexpr std::size_t irs_size{0x8000}; 624 constexpr std::size_t irs_size{0x8000};
633 constexpr std::size_t time_size{0x1000}; 625 constexpr std::size_t time_size{0x1000};
626 constexpr std::size_t hidbus_size{0x1000};
634 627
635 const PAddr hid_phys_addr{system_pool.GetAddress()}; 628 const PAddr hid_phys_addr{system_pool.GetAddress()};
636 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; 629 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
637 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; 630 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
638 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; 631 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
632 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
633 time_size};
639 634
640 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 635 hid_shared_mem = KSharedMemory::Create(system.Kernel());
641 font_shared_mem = KSharedMemory::Create(system.Kernel()); 636 font_shared_mem = KSharedMemory::Create(system.Kernel());
642 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 637 irs_shared_mem = KSharedMemory::Create(system.Kernel());
643 time_shared_mem = KSharedMemory::Create(system.Kernel()); 638 time_shared_mem = KSharedMemory::Create(system.Kernel());
639 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
644 640
645 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 641 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
646 {hid_phys_addr, hid_size / PageSize}, 642 {hid_phys_addr, hid_size / PageSize},
@@ -658,22 +654,10 @@ struct KernelCore::Impl {
658 {time_phys_addr, time_size / PageSize}, 654 {time_phys_addr, time_size / PageSize},
659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 655 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
660 time_phys_addr, time_size, "Time:SharedMemory"); 656 time_phys_addr, time_size, "Time:SharedMemory");
661 } 657 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
662 658 {hidbus_phys_addr, hidbus_size / PageSize},
663 void InitializePageSlab() { 659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
664 // Allocate slab heaps 660 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
665 user_slab_heap_pages =
666 std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
667
668 // TODO(ameerj): This should be derived, not hardcoded within the kernel
669 constexpr u64 user_slab_heap_size{0x3de000};
670 // Reserve slab heaps
671 ASSERT(
672 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
673 // Initialize slab heap
674 user_slab_heap_pages->Initialize(
675 system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
676 user_slab_heap_size);
677 } 661 }
678 662
679 KClientPort* CreateNamedServicePort(std::string name) { 663 KClientPort* CreateNamedServicePort(std::string name) {
@@ -684,13 +668,20 @@ struct KernelCore::Impl {
684 } 668 }
685 669
686 KClientPort* port = &search->second(system.ServiceManager(), system); 670 KClientPort* port = &search->second(system.ServiceManager(), system);
687 { 671 RegisterServerObject(&port->GetParent()->GetServerPort());
688 std::lock_guard lk(server_ports_lock);
689 server_ports.insert(&port->GetParent()->GetServerPort());
690 }
691 return port; 672 return port;
692 } 673 }
693 674
675 void RegisterServerObject(KAutoObject* server_object) {
676 std::scoped_lock lk(server_objects_lock);
677 server_objects.insert(server_object);
678 }
679
680 void UnregisterServerObject(KAutoObject* server_object) {
681 std::scoped_lock lk(server_objects_lock);
682 server_objects.erase(server_object);
683 }
684
694 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 685 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
695 const std::string& name) { 686 const std::string& name) {
696 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 687 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -703,6 +694,12 @@ struct KernelCore::Impl {
703 694
704 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 695 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
705 if (auto strong_ptr = service_thread.lock()) { 696 if (auto strong_ptr = service_thread.lock()) {
697 if (strong_ptr == default_service_thread.lock()) {
698 // Nothing to do here, the service is using default_service_thread, which will be
699 // released on shutdown.
700 return;
701 }
702
706 service_threads_manager.QueueWork( 703 service_threads_manager.QueueWork(
707 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 704 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
708 } 705 }
@@ -712,8 +709,7 @@ struct KernelCore::Impl {
712 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 709 service_threads_manager.QueueWork([this]() { service_threads.clear(); });
713 } 710 }
714 711
715 std::mutex server_ports_lock; 712 std::mutex server_objects_lock;
716 std::mutex server_sessions_lock;
717 std::mutex registered_objects_lock; 713 std::mutex registered_objects_lock;
718 std::mutex registered_in_use_objects_lock; 714 std::mutex registered_in_use_objects_lock;
719 715
@@ -724,7 +720,7 @@ struct KernelCore::Impl {
724 720
725 // Lists all processes that exist in the current session. 721 // Lists all processes that exist in the current session.
726 std::vector<KProcess*> process_list; 722 std::vector<KProcess*> process_list;
727 KProcess* current_process{}; 723 std::atomic<KProcess*> current_process{};
728 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 724 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
729 Kernel::TimeManager time_manager; 725 Kernel::TimeManager time_manager;
730 726
@@ -737,14 +733,13 @@ struct KernelCore::Impl {
737 // stores all the objects in place. 733 // stores all the objects in place.
738 std::unique_ptr<KHandleTable> global_handle_table; 734 std::unique_ptr<KHandleTable> global_handle_table;
739 735
740 KAutoObjectWithListContainer object_list_container; 736 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
741 737
742 /// Map of named ports managed by the kernel, which can be retrieved using 738 /// Map of named ports managed by the kernel, which can be retrieved using
743 /// the ConnectToPort SVC. 739 /// the ConnectToPort SVC.
744 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 740 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
745 NamedPortTable named_ports; 741 NamedPortTable named_ports;
746 std::unordered_set<KServerPort*> server_ports; 742 std::unordered_set<KAutoObject*> server_objects;
747 std::unordered_set<KServerSession*> server_sessions;
748 std::unordered_set<KAutoObject*> registered_objects; 743 std::unordered_set<KAutoObject*> registered_objects;
749 std::unordered_set<KAutoObject*> registered_in_use_objects; 744 std::unordered_set<KAutoObject*> registered_in_use_objects;
750 745
@@ -756,19 +751,20 @@ struct KernelCore::Impl {
756 751
757 // Kernel memory management 752 // Kernel memory management
758 std::unique_ptr<KMemoryManager> memory_manager; 753 std::unique_ptr<KMemoryManager> memory_manager;
759 std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
760 754
761 // Shared memory for services 755 // Shared memory for services
762 Kernel::KSharedMemory* hid_shared_mem{}; 756 Kernel::KSharedMemory* hid_shared_mem{};
763 Kernel::KSharedMemory* font_shared_mem{}; 757 Kernel::KSharedMemory* font_shared_mem{};
764 Kernel::KSharedMemory* irs_shared_mem{}; 758 Kernel::KSharedMemory* irs_shared_mem{};
765 Kernel::KSharedMemory* time_shared_mem{}; 759 Kernel::KSharedMemory* time_shared_mem{};
760 Kernel::KSharedMemory* hidbus_shared_mem{};
766 761
767 // Memory layout 762 // Memory layout
768 std::unique_ptr<KMemoryLayout> memory_layout; 763 std::unique_ptr<KMemoryLayout> memory_layout;
769 764
770 // Threads used for services 765 // Threads used for services
771 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 766 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
767 std::weak_ptr<ServiceThread> default_service_thread;
772 Common::ThreadWorker service_threads_manager; 768 Common::ThreadWorker service_threads_manager;
773 769
774 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; 770 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -915,11 +911,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
915} 911}
916 912
917KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 913KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
918 return impl->object_list_container; 914 return *impl->global_object_list_container;
919} 915}
920 916
921const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { 917const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
922 return impl->object_list_container; 918 return *impl->global_object_list_container;
923} 919}
924 920
925void KernelCore::InvalidateAllInstructionCaches() { 921void KernelCore::InvalidateAllInstructionCaches() {
@@ -949,33 +945,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
949 return impl->CreateNamedServicePort(std::move(name)); 945 return impl->CreateNamedServicePort(std::move(name));
950} 946}
951 947
952void KernelCore::RegisterServerSession(KServerSession* server_session) { 948void KernelCore::RegisterServerObject(KAutoObject* server_object) {
953 std::lock_guard lk(impl->server_sessions_lock); 949 impl->RegisterServerObject(server_object);
954 impl->server_sessions.insert(server_session);
955} 950}
956 951
957void KernelCore::UnregisterServerSession(KServerSession* server_session) { 952void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
958 std::lock_guard lk(impl->server_sessions_lock); 953 impl->UnregisterServerObject(server_object);
959 impl->server_sessions.erase(server_session);
960} 954}
961 955
962void KernelCore::RegisterKernelObject(KAutoObject* object) { 956void KernelCore::RegisterKernelObject(KAutoObject* object) {
963 std::lock_guard lk(impl->registered_objects_lock); 957 std::scoped_lock lk{impl->registered_objects_lock};
964 impl->registered_objects.insert(object); 958 impl->registered_objects.insert(object);
965} 959}
966 960
967void KernelCore::UnregisterKernelObject(KAutoObject* object) { 961void KernelCore::UnregisterKernelObject(KAutoObject* object) {
968 std::lock_guard lk(impl->registered_objects_lock); 962 std::scoped_lock lk{impl->registered_objects_lock};
969 impl->registered_objects.erase(object); 963 impl->registered_objects.erase(object);
970} 964}
971 965
972void KernelCore::RegisterInUseObject(KAutoObject* object) { 966void KernelCore::RegisterInUseObject(KAutoObject* object) {
973 std::lock_guard lk(impl->registered_in_use_objects_lock); 967 std::scoped_lock lk{impl->registered_in_use_objects_lock};
974 impl->registered_in_use_objects.insert(object); 968 impl->registered_in_use_objects.insert(object);
975} 969}
976 970
977void KernelCore::UnregisterInUseObject(KAutoObject* object) { 971void KernelCore::UnregisterInUseObject(KAutoObject* object) {
978 std::lock_guard lk(impl->registered_in_use_objects_lock); 972 std::scoped_lock lk{impl->registered_in_use_objects_lock};
979 impl->registered_in_use_objects.erase(object); 973 impl->registered_in_use_objects.erase(object);
980} 974}
981 975
@@ -1031,14 +1025,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1031 return *impl->memory_manager; 1025 return *impl->memory_manager;
1032} 1026}
1033 1027
1034KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
1035 return *impl->user_slab_heap_pages;
1036}
1037
1038const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
1039 return *impl->user_slab_heap_pages;
1040}
1041
1042Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 1028Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1043 return *impl->hid_shared_mem; 1029 return *impl->hid_shared_mem;
1044} 1030}
@@ -1071,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
1071 return *impl->time_shared_mem; 1057 return *impl->time_shared_mem;
1072} 1058}
1073 1059
1060Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
1061 return *impl->hidbus_shared_mem;
1062}
1063
1064const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1065 return *impl->hidbus_shared_mem;
1066}
1067
1074void KernelCore::Suspend(bool in_suspention) { 1068void KernelCore::Suspend(bool in_suspention) {
1075 const bool should_suspend = exception_exited || in_suspention; 1069 const bool should_suspend = exception_exited || in_suspention;
1076 { 1070 {
@@ -1112,6 +1106,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
1112 return impl->CreateServiceThread(*this, name); 1106 return impl->CreateServiceThread(*this, name);
1113} 1107}
1114 1108
1109std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
1110 return impl->default_service_thread;
1111}
1112
1115void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1113void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
1116 impl->ReleaseServiceThread(service_thread); 1114 impl->ReleaseServiceThread(service_thread);
1117} 1115}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c1254b18d..12e44b8a5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -14,7 +14,6 @@
14#include "core/hardware_properties.h" 14#include "core/hardware_properties.h"
15#include "core/hle/kernel/k_auto_object.h" 15#include "core/hle/kernel/k_auto_object.h"
16#include "core/hle/kernel/k_slab_heap.h" 16#include "core/hle/kernel/k_slab_heap.h"
17#include "core/hle/kernel/memory_types.h"
18#include "core/hle/kernel/svc_common.h" 17#include "core/hle/kernel/svc_common.h"
19 18
20namespace Core { 19namespace Core {
@@ -43,6 +42,7 @@ class KHandleTable;
43class KLinkedListNode; 42class KLinkedListNode;
44class KMemoryLayout; 43class KMemoryLayout;
45class KMemoryManager; 44class KMemoryManager;
45class KPageBuffer;
46class KPort; 46class KPort;
47class KProcess; 47class KProcess;
48class KResourceLimit; 48class KResourceLimit;
@@ -52,6 +52,7 @@ class KSession;
52class KSharedMemory; 52class KSharedMemory;
53class KSharedMemoryInfo; 53class KSharedMemoryInfo;
54class KThread; 54class KThread;
55class KThreadLocalPage;
55class KTransferMemory; 56class KTransferMemory;
56class KWorkerTaskManager; 57class KWorkerTaskManager;
57class KWritableEvent; 58class KWritableEvent;
@@ -194,13 +195,13 @@ public:
194 /// Opens a port to a service previously registered with RegisterNamedService. 195 /// Opens a port to a service previously registered with RegisterNamedService.
195 KClientPort* CreateNamedServicePort(std::string name); 196 KClientPort* CreateNamedServicePort(std::string name);
196 197
197 /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is 198 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
198 /// necessary because we do not emulate processes for HLE sessions. 199 /// This is necessary because we do not emulate processes for HLE sessions and ports.
199 void RegisterServerSession(KServerSession* server_session); 200 void RegisterServerObject(KAutoObject* server_object);
200 201
201 /// Unregisters a server session previously registered with RegisterServerSession when it was 202 /// Unregisters a server session or port previously registered with RegisterServerSession when
202 /// destroyed during the current emulation session. 203 /// it was destroyed during the current emulation session.
203 void UnregisterServerSession(KServerSession* server_session); 204 void UnregisterServerObject(KAutoObject* server_object);
204 205
205 /// Registers all kernel objects with the global emulation state, this is purely for tracking 206 /// Registers all kernel objects with the global emulation state, this is purely for tracking
206 /// leaks after emulation has been shutdown. 207 /// leaks after emulation has been shutdown.
@@ -239,12 +240,6 @@ public:
239 /// Gets the virtual memory manager for the kernel. 240 /// Gets the virtual memory manager for the kernel.
240 const KMemoryManager& MemoryManager() const; 241 const KMemoryManager& MemoryManager() const;
241 242
242 /// Gets the slab heap allocated for user space pages.
243 KSlabHeap<Page>& GetUserSlabHeapPages();
244
245 /// Gets the slab heap allocated for user space pages.
246 const KSlabHeap<Page>& GetUserSlabHeapPages() const;
247
248 /// Gets the shared memory object for HID services. 243 /// Gets the shared memory object for HID services.
249 Kernel::KSharedMemory& GetHidSharedMem(); 244 Kernel::KSharedMemory& GetHidSharedMem();
250 245
@@ -269,6 +264,12 @@ public:
269 /// Gets the shared memory object for Time services. 264 /// Gets the shared memory object for Time services.
270 const Kernel::KSharedMemory& GetTimeSharedMem() const; 265 const Kernel::KSharedMemory& GetTimeSharedMem() const;
271 266
267 /// Gets the shared memory object for HIDBus services.
268 Kernel::KSharedMemory& GetHidBusSharedMem();
269
270 /// Gets the shared memory object for HIDBus services.
271 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
272
272 /// Suspend/unsuspend the OS. 273 /// Suspend/unsuspend the OS.
273 void Suspend(bool in_suspention); 274 void Suspend(bool in_suspention);
274 275
@@ -284,9 +285,11 @@ public:
284 void ExitSVCProfile(); 285 void ExitSVCProfile();
285 286
286 /** 287 /**
287 * Creates an HLE service thread, which are used to execute service routines asynchronously. 288 * Creates a host thread to execute HLE service requests, which are used to execute service
288 * While these are allocated per ServerSession, these need to be owned and managed outside 289 * routines asynchronously. While these are allocated per ServerSession, these need to be owned
289 * of ServerSession to avoid a circular dependency. 290 * and managed outside of ServerSession to avoid a circular dependency. In general, most
291 * services can just use the default service thread, and not need their own host service thread.
292 * See GetDefaultServiceThread.
290 * @param name String name for the ServerSession creating this thread, used for debug 293 * @param name String name for the ServerSession creating this thread, used for debug
291 * purposes. 294 * purposes.
292 * @returns The a weak pointer newly created service thread. 295 * @returns The a weak pointer newly created service thread.
@@ -294,6 +297,14 @@ public:
294 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 297 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
295 298
296 /** 299 /**
300 * Gets the default host service thread, which executes HLE service requests. Unless service
301 * requests need to block on the host, the default service thread should be used in favor of
302 * creating a new service thread.
303 * @returns The a weak pointer for the default service thread.
304 */
305 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
306
307 /**
297 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 308 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
298 * the ServerSession associated with the thread is destroyed. 309 * the ServerSession associated with the thread is destroyed.
299 * @param service_thread Service thread to release. 310 * @param service_thread Service thread to release.
@@ -336,6 +347,10 @@ public:
336 return slab_heap_container->writeable_event; 347 return slab_heap_container->writeable_event;
337 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 348 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
338 return slab_heap_container->code_memory; 349 return slab_heap_container->code_memory;
350 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
351 return slab_heap_container->page_buffer;
352 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
353 return slab_heap_container->thread_local_page;
339 } 354 }
340 } 355 }
341 356
@@ -397,6 +412,8 @@ private:
397 KSlabHeap<KTransferMemory> transfer_memory; 412 KSlabHeap<KTransferMemory> transfer_memory;
398 KSlabHeap<KWritableEvent> writeable_event; 413 KSlabHeap<KWritableEvent> writeable_event;
399 KSlabHeap<KCodeMemory> code_memory; 414 KSlabHeap<KCodeMemory> code_memory;
415 KSlabHeap<KPageBuffer> page_buffer;
416 KSlabHeap<KThreadLocalPage> thread_local_page;
400 }; 417 };
401 418
402 std::unique_ptr<SlabHeapContainer> slab_heap_container; 419 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 7477668e4..cc49e8c7e 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/spin_lock.h"
6#include "core/arm/cpu_interrupt_handler.h" 5#include "core/arm/cpu_interrupt_handler.h"
7#include "core/arm/dynarmic/arm_dynarmic_32.h" 6#include "core/arm/dynarmic/arm_dynarmic_32.h"
8#include "core/arm/dynarmic/arm_dynarmic_64.h" 7#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -16,7 +15,7 @@ namespace Kernel {
16PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 15PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
17 Core::CPUInterrupts& interrupts_) 16 Core::CPUInterrupts& interrupts_)
18 : core_index{core_index_}, system{system_}, scheduler{scheduler_}, 17 : core_index{core_index_}, system{system_}, scheduler{scheduler_},
19 interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { 18 interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
20#ifdef ARCHITECTURE_x86_64 19#ifdef ARCHITECTURE_x86_64
21 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 20 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
22 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 21 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
@@ -58,6 +57,7 @@ bool PhysicalCore::IsInterrupted() const {
58void PhysicalCore::Interrupt() { 57void PhysicalCore::Interrupt() {
59 guard->lock(); 58 guard->lock();
60 interrupts[core_index].SetInterrupt(true); 59 interrupts[core_index].SetInterrupt(true);
60 arm_interface->SignalInterrupt();
61 guard->unlock(); 61 guard->unlock();
62} 62}
63 63
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 16a032e89..f2112fc1d 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,13 +6,10 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9#include <mutex>
9 10
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11 12
12namespace Common {
13class SpinLock;
14}
15
16namespace Kernel { 13namespace Kernel {
17class KScheduler; 14class KScheduler;
18} // namespace Kernel 15} // namespace Kernel
@@ -91,7 +88,7 @@ private:
91 Core::System& system; 88 Core::System& system;
92 Kernel::KScheduler& scheduler; 89 Kernel::KScheduler& scheduler;
93 Core::CPUInterrupts& interrupts; 90 Core::CPUInterrupts& interrupts;
94 std::unique_ptr<Common::SpinLock> guard; 91 std::unique_ptr<std::mutex> guard;
95 std::unique_ptr<Core::ARM_Interface> arm_interface; 92 std::unique_ptr<Core::ARM_Interface> arm_interface;
96}; 93};
97 94
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 4eb3a5988..52d25b837 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -49,12 +49,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
49 return; 49 return;
50 } 50 }
51 51
52 // Allocate a dummy guest thread for this host thread.
52 kernel.RegisterHostThread(); 53 kernel.RegisterHostThread();
53 54
54 // Ensure the dummy thread allocated for this host thread is closed on exit.
55 auto* dummy_thread = kernel.GetCurrentEmuThread();
56 SCOPE_EXIT({ dummy_thread->Close(); });
57
58 while (true) { 55 while (true) {
59 std::function<void()> task; 56 std::function<void()> task;
60 57
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index f1c11256e..dc1e48fc9 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -59,7 +59,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base {
59 59
60private: 60private:
61 static Derived* Allocate(KernelCore& kernel) { 61 static Derived* Allocate(KernelCore& kernel) {
62 return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel); 62 return kernel.SlabHeap<Derived>().Allocate(kernel);
63 } 63 }
64 64
65 static void Free(KernelCore& kernel, Derived* obj) { 65 static void Free(KernelCore& kernel, Derived* obj) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 839171e85..0c86435b5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
1362 ResultInvalidMemoryRegion); 1362 ResultInvalidMemoryRegion);
1363 1363
1364 // Create a new page group. 1364 // Create a new page group.
1365 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); 1365 KPageLinkedList pg;
1366 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); 1366 R_TRY(src_pt.MakeAndOpenPageGroup(
1367 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1368 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1369 KMemoryAttribute::All, KMemoryAttribute::None));
1367 1370
1368 // Map the group. 1371 // Map the group.
1369 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1372 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1408} 1411}
1409 1412
1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1413static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", 1414 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1412 static_cast<void*>(out), address, size); 1415
1413 // Get kernel instance. 1416 // Get kernel instance.
1414 auto& kernel = system.Kernel(); 1417 auto& kernel = system.Kernel();
1415 1418
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1664 return ResultInvalidAddress; 1667 return ResultInvalidAddress;
1665 } 1668 }
1666 1669
1667 if (size == 0 || Common::Is4KBAligned(size)) { 1670 if (size == 0 || !Common::Is4KBAligned(size)) {
1668 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); 1671 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1669 return ResultInvalidSize; 1672 return ResultInvalidSize;
1670 } 1673 }
@@ -1710,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1710 return ResultInvalidMemoryRegion; 1713 return ResultInvalidMemoryRegion;
1711 } 1714 }
1712 1715
1713 return page_table.UnmapCodeMemory(dst_address, src_address, size); 1716 return page_table.UnmapCodeMemory(dst_address, src_address, size,
1717 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1714} 1718}
1715 1719
1716/// Exits the current process 1720/// Exits the current process
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 365e22e4e..b2e9ec092 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -96,4 +96,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
96constexpr inline s32 LowestThreadPriority = 63; 96constexpr inline s32 LowestThreadPriority = 63;
97constexpr inline s32 HighestThreadPriority = 0; 97constexpr inline s32 HighestThreadPriority = 0;
98 98
99constexpr inline size_t ThreadLocalRegionSize = 0x200;
100
99} // namespace Kernel::Svc 101} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index aa985d820..5b8fe8eae 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
24} 24}
25 25
26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { 26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
27 std::lock_guard lock{mutex}; 27 std::scoped_lock lock{mutex};
28 if (nanoseconds > 0) { 28 if (nanoseconds > 0) {
29 ASSERT(thread); 29 ASSERT(thread);
30 ASSERT(thread->GetState() != ThreadState::Runnable); 30 ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
35} 35}
36 36
37void TimeManager::UnscheduleTimeEvent(KThread* thread) { 37void TimeManager::UnscheduleTimeEvent(KThread* thread) {
38 std::lock_guard lock{mutex}; 38 std::scoped_lock lock{mutex};
39 system.CoreTiming().UnscheduleEvent(time_manager_event_type, 39 system.CoreTiming().UnscheduleEvent(time_manager_event_type,
40 reinterpret_cast<uintptr_t>(thread)); 40 reinterpret_cast<uintptr_t>(thread));
41} 41}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e34ef5a78..8a5332991 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -16,7 +16,6 @@
16#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/patch_manager.h" 17#include "core/file_sys/patch_manager.h"
18#include "core/hle/ipc_helpers.h" 18#include "core/hle/ipc_helpers.h"
19#include "core/hle/kernel/kernel.h"
20#include "core/hle/service/acc/acc.h" 19#include "core/hle/service/acc/acc.h"
21#include "core/hle/service/acc/acc_aa.h" 20#include "core/hle/service/acc/acc_aa.h"
22#include "core/hle/service/acc/acc_su.h" 21#include "core/hle/service/acc/acc_su.h"
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 17347f7ef..5b690b406 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9 9
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "common/uuid.h" 13#include "common/uuid.h"
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 2f8e21568..4d7e5ecd3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -980,7 +980,7 @@ private:
980 LOG_DEBUG(Service_AM, "called"); 980 LOG_DEBUG(Service_AM, "called");
981 981
982 IPC::RequestParser rp{ctx}; 982 IPC::RequestParser rp{ctx};
983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); 983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
984 984
985 IPC::ResponseBuilder rb{ctx, 2}; 985 IPC::ResponseBuilder rb{ctx, 2};
986 rb.Push(ResultSuccess); 986 rb.Push(ResultSuccess);
@@ -1007,7 +1007,7 @@ private:
1007 LOG_DEBUG(Service_AM, "called"); 1007 LOG_DEBUG(Service_AM, "called");
1008 1008
1009 IPC::RequestParser rp{ctx}; 1009 IPC::RequestParser rp{ctx};
1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); 1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
1011 1011
1012 ASSERT(applet->IsInitialized()); 1012 ASSERT(applet->IsInitialized());
1013 applet->ExecuteInteractive(); 1013 applet->ExecuteInteractive();
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1337 {200, nullptr, "GetLastApplicationExitReason"}, 1337 {200, nullptr, "GetLastApplicationExitReason"},
1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1339 {1000, nullptr, "CreateMovieMaker"}, 1339 {1000, nullptr, "CreateMovieMaker"},
1340 {1001, nullptr, "PrepareForJit"}, 1340 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
1341 }; 1341 };
1342 // clang-format on 1342 // clang-format on
1343 1343
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); 1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
1788} 1788}
1789 1789
1790void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
1791 LOG_WARNING(Service_AM, "(STUBBED) called");
1792
1793 IPC::ResponseBuilder rb{ctx, 2};
1794 rb.Push(ResultSuccess);
1795}
1796
1790void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 1797void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1791 Core::System& system) { 1798 Core::System& system) {
1792 auto message_queue = std::make_shared<AppletMessageQueue>(system); 1799 auto message_queue = std::make_shared<AppletMessageQueue>(system);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index fdd937b82..11a3c0459 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -336,6 +336,7 @@ private:
336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); 336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); 337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); 338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
339 void PrepareForJit(Kernel::HLERequestContext& ctx);
339 340
340 KernelHelpers::ServiceContext service_context; 341 KernelHelpers::ServiceContext service_context;
341 342
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
new file mode 100644
index 000000000..8fbde1be4
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -0,0 +1,139 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/mii_edit.h"
9#include "core/hle/service/am/am.h"
10#include "core/hle/service/am/applets/applet_mii_edit.h"
11#include "core/hle/service/mii/mii_manager.h"
12
13namespace Service::AM::Applets {
14
15MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
16 const Core::Frontend::MiiEditApplet& frontend_)
17 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
18
19MiiEdit::~MiiEdit() = default;
20
21void MiiEdit::Initialize() {
22 // Note: MiiEdit is not initialized with common arguments.
23 // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
24 // Do NOT call Applet::Initialize() here.
25
26 const auto storage = broker.PopNormalDataToApplet();
27 ASSERT(storage != nullptr);
28
29 const auto applet_input_data = storage->GetData();
30 ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
31
32 std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
33
34 LOG_INFO(Service_AM,
35 "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
36 applet_input_common.version, applet_input_common.applet_mode);
37
38 switch (applet_input_common.version) {
39 case MiiEditAppletVersion::Version3:
40 ASSERT(applet_input_data.size() ==
41 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
42 std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
43 sizeof(MiiEditAppletInputV3));
44 break;
45 case MiiEditAppletVersion::Version4:
46 ASSERT(applet_input_data.size() ==
47 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
48 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
49 sizeof(MiiEditAppletInputV4));
50 break;
51 default:
52 UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
53 applet_input_common.version, applet_input_data.size());
54 ASSERT(applet_input_data.size() >=
55 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
56 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
57 sizeof(MiiEditAppletInputV4));
58 break;
59 }
60}
61
62bool MiiEdit::TransactionComplete() const {
63 return is_complete;
64}
65
66ResultCode MiiEdit::GetStatus() const {
67 return ResultSuccess;
68}
69
70void MiiEdit::ExecuteInteractive() {
71 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
72}
73
74void MiiEdit::Execute() {
75 if (is_complete) {
76 return;
77 }
78
79 // This is a default stub for each of the MiiEdit applet modes.
80 switch (applet_input_common.applet_mode) {
81 case MiiEditAppletMode::ShowMiiEdit:
82 case MiiEditAppletMode::AppendMii:
83 case MiiEditAppletMode::AppendMiiImage:
84 case MiiEditAppletMode::UpdateMiiImage:
85 MiiEditOutput(MiiEditResult::Success, 0);
86 break;
87 case MiiEditAppletMode::CreateMii:
88 case MiiEditAppletMode::EditMii: {
89 Service::Mii::MiiManager mii_manager;
90
91 const MiiEditCharInfo char_info{
92 .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
93 ? applet_input_v4.char_info.mii_info
94 : mii_manager.BuildDefault(0)},
95 };
96
97 MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
98 break;
99 }
100 default:
101 UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
102
103 MiiEditOutput(MiiEditResult::Success, 0);
104 break;
105 }
106}
107
108void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
109 const MiiEditAppletOutput applet_output{
110 .result{result},
111 .index{index},
112 };
113
114 std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
115 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
116
117 is_complete = true;
118
119 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
120 broker.SignalStateChanged();
121}
122
123void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
124 const MiiEditCharInfo& char_info) {
125 const MiiEditAppletOutputForCharInfoEditing applet_output{
126 .result{result},
127 .char_info{char_info},
128 };
129
130 std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
131 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
132
133 is_complete = true;
134
135 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
136 broker.SignalStateChanged();
137}
138
139} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h
new file mode 100644
index 000000000..e9ca0e2af
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.h
@@ -0,0 +1,45 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8#include "core/hle/service/am/applets/applet_mii_edit_types.h"
9#include "core/hle/service/am/applets/applets.h"
10
11namespace Core {
12class System;
13} // namespace Core
14
15namespace Service::AM::Applets {
16
17class MiiEdit final : public Applet {
18public:
19 explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
20 const Core::Frontend::MiiEditApplet& frontend_);
21 ~MiiEdit() override;
22
23 void Initialize() override;
24
25 bool TransactionComplete() const override;
26 ResultCode GetStatus() const override;
27 void ExecuteInteractive() override;
28 void Execute() override;
29
30 void MiiEditOutput(MiiEditResult result, s32 index);
31
32 void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
33
34private:
35 const Core::Frontend::MiiEditApplet& frontend;
36 Core::System& system;
37
38 MiiEditAppletInputCommon applet_input_common{};
39 MiiEditAppletInputV3 applet_input_v3{};
40 MiiEditAppletInputV4 applet_input_v4{};
41
42 bool is_complete{false};
43};
44
45} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
new file mode 100644
index 000000000..70dea0007
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -0,0 +1,83 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/mii/types.h"
12
13namespace Service::AM::Applets {
14
15enum class MiiEditAppletVersion : s32 {
16 Version3 = 0x3, // 1.0.0 - 10.1.1
17 Version4 = 0x4, // 10.2.0+
18};
19
20// This is nn::mii::AppletMode
21enum class MiiEditAppletMode : u32 {
22 ShowMiiEdit = 0,
23 AppendMii = 1,
24 AppendMiiImage = 2,
25 UpdateMiiImage = 3,
26 CreateMii = 4,
27 EditMii = 5,
28};
29
30enum class MiiEditResult : u32 {
31 Success,
32 Cancel,
33};
34
35struct MiiEditCharInfo {
36 Service::Mii::MiiInfo mii_info{};
37};
38static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
39
40struct MiiEditAppletInputCommon {
41 MiiEditAppletVersion version{};
42 MiiEditAppletMode applet_mode{};
43};
44static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
45 "MiiEditAppletInputCommon has incorrect size.");
46
47struct MiiEditAppletInputV3 {
48 u32 special_mii_key_code{};
49 std::array<Common::UUID, 8> valid_uuids{};
50 Common::UUID used_uuid{};
51 INSERT_PADDING_BYTES(0x64);
52};
53static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
54 "MiiEditAppletInputV3 has incorrect size.");
55
56struct MiiEditAppletInputV4 {
57 u32 special_mii_key_code{};
58 MiiEditCharInfo char_info{};
59 INSERT_PADDING_BYTES(0x28);
60 Common::UUID used_uuid{};
61 INSERT_PADDING_BYTES(0x64);
62};
63static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
64 "MiiEditAppletInputV4 has incorrect size.");
65
66// This is nn::mii::AppletOutput
67struct MiiEditAppletOutput {
68 MiiEditResult result{};
69 s32 index{};
70 INSERT_PADDING_BYTES(0x18);
71};
72static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
73
74// This is nn::mii::AppletOutputForCharInfoEditing
75struct MiiEditAppletOutputForCharInfoEditing {
76 MiiEditResult result{};
77 MiiEditCharInfo char_info{};
78 INSERT_PADDING_BYTES(0x24);
79};
80static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
81 "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
82
83} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index f38f53f69..ee669686c 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
226 ASSERT(work_buffer_storage != nullptr); 226 ASSERT(work_buffer_storage != nullptr);
227 227
228 if (swkbd_config_common.initial_string_length == 0) { 228 if (swkbd_config_common.initial_string_length == 0) {
229 InitializeFrontendKeyboard(); 229 InitializeFrontendNormalKeyboard();
230 return; 230 return;
231 } 231 }
232 232
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
243 243
244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); 244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
245 245
246 InitializeFrontendKeyboard(); 246 InitializeFrontendNormalKeyboard();
247} 247}
248 248
249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { 249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
480 ReplyDefault(); 480 ReplyDefault();
481} 481}
482 482
483void SoftwareKeyboard::InitializeFrontendKeyboard() { 483void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
484 if (is_background) { 484 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
485 const auto& appear_arg = swkbd_calc_arg.appear_arg; 485 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
486 486
487 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 487 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
488 appear_arg.ok_text.data(), appear_arg.ok_text.size()); 488 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
489 489
490 const u32 max_text_length = 490 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
491 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 491 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
492 ? appear_arg.max_text_length 492
493 : DEFAULT_MAX_TEXT_LENGTH; 493 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
494 494 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
495 const u32 min_text_length = 495
496 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; 496 const u32 max_text_length =
497 497 swkbd_config_common.max_text_length > 0 &&
498 const s32 initial_cursor_position = 498 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
499 current_cursor_position > 0 ? current_cursor_position : 0; 499 ? swkbd_config_common.max_text_length
500 500 : DEFAULT_MAX_TEXT_LENGTH;
501 const auto text_draw_type = 501
502 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 502 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
503 503 ? swkbd_config_common.min_text_length
504 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 504 : 0;
505 .ok_text{std::move(ok_text)}, 505
506 .header_text{}, 506 const s32 initial_cursor_position = [this] {
507 .sub_text{}, 507 switch (swkbd_config_common.initial_cursor_position) {
508 .guide_text{}, 508 case SwkbdInitialCursorPosition::Start:
509 .initial_text{current_text}, 509 default:
510 .max_text_length{max_text_length}, 510 return 0;
511 .min_text_length{min_text_length}, 511 case SwkbdInitialCursorPosition::End:
512 .initial_cursor_position{initial_cursor_position}, 512 return static_cast<s32>(initial_text.size());
513 .type{appear_arg.type}, 513 }
514 .password_mode{SwkbdPasswordMode::Disabled}, 514 }();
515 .text_draw_type{text_draw_type}, 515
516 .key_disable_flags{appear_arg.key_disable_flags}, 516 const auto text_draw_type = [this, max_text_length] {
517 .use_blur_background{false}, 517 switch (swkbd_config_common.text_draw_type) {
518 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 518 case SwkbdTextDrawType::Line:
519 .enable_return_button{appear_arg.enable_return_button}, 519 default:
520 .disable_cancel_button{appear_arg.disable_cancel_button}, 520 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
521 }; 521 case SwkbdTextDrawType::Box:
522 522 case SwkbdTextDrawType::DownloadCode:
523 frontend.InitializeKeyboard( 523 return swkbd_config_common.text_draw_type;
524 true, std::move(initialize_parameters), {}, 524 }
525 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { 525 }();
526 SubmitTextInline(reply_type, submitted_text, cursor_position); 526
527 }); 527 const auto enable_return_button =
528 } else { 528 text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
529 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 529
530 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); 530 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
531 531 ? swkbd_config_new.disable_cancel_button
532 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 532 : false;
533 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); 533
534 534 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
535 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 535 .ok_text{std::move(ok_text)},
536 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); 536 .header_text{std::move(header_text)},
537 537 .sub_text{std::move(sub_text)},
538 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 538 .guide_text{std::move(guide_text)},
539 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); 539 .initial_text{initial_text},
540 540 .max_text_length{max_text_length},
541 const u32 max_text_length = 541 .min_text_length{min_text_length},
542 swkbd_config_common.max_text_length > 0 && 542 .initial_cursor_position{initial_cursor_position},
543 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 543 .type{swkbd_config_common.type},
544 ? swkbd_config_common.max_text_length 544 .password_mode{swkbd_config_common.password_mode},
545 : DEFAULT_MAX_TEXT_LENGTH; 545 .text_draw_type{text_draw_type},
546 546 .key_disable_flags{swkbd_config_common.key_disable_flags},
547 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length 547 .use_blur_background{swkbd_config_common.use_blur_background},
548 ? swkbd_config_common.min_text_length 548 .enable_backspace_button{true},
549 : 0; 549 .enable_return_button{enable_return_button},
550 550 .disable_cancel_button{disable_cancel_button},
551 const s32 initial_cursor_position = [this] { 551 };
552 switch (swkbd_config_common.initial_cursor_position) { 552
553 case SwkbdInitialCursorPosition::Start: 553 frontend.InitializeKeyboard(
554 default: 554 false, std::move(initialize_parameters),
555 return 0; 555 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
556 case SwkbdInitialCursorPosition::End: 556 SubmitTextNormal(result, submitted_text, confirmed);
557 return static_cast<s32>(initial_text.size()); 557 },
558 } 558 {});
559 }(); 559}
560 560
561 const auto text_draw_type = [this, max_text_length] { 561void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
562 switch (swkbd_config_common.text_draw_type) { 562 Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
563 case SwkbdTextDrawType::Line: 563 frontend.InitializeKeyboard(
564 default: 564 true, std::move(initialize_parameters), {},
565 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 565 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
566 case SwkbdTextDrawType::Box: 566 SubmitTextInline(reply_type, submitted_text, cursor_position);
567 case SwkbdTextDrawType::DownloadCode: 567 });
568 return swkbd_config_common.text_draw_type; 568}
569 } 569
570 }(); 570void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
571 571 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
572 const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box 572
573 ? swkbd_config_common.enable_return_button 573 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
574 : false; 574 appear_arg.ok_text.data(), appear_arg.ok_text.size());
575 575
576 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 576 const u32 max_text_length =
577 ? swkbd_config_new.disable_cancel_button 577 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
578 : false; 578 ? appear_arg.max_text_length
579 579 : DEFAULT_MAX_TEXT_LENGTH;
580 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 580
581 .ok_text{std::move(ok_text)}, 581 const u32 min_text_length =
582 .header_text{std::move(header_text)}, 582 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
583 .sub_text{std::move(sub_text)}, 583
584 .guide_text{std::move(guide_text)}, 584 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
585 .initial_text{initial_text}, 585
586 .max_text_length{max_text_length}, 586 const auto text_draw_type =
587 .min_text_length{min_text_length}, 587 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
588 .initial_cursor_position{initial_cursor_position}, 588
589 .type{swkbd_config_common.type}, 589 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
590 .password_mode{swkbd_config_common.password_mode}, 590 .ok_text{std::move(ok_text)},
591 .text_draw_type{text_draw_type}, 591 .header_text{},
592 .key_disable_flags{swkbd_config_common.key_disable_flags}, 592 .sub_text{},
593 .use_blur_background{swkbd_config_common.use_blur_background}, 593 .guide_text{},
594 .enable_backspace_button{true}, 594 .initial_text{current_text},
595 .enable_return_button{enable_return_button}, 595 .max_text_length{max_text_length},
596 .disable_cancel_button{disable_cancel_button}, 596 .min_text_length{min_text_length},
597 }; 597 .initial_cursor_position{initial_cursor_position},
598 598 .type{appear_arg.type},
599 frontend.InitializeKeyboard( 599 .password_mode{SwkbdPasswordMode::Disabled},
600 false, std::move(initialize_parameters), 600 .text_draw_type{text_draw_type},
601 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { 601 .key_disable_flags{appear_arg.key_disable_flags},
602 SubmitTextNormal(result, submitted_text, confirmed); 602 .use_blur_background{false},
603 }, 603 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
604 {}); 604 .enable_return_button{appear_arg.enable_return_button},
605 } 605 .disable_cancel_button{appear_arg.disable_cancel_button},
606 };
607
608 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
609}
610
611void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
612 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
613
614 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
615 appear_arg.ok_text.data(), appear_arg.ok_text.size());
616
617 const u32 max_text_length =
618 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
619 ? appear_arg.max_text_length
620 : DEFAULT_MAX_TEXT_LENGTH;
621
622 const u32 min_text_length =
623 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
624
625 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
626
627 const auto text_draw_type =
628 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
629
630 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
631 .ok_text{std::move(ok_text)},
632 .header_text{},
633 .sub_text{},
634 .guide_text{},
635 .initial_text{current_text},
636 .max_text_length{max_text_length},
637 .min_text_length{min_text_length},
638 .initial_cursor_position{initial_cursor_position},
639 .type{appear_arg.type},
640 .password_mode{SwkbdPasswordMode::Disabled},
641 .text_draw_type{text_draw_type},
642 .key_disable_flags{appear_arg.key_disable_flags},
643 .use_blur_background{false},
644 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button},
647 };
648
649 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
606} 650}
607 651
608void SoftwareKeyboard::ShowNormalKeyboard() { 652void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
614 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); 658 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
615} 659}
616 660
617void SoftwareKeyboard::ShowInlineKeyboard() { 661void SoftwareKeyboard::ShowInlineKeyboard(
662 Core::Frontend::InlineAppearParameters appear_parameters) {
663 frontend.ShowInlineKeyboard(std::move(appear_parameters));
664
665 ChangeState(SwkbdState::InitializedIsShown);
666}
667
668void SoftwareKeyboard::ShowInlineKeyboardOld() {
618 if (swkbd_state != SwkbdState::InitializedIsHidden) { 669 if (swkbd_state != SwkbdState::InitializedIsHidden) {
619 return; 670 return;
620 } 671 }
621 672
622 ChangeState(SwkbdState::InitializedIsAppearing); 673 ChangeState(SwkbdState::InitializedIsAppearing);
623 674
624 const auto& appear_arg = swkbd_calc_arg.appear_arg; 675 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
625 676
626 const u32 max_text_length = 677 const u32 max_text_length =
627 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 678 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
634 Core::Frontend::InlineAppearParameters appear_parameters{ 685 Core::Frontend::InlineAppearParameters appear_parameters{
635 .max_text_length{max_text_length}, 686 .max_text_length{max_text_length},
636 .min_text_length{min_text_length}, 687 .min_text_length{min_text_length},
637 .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, 688 .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
638 .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, 689 .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
639 .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, 690 .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
640 .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, 691 .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
641 .type{appear_arg.type}, 692 .type{appear_arg.type},
642 .key_disable_flags{appear_arg.key_disable_flags}, 693 .key_disable_flags{appear_arg.key_disable_flags},
643 .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, 694 .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
644 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 695 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button}, 696 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button}, 697 .disable_cancel_button{appear_arg.disable_cancel_button},
647 }; 698 };
648 699
649 frontend.ShowInlineKeyboard(std::move(appear_parameters)); 700 ShowInlineKeyboard(std::move(appear_parameters));
701}
650 702
651 ChangeState(SwkbdState::InitializedIsShown); 703void SoftwareKeyboard::ShowInlineKeyboardNew() {
704 if (swkbd_state != SwkbdState::InitializedIsHidden) {
705 return;
706 }
707
708 ChangeState(SwkbdState::InitializedIsAppearing);
709
710 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
711
712 const u32 max_text_length =
713 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
714 ? appear_arg.max_text_length
715 : DEFAULT_MAX_TEXT_LENGTH;
716
717 const u32 min_text_length =
718 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
719
720 Core::Frontend::InlineAppearParameters appear_parameters{
721 .max_text_length{max_text_length},
722 .min_text_length{min_text_length},
723 .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
724 .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
725 .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
726 .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
727 .type{appear_arg.type},
728 .key_disable_flags{appear_arg.key_disable_flags},
729 .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
730 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
731 .enable_return_button{appear_arg.enable_return_button},
732 .disable_cancel_button{appear_arg.disable_cancel_button},
733 };
734
735 ShowInlineKeyboard(std::move(appear_parameters));
652} 736}
653 737
654void SoftwareKeyboard::HideInlineKeyboard() { 738void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
693 777
694void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { 778void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
695 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); 779 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
780
781 ReplyReleasedUserWordInfo();
696} 782}
697 783
698void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { 784void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
702void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { 788void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
703 LOG_DEBUG(Service_AM, "Processing Request: Calc"); 789 LOG_DEBUG(Service_AM, "Processing Request: Calc");
704 790
705 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); 791 ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
706 792
707 std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), 793 std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
708 sizeof(SwkbdCalcArg)); 794 sizeof(SwkbdCalcArgCommon));
795
796 switch (swkbd_calc_arg_common.calc_arg_size) {
797 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
798 ASSERT(request_data.size() ==
799 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
800 std::memcpy(&swkbd_calc_arg_old,
801 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
802 sizeof(SwkbdCalcArgOld));
803 RequestCalcOld();
804 break;
805 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
806 ASSERT(request_data.size() ==
807 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
808 std::memcpy(&swkbd_calc_arg_new,
809 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
810 sizeof(SwkbdCalcArgNew));
811 RequestCalcNew();
812 break;
813 default:
814 UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
815 ASSERT(request_data.size() >=
816 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
817 std::memcpy(&swkbd_calc_arg_new,
818 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
819 sizeof(SwkbdCalcArgNew));
820 RequestCalcNew();
821 break;
822 }
823}
824
825void SoftwareKeyboard::RequestCalcOld() {
826 if (swkbd_calc_arg_common.flags.set_input_text) {
827 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
828 swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
829 }
830
831 if (swkbd_calc_arg_common.flags.set_cursor_position) {
832 current_cursor_position = swkbd_calc_arg_old.cursor_position;
833 }
834
835 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
836 inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
837 }
838
839 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
840 swkbd_calc_arg_common.flags.unset_customize_dic) {
841 ReplyUnsetCustomizeDic();
842 }
843
844 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
845 swkbd_calc_arg_common.flags.unset_user_word_info) {
846 ReplyReleasedUserWordInfo();
847 }
848
849 if (swkbd_state == SwkbdState::NotInitialized &&
850 swkbd_calc_arg_common.flags.set_initialize_arg) {
851 InitializeFrontendInlineKeyboardOld();
852
853 ChangeState(SwkbdState::InitializedIsHidden);
854
855 ReplyFinishedInitialize();
856 }
857
858 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
859 (swkbd_calc_arg_common.flags.set_input_text ||
860 swkbd_calc_arg_common.flags.set_cursor_position)) {
861 InlineTextChanged();
862 }
863
864 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
865 ShowInlineKeyboardOld();
866 return;
867 }
868
869 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
870 HideInlineKeyboard();
871 return;
872 }
873}
709 874
710 if (swkbd_calc_arg.flags.set_input_text) { 875void SoftwareKeyboard::RequestCalcNew() {
876 if (swkbd_calc_arg_common.flags.set_input_text) {
711 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 877 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
712 swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); 878 swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
713 } 879 }
714 880
715 if (swkbd_calc_arg.flags.set_cursor_position) { 881 if (swkbd_calc_arg_common.flags.set_cursor_position) {
716 current_cursor_position = swkbd_calc_arg.cursor_position; 882 current_cursor_position = swkbd_calc_arg_new.cursor_position;
717 } 883 }
718 884
719 if (swkbd_calc_arg.flags.set_utf8_mode) { 885 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
720 inline_use_utf8 = swkbd_calc_arg.utf8_mode; 886 inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
721 } 887 }
722 888
723 if (swkbd_state <= SwkbdState::InitializedIsHidden && 889 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
724 swkbd_calc_arg.flags.unset_customize_dic) { 890 swkbd_calc_arg_common.flags.unset_customize_dic) {
725 ReplyUnsetCustomizeDic(); 891 ReplyUnsetCustomizeDic();
726 } 892 }
727 893
728 if (swkbd_state <= SwkbdState::InitializedIsHidden && 894 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
729 swkbd_calc_arg.flags.unset_user_word_info) { 895 swkbd_calc_arg_common.flags.unset_user_word_info) {
730 ReplyReleasedUserWordInfo(); 896 ReplyReleasedUserWordInfo();
731 } 897 }
732 898
733 if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { 899 if (swkbd_state == SwkbdState::NotInitialized &&
734 InitializeFrontendKeyboard(); 900 swkbd_calc_arg_common.flags.set_initialize_arg) {
901 InitializeFrontendInlineKeyboardNew();
735 902
736 ChangeState(SwkbdState::InitializedIsHidden); 903 ChangeState(SwkbdState::InitializedIsHidden);
737 904
738 ReplyFinishedInitialize(); 905 ReplyFinishedInitialize();
739 } 906 }
740 907
741 if (!swkbd_calc_arg.flags.set_initialize_arg && 908 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
742 (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { 909 (swkbd_calc_arg_common.flags.set_input_text ||
910 swkbd_calc_arg_common.flags.set_cursor_position)) {
743 InlineTextChanged(); 911 InlineTextChanged();
744 } 912 }
745 913
746 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { 914 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
747 ShowInlineKeyboard(); 915 ShowInlineKeyboardNew();
748 return; 916 return;
749 } 917 }
750 918
751 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { 919 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
752 HideInlineKeyboard(); 920 HideInlineKeyboard();
753 return; 921 return;
754 } 922 }
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h
index a0fddd965..7ee4c5eea 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -13,6 +13,11 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace Core::Frontend {
17struct KeyboardInitializeParameters;
18struct InlineAppearParameters;
19} // namespace Core::Frontend
20
16namespace Service::AM::Applets { 21namespace Service::AM::Applets {
17 22
18class SoftwareKeyboard final : public Applet { 23class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
78 void ChangeState(SwkbdState state); 83 void ChangeState(SwkbdState state);
79 84
80 /** 85 /**
81 * Signals the frontend to initialize the software keyboard with common parameters. 86 * Signals the frontend to initialize the normal software keyboard with common parameters.
82 * This initializes either the normal software keyboard or the inline software keyboard
83 * depending on the state of is_background.
84 * Note that this does not cause the keyboard to appear. 87 * Note that this does not cause the keyboard to appear.
85 * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. 88 * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
86 */ 89 */
87 void InitializeFrontendKeyboard(); 90 void InitializeFrontendNormalKeyboard();
91
92 /**
93 * Signals the frontend to initialize the inline software keyboard with common parameters.
94 * Note that this does not cause the keyboard to appear.
95 * Use the ShowInlineKeyboard() to cause the keyboard to appear.
96 */
97 void InitializeFrontendInlineKeyboard(
98 Core::Frontend::KeyboardInitializeParameters initialize_parameters);
99
100 void InitializeFrontendInlineKeyboardOld();
101 void InitializeFrontendInlineKeyboardNew();
88 102
89 /// Signals the frontend to show the normal software keyboard. 103 /// Signals the frontend to show the normal software keyboard.
90 void ShowNormalKeyboard(); 104 void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
94 std::u16string text_check_message); 108 std::u16string text_check_message);
95 109
96 /// Signals the frontend to show the inline software keyboard. 110 /// Signals the frontend to show the inline software keyboard.
97 void ShowInlineKeyboard(); 111 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
112
113 void ShowInlineKeyboardOld();
114 void ShowInlineKeyboardNew();
98 115
99 /// Signals the frontend to hide the inline software keyboard. 116 /// Signals the frontend to hide the inline software keyboard.
100 void HideInlineKeyboard(); 117 void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
111 void RequestSetUserWordInfo(const std::vector<u8>& request_data); 128 void RequestSetUserWordInfo(const std::vector<u8>& request_data);
112 void RequestSetCustomizeDic(const std::vector<u8>& request_data); 129 void RequestSetCustomizeDic(const std::vector<u8>& request_data);
113 void RequestCalc(const std::vector<u8>& request_data); 130 void RequestCalc(const std::vector<u8>& request_data);
131 void RequestCalcOld();
132 void RequestCalcNew();
114 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); 133 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
115 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); 134 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
116 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); 135 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
149 168
150 SwkbdState swkbd_state{SwkbdState::NotInitialized}; 169 SwkbdState swkbd_state{SwkbdState::NotInitialized};
151 SwkbdInitializeArg swkbd_initialize_arg; 170 SwkbdInitializeArg swkbd_initialize_arg;
152 SwkbdCalcArg swkbd_calc_arg; 171 SwkbdCalcArgCommon swkbd_calc_arg_common;
172 SwkbdCalcArgOld swkbd_calc_arg_old;
173 SwkbdCalcArgNew swkbd_calc_arg_new;
153 bool use_changed_string_v2{false}; 174 bool use_changed_string_v2{false};
154 bool use_moved_cursor_v2{false}; 175 bool use_moved_cursor_v2{false};
155 bool inline_use_utf8{false}; 176 bool inline_use_utf8{false};
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
index 21aa8e800..de1ca7aa5 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
@@ -10,6 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "common/uuid.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Applets {
15 16
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
216}; 217};
217static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); 218static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
218 219
219struct SwkbdAppearArg { 220struct SwkbdAppearArgOld {
220 SwkbdType type{}; 221 SwkbdType type{};
221 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; 222 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
222 char16_t left_optional_symbol_key{}; 223 char16_t left_optional_symbol_key{};
@@ -229,19 +230,76 @@ struct SwkbdAppearArg {
229 bool enable_return_button{}; 230 bool enable_return_button{};
230 INSERT_PADDING_BYTES(3); 231 INSERT_PADDING_BYTES(3);
231 u32 flags{}; 232 u32 flags{};
232 INSERT_PADDING_WORDS(6); 233 bool is_use_save_data{};
234 INSERT_PADDING_BYTES(7);
235 Common::UUID user_id{};
233}; 236};
234static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); 237static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
235 238
236struct SwkbdCalcArg { 239struct SwkbdAppearArgNew {
240 SwkbdType type{};
241 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
242 char16_t left_optional_symbol_key{};
243 char16_t right_optional_symbol_key{};
244 bool use_prediction{};
245 bool disable_cancel_button{};
246 SwkbdKeyDisableFlags key_disable_flags{};
247 u32 max_text_length{};
248 u32 min_text_length{};
249 bool enable_return_button{};
250 INSERT_PADDING_BYTES(3);
251 u32 flags{};
252 bool is_use_save_data{};
253 INSERT_PADDING_BYTES(7);
254 Common::UUID user_id{};
255 u64 start_sampling_number{};
256 INSERT_PADDING_WORDS(8);
257};
258static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
259
260struct SwkbdCalcArgCommon {
237 u32 unknown{}; 261 u32 unknown{};
238 u16 calc_arg_size{}; 262 u16 calc_arg_size{};
239 INSERT_PADDING_BYTES(2); 263 INSERT_PADDING_BYTES(2);
240 SwkbdCalcArgFlags flags{}; 264 SwkbdCalcArgFlags flags{};
241 SwkbdInitializeArg initialize_arg{}; 265 SwkbdInitializeArg initialize_arg{};
266};
267static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
268
269struct SwkbdCalcArgOld {
270 f32 volume{};
271 s32 cursor_position{};
272 SwkbdAppearArgOld appear_arg{};
273 std::array<char16_t, 0x1FA> input_text{};
274 bool utf8_mode{};
275 INSERT_PADDING_BYTES(1);
276 bool enable_backspace_button{};
277 INSERT_PADDING_BYTES(3);
278 bool key_top_as_floating{};
279 bool footer_scalable{};
280 bool alpha_enabled_in_input_mode{};
281 u8 input_mode_fade_type{};
282 bool disable_touch{};
283 bool disable_hardware_keyboard{};
284 INSERT_PADDING_BYTES(8);
285 f32 key_top_scale_x{};
286 f32 key_top_scale_y{};
287 f32 key_top_translate_x{};
288 f32 key_top_translate_y{};
289 f32 key_top_bg_alpha{};
290 f32 footer_bg_alpha{};
291 f32 balloon_scale{};
292 INSERT_PADDING_WORDS(4);
293 u8 se_group{};
294 INSERT_PADDING_BYTES(3);
295};
296static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
297 "SwkbdCalcArgOld has incorrect size.");
298
299struct SwkbdCalcArgNew {
300 SwkbdAppearArgNew appear_arg{};
242 f32 volume{}; 301 f32 volume{};
243 s32 cursor_position{}; 302 s32 cursor_position{};
244 SwkbdAppearArg appear_arg{};
245 std::array<char16_t, 0x1FA> input_text{}; 303 std::array<char16_t, 0x1FA> input_text{};
246 bool utf8_mode{}; 304 bool utf8_mode{};
247 INSERT_PADDING_BYTES(1); 305 INSERT_PADDING_BYTES(1);
@@ -264,8 +322,10 @@ struct SwkbdCalcArg {
264 INSERT_PADDING_WORDS(4); 322 INSERT_PADDING_WORDS(4);
265 u8 se_group{}; 323 u8 se_group{};
266 INSERT_PADDING_BYTES(3); 324 INSERT_PADDING_BYTES(3);
325 INSERT_PADDING_WORDS(8);
267}; 326};
268static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); 327static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
328 "SwkbdCalcArgNew has incorrect size.");
269 329
270struct SwkbdChangedStringArg { 330struct SwkbdChangedStringArg {
271 u32 text_length{}; 331 u32 text_length{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index bb5cb61be..a4b3fb187 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
446} 446}
447 447
448void WebBrowser::ExecuteOffline() { 448void WebBrowser::ExecuteOffline() {
449 // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
450 // Super Mario 3D All-Stars.
451 // TODO (Morph): Implement WebSession.
452 if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
453 LOG_WARNING(Service_AM, "WebSession is not implemented");
454 return;
455 }
456
449 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); 457 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
450 458
451 if (!Common::FS::Exists(main_url)) { 459 if (!Common::FS::Exists(main_url)) {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 134ac1ee2..1f4c9786a 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -9,6 +9,7 @@
9#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
12#include "core/frontend/applets/mii_edit.h"
12#include "core/frontend/applets/profile_select.h" 13#include "core/frontend/applets/profile_select.h"
13#include "core/frontend/applets/software_keyboard.h" 14#include "core/frontend/applets/software_keyboard.h"
14#include "core/frontend/applets/web_browser.h" 15#include "core/frontend/applets/web_browser.h"
@@ -19,6 +20,7 @@
19#include "core/hle/service/am/applets/applet_controller.h" 20#include "core/hle/service/am/applets/applet_controller.h"
20#include "core/hle/service/am/applets/applet_error.h" 21#include "core/hle/service/am/applets/applet_error.h"
21#include "core/hle/service/am/applets/applet_general_backend.h" 22#include "core/hle/service/am/applets/applet_general_backend.h"
23#include "core/hle/service/am/applets/applet_mii_edit.h"
22#include "core/hle/service/am/applets/applet_profile_select.h" 24#include "core/hle/service/am/applets/applet_profile_select.h"
23#include "core/hle/service/am/applets/applet_software_keyboard.h" 25#include "core/hle/service/am/applets/applet_software_keyboard.h"
24#include "core/hle/service/am/applets/applet_web_browser.h" 26#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -171,11 +173,12 @@ void Applet::Initialize() {
171AppletFrontendSet::AppletFrontendSet() = default; 173AppletFrontendSet::AppletFrontendSet() = default;
172 174
173AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 175AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
176 MiiEdit mii_edit_,
174 ParentalControlsApplet parental_controls_applet, 177 ParentalControlsApplet parental_controls_applet,
175 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 178 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
176 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) 179 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
177 : controller{std::move(controller_applet)}, error{std::move(error_applet)}, 180 : controller{std::move(controller_applet)}, error{std::move(error_applet)},
178 parental_controls{std::move(parental_controls_applet)}, 181 mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
179 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, 182 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
180 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} 183 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
181 184
@@ -202,6 +205,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
202 frontend.error = std::move(set.error); 205 frontend.error = std::move(set.error);
203 } 206 }
204 207
208 if (set.mii_edit != nullptr) {
209 frontend.mii_edit = std::move(set.mii_edit);
210 }
211
205 if (set.parental_controls != nullptr) { 212 if (set.parental_controls != nullptr) {
206 frontend.parental_controls = std::move(set.parental_controls); 213 frontend.parental_controls = std::move(set.parental_controls);
207 } 214 }
@@ -238,6 +245,10 @@ void AppletManager::SetDefaultAppletsIfMissing() {
238 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 245 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
239 } 246 }
240 247
248 if (frontend.mii_edit == nullptr) {
249 frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
250 }
251
241 if (frontend.parental_controls == nullptr) { 252 if (frontend.parental_controls == nullptr) {
242 frontend.parental_controls = 253 frontend.parental_controls =
243 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 254 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
@@ -277,6 +288,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
277 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); 288 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
278 case AppletId::SoftwareKeyboard: 289 case AppletId::SoftwareKeyboard:
279 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); 290 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
291 case AppletId::MiiEdit:
292 return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
280 case AppletId::Web: 293 case AppletId::Web:
281 case AppletId::Shop: 294 case AppletId::Shop:
282 case AppletId::OfflineWeb: 295 case AppletId::OfflineWeb:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 15eeb4ee1..50a7bdceb 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -20,6 +20,7 @@ namespace Core::Frontend {
20class ControllerApplet; 20class ControllerApplet;
21class ECommerceApplet; 21class ECommerceApplet;
22class ErrorApplet; 22class ErrorApplet;
23class MiiEditApplet;
23class ParentalControlsApplet; 24class ParentalControlsApplet;
24class PhotoViewerApplet; 25class PhotoViewerApplet;
25class ProfileSelectApplet; 26class ProfileSelectApplet;
@@ -178,6 +179,7 @@ protected:
178struct AppletFrontendSet { 179struct AppletFrontendSet {
179 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 180 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
180 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 181 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
182 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
181 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 183 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
182 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 184 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
183 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 185 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
@@ -186,9 +188,9 @@ struct AppletFrontendSet {
186 188
187 AppletFrontendSet(); 189 AppletFrontendSet();
188 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 190 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
189 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, 191 MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
190 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, 192 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
191 WebBrowser web_browser_); 193 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
192 ~AppletFrontendSet(); 194 ~AppletFrontendSet();
193 195
194 AppletFrontendSet(const AppletFrontendSet&) = delete; 196 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -199,6 +201,7 @@ struct AppletFrontendSet {
199 201
200 ControllerApplet controller; 202 ControllerApplet controller;
201 ErrorApplet error; 203 ErrorApplet error;
204 MiiEdit mii_edit;
202 ParentalControlsApplet parental_controls; 205 ParentalControlsApplet parental_controls;
203 PhotoViewer photo_viewer; 206 PhotoViewer photo_viewer;
204 ProfileSelect profile_select; 207 ProfileSelect profile_select;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index affa7971c..a72956a28 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -41,9 +41,10 @@ public:
41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, 41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
42 AudioCore::AudioOut& audio_core_, std::string&& device_name_, 42 AudioCore::AudioOut& audio_core_, std::string&& device_name_,
43 std::string&& unique_name) 43 std::string&& unique_name)
44 : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, 44 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
45 device_name{std::move(device_name_)}, audio_params{audio_params_}, 45 audio_core{audio_core_}, device_name{std::move(device_name_)},
46 main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { 46 audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
47 "IAudioOut"} {
47 // clang-format off 48 // clang-format off
48 static const FunctionInfo functions[] = { 49 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 50 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f45e5cecc..d4ffeb21d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -24,7 +24,8 @@ public:
24 explicit IAudioRenderer(Core::System& system_, 24 explicit IAudioRenderer(Core::System& system_,
25 const AudioCommon::AudioRendererParameter& audren_params, 25 const AudioCommon::AudioRendererParameter& audren_params,
26 const std::size_t instance_number) 26 const std::size_t instance_number)
27 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { 27 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
28 service_context{system_, "IAudioRenderer"} {
28 // clang-format off 29 // clang-format off
29 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
30 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 31 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3703ca4c6..4208337db 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); 174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
175 175
176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
177 "Could not write all of the bytes but everything else has succeded."); 177 "Could not write all of the bytes but everything else has succeeded.");
178 178
179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { 179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
180 // TODO(DarkLordZach): Find a better error code for this 180 // TODO(DarkLordZach): Find a better error code for this
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b087e7bba..c07929ab8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
58class IStorage final : public ServiceFramework<IStorage> { 58class IStorage final : public ServiceFramework<IStorage> {
59public: 59public:
60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) 60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
61 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { 61 : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
62 backend(std::move(backend_)) {
62 static const FunctionInfo functions[] = { 63 static const FunctionInfo functions[] = {
63 {0, &IStorage::Read, "Read"}, 64 {0, &IStorage::Read, "Read"},
64 {1, nullptr, "Write"}, 65 {1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
116class IFile final : public ServiceFramework<IFile> { 117class IFile final : public ServiceFramework<IFile> {
117public: 118public:
118 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) 119 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
119 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { 120 : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
121 backend(std::move(backend_)) {
120 static const FunctionInfo functions[] = { 122 static const FunctionInfo functions[] = {
121 {0, &IFile::Read, "Read"}, 123 {0, &IFile::Read, "Read"},
122 {1, &IFile::Write, "Write"}, 124 {1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
252class IDirectory final : public ServiceFramework<IDirectory> { 254class IDirectory final : public ServiceFramework<IDirectory> {
253public: 255public:
254 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) 256 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
255 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { 257 : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
258 backend(std::move(backend_)) {
256 static const FunctionInfo functions[] = { 259 static const FunctionInfo functions[] = {
257 {0, &IDirectory::Read, "Read"}, 260 {0, &IDirectory::Read, "Read"},
258 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 261 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
308class IFileSystem final : public ServiceFramework<IFileSystem> { 311class IFileSystem final : public ServiceFramework<IFileSystem> {
309public: 312public:
310 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) 313 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
311 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( 314 : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
312 size_)} { 315 backend{std::move(backend_)}, size{std::move(size_)} {
313 static const FunctionInfo functions[] = { 316 static const FunctionInfo functions[] = {
314 {0, &IFileSystem::CreateFile, "CreateFile"}, 317 {0, &IFileSystem::CreateFile, "CreateFile"},
315 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 318 {1, &IFileSystem::DeleteFile, "DeleteFile"},
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index a727b3582..8450eaf93 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/settings.h"
6#include "core/core_timing.h" 5#include "core/core_timing.h"
7#include "core/hid/emulated_console.h" 6#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index 26d153f0c..93454585a 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -8,7 +8,6 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/quaternion.h" 10#include "common/quaternion.h"
11#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 12#include "core/hle/service/hid/ring_lifo.h"
14 13
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 7450eb20a..5e464149c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/swap.h"
9 8
10namespace Core::Timing { 9namespace Core::Timing {
11class CoreTiming; 10class CoreTiming;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index afe374fc2..4396780a1 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -4,11 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h" 7#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h" 8#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/ring_lifo.h"
14 11
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index cf62d3896..b869352b9 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -4,11 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h" 7#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 8#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 9#include "core/hle/service/hid/ring_lifo.h"
14 10
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 7559fc78d..8858887b2 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -4,10 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h"
9#include "common/common_types.h" 7#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 8#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h" 9#include "core/hle/service/hid/ring_lifo.h"
13 10
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e5c951e06..68407e39a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h"
20#include "core/hle/service/kernel_helpers.h" 21#include "core/hle/service/kernel_helpers.h"
21 22
22namespace Service::HID { 23namespace Service::HID {
@@ -48,15 +49,17 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
48} 49}
49 50
50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { 51bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55 return npad_id && npad_type && device_index;
54} 56}
55 57
56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { 58bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 59 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 60 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 61 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
62 return npad_id && npad_type && device_index;
60} 63}
61 64
62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, 65Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
@@ -262,11 +265,6 @@ void Controller_NPad::OnInit() {
262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 265 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
263 } 266 }
264 267
265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
266 // We want to support all controllers
267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
268 }
269
270 supported_npad_id_types.resize(npad_id_list.size()); 268 supported_npad_id_types.resize(npad_id_list.size());
271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 269 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
272 npad_id_list.size() * sizeof(Core::HID::NpadIdType)); 270 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +286,6 @@ void Controller_NPad::OnInit() {
288 WriteEmptyEntry(npad); 286 WriteEmptyEntry(npad);
289 } 287 }
290 } 288 }
291
292 // Connect controllers
293 for (auto& controller : controller_data) {
294 const auto& device = controller.device;
295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
297 }
298 }
299} 289}
300 290
301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { 291void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +310,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
320} 310}
321 311
322void Controller_NPad::OnRelease() { 312void Controller_NPad::OnRelease() {
313 is_controller_initialized = false;
323 for (std::size_t i = 0; i < controller_data.size(); ++i) { 314 for (std::size_t i = 0; i < controller_data.size(); ++i) {
324 auto& controller = controller_data[i]; 315 auto& controller = controller_data[i];
325 service_context.CloseEvent(controller.styleset_changed_event); 316 service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +321,7 @@ void Controller_NPad::OnRelease() {
330} 321}
331 322
332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 323void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
333 std::lock_guard lock{mutex}; 324 std::scoped_lock lock{mutex};
334 auto& controller = GetControllerFromNpadIdType(npad_id); 325 auto& controller = GetControllerFromNpadIdType(npad_id);
335 const auto controller_type = controller.device->GetNpadStyleIndex(); 326 const auto controller_type = controller.device->GetNpadStyleIndex();
336 if (!controller.device->IsConnected()) { 327 if (!controller.device->IsConnected()) {
@@ -651,9 +642,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
651 642
652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 643void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
653 hid_core.SetSupportedStyleTag(style_set); 644 hid_core.SetSupportedStyleTag(style_set);
645
646 if (is_controller_initialized) {
647 return;
648 }
649
650 // Once SetSupportedStyleSet is called controllers are fully initialized
651 is_controller_initialized = true;
652
653 // Connect all active controllers
654 for (auto& controller : controller_data) {
655 const auto& device = controller.device;
656 if (device->IsConnected()) {
657 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
658 }
659 }
654} 660}
655 661
656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 662Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
663 if (!is_controller_initialized) {
664 return {Core::HID::NpadStyleSet::None};
665 }
657 return hid_core.GetSupportedStyleTag(); 666 return hid_core.GetSupportedStyleTag();
658} 667}
659 668
@@ -1001,87 +1010,271 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1001 WriteEmptyEntry(controller.shared_memory_entry); 1010 WriteEmptyEntry(controller.shared_memory_entry);
1002} 1011}
1003 1012
1004void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1013ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1005 GyroscopeZeroDriftMode drift_mode) { 1014 GyroscopeZeroDriftMode drift_mode) {
1006 if (!IsDeviceHandleValid(sixaxis_handle)) { 1015 if (!IsDeviceHandleValid(sixaxis_handle)) {
1007 LOG_ERROR(Service_HID, "Invalid handle"); 1016 LOG_ERROR(Service_HID, "Invalid handle");
1008 return; 1017 return NpadInvalidHandle;
1018 }
1019
1020 auto& controller = GetControllerFromHandle(sixaxis_handle);
1021 switch (sixaxis_handle.npad_type) {
1022 case Core::HID::NpadStyleIndex::ProController:
1023 controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
1024 break;
1025 case Core::HID::NpadStyleIndex::Handheld:
1026 controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
1027 break;
1028 case Core::HID::NpadStyleIndex::JoyconDual:
1029 case Core::HID::NpadStyleIndex::GameCube:
1030 case Core::HID::NpadStyleIndex::Pokeball:
1031 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1032 controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
1033 break;
1034 }
1035 controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
1036 break;
1037 case Core::HID::NpadStyleIndex::JoyconLeft:
1038 controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
1039 break;
1040 case Core::HID::NpadStyleIndex::JoyconRight:
1041 controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
1042 break;
1043 default:
1044 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1045 return NpadInvalidHandle;
1046 }
1047
1048 return ResultSuccess;
1049}
1050
1051ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1052 GyroscopeZeroDriftMode& drift_mode) const {
1053 if (!IsDeviceHandleValid(sixaxis_handle)) {
1054 LOG_ERROR(Service_HID, "Invalid handle");
1055 return NpadInvalidHandle;
1009 } 1056 }
1057
1010 auto& controller = GetControllerFromHandle(sixaxis_handle); 1058 auto& controller = GetControllerFromHandle(sixaxis_handle);
1011 controller.gyroscope_zero_drift_mode = drift_mode; 1059 switch (sixaxis_handle.npad_type) {
1060 case Core::HID::NpadStyleIndex::ProController:
1061 drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
1062 break;
1063 case Core::HID::NpadStyleIndex::Handheld:
1064 drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
1065 break;
1066 case Core::HID::NpadStyleIndex::JoyconDual:
1067 case Core::HID::NpadStyleIndex::GameCube:
1068 case Core::HID::NpadStyleIndex::Pokeball:
1069 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1070 drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
1071 break;
1072 }
1073 drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
1074 break;
1075 case Core::HID::NpadStyleIndex::JoyconLeft:
1076 drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
1077 break;
1078 case Core::HID::NpadStyleIndex::JoyconRight:
1079 drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
1080 break;
1081 default:
1082 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1083 return NpadInvalidHandle;
1084 }
1085
1086 return ResultSuccess;
1012} 1087}
1013 1088
1014Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( 1089ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
1015 Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1090 bool& is_at_rest) const {
1016 if (!IsDeviceHandleValid(sixaxis_handle)) { 1091 if (!IsDeviceHandleValid(sixaxis_handle)) {
1017 LOG_ERROR(Service_HID, "Invalid handle"); 1092 LOG_ERROR(Service_HID, "Invalid handle");
1018 // Return the default value 1093 return NpadInvalidHandle;
1019 return GyroscopeZeroDriftMode::Standard;
1020 } 1094 }
1021 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1095 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1022 return controller.gyroscope_zero_drift_mode; 1096 is_at_rest = controller.sixaxis_at_rest;
1097 return ResultSuccess;
1023} 1098}
1024 1099
1025bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1100ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1101 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
1026 if (!IsDeviceHandleValid(sixaxis_handle)) { 1102 if (!IsDeviceHandleValid(sixaxis_handle)) {
1027 LOG_ERROR(Service_HID, "Invalid handle"); 1103 LOG_ERROR(Service_HID, "Invalid handle");
1028 // Return the default value 1104 return NpadInvalidHandle;
1029 return true;
1030 } 1105 }
1031 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1106
1032 return controller.sixaxis_at_rest; 1107 // We don't support joycon firmware updates
1108 is_firmware_available = false;
1109 return ResultSuccess;
1033} 1110}
1034 1111
1035void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1112ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1036 bool sixaxis_status) { 1113 bool sixaxis_status) {
1037 if (!IsDeviceHandleValid(sixaxis_handle)) { 1114 if (!IsDeviceHandleValid(sixaxis_handle)) {
1038 LOG_ERROR(Service_HID, "Invalid handle"); 1115 LOG_ERROR(Service_HID, "Invalid handle");
1039 return; 1116 return NpadInvalidHandle;
1040 } 1117 }
1041 auto& controller = GetControllerFromHandle(sixaxis_handle); 1118 auto& controller = GetControllerFromHandle(sixaxis_handle);
1042 controller.sixaxis_sensor_enabled = sixaxis_status; 1119 controller.sixaxis_sensor_enabled = sixaxis_status;
1120 return ResultSuccess;
1043} 1121}
1044 1122
1045void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1123ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
1046 bool sixaxis_fusion_status) { 1124 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
1047 if (!IsDeviceHandleValid(sixaxis_handle)) { 1125 if (!IsDeviceHandleValid(sixaxis_handle)) {
1048 LOG_ERROR(Service_HID, "Invalid handle"); 1126 LOG_ERROR(Service_HID, "Invalid handle");
1049 return; 1127 return NpadInvalidHandle;
1050 } 1128 }
1129
1051 auto& controller = GetControllerFromHandle(sixaxis_handle); 1130 auto& controller = GetControllerFromHandle(sixaxis_handle);
1052 controller.sixaxis_fusion_enabled = sixaxis_fusion_status; 1131 switch (sixaxis_handle.npad_type) {
1053} 1132 case Core::HID::NpadStyleIndex::ProController:
1133 is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
1134 break;
1135 case Core::HID::NpadStyleIndex::Handheld:
1136 is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
1137 break;
1138 case Core::HID::NpadStyleIndex::JoyconDual:
1139 case Core::HID::NpadStyleIndex::GameCube:
1140 case Core::HID::NpadStyleIndex::Pokeball:
1141 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1142 is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
1143 break;
1144 }
1145 is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
1146 break;
1147 case Core::HID::NpadStyleIndex::JoyconLeft:
1148 is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
1149 break;
1150 case Core::HID::NpadStyleIndex::JoyconRight:
1151 is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
1152 break;
1153 default:
1154 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1155 return NpadInvalidHandle;
1156 }
1054 1157
1055void Controller_NPad::SetSixAxisFusionParameters( 1158 return ResultSuccess;
1056 Core::HID::SixAxisSensorHandle sixaxis_handle, 1159}
1057 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { 1160ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1161 bool is_fusion_enabled) {
1058 if (!IsDeviceHandleValid(sixaxis_handle)) { 1162 if (!IsDeviceHandleValid(sixaxis_handle)) {
1059 LOG_ERROR(Service_HID, "Invalid handle"); 1163 LOG_ERROR(Service_HID, "Invalid handle");
1060 return; 1164 return NpadInvalidHandle;
1061 } 1165 }
1166
1062 auto& controller = GetControllerFromHandle(sixaxis_handle); 1167 auto& controller = GetControllerFromHandle(sixaxis_handle);
1063 controller.sixaxis_fusion = sixaxis_fusion_parameters; 1168 switch (sixaxis_handle.npad_type) {
1169 case Core::HID::NpadStyleIndex::ProController:
1170 controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
1171 break;
1172 case Core::HID::NpadStyleIndex::Handheld:
1173 controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
1174 break;
1175 case Core::HID::NpadStyleIndex::JoyconDual:
1176 case Core::HID::NpadStyleIndex::GameCube:
1177 case Core::HID::NpadStyleIndex::Pokeball:
1178 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1179 controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
1180 break;
1181 }
1182 controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
1183 break;
1184 case Core::HID::NpadStyleIndex::JoyconLeft:
1185 controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
1186 break;
1187 case Core::HID::NpadStyleIndex::JoyconRight:
1188 controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
1189 break;
1190 default:
1191 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1192 return NpadInvalidHandle;
1193 }
1194
1195 return ResultSuccess;
1064} 1196}
1065 1197
1066Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( 1198ResultCode Controller_NPad::SetSixAxisFusionParameters(
1067 Core::HID::SixAxisSensorHandle sixaxis_handle) { 1199 Core::HID::SixAxisSensorHandle sixaxis_handle,
1200 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1068 if (!IsDeviceHandleValid(sixaxis_handle)) { 1201 if (!IsDeviceHandleValid(sixaxis_handle)) {
1069 LOG_ERROR(Service_HID, "Invalid handle"); 1202 LOG_ERROR(Service_HID, "Invalid handle");
1070 // Since these parameters are unknow just return zeros 1203 return NpadInvalidHandle;
1071 return {}; 1204 }
1205 const auto param1 = sixaxis_fusion_parameters.parameter1;
1206 if (param1 < 0.0f || param1 > 1.0f) {
1207 return InvalidSixAxisFusionRange;
1072 } 1208 }
1209
1073 auto& controller = GetControllerFromHandle(sixaxis_handle); 1210 auto& controller = GetControllerFromHandle(sixaxis_handle);
1074 return controller.sixaxis_fusion; 1211 switch (sixaxis_handle.npad_type) {
1212 case Core::HID::NpadStyleIndex::ProController:
1213 controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
1214 break;
1215 case Core::HID::NpadStyleIndex::Handheld:
1216 controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
1217 break;
1218 case Core::HID::NpadStyleIndex::JoyconDual:
1219 case Core::HID::NpadStyleIndex::GameCube:
1220 case Core::HID::NpadStyleIndex::Pokeball:
1221 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1222 controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
1223 break;
1224 }
1225 controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
1226 break;
1227 case Core::HID::NpadStyleIndex::JoyconLeft:
1228 controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
1229 break;
1230 case Core::HID::NpadStyleIndex::JoyconRight:
1231 controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
1232 break;
1233 default:
1234 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1235 return NpadInvalidHandle;
1236 }
1237
1238 return ResultSuccess;
1075} 1239}
1076 1240
1077void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { 1241ResultCode Controller_NPad::GetSixAxisFusionParameters(
1242 Core::HID::SixAxisSensorHandle sixaxis_handle,
1243 Core::HID::SixAxisSensorFusionParameters& parameters) const {
1078 if (!IsDeviceHandleValid(sixaxis_handle)) { 1244 if (!IsDeviceHandleValid(sixaxis_handle)) {
1079 LOG_ERROR(Service_HID, "Invalid handle"); 1245 LOG_ERROR(Service_HID, "Invalid handle");
1080 return; 1246 return NpadInvalidHandle;
1081 } 1247 }
1082 auto& controller = GetControllerFromHandle(sixaxis_handle); 1248
1083 // Since these parameters are unknow just fill with zeros 1249 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1084 controller.sixaxis_fusion = {}; 1250 switch (sixaxis_handle.npad_type) {
1251 case Core::HID::NpadStyleIndex::ProController:
1252 parameters = controller.sixaxis_fullkey.fusion;
1253 break;
1254 case Core::HID::NpadStyleIndex::Handheld:
1255 parameters = controller.sixaxis_handheld.fusion;
1256 break;
1257 case Core::HID::NpadStyleIndex::JoyconDual:
1258 case Core::HID::NpadStyleIndex::GameCube:
1259 case Core::HID::NpadStyleIndex::Pokeball:
1260 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1261 parameters = controller.sixaxis_dual_left.fusion;
1262 break;
1263 }
1264 parameters = controller.sixaxis_dual_right.fusion;
1265 break;
1266 case Core::HID::NpadStyleIndex::JoyconLeft:
1267 parameters = controller.sixaxis_left.fusion;
1268 break;
1269 case Core::HID::NpadStyleIndex::JoyconRight:
1270 parameters = controller.sixaxis_right.fusion;
1271 break;
1272 default:
1273 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1274 return NpadInvalidHandle;
1275 }
1276
1277 return ResultSuccess;
1085} 1278}
1086 1279
1087void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1280void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 6b2872bad..f7313711f 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,7 +10,8 @@
10 10
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/quaternion.h" 13#include "common/vector_math.h"
14
14#include "core/hid/hid_types.h" 15#include "core/hid/hid_types.h"
15#include "core/hle/service/hid/controllers/controller_base.h" 16#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h" 17#include "core/hle/service/hid/ring_lifo.h"
@@ -27,7 +28,9 @@ class KReadableEvent;
27 28
28namespace Service::KernelHelpers { 29namespace Service::KernelHelpers {
29class ServiceContext; 30class ServiceContext;
30} 31} // namespace Service::KernelHelpers
32
33union ResultCode;
31 34
32namespace Service::HID { 35namespace Service::HID {
33 36
@@ -142,20 +145,26 @@ public:
142 145
143 void DisconnectNpad(Core::HID::NpadIdType npad_id); 146 void DisconnectNpad(Core::HID::NpadIdType npad_id);
144 147
145 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 148 ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
146 GyroscopeZeroDriftMode drift_mode); 149 GyroscopeZeroDriftMode drift_mode);
147 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( 150 ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
148 Core::HID::SixAxisSensorHandle sixaxis_handle) const; 151 GyroscopeZeroDriftMode& drift_mode) const;
149 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; 152 ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
150 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); 153 bool& is_at_rest) const;
151 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 154 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
152 bool sixaxis_fusion_status); 155 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
153 void SetSixAxisFusionParameters( 156 ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
157 bool sixaxis_status);
158 ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
159 bool& is_fusion_enabled) const;
160 ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
161 bool is_fusion_enabled);
162 ResultCode SetSixAxisFusionParameters(
154 Core::HID::SixAxisSensorHandle sixaxis_handle, 163 Core::HID::SixAxisSensorHandle sixaxis_handle,
155 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); 164 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
156 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( 165 ResultCode GetSixAxisFusionParameters(
157 Core::HID::SixAxisSensorHandle sixaxis_handle); 166 Core::HID::SixAxisSensorHandle sixaxis_handle,
158 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); 167 Core::HID::SixAxisSensorFusionParameters& parameters) const;
159 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); 168 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
160 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; 169 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
161 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 170 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
@@ -190,16 +199,16 @@ private:
190 199
191 // This is nn::hid::detail::NpadFullKeyColorState 200 // This is nn::hid::detail::NpadFullKeyColorState
192 struct NpadFullKeyColorState { 201 struct NpadFullKeyColorState {
193 ColorAttribute attribute; 202 ColorAttribute attribute{ColorAttribute::NoController};
194 Core::HID::NpadControllerColor fullkey; 203 Core::HID::NpadControllerColor fullkey{};
195 }; 204 };
196 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); 205 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
197 206
198 // This is nn::hid::detail::NpadJoyColorState 207 // This is nn::hid::detail::NpadJoyColorState
199 struct NpadJoyColorState { 208 struct NpadJoyColorState {
200 ColorAttribute attribute; 209 ColorAttribute attribute{ColorAttribute::NoController};
201 Core::HID::NpadControllerColor left; 210 Core::HID::NpadControllerColor left{};
202 Core::HID::NpadControllerColor right; 211 Core::HID::NpadControllerColor right{};
203 }; 212 };
204 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); 213 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
205 214
@@ -225,11 +234,11 @@ private:
225 // This is nn::hid::NpadPalmaState 234 // This is nn::hid::NpadPalmaState
226 // This is nn::hid::NpadSystemExtState 235 // This is nn::hid::NpadSystemExtState
227 struct NPadGenericState { 236 struct NPadGenericState {
228 s64_le sampling_number; 237 s64_le sampling_number{};
229 Core::HID::NpadButtonState npad_buttons; 238 Core::HID::NpadButtonState npad_buttons{};
230 Core::HID::AnalogStickState l_stick; 239 Core::HID::AnalogStickState l_stick{};
231 Core::HID::AnalogStickState r_stick; 240 Core::HID::AnalogStickState r_stick{};
232 NpadAttribute connection_status; 241 NpadAttribute connection_status{};
233 INSERT_PADDING_BYTES(4); // Reserved 242 INSERT_PADDING_BYTES(4); // Reserved
234 }; 243 };
235 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 244 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -252,7 +261,7 @@ private:
252 Common::Vec3f gyro{}; 261 Common::Vec3f gyro{};
253 Common::Vec3f rotation{}; 262 Common::Vec3f rotation{};
254 std::array<Common::Vec3f, 3> orientation{}; 263 std::array<Common::Vec3f, 3> orientation{};
255 SixAxisSensorAttribute attribute; 264 SixAxisSensorAttribute attribute{};
256 INSERT_PADDING_BYTES(4); // Reserved 265 INSERT_PADDING_BYTES(4); // Reserved
257 }; 266 };
258 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); 267 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -324,11 +333,11 @@ private:
324 333
325 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl 334 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
326 struct NfcXcdDeviceHandleStateImpl { 335 struct NfcXcdDeviceHandleStateImpl {
327 u64 handle; 336 u64 handle{};
328 bool is_available; 337 bool is_available{};
329 bool is_activated; 338 bool is_activated{};
330 INSERT_PADDING_BYTES(0x6); // Reserved 339 INSERT_PADDING_BYTES(0x6); // Reserved
331 u64 sampling_number; 340 u64 sampling_number{};
332 }; 341 };
333 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 342 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
334 "NfcXcdDeviceHandleStateImpl is an invalid size"); 343 "NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -365,8 +374,8 @@ private:
365 }; 374 };
366 375
367 struct AppletFooterUi { 376 struct AppletFooterUi {
368 AppletFooterUiAttributes attributes; 377 AppletFooterUiAttributes attributes{};
369 AppletFooterUiType type; 378 AppletFooterUiType type{AppletFooterUiType::None};
370 INSERT_PADDING_BYTES(0x5B); // Reserved 379 INSERT_PADDING_BYTES(0x5B); // Reserved
371 }; 380 };
372 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); 381 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -403,41 +412,41 @@ private:
403 412
404 // This is nn::hid::detail::NpadInternalState 413 // This is nn::hid::detail::NpadInternalState
405 struct NpadInternalState { 414 struct NpadInternalState {
406 Core::HID::NpadStyleTag style_tag; 415 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
407 NpadJoyAssignmentMode assignment_mode; 416 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
408 NpadFullKeyColorState fullkey_color; 417 NpadFullKeyColorState fullkey_color{};
409 NpadJoyColorState joycon_color; 418 NpadJoyColorState joycon_color{};
410 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; 419 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
411 Lifo<NPadGenericState, hid_entry_count> handheld_lifo; 420 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
412 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; 421 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
413 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; 422 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
414 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; 423 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
415 Lifo<NPadGenericState, hid_entry_count> palma_lifo; 424 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
416 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; 425 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
417 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; 426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; 427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; 428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; 429 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; 430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; 431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
423 DeviceType device_type; 432 DeviceType device_type{};
424 INSERT_PADDING_BYTES(0x4); // Reserved 433 INSERT_PADDING_BYTES(0x4); // Reserved
425 NPadSystemProperties system_properties; 434 NPadSystemProperties system_properties{};
426 NpadSystemButtonProperties button_properties; 435 NpadSystemButtonProperties button_properties{};
427 Core::HID::NpadBatteryLevel battery_level_dual; 436 Core::HID::NpadBatteryLevel battery_level_dual{};
428 Core::HID::NpadBatteryLevel battery_level_left; 437 Core::HID::NpadBatteryLevel battery_level_left{};
429 Core::HID::NpadBatteryLevel battery_level_right; 438 Core::HID::NpadBatteryLevel battery_level_right{};
430 union { 439 union {
431 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; 440 AppletFooterUi applet_footer{};
432 AppletFooterUi applet_footer; 441 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
433 }; 442 };
434 INSERT_PADDING_BYTES(0x20); // Unknown 443 INSERT_PADDING_BYTES(0x20); // Unknown
435 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; 444 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
436 NpadLarkType lark_type_l_and_main; 445 NpadLarkType lark_type_l_and_main{};
437 NpadLarkType lark_type_r; 446 NpadLarkType lark_type_r{};
438 NpadLuciaType lucia_type; 447 NpadLuciaType lucia_type{};
439 NpadLagonType lagon_type; 448 NpadLagonType lagon_type{};
440 NpadLagerType lager_type; 449 NpadLagerType lager_type{};
441 // FW 13.x Investigate there is some sort of bitflag related to joycons 450 // FW 13.x Investigate there is some sort of bitflag related to joycons
442 INSERT_PADDING_BYTES(0x4); 451 INSERT_PADDING_BYTES(0x4);
443 INSERT_PADDING_BYTES(0xc08); // Unknown 452 INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -450,6 +459,12 @@ private:
450 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 459 std::chrono::steady_clock::time_point last_vibration_timepoint{};
451 }; 460 };
452 461
462 struct SixaxisParameters {
463 bool is_fusion_enabled{true};
464 Core::HID::SixAxisSensorFusionParameters fusion{};
465 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
466 };
467
453 struct NpadControllerData { 468 struct NpadControllerData {
454 Core::HID::EmulatedController* device; 469 Core::HID::EmulatedController* device;
455 Kernel::KEvent* styleset_changed_event{}; 470 Kernel::KEvent* styleset_changed_event{};
@@ -466,9 +481,12 @@ private:
466 // Motion parameters 481 // Motion parameters
467 bool sixaxis_at_rest{true}; 482 bool sixaxis_at_rest{true};
468 bool sixaxis_sensor_enabled{true}; 483 bool sixaxis_sensor_enabled{true};
469 bool sixaxis_fusion_enabled{false}; 484 SixaxisParameters sixaxis_fullkey{};
470 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; 485 SixaxisParameters sixaxis_handheld{};
471 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 486 SixaxisParameters sixaxis_dual_left{};
487 SixaxisParameters sixaxis_dual_right{};
488 SixaxisParameters sixaxis_left{};
489 SixaxisParameters sixaxis_right{};
472 490
473 // Current pad state 491 // Current pad state
474 NPadGenericState npad_pad_state{}; 492 NPadGenericState npad_pad_state{};
@@ -480,6 +498,7 @@ private:
480 SixAxisSensorState sixaxis_dual_right_state{}; 498 SixAxisSensorState sixaxis_dual_right_state{};
481 SixAxisSensorState sixaxis_left_lifo_state{}; 499 SixAxisSensorState sixaxis_left_lifo_state{};
482 SixAxisSensorState sixaxis_right_lifo_state{}; 500 SixAxisSensorState sixaxis_right_lifo_state{};
501
483 int callback_key; 502 int callback_key;
484 }; 503 };
485 504
@@ -510,7 +529,8 @@ private:
510 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 529 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
511 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 530 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
512 bool permit_vibration_session_enabled{false}; 531 bool permit_vibration_session_enabled{false};
513 bool analog_stick_use_center_clamp{}; 532 bool analog_stick_use_center_clamp{false};
514 bool is_in_lr_assignment_mode{false}; 533 bool is_in_lr_assignment_mode{false};
534 bool is_controller_initialized{false};
515}; 535};
516} // namespace Service::HID 536} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 48978e5c6..65b799e78 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "common/settings.h" 8#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/core_timing.h" 10#include "core/core_timing.h"
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 708dde4f0..483552767 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,11 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
8#include "common/common_funcs.h" 7#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/point.h"
11#include "common/swap.h"
12#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 10#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/ring_lifo.h" 11#include "core/hle/service/hid/ring_lifo.h"
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ba8db8d9d..4973e1bd9 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 10#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 11#include "core/hle/service/hid/ring_lifo.h"
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 3583642e7..7c5fb3e52 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -8,6 +8,8 @@
8 8
9namespace Service::HID { 9namespace Service::HID {
10 10
11constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; 11constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
12constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
12 14
13} // namespace Service::HID 15} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..baf21df62 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/service/hid/errors.h" 17#include "core/hle/service/hid/errors.h"
18#include "core/hle/service/hid/hid.h" 18#include "core/hle/service/hid/hid.h"
19#include "core/hle/service/hid/hidbus.h"
19#include "core/hle/service/hid/irs.h" 20#include "core/hle/service/hid/irs.h"
20#include "core/hle/service/hid/xcd.h" 21#include "core/hle/service/hid/xcd.h"
21#include "core/memory.h" 22#include "core/memory.h"
@@ -247,7 +248,7 @@ Hid::Hid(Core::System& system_)
247 {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, 248 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
248 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, 249 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
249 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, 250 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
250 {68, nullptr, "IsSixAxisSensorFusionEnabled"}, 251 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
251 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, 252 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
252 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, 253 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
253 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, 254 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
@@ -527,8 +528,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
527 528
528 const auto parameters{rp.PopRaw<Parameters>()}; 529 const auto parameters{rp.PopRaw<Parameters>()};
529 530
530 applet_resource->GetController<Controller_NPad>(HidController::NPad) 531 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
531 .SetSixAxisEnabled(parameters.sixaxis_handle, true); 532 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
532 533
533 LOG_DEBUG(Service_HID, 534 LOG_DEBUG(Service_HID,
534 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 535 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -536,7 +537,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
536 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 537 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
537 538
538 IPC::ResponseBuilder rb{ctx, 2}; 539 IPC::ResponseBuilder rb{ctx, 2};
539 rb.Push(ResultSuccess); 540 rb.Push(result);
540} 541}
541 542
542void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 543void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -550,8 +551,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
550 551
551 const auto parameters{rp.PopRaw<Parameters>()}; 552 const auto parameters{rp.PopRaw<Parameters>()};
552 553
553 applet_resource->GetController<Controller_NPad>(HidController::NPad) 554 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
554 .SetSixAxisEnabled(parameters.sixaxis_handle, false); 555 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
555 556
556 LOG_DEBUG(Service_HID, 557 LOG_DEBUG(Service_HID,
557 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 558 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,7 +560,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
559 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 560 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
560 561
561 IPC::ResponseBuilder rb{ctx, 2}; 562 IPC::ResponseBuilder rb{ctx, 2};
562 rb.Push(ResultSuccess); 563 rb.Push(result);
564}
565
566void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) {
567 IPC::RequestParser rp{ctx};
568 struct Parameters {
569 Core::HID::SixAxisSensorHandle sixaxis_handle;
570 INSERT_PADDING_WORDS_NOINIT(1);
571 u64 applet_resource_user_id;
572 };
573 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
574
575 const auto parameters{rp.PopRaw<Parameters>()};
576
577 bool is_enabled{};
578 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
579 const auto result =
580 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
581
582 LOG_DEBUG(Service_HID,
583 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
584 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
585 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
586
587 IPC::ResponseBuilder rb{ctx, 3};
588 rb.Push(result);
589 rb.Push(is_enabled);
563} 590}
564 591
565void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { 592void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
@@ -574,9 +601,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
574 601
575 const auto parameters{rp.PopRaw<Parameters>()}; 602 const auto parameters{rp.PopRaw<Parameters>()};
576 603
577 applet_resource->GetController<Controller_NPad>(HidController::NPad) 604 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
578 .SetSixAxisFusionEnabled(parameters.sixaxis_handle, 605 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
579 parameters.enable_sixaxis_sensor_fusion); 606 parameters.enable_sixaxis_sensor_fusion);
580 607
581 LOG_DEBUG(Service_HID, 608 LOG_DEBUG(Service_HID,
582 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 609 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -586,7 +613,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
586 parameters.applet_resource_user_id); 613 parameters.applet_resource_user_id);
587 614
588 IPC::ResponseBuilder rb{ctx, 2}; 615 IPC::ResponseBuilder rb{ctx, 2};
589 rb.Push(ResultSuccess); 616 rb.Push(result);
590} 617}
591 618
592void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 619void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -601,8 +628,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
601 628
602 const auto parameters{rp.PopRaw<Parameters>()}; 629 const auto parameters{rp.PopRaw<Parameters>()};
603 630
604 applet_resource->GetController<Controller_NPad>(HidController::NPad) 631 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
605 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); 632 const auto result =
633 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
606 634
607 LOG_DEBUG(Service_HID, 635 LOG_DEBUG(Service_HID,
608 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 636 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -612,7 +640,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
612 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); 640 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
613 641
614 IPC::ResponseBuilder rb{ctx, 2}; 642 IPC::ResponseBuilder rb{ctx, 2};
615 rb.Push(ResultSuccess); 643 rb.Push(result);
616} 644}
617 645
618void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 646void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -626,9 +654,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
626 654
627 const auto parameters{rp.PopRaw<Parameters>()}; 655 const auto parameters{rp.PopRaw<Parameters>()};
628 656
629 const auto sixaxis_fusion_parameters = 657 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
630 applet_resource->GetController<Controller_NPad>(HidController::NPad) 658 const auto& controller =
631 .GetSixAxisFusionParameters(parameters.sixaxis_handle); 659 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
660 const auto result =
661 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632 662
633 LOG_DEBUG(Service_HID, 663 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 664 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -636,8 +666,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 666 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637 667
638 IPC::ResponseBuilder rb{ctx, 4}; 668 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(ResultSuccess); 669 rb.Push(result);
640 rb.PushRaw(sixaxis_fusion_parameters); 670 rb.PushRaw(fusion_parameters);
641} 671}
642 672
643void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 673void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -651,8 +681,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
651 681
652 const auto parameters{rp.PopRaw<Parameters>()}; 682 const auto parameters{rp.PopRaw<Parameters>()};
653 683
654 applet_resource->GetController<Controller_NPad>(HidController::NPad) 684 // Since these parameters are unknow just use what HW outputs
655 .ResetSixAxisFusionParameters(parameters.sixaxis_handle); 685 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
686 .parameter1 = 0.03f,
687 .parameter2 = 0.4f,
688 };
689 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
690 const auto result1 =
691 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
692 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
656 693
657 LOG_DEBUG(Service_HID, 694 LOG_DEBUG(Service_HID,
658 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 695 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,6 +697,14 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
660 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 697 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
661 698
662 IPC::ResponseBuilder rb{ctx, 2}; 699 IPC::ResponseBuilder rb{ctx, 2};
700 if (result1.IsError()) {
701 rb.Push(result1);
702 return;
703 }
704 if (result2.IsError()) {
705 rb.Push(result2);
706 return;
707 }
663 rb.Push(ResultSuccess); 708 rb.Push(ResultSuccess);
664} 709}
665 710
@@ -669,8 +714,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
669 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 714 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
670 const auto applet_resource_user_id{rp.Pop<u64>()}; 715 const auto applet_resource_user_id{rp.Pop<u64>()};
671 716
672 applet_resource->GetController<Controller_NPad>(HidController::NPad) 717 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
673 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); 718 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
674 719
675 LOG_DEBUG(Service_HID, 720 LOG_DEBUG(Service_HID,
676 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 721 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -679,7 +724,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
679 drift_mode, applet_resource_user_id); 724 drift_mode, applet_resource_user_id);
680 725
681 IPC::ResponseBuilder rb{ctx, 2}; 726 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(ResultSuccess); 727 rb.Push(result);
683} 728}
684 729
685void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 730void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -693,15 +738,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
693 738
694 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
695 740
741 auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
742 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
743 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
744
696 LOG_DEBUG(Service_HID, 745 LOG_DEBUG(Service_HID,
697 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 746 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
698 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 747 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
699 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 748 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
700 749
701 IPC::ResponseBuilder rb{ctx, 3}; 750 IPC::ResponseBuilder rb{ctx, 3};
702 rb.Push(ResultSuccess); 751 rb.Push(result);
703 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 752 rb.PushEnum(drift_mode);
704 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
705} 753}
706 754
707void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 755void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -714,10 +762,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
714 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 762 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
715 763
716 const auto parameters{rp.PopRaw<Parameters>()}; 764 const auto parameters{rp.PopRaw<Parameters>()};
717 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
718 765
719 applet_resource->GetController<Controller_NPad>(HidController::NPad) 766 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
720 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); 767 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
768 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
721 769
722 LOG_DEBUG(Service_HID, 770 LOG_DEBUG(Service_HID,
723 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 771 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -725,7 +773,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
725 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 773 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
726 774
727 IPC::ResponseBuilder rb{ctx, 2}; 775 IPC::ResponseBuilder rb{ctx, 2};
728 rb.Push(ResultSuccess); 776 rb.Push(result);
729} 777}
730 778
731void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 779void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
@@ -739,15 +787,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
739 787
740 const auto parameters{rp.PopRaw<Parameters>()}; 788 const auto parameters{rp.PopRaw<Parameters>()};
741 789
790 bool is_at_rest{};
791 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
792 const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
793
742 LOG_DEBUG(Service_HID, 794 LOG_DEBUG(Service_HID,
743 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 795 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
744 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 796 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
745 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 797 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
746 798
747 IPC::ResponseBuilder rb{ctx, 3}; 799 IPC::ResponseBuilder rb{ctx, 3};
748 rb.Push(ResultSuccess); 800 rb.Push(result);
749 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 801 rb.Push(is_at_rest);
750 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
751} 802}
752 803
753void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 804void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -761,6 +812,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
761 812
762 const auto parameters{rp.PopRaw<Parameters>()}; 813 const auto parameters{rp.PopRaw<Parameters>()};
763 814
815 bool is_firmware_available{};
816 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
817 const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
818 parameters.sixaxis_handle, is_firmware_available);
819
764 LOG_WARNING( 820 LOG_WARNING(
765 Service_HID, 821 Service_HID,
766 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 822 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -768,8 +824,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
768 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 824 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
769 825
770 IPC::ResponseBuilder rb{ctx, 3}; 826 IPC::ResponseBuilder rb{ctx, 3};
771 rb.Push(ResultSuccess); 827 rb.Push(result);
772 rb.Push(false); 828 rb.Push(is_firmware_available);
773} 829}
774 830
775void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 831void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
@@ -878,6 +934,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 934 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 935 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 936
937 // Games expect this event to be signaled after calling this function
938 applet_resource->GetController<Controller_NPad>(HidController::NPad)
939 .SignalStyleSetChangedEvent(parameters.npad_id);
940
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 941 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 942 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 943 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1115,7 +1175,7 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1115 rb.Push(ResultSuccess); 1175 rb.Push(ResultSuccess);
1116 } else { 1176 } else {
1117 LOG_ERROR(Service_HID, "Npads are not connected!"); 1177 LOG_ERROR(Service_HID, "Npads are not connected!");
1118 rb.Push(ERR_NPAD_NOT_CONNECTED); 1178 rb.Push(NpadNotConnected);
1119 } 1179 }
1120} 1180}
1121 1181
@@ -2124,32 +2184,6 @@ public:
2124 } 2184 }
2125}; 2185};
2126 2186
2127class HidBus final : public ServiceFramework<HidBus> {
2128public:
2129 explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
2130 // clang-format off
2131 static const FunctionInfo functions[] = {
2132 {1, nullptr, "GetBusHandle"},
2133 {2, nullptr, "IsExternalDeviceConnected"},
2134 {3, nullptr, "Initialize"},
2135 {4, nullptr, "Finalize"},
2136 {5, nullptr, "EnableExternalDevice"},
2137 {6, nullptr, "GetExternalDeviceId"},
2138 {7, nullptr, "SendCommandAsync"},
2139 {8, nullptr, "GetSendCommandAsynceResult"},
2140 {9, nullptr, "SetEventForSendCommandAsycResult"},
2141 {10, nullptr, "GetSharedMemoryHandle"},
2142 {11, nullptr, "EnableJoyPollingReceiveMode"},
2143 {12, nullptr, "DisableJoyPollingReceiveMode"},
2144 {13, nullptr, "GetPollingData"},
2145 {14, nullptr, "SetStatusManagerType"},
2146 };
2147 // clang-format on
2148
2149 RegisterHandlers(functions);
2150 }
2151};
2152
2153void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2187void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2154 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2188 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2155 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2189 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c281081a7..666cb6ff5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -103,6 +103,7 @@ private:
103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); 103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
106 void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
106 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); 107 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
107 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 108 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
108 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 109 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..af7662a15
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,531 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
10#include "core/hid/hid_types.h"
11#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h"
15#include "core/hle/kernel/k_transfer_memory.h"
16#include "core/hle/service/hid/hidbus.h"
17#include "core/hle/service/hid/hidbus/ringcon.h"
18#include "core/hle/service/hid/hidbus/starlink.h"
19#include "core/hle/service/hid/hidbus/stubbed.h"
20#include "core/hle/service/service.h"
21#include "core/memory.h"
22
23namespace Service::HID {
24// (15ms, 66Hz)
25constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
26
27HidBus::HidBus(Core::System& system_)
28 : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
29
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {1, &HidBus::GetBusHandle, "GetBusHandle"},
33 {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
34 {3, &HidBus::Initialize, "Initialize"},
35 {4, &HidBus::Finalize, "Finalize"},
36 {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
37 {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
38 {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
39 {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
40 {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
41 {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
42 {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
43 {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
44 {13, nullptr, "GetPollingData"},
45 {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
46 };
47 // clang-format on
48
49 RegisterHandlers(functions);
50
51 // Register update callbacks
52 hidbus_update_event = Core::Timing::CreateEvent(
53 "Hidbus::UpdateCallback",
54 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
55 const auto guard = LockService();
56 UpdateHidbus(user_data, ns_late);
57 });
58
59 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
60}
61
62HidBus::~HidBus() {
63 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
64}
65
66void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
67 auto& core_timing = system.CoreTiming();
68
69 if (is_hidbus_enabled) {
70 for (std::size_t i = 0; i < devices.size(); ++i) {
71 if (!devices[i].is_device_initializated) {
72 continue;
73 }
74 auto& device = devices[i].device;
75 device->OnUpdate();
76 auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
77 cur_entry.is_polling_mode = device->IsPollingMode();
78 cur_entry.polling_mode = device->GetPollingMode();
79 cur_entry.is_enabled = device->IsEnabled();
80
81 u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
82 std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
83 sizeof(HidbusStatusManagerEntry));
84 }
85 }
86
87 // If ns_late is higher than the update rate ignore the delay
88 if (ns_late > hidbus_update_ns) {
89 ns_late = {};
90 }
91
92 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
93}
94
95std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
96 for (std::size_t i = 0; i < devices.size(); ++i) {
97 const auto& device_handle = devices[i].handle;
98 if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
99 handle.internal_index == device_handle.internal_index &&
100 handle.player_number == device_handle.player_number &&
101 handle.bus_type == device_handle.bus_type &&
102 handle.is_valid == device_handle.is_valid) {
103 return i;
104 }
105 }
106 return std::nullopt;
107}
108
109void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
110 IPC::RequestParser rp{ctx};
111 struct Parameters {
112 Core::HID::NpadIdType npad_id;
113 INSERT_PADDING_WORDS_NOINIT(1);
114 BusType bus_type;
115 u64 applet_resource_user_id;
116 };
117 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
118
119 const auto parameters{rp.PopRaw<Parameters>()};
120
121 LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
122 parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
123
124 bool is_handle_found = 0;
125 std::size_t handle_index = 0;
126
127 for (std::size_t i = 0; i < devices.size(); i++) {
128 const auto& handle = devices[i].handle;
129 if (!handle.is_valid) {
130 continue;
131 }
132 if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
133 handle.bus_type == parameters.bus_type) {
134 is_handle_found = true;
135 handle_index = i;
136 break;
137 }
138 }
139
140 // Handle not found. Create a new one
141 if (!is_handle_found) {
142 for (std::size_t i = 0; i < devices.size(); i++) {
143 if (devices[i].handle.is_valid) {
144 continue;
145 }
146 devices[i].handle = {
147 .abstracted_pad_id = static_cast<u8>(i),
148 .internal_index = static_cast<u8>(i),
149 .player_number = static_cast<u8>(parameters.npad_id),
150 .bus_type = parameters.bus_type,
151 .is_valid = true,
152 };
153 handle_index = i;
154 break;
155 }
156 }
157
158 struct OutData {
159 bool is_valid;
160 INSERT_PADDING_BYTES(7);
161 BusHandle handle;
162 };
163 static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
164
165 const OutData out_data{
166 .is_valid = true,
167 .handle = devices[handle_index].handle,
168 };
169
170 IPC::ResponseBuilder rb{ctx, 6};
171 rb.Push(ResultSuccess);
172 rb.PushRaw(out_data);
173}
174
175void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx};
177 const auto bus_handle_{rp.PopRaw<BusHandle>()};
178
179 LOG_INFO(Service_HID,
180 "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
181 "player_number={}, is_valid={}",
182 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
183 bus_handle_.player_number, bus_handle_.is_valid);
184
185 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
186
187 if (device_index) {
188 const auto& device = devices[device_index.value()].device;
189 const bool is_attached = device->IsDeviceActivated();
190
191 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess);
193 rb.Push(is_attached);
194 return;
195 }
196
197 LOG_ERROR(Service_HID, "Invalid handle");
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ResultUnknown);
200 return;
201}
202
203void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto bus_handle_{rp.PopRaw<BusHandle>()};
206 const auto applet_resource_user_id{rp.Pop<u64>()};
207
208 LOG_INFO(Service_HID,
209 "called, abstracted_pad_id={} bus_type={} internal_index={} "
210 "player_number={} is_valid={}, applet_resource_user_id={}",
211 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
212 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
213
214 is_hidbus_enabled = true;
215
216 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
217
218 if (device_index) {
219 const auto entry_index = devices[device_index.value()].handle.internal_index;
220 auto& cur_entry = hidbus_status.entries[entry_index];
221
222 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
223 MakeDevice<RingController>(bus_handle_);
224 devices[device_index.value()].is_device_initializated = true;
225 devices[device_index.value()].device->ActivateDevice();
226 cur_entry.is_in_focus = true;
227 cur_entry.is_connected = true;
228 cur_entry.is_connected_result = ResultSuccess;
229 cur_entry.is_enabled = false;
230 cur_entry.is_polling_mode = false;
231 } else {
232 MakeDevice<HidbusStubbed>(bus_handle_);
233 devices[device_index.value()].is_device_initializated = true;
234 cur_entry.is_in_focus = true;
235 cur_entry.is_connected = false;
236 cur_entry.is_connected_result = ResultSuccess;
237 cur_entry.is_enabled = false;
238 cur_entry.is_polling_mode = false;
239 }
240
241 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
242 sizeof(hidbus_status));
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ResultSuccess);
246 return;
247 }
248
249 LOG_ERROR(Service_HID, "Invalid handle");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ResultUnknown);
252 return;
253}
254
255void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto bus_handle_{rp.PopRaw<BusHandle>()};
258 const auto applet_resource_user_id{rp.Pop<u64>()};
259
260 LOG_INFO(Service_HID,
261 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
262 "player_number={}, is_valid={}, applet_resource_user_id={}",
263 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
264 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
265
266 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
267
268 if (device_index) {
269 const auto entry_index = devices[device_index.value()].handle.internal_index;
270 auto& cur_entry = hidbus_status.entries[entry_index];
271 auto& device = devices[device_index.value()].device;
272 devices[device_index.value()].is_device_initializated = false;
273 device->DeactivateDevice();
274
275 cur_entry.is_in_focus = true;
276 cur_entry.is_connected = false;
277 cur_entry.is_connected_result = ResultSuccess;
278 cur_entry.is_enabled = false;
279 cur_entry.is_polling_mode = false;
280 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
281 sizeof(hidbus_status));
282
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(ResultSuccess);
285 return;
286 }
287
288 LOG_ERROR(Service_HID, "Invalid handle");
289 IPC::ResponseBuilder rb{ctx, 2};
290 rb.Push(ResultUnknown);
291 return;
292}
293
294void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 struct Parameters {
297 bool enable;
298 INSERT_PADDING_BYTES_NOINIT(7);
299 BusHandle bus_handle;
300 u64 inval;
301 u64 applet_resource_user_id;
302 };
303 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
304
305 const auto parameters{rp.PopRaw<Parameters>()};
306
307 LOG_INFO(Service_HID,
308 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
309 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
310 parameters.enable, parameters.bus_handle.abstracted_pad_id,
311 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
312 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
313 parameters.applet_resource_user_id);
314
315 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
316
317 if (device_index) {
318 auto& device = devices[device_index.value()].device;
319 device->Enable(parameters.enable);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323 return;
324 }
325
326 LOG_ERROR(Service_HID, "Invalid handle");
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ResultUnknown);
329 return;
330}
331
332void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto bus_handle_{rp.PopRaw<BusHandle>()};
335
336 LOG_INFO(Service_HID,
337 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
338 "is_valid={}",
339 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
340 bus_handle_.player_number, bus_handle_.is_valid);
341
342 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
343
344 if (device_index) {
345 const auto& device = devices[device_index.value()].device;
346 u32 device_id = device->GetDeviceId();
347 IPC::ResponseBuilder rb{ctx, 3};
348 rb.Push(ResultSuccess);
349 rb.Push<u32>(device_id);
350 return;
351 }
352
353 LOG_ERROR(Service_HID, "Invalid handle");
354 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultUnknown);
356 return;
357}
358
359void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto data = ctx.ReadBuffer();
362 const auto bus_handle_{rp.PopRaw<BusHandle>()};
363
364 LOG_DEBUG(Service_HID,
365 "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
366 "player_number={}, is_valid={}",
367 data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
368 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
369
370 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
371
372 if (device_index) {
373 auto& device = devices[device_index.value()].device;
374 device->SetCommand(data);
375
376 IPC::ResponseBuilder rb{ctx, 2};
377 rb.Push(ResultSuccess);
378 return;
379 }
380
381 LOG_ERROR(Service_HID, "Invalid handle");
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ResultUnknown);
384 return;
385};
386
387void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto bus_handle_{rp.PopRaw<BusHandle>()};
390
391 LOG_DEBUG(Service_HID,
392 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
393 "is_valid={}",
394 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
395 bus_handle_.player_number, bus_handle_.is_valid);
396
397 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
398
399 if (device_index) {
400 const auto& device = devices[device_index.value()].device;
401 const std::vector<u8> data = device->GetReply();
402 const u64 data_size = ctx.WriteBuffer(data);
403
404 IPC::ResponseBuilder rb{ctx, 4};
405 rb.Push(ResultSuccess);
406 rb.Push<u64>(data_size);
407 return;
408 }
409
410 LOG_ERROR(Service_HID, "Invalid handle");
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ResultUnknown);
413 return;
414};
415
416void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
417 IPC::RequestParser rp{ctx};
418 const auto bus_handle_{rp.PopRaw<BusHandle>()};
419
420 LOG_INFO(Service_HID,
421 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
422 "is_valid={}",
423 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
424 bus_handle_.player_number, bus_handle_.is_valid);
425
426 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
427
428 if (device_index) {
429 const auto& device = devices[device_index.value()].device;
430 IPC::ResponseBuilder rb{ctx, 2, 1};
431 rb.Push(ResultSuccess);
432 rb.PushCopyObjects(device->GetSendCommandAsycEvent());
433 return;
434 }
435
436 LOG_ERROR(Service_HID, "Invalid handle");
437 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(ResultUnknown);
439 return;
440};
441
442void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
443 LOG_DEBUG(Service_HID, "called");
444
445 IPC::ResponseBuilder rb{ctx, 2, 1};
446 rb.Push(ResultSuccess);
447 rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
448}
449
450void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto t_mem_size{rp.Pop<u32>()};
453 const auto t_mem_handle{ctx.GetCopyHandle(0)};
454 const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
455 const auto bus_handle_{rp.PopRaw<BusHandle>()};
456
457 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
458
459 auto t_mem =
460 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
461
462 if (t_mem.IsNull()) {
463 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
464 IPC::ResponseBuilder rb{ctx, 2};
465 rb.Push(ResultUnknown);
466 return;
467 }
468
469 ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
470
471 LOG_INFO(Service_HID,
472 "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
473 "internal_index={}, player_number={}, is_valid={}",
474 t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
475 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
476
477 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
478
479 if (device_index) {
480 auto& device = devices[device_index.value()].device;
481 device->SetPollingMode(polling_mode_);
482 device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
483
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(ResultSuccess);
486 return;
487 }
488
489 LOG_ERROR(Service_HID, "Invalid handle");
490 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(ResultUnknown);
492 return;
493}
494
495void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
496 IPC::RequestParser rp{ctx};
497 const auto bus_handle_{rp.PopRaw<BusHandle>()};
498
499 LOG_INFO(Service_HID,
500 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
501 "is_valid={}",
502 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
503 bus_handle_.player_number, bus_handle_.is_valid);
504
505 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
506
507 if (device_index) {
508 auto& device = devices[device_index.value()].device;
509 device->DisablePollingMode();
510
511 IPC::ResponseBuilder rb{ctx, 2};
512 rb.Push(ResultSuccess);
513 return;
514 }
515
516 LOG_ERROR(Service_HID, "Invalid handle");
517 IPC::ResponseBuilder rb{ctx, 2};
518 rb.Push(ResultUnknown);
519 return;
520}
521
522void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
523 IPC::RequestParser rp{ctx};
524 const auto manager_type{rp.PopEnum<StatusManagerType>()};
525
526 LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
527
528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(ResultSuccess);
530};
531} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
new file mode 100644
index 000000000..b10d5156a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,131 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Core {
18class System;
19} // namespace Core
20
21namespace Service::HID {
22
23class HidBus final : public ServiceFramework<HidBus> {
24public:
25 explicit HidBus(Core::System& system_);
26 ~HidBus() override;
27
28private:
29 static const std::size_t max_number_of_handles = 0x13;
30
31 enum class HidBusDeviceId : std::size_t {
32 RingController = 0x20,
33 FamicomRight = 0x21,
34 Starlink = 0x28,
35 };
36
37 // This is nn::hidbus::detail::StatusManagerType
38 enum class StatusManagerType : u32 {
39 None,
40 Type16,
41 Type32,
42 };
43
44 // This is nn::hidbus::BusType
45 enum class BusType : u8 {
46 LeftJoyRail,
47 RightJoyRail,
48 InternalBus, // Lark microphone
49
50 MaxBusType,
51 };
52
53 // This is nn::hidbus::BusHandle
54 struct BusHandle {
55 u32 abstracted_pad_id;
56 u8 internal_index;
57 u8 player_number;
58 BusType bus_type;
59 bool is_valid;
60 };
61 static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
62
63 // This is nn::hidbus::JoyPollingReceivedData
64 struct JoyPollingReceivedData {
65 std::array<u8, 0x30> data;
66 u64 out_size;
67 u64 sampling_number;
68 };
69 static_assert(sizeof(JoyPollingReceivedData) == 0x40,
70 "JoyPollingReceivedData is an invalid size");
71
72 struct HidbusStatusManagerEntry {
73 u8 is_connected{};
74 INSERT_PADDING_BYTES(0x3);
75 ResultCode is_connected_result{0};
76 u8 is_enabled{};
77 u8 is_in_focus{};
78 u8 is_polling_mode{};
79 u8 reserved{};
80 JoyPollingMode polling_mode{};
81 INSERT_PADDING_BYTES(0x70); // Unknown
82 };
83 static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
84 "HidbusStatusManagerEntry is an invalid size");
85
86 struct HidbusStatusManager {
87 std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
88 INSERT_PADDING_BYTES(0x680); // Unused
89 };
90 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
91
92 struct HidbusDevice {
93 bool is_device_initializated{};
94 BusHandle handle{};
95 std::unique_ptr<HidbusBase> device{nullptr};
96 };
97
98 void GetBusHandle(Kernel::HLERequestContext& ctx);
99 void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
100 void Initialize(Kernel::HLERequestContext& ctx);
101 void Finalize(Kernel::HLERequestContext& ctx);
102 void EnableExternalDevice(Kernel::HLERequestContext& ctx);
103 void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
104 void SendCommandAsync(Kernel::HLERequestContext& ctx);
105 void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
106 void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
107 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
108 void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
109 void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
110 void SetStatusManagerType(Kernel::HLERequestContext& ctx);
111
112 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
113 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
114
115 template <typename T>
116 void MakeDevice(BusHandle handle) {
117 const auto device_index = GetDeviceIndexFromHandle(handle);
118 if (device_index) {
119 devices[device_index.value()].device =
120 std::make_unique<T>(system.HIDCore(), service_context);
121 }
122 }
123
124 bool is_hidbus_enabled{false};
125 HidbusStatusManager hidbus_status{};
126 std::array<HidbusDevice, max_number_of_handles> devices{};
127 std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
128 KernelHelpers::ServiceContext service_context;
129};
130
131} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..09bff10e5
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,72 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Service::HID {
12
13HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
14 : service_context(service_context_) {
15 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
16}
17HidbusBase::~HidbusBase() = default;
18
19void HidbusBase::ActivateDevice() {
20 if (is_activated) {
21 return;
22 }
23 is_activated = true;
24 OnInit();
25}
26
27void HidbusBase::DeactivateDevice() {
28 if (is_activated) {
29 OnRelease();
30 }
31 is_activated = false;
32}
33
34bool HidbusBase::IsDeviceActivated() const {
35 return is_activated;
36}
37
38void HidbusBase::Enable(bool enable) {
39 device_enabled = enable;
40}
41
42bool HidbusBase::IsEnabled() const {
43 return device_enabled;
44}
45
46bool HidbusBase::IsPollingMode() const {
47 return polling_mode_enabled;
48}
49
50JoyPollingMode HidbusBase::GetPollingMode() const {
51 return polling_mode;
52}
53
54void HidbusBase::SetPollingMode(JoyPollingMode mode) {
55 polling_mode = mode;
56 polling_mode_enabled = true;
57}
58
59void HidbusBase::DisablePollingMode() {
60 polling_mode_enabled = false;
61}
62
63void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
64 is_transfer_memory_set = true;
65 transfer_memory = t_mem;
66}
67
68Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
69 return send_command_async_event->GetReadableEvent();
70}
71
72} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
new file mode 100644
index 000000000..13d073a3d
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,179 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::KernelHelpers {
17class ServiceContext;
18}
19
20namespace Service::HID {
21
22// This is nn::hidbus::JoyPollingMode
23enum class JoyPollingMode : u32 {
24 SixAxisSensorDisable,
25 SixAxisSensorEnable,
26 ButtonOnly,
27};
28
29struct DataAccessorHeader {
30 ResultCode result{ResultUnknown};
31 INSERT_PADDING_WORDS(0x1);
32 std::array<u8, 0x18> unused{};
33 u64 latest_entry{};
34 u64 total_entries{};
35};
36static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
37
38struct JoyDisableSixAxisPollingData {
39 std::array<u8, 0x26> data;
40 u8 out_size;
41 INSERT_PADDING_BYTES(0x1);
42 u64 sampling_number;
43};
44static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
45 "JoyDisableSixAxisPollingData is an invalid size");
46
47struct JoyEnableSixAxisPollingData {
48 std::array<u8, 0x8> data;
49 u8 out_size;
50 INSERT_PADDING_BYTES(0x7);
51 u64 sampling_number;
52};
53static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
54 "JoyEnableSixAxisPollingData is an invalid size");
55
56struct JoyButtonOnlyPollingData {
57 std::array<u8, 0x2c> data;
58 u8 out_size;
59 INSERT_PADDING_BYTES(0x3);
60 u64 sampling_number;
61};
62static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
63 "JoyButtonOnlyPollingData is an invalid size");
64
65struct JoyDisableSixAxisPollingEntry {
66 u64 sampling_number;
67 JoyDisableSixAxisPollingData polling_data;
68};
69static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
70 "JoyDisableSixAxisPollingEntry is an invalid size");
71
72struct JoyEnableSixAxisPollingEntry {
73 u64 sampling_number;
74 JoyEnableSixAxisPollingData polling_data;
75};
76static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
77 "JoyEnableSixAxisPollingEntry is an invalid size");
78
79struct JoyButtonOnlyPollingEntry {
80 u64 sampling_number;
81 JoyButtonOnlyPollingData polling_data;
82};
83static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
84 "JoyButtonOnlyPollingEntry is an invalid size");
85
86struct JoyDisableSixAxisDataAccessor {
87 DataAccessorHeader header{};
88 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
89};
90static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
91 "JoyDisableSixAxisDataAccessor is an invalid size");
92
93struct JoyEnableSixAxisDataAccessor {
94 DataAccessorHeader header{};
95 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
96};
97static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
98 "JoyEnableSixAxisDataAccessor is an invalid size");
99
100struct ButtonOnlyPollingDataAccessor {
101 DataAccessorHeader header;
102 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
103};
104static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
105 "ButtonOnlyPollingDataAccessor is an invalid size");
106
107class HidbusBase {
108public:
109 explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
110 virtual ~HidbusBase();
111
112 void ActivateDevice();
113
114 void DeactivateDevice();
115
116 bool IsDeviceActivated() const;
117
118 // Enables/disables the device
119 void Enable(bool enable);
120
121 // returns true if device is enabled
122 bool IsEnabled() const;
123
124 // returns true if polling mode is enabled
125 bool IsPollingMode() const;
126
127 // returns polling mode
128 JoyPollingMode GetPollingMode() const;
129
130 // Sets and enables JoyPollingMode
131 void SetPollingMode(JoyPollingMode mode);
132
133 // Disables JoyPollingMode
134 void DisablePollingMode();
135
136 // Called on EnableJoyPollingReceiveMode
137 void SetTransferMemoryPointer(u8* t_mem);
138
139 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
140
141 virtual void OnInit() {}
142
143 virtual void OnRelease() {}
144
145 // Updates device transfer memory
146 virtual void OnUpdate() {}
147
148 // Returns the device ID of the joycon
149 virtual u8 GetDeviceId() const {
150 return {};
151 }
152
153 // Assigns a command from data
154 virtual bool SetCommand(const std::vector<u8>& data) {
155 return {};
156 }
157
158 // Returns a reply from a command
159 virtual std::vector<u8> GetReply() const {
160 return {};
161 }
162
163protected:
164 bool is_activated{};
165 bool device_enabled{};
166 bool polling_mode_enabled{};
167 JoyPollingMode polling_mode = {};
168 // TODO(German77): All data accessors need to be replaced with a ring lifo object
169 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
170 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
171 ButtonOnlyPollingDataAccessor button_only_data{};
172
173 u8* transfer_memory{nullptr};
174 bool is_transfer_memory_set{};
175
176 Kernel::KEvent* send_command_async_event;
177 KernelHelpers::ServiceContext& service_context;
178};
179} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
new file mode 100644
index 000000000..5ec3cc83c
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,286 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_devices.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/hid/hidbus/ringcon.h"
10
11namespace Service::HID {
12
13RingController::RingController(Core::HID::HIDCore& hid_core_,
14 KernelHelpers::ServiceContext& service_context_)
15 : HidbusBase(service_context_) {
16 input = hid_core_.GetEmulatedDevices();
17}
18
19RingController::~RingController() = default;
20
21void RingController::OnInit() {
22 return;
23}
24
25void RingController::OnRelease() {
26 return;
27};
28
29void RingController::OnUpdate() {
30 if (!is_activated) {
31 return;
32 }
33
34 if (!device_enabled) {
35 return;
36 }
37
38 if (!polling_mode_enabled || !is_transfer_memory_set) {
39 return;
40 }
41
42 // TODO: Increment multitasking counters from motion and sensor data
43
44 switch (polling_mode) {
45 case JoyPollingMode::SixAxisSensorEnable: {
46 enable_sixaxis_data.header.total_entries = 10;
47 enable_sixaxis_data.header.result = ResultSuccess;
48 const auto& last_entry =
49 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
50
51 enable_sixaxis_data.header.latest_entry =
52 (enable_sixaxis_data.header.latest_entry + 1) % 10;
53 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
54
55 curr_entry.sampling_number = last_entry.sampling_number + 1;
56 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
57
58 const RingConData ringcon_value = GetSensorValue();
59 curr_entry.polling_data.out_size = sizeof(ringcon_value);
60 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
61
62 std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
63 break;
64 }
65 default:
66 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
67 break;
68 }
69}
70
71RingController::RingConData RingController::GetSensorValue() const {
72 RingConData ringcon_sensor_value{
73 .status = DataValid::Valid,
74 .data = 0,
75 };
76
77 const f32 force_value = input->GetRingSensorForce().force * range;
78 ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
79
80 return ringcon_sensor_value;
81}
82
83u8 RingController::GetDeviceId() const {
84 return device_id;
85}
86
87std::vector<u8> RingController::GetReply() const {
88 const RingConCommands current_command = command;
89
90 switch (current_command) {
91 case RingConCommands::GetFirmwareVersion:
92 return GetFirmwareVersionReply();
93 case RingConCommands::ReadId:
94 return GetReadIdReply();
95 case RingConCommands::c20105:
96 return GetC020105Reply();
97 case RingConCommands::ReadUnkCal:
98 return GetReadUnkCalReply();
99 case RingConCommands::ReadFactoryCal:
100 return GetReadFactoryCalReply();
101 case RingConCommands::ReadUserCal:
102 return GetReadUserCalReply();
103 case RingConCommands::ReadRepCount:
104 return GetReadRepCountReply();
105 case RingConCommands::ReadTotalPushCount:
106 return GetReadTotalPushCountReply();
107 case RingConCommands::ResetRepCount:
108 return GetResetRepCountReply();
109 case RingConCommands::SaveCalData:
110 return GetSaveDataReply();
111 default:
112 return GetErrorReply();
113 }
114}
115
116bool RingController::SetCommand(const std::vector<u8>& data) {
117 if (data.size() < 4) {
118 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
119 command = RingConCommands::Error;
120 return false;
121 }
122
123 std::memcpy(&command, data.data(), sizeof(RingConCommands));
124
125 switch (command) {
126 case RingConCommands::GetFirmwareVersion:
127 case RingConCommands::ReadId:
128 case RingConCommands::c20105:
129 case RingConCommands::ReadUnkCal:
130 case RingConCommands::ReadFactoryCal:
131 case RingConCommands::ReadUserCal:
132 case RingConCommands::ReadRepCount:
133 case RingConCommands::ReadTotalPushCount:
134 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
135 send_command_async_event->GetWritableEvent().Signal();
136 return true;
137 case RingConCommands::ResetRepCount:
138 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
139 total_rep_count = 0;
140 send_command_async_event->GetWritableEvent().Signal();
141 return true;
142 case RingConCommands::SaveCalData: {
143 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
144
145 SaveCalData save_info{};
146 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
147 user_calibration = save_info.calibration;
148 send_command_async_event->GetWritableEvent().Signal();
149 return true;
150 }
151 default:
152 LOG_ERROR(Service_HID, "Command not implemented {}", command);
153 command = RingConCommands::Error;
154 // Signal a reply to avoid softlocking the game
155 send_command_async_event->GetWritableEvent().Signal();
156 return false;
157 }
158}
159
160std::vector<u8> RingController::GetFirmwareVersionReply() const {
161 const FirmwareVersionReply reply{
162 .status = DataValid::Valid,
163 .firmware = version,
164 };
165
166 return GetDataVector(reply);
167}
168
169std::vector<u8> RingController::GetReadIdReply() const {
170 // The values are hardcoded from a real joycon
171 const ReadIdReply reply{
172 .status = DataValid::Valid,
173 .id_l_x0 = 8,
174 .id_l_x0_2 = 41,
175 .id_l_x4 = 22294,
176 .id_h_x0 = 19777,
177 .id_h_x0_2 = 13621,
178 .id_h_x4 = 8245,
179 };
180
181 return GetDataVector(reply);
182}
183
184std::vector<u8> RingController::GetC020105Reply() const {
185 const Cmd020105Reply reply{
186 .status = DataValid::Valid,
187 .data = 1,
188 };
189
190 return GetDataVector(reply);
191}
192
193std::vector<u8> RingController::GetReadUnkCalReply() const {
194 const ReadUnkCalReply reply{
195 .status = DataValid::Valid,
196 .data = 0,
197 };
198
199 return GetDataVector(reply);
200}
201
202std::vector<u8> RingController::GetReadFactoryCalReply() const {
203 const ReadFactoryCalReply reply{
204 .status = DataValid::Valid,
205 .calibration = factory_calibration,
206 };
207
208 return GetDataVector(reply);
209}
210
211std::vector<u8> RingController::GetReadUserCalReply() const {
212 const ReadUserCalReply reply{
213 .status = DataValid::Valid,
214 .calibration = user_calibration,
215 };
216
217 return GetDataVector(reply);
218}
219
220std::vector<u8> RingController::GetReadRepCountReply() const {
221 const GetThreeByteReply reply{
222 .status = DataValid::Valid,
223 .data = {total_rep_count, 0, 0},
224 .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
225 };
226
227 return GetDataVector(reply);
228}
229
230std::vector<u8> RingController::GetReadTotalPushCountReply() const {
231 const GetThreeByteReply reply{
232 .status = DataValid::Valid,
233 .data = {total_push_count, 0, 0},
234 .crc = GetCrcValue({total_push_count, 0, 0, 0}),
235 };
236
237 return GetDataVector(reply);
238}
239
240std::vector<u8> RingController::GetResetRepCountReply() const {
241 return GetReadRepCountReply();
242}
243
244std::vector<u8> RingController::GetSaveDataReply() const {
245 const StatusReply reply{
246 .status = DataValid::Valid,
247 };
248
249 return GetDataVector(reply);
250}
251
252std::vector<u8> RingController::GetErrorReply() const {
253 const ErrorReply reply{
254 .status = DataValid::BadCRC,
255 };
256
257 return GetDataVector(reply);
258}
259
260u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
261 u8 crc = 0;
262 for (std::size_t index = 0; index < data.size(); index++) {
263 for (u8 i = 0x80; i > 0; i >>= 1) {
264 bool bit = (crc & 0x80) != 0;
265 if ((data[index] & i) != 0) {
266 bit = !bit;
267 }
268 crc <<= 1;
269 if (bit) {
270 crc ^= 0x8d;
271 }
272 }
273 }
274 return crc;
275}
276
277template <typename T>
278std::vector<u8> RingController::GetDataVector(const T& reply) const {
279 static_assert(std::is_trivially_copyable_v<T>);
280 std::vector<u8> data;
281 data.resize(sizeof(reply));
282 std::memcpy(data.data(), &reply, sizeof(reply));
283 return data;
284}
285
286} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
new file mode 100644
index 000000000..2dbc6150e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,254 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/service/hid/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedDevices;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::HID::HIDCore& hid_core_,
21 KernelHelpers::ServiceContext& service_context_);
22 ~RingController() override;
23
24 void OnInit() override;
25
26 void OnRelease() override;
27
28 // Updates ringcon transfer memory
29 void OnUpdate() override;
30
31 // Returns the device ID of the joycon
32 u8 GetDeviceId() const override;
33
34 // Assigns a command from data
35 bool SetCommand(const std::vector<u8>& data) override;
36
37 // Returns a reply from a command
38 std::vector<u8> GetReply() const override;
39
40private:
41 // These values are obtained from a real ring controller
42 static constexpr s16 idle_value = 2280;
43 static constexpr s16 idle_deadzone = 120;
44 static constexpr s16 range = 2500;
45
46 // Most missing command names are leftovers from other firmware versions
47 enum class RingConCommands : u32 {
48 GetFirmwareVersion = 0x00020000,
49 ReadId = 0x00020100,
50 JoyPolling = 0x00020101,
51 Unknown1 = 0x00020104,
52 c20105 = 0x00020105,
53 Unknown2 = 0x00020204,
54 Unknown3 = 0x00020304,
55 Unknown4 = 0x00020404,
56 ReadUnkCal = 0x00020504,
57 ReadFactoryCal = 0x00020A04,
58 Unknown5 = 0x00021104,
59 Unknown6 = 0x00021204,
60 Unknown7 = 0x00021304,
61 ReadUserCal = 0x00021A04,
62 ReadRepCount = 0x00023104,
63 ReadTotalPushCount = 0x00023204,
64 ResetRepCount = 0x04013104,
65 Unknown8 = 0x04011104,
66 Unknown9 = 0x04011204,
67 Unknown10 = 0x04011304,
68 SaveCalData = 0x10011A04,
69 Error = 0xFFFFFFFF,
70 };
71
72 enum class DataValid : u32 {
73 Valid,
74 BadCRC,
75 Cal,
76 };
77
78 struct FirmwareVersion {
79 u8 sub;
80 u8 main;
81 };
82 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
83
84 struct FactoryCalibration {
85 s32_le os_max;
86 s32_le hk_max;
87 s32_le zero_min;
88 s32_le zero_max;
89 };
90 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
91
92 struct CalibrationValue {
93 s16 value;
94 u16 crc;
95 };
96 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
97
98 struct UserCalibration {
99 CalibrationValue os_max;
100 CalibrationValue hk_max;
101 CalibrationValue zero;
102 };
103 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
104
105 struct SaveCalData {
106 RingConCommands command;
107 UserCalibration calibration;
108 INSERT_PADDING_BYTES_NOINIT(4);
109 };
110 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
111 static_assert(std::is_trivially_copyable_v<SaveCalData>,
112 "SaveCalData must be trivially copyable");
113
114 struct FirmwareVersionReply {
115 DataValid status;
116 FirmwareVersion firmware;
117 INSERT_PADDING_BYTES(0x2);
118 };
119 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
120
121 struct Cmd020105Reply {
122 DataValid status;
123 u8 data;
124 INSERT_PADDING_BYTES(0x3);
125 };
126 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
127
128 struct StatusReply {
129 DataValid status;
130 };
131 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
132
133 struct GetThreeByteReply {
134 DataValid status;
135 std::array<u8, 3> data;
136 u8 crc;
137 };
138 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
139
140 struct ReadUnkCalReply {
141 DataValid status;
142 u16 data;
143 INSERT_PADDING_BYTES(0x2);
144 };
145 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
146
147 struct ReadFactoryCalReply {
148 DataValid status;
149 FactoryCalibration calibration;
150 };
151 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
152
153 struct ReadUserCalReply {
154 DataValid status;
155 UserCalibration calibration;
156 INSERT_PADDING_BYTES(0x4);
157 };
158 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
159
160 struct ReadIdReply {
161 DataValid status;
162 u16 id_l_x0;
163 u16 id_l_x0_2;
164 u16 id_l_x4;
165 u16 id_h_x0;
166 u16 id_h_x0_2;
167 u16 id_h_x4;
168 };
169 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
170
171 struct ErrorReply {
172 DataValid status;
173 INSERT_PADDING_BYTES(0x3);
174 };
175 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
176
177 struct RingConData {
178 DataValid status;
179 s16_le data;
180 INSERT_PADDING_BYTES(0x2);
181 };
182 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
183
184 // Returns RingConData struct with pressure sensor values
185 RingConData GetSensorValue() const;
186
187 // Returns 8 byte reply with firmware version
188 std::vector<u8> GetFirmwareVersionReply() const;
189
190 // Returns 16 byte reply with ID values
191 std::vector<u8> GetReadIdReply() const;
192
193 // (STUBBED) Returns 8 byte reply
194 std::vector<u8> GetC020105Reply() const;
195
196 // (STUBBED) Returns 8 byte empty reply
197 std::vector<u8> GetReadUnkCalReply() const;
198
199 // Returns 20 byte reply with factory calibration values
200 std::vector<u8> GetReadFactoryCalReply() const;
201
202 // Returns 20 byte reply with user calibration values
203 std::vector<u8> GetReadUserCalReply() const;
204
205 // Returns 8 byte reply
206 std::vector<u8> GetReadRepCountReply() const;
207
208 // Returns 8 byte reply
209 std::vector<u8> GetReadTotalPushCountReply() const;
210
211 // Returns 8 byte reply
212 std::vector<u8> GetResetRepCountReply() const;
213
214 // Returns 4 byte save data reply
215 std::vector<u8> GetSaveDataReply() const;
216
217 // Returns 8 byte error reply
218 std::vector<u8> GetErrorReply() const;
219
220 // Returns 8 bit redundancy check from provided data
221 u8 GetCrcValue(const std::vector<u8>& data) const;
222
223 // Converts structs to an u8 vector equivalent
224 template <typename T>
225 std::vector<u8> GetDataVector(const T& reply) const;
226
227 RingConCommands command{RingConCommands::Error};
228
229 // These counters are used in multitasking mode while the switch is sleeping
230 // Total steps taken
231 u8 total_rep_count = 0;
232 // Total times the ring was pushed
233 u8 total_push_count = 0;
234
235 const u8 device_id = 0x20;
236 const FirmwareVersion version = {
237 .sub = 0x0,
238 .main = 0x2c,
239 };
240 const FactoryCalibration factory_calibration = {
241 .os_max = idle_value + range + idle_deadzone,
242 .hk_max = idle_value - range - idle_deadzone,
243 .zero_min = idle_value - idle_deadzone,
244 .zero_max = idle_value + idle_deadzone,
245 };
246 UserCalibration user_calibration = {
247 .os_max = {.value = range, .crc = 228},
248 .hk_max = {.value = -range, .crc = 239},
249 .zero = {.value = idle_value, .crc = 225},
250 };
251
252 Core::HID::EmulatedDevices* input;
253};
254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
new file mode 100644
index 000000000..3175c48da
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/starlink.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0x28;
11
12Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
13 : HidbusBase(service_context_) {}
14Starlink::~Starlink() = default;
15
16void Starlink::OnInit() {
17 return;
18}
19
20void Starlink::OnRelease() {
21 return;
22};
23
24void Starlink::OnUpdate() {
25 if (!is_activated) {
26 return;
27 }
28 if (!device_enabled) {
29 return;
30 }
31 if (!polling_mode_enabled || !is_transfer_memory_set) {
32 return;
33 }
34
35 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
36}
37
38u8 Starlink::GetDeviceId() const {
39 return DEVICE_ID;
40}
41
42std::vector<u8> Starlink::GetReply() const {
43 return {};
44}
45
46bool Starlink::SetCommand(const std::vector<u8>& data) {
47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false;
49}
50
51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
new file mode 100644
index 000000000..79770b68e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class Starlink final : public HidbusBase {
17public:
18 explicit Starlink(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~Starlink() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
new file mode 100644
index 000000000..5474657be
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/stubbed.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0xFF;
11
12HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) {}
15HidbusStubbed::~HidbusStubbed() = default;
16
17void HidbusStubbed::OnInit() {
18 return;
19}
20
21void HidbusStubbed::OnRelease() {
22 return;
23};
24
25void HidbusStubbed::OnUpdate() {
26 if (!is_activated) {
27 return;
28 }
29 if (!device_enabled) {
30 return;
31 }
32 if (!polling_mode_enabled || !is_transfer_memory_set) {
33 return;
34 }
35
36 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
37}
38
39u8 HidbusStubbed::GetDeviceId() const {
40 return DEVICE_ID;
41}
42
43std::vector<u8> HidbusStubbed::GetReply() const {
44 return {};
45}
46
47bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
48 LOG_ERROR(Service_HID, "Command not implemented");
49 return false;
50}
51
52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
new file mode 100644
index 000000000..40acdfe8f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class HidbusStubbed final : public HidbusBase {
17public:
18 explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~HidbusStubbed() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
new file mode 100644
index 000000000..0f9e33ef6
--- /dev/null
+++ b/src/core/hle/service/jit/jit.cpp
@@ -0,0 +1,332 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/symbols.h"
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_transfer_memory.h"
10#include "core/hle/result.h"
11#include "core/hle/service/jit/jit.h"
12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/service.h"
14#include "core/memory.h"
15
16namespace Service::JIT {
17
18struct CodeRange {
19 u64 offset;
20 u64 size;
21};
22
23class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
24public:
25 explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
26 : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
27 context{system_.Memory()} {
28 // clang-format off
29 static const FunctionInfo functions[] = {
30 {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
31 {1, &IJitEnvironment::Control, "Control"},
32 {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
33 {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38
39 // Identity map user code range into sysmodule context
40 configuration.user_ro_memory = user_ro;
41 configuration.user_rx_memory = user_rx;
42 configuration.sys_ro_memory = user_ro;
43 configuration.sys_rx_memory = user_rx;
44 }
45
46 void GenerateCode(Kernel::HLERequestContext& ctx) {
47 struct Parameters {
48 u32 data_size;
49 u64 command;
50 CodeRange cr1;
51 CodeRange cr2;
52 Struct32 data;
53 };
54
55 IPC::RequestParser rp{ctx};
56 const auto parameters{rp.PopRaw<Parameters>()};
57 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
58 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
59
60 const VAddr return_ptr{context.AddHeap(0u)};
61 const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
62 const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
63 const VAddr cr1_out_ptr{
64 context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
65 const VAddr cr2_out_ptr{
66 context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
67 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
68 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
69 const VAddr data_ptr{context.AddHeap(parameters.data)};
70 const VAddr configuration_ptr{context.AddHeap(configuration)};
71
72 context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
73 configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
74 cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
75 output_buffer.size());
76
77 const s32 return_value{context.GetHeap<s32>(return_ptr)};
78
79 if (return_value == 0) {
80 system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
81 configuration.user_rx_memory.size);
82
83 if (ctx.CanWriteBuffer()) {
84 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
85 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
86 }
87 const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
88 const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
89
90 IPC::ResponseBuilder rb{ctx, 8};
91 rb.Push(ResultSuccess);
92 rb.Push<u64>(return_value);
93 rb.PushRaw(cr1_out);
94 rb.PushRaw(cr2_out);
95 } else {
96 LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ResultUnknown);
99 }
100 };
101
102 void Control(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto command{rp.PopRaw<u64>()};
105 const auto input_buffer{ctx.ReadBuffer()};
106 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
107
108 const VAddr return_ptr{context.AddHeap(0u)};
109 const VAddr configuration_ptr{context.AddHeap(configuration)};
110 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
111 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
112 const u64 wrapper_value{
113 context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
114 input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
115 const s32 return_value{context.GetHeap<s32>(return_ptr)};
116
117 if (wrapper_value == 0 && return_value == 0) {
118 if (ctx.CanWriteBuffer()) {
119 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
120 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
121 }
122 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(ResultSuccess);
124 rb.Push(return_value);
125 } else {
126 LOG_WARNING(Service_JIT, "plugin Control callback failed");
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ResultUnknown);
129 }
130 }
131
132 void LoadPlugin(Kernel::HLERequestContext& ctx) {
133 IPC::RequestParser rp{ctx};
134 const auto tmem_size{rp.PopRaw<u64>()};
135 if (tmem_size == 0) {
136 LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
137 IPC::ResponseBuilder rb{ctx, 2};
138 rb.Push(ResultUnknown);
139 return;
140 }
141
142 const auto tmem_handle{ctx.GetCopyHandle(0)};
143 auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
144 tmem_handle)};
145 if (tmem.IsNull()) {
146 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ResultUnknown);
149 return;
150 }
151
152 configuration.work_memory.offset = tmem->GetSourceAddress();
153 configuration.work_memory.size = tmem_size;
154
155 const auto nro_plugin{ctx.ReadBuffer(1)};
156 auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
157 const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
158
159 callbacks =
160 GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
161 .rtld_init = GetSymbol("_init"),
162 .Control = GetSymbol("nnjitpluginControl"),
163 .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
164 .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
165 .Configure = GetSymbol("nnjitpluginConfigure"),
166 .GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
167 .GetVersion = GetSymbol("nnjitpluginGetVersion"),
168 .Keeper = GetSymbol("nnjitpluginKeeper"),
169 .OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
170
171 if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
172 callbacks.OnPrepared == 0) {
173 LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
174 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(ResultUnknown);
176 return;
177 }
178
179 if (!context.LoadNRO(nro_plugin)) {
180 LOG_ERROR(Service_JIT, "failed to load plugin");
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(ResultUnknown);
183 return;
184 }
185
186 context.MapProcessMemory(configuration.sys_ro_memory.offset,
187 configuration.sys_ro_memory.size);
188 context.MapProcessMemory(configuration.sys_rx_memory.offset,
189 configuration.sys_rx_memory.size);
190 context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
191
192 if (callbacks.rtld_init != 0) {
193 context.CallFunction(callbacks.rtld_init);
194 }
195
196 const auto version{context.CallFunction(callbacks.GetVersion)};
197 if (version != 1) {
198 LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultUnknown);
201 return;
202 }
203
204 const auto resolve{context.GetHelper("_resolve")};
205 if (callbacks.ResolveBasicSymbols != 0) {
206 context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
207 }
208 const auto resolve_ptr{context.AddHeap(resolve)};
209 if (callbacks.SetupDiagnostics != 0) {
210 context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
211 }
212
213 context.CallFunction(callbacks.Configure, 0u);
214 const auto configuration_ptr{context.AddHeap(configuration)};
215 context.CallFunction(callbacks.OnPrepared, configuration_ptr);
216
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219 }
220
221 void GetCodeAddress(Kernel::HLERequestContext& ctx) {
222 IPC::ResponseBuilder rb{ctx, 6};
223 rb.Push(ResultSuccess);
224 rb.Push(configuration.user_rx_memory.offset);
225 rb.Push(configuration.user_ro_memory.offset);
226 }
227
228private:
229 using Struct32 = std::array<u8, 32>;
230
231 struct GuestCallbacks {
232 VAddr rtld_fini;
233 VAddr rtld_init;
234 VAddr Control;
235 VAddr ResolveBasicSymbols;
236 VAddr SetupDiagnostics;
237 VAddr Configure;
238 VAddr GenerateCode;
239 VAddr GetVersion;
240 VAddr Keeper;
241 VAddr OnPrepared;
242 };
243
244 struct JITConfiguration {
245 CodeRange user_rx_memory;
246 CodeRange user_ro_memory;
247 CodeRange work_memory;
248 CodeRange sys_rx_memory;
249 CodeRange sys_ro_memory;
250 };
251
252 GuestCallbacks callbacks;
253 JITConfiguration configuration;
254 JITContext context;
255};
256
257class JITU final : public ServiceFramework<JITU> {
258public:
259 explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
260 // clang-format off
261 static const FunctionInfo functions[] = {
262 {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
263 };
264 // clang-format on
265
266 RegisterHandlers(functions);
267 }
268
269 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
270 LOG_DEBUG(Service_JIT, "called");
271
272 struct Parameters {
273 u64 rx_size;
274 u64 ro_size;
275 };
276
277 IPC::RequestParser rp{ctx};
278 const auto parameters{rp.PopRaw<Parameters>()};
279 const auto executable_mem_handle{ctx.GetCopyHandle(1)};
280 const auto readable_mem_handle{ctx.GetCopyHandle(2)};
281
282 if (parameters.rx_size == 0 || parameters.ro_size == 0) {
283 LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ResultUnknown);
286 return;
287 }
288
289 // The copy handle at index 0 is the process handle, but handle tables are
290 // per-process, so there is no point reading it here until we are multiprocess
291 const auto& process{*system.CurrentProcess()};
292
293 auto executable_mem{
294 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
295 if (executable_mem.IsNull()) {
296 LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
297 executable_mem_handle);
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(ResultUnknown);
300 return;
301 }
302
303 auto readable_mem{
304 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
305 if (readable_mem.IsNull()) {
306 LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultUnknown);
309 return;
310 }
311
312 const CodeRange user_rx{
313 .offset = executable_mem->GetSourceAddress(),
314 .size = parameters.rx_size,
315 };
316
317 const CodeRange user_ro{
318 .offset = readable_mem->GetSourceAddress(),
319 .size = parameters.ro_size,
320 };
321
322 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
323 rb.Push(ResultSuccess);
324 rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
325 }
326};
327
328void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
329 std::make_shared<JITU>(system)->InstallAsService(sm);
330}
331
332} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h
new file mode 100644
index 000000000..8fbf504a1
--- /dev/null
+++ b/src/core/hle/service/jit/jit.h
@@ -0,0 +1,20 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::JIT {
16
17/// Registers all JIT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
19
20} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
new file mode 100644
index 000000000..630368fb3
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <map>
7#include <span>
8#include <boost/icl/interval_set.hpp>
9#include <dynarmic/interface/A64/a64.h>
10#include <dynarmic/interface/A64/config.h>
11
12#include "common/alignment.h"
13#include "common/common_funcs.h"
14#include "common/div_ceil.h"
15#include "common/logging/log.h"
16#include "core/hle/service/jit/jit_context.h"
17#include "core/memory.h"
18
19namespace Service::JIT {
20
21constexpr std::array<u8, 4> STOP_ARM64 = {
22 0x01, 0x00, 0x00, 0xd4, // svc #0
23};
24
25constexpr std::array<u8, 8> RESOLVE_ARM64 = {
26 0x21, 0x00, 0x00, 0xd4, // svc #1
27 0xc0, 0x03, 0x5f, 0xd6, // ret
28};
29
30constexpr std::array<u8, 4> PANIC_ARM64 = {
31 0x41, 0x00, 0x00, 0xd4, // svc #2
32};
33
34constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
35 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
36 0x83, 0x01, 0x00, 0x54, // b.lo #+34
37 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
38 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
39 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
40 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
41 0xfc, 0xff, 0xff, 0x17, // b #-16
42 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
43 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
44 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
45 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
46 0x8b, 0xff, 0xff, 0x54, // b.lt #-16
47 0xc0, 0x03, 0x5f, 0xd6, // ret
48 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
49 0xfc, 0xff, 0xff, 0x17, // b #-16
50};
51
52constexpr std::array<u8, 28> MEMSET_ARM64 = {
53 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
54 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
55 0x4b, 0x00, 0x00, 0x54, // b.lt #+8
56 0xc0, 0x03, 0x5f, 0xd6, // ret
57 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
58 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
59 0xfb, 0xff, 0xff, 0x17, // b #-20
60};
61
62struct HelperFunction {
63 const char* name;
64 const std::span<const u8> data;
65};
66
67constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
68 {"_stop", STOP_ARM64},
69 {"_resolve", RESOLVE_ARM64},
70 {"_panic", PANIC_ARM64},
71 {"memcpy", MEMMOVE_ARM64},
72 {"memmove", MEMMOVE_ARM64},
73 {"memset", MEMSET_ARM64},
74}};
75
76struct Elf64_Dyn {
77 u64 d_tag;
78 u64 d_un;
79};
80
81struct Elf64_Rela {
82 u64 r_offset;
83 u64 r_info;
84 s64 r_addend;
85};
86
87static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
88 return static_cast<u32>(rela->r_info);
89}
90
91constexpr int DT_RELA = 7; /* Address of Rela relocs */
92constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
93constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
94
95constexpr size_t STACK_ALIGN = 16;
96
97class JITContextImpl;
98
99using IntervalSet = boost::icl::interval_set<VAddr>::type;
100using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
101
102class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
103public:
104 explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
105 IntervalSet& mapped_ranges_, JITContextImpl& parent_)
106 : memory{memory_}, local_memory{local_memory_},
107 mapped_ranges{mapped_ranges_}, parent{parent_} {}
108
109 u8 MemoryRead8(u64 vaddr) override {
110 return ReadMemory<u8>(vaddr);
111 }
112 u16 MemoryRead16(u64 vaddr) override {
113 return ReadMemory<u16>(vaddr);
114 }
115 u32 MemoryRead32(u64 vaddr) override {
116 return ReadMemory<u32>(vaddr);
117 }
118 u64 MemoryRead64(u64 vaddr) override {
119 return ReadMemory<u64>(vaddr);
120 }
121 u128 MemoryRead128(u64 vaddr) override {
122 return ReadMemory<u128>(vaddr);
123 }
124 std::string MemoryReadCString(u64 vaddr) {
125 std::string result;
126 u8 next;
127
128 while ((next = MemoryRead8(vaddr++)) != 0) {
129 result += next;
130 }
131
132 return result;
133 }
134
135 void MemoryWrite8(u64 vaddr, u8 value) override {
136 WriteMemory<u8>(vaddr, value);
137 }
138 void MemoryWrite16(u64 vaddr, u16 value) override {
139 WriteMemory<u16>(vaddr, value);
140 }
141 void MemoryWrite32(u64 vaddr, u32 value) override {
142 WriteMemory<u32>(vaddr, value);
143 }
144 void MemoryWrite64(u64 vaddr, u64 value) override {
145 WriteMemory<u64>(vaddr, value);
146 }
147 void MemoryWrite128(u64 vaddr, u128 value) override {
148 WriteMemory<u128>(vaddr, value);
149 }
150
151 bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
152 return WriteMemory<u8>(vaddr, value);
153 }
154 bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
155 return WriteMemory<u16>(vaddr, value);
156 }
157 bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
158 return WriteMemory<u32>(vaddr, value);
159 }
160 bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
161 return WriteMemory<u64>(vaddr, value);
162 }
163 bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
164 return WriteMemory<u128>(vaddr, value);
165 }
166
167 void CallSVC(u32 swi) override;
168 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
169 void InterpreterFallback(u64 pc, size_t num_instructions) override;
170
171 void AddTicks(u64 ticks) override {}
172 u64 GetTicksRemaining() override {
173 return std::numeric_limits<u32>::max();
174 }
175 u64 GetCNTPCT() override {
176 return 0;
177 }
178
179 template <class T>
180 T ReadMemory(u64 vaddr) {
181 T ret{};
182 if (boost::icl::contains(mapped_ranges, vaddr)) {
183 memory.ReadBlock(vaddr, &ret, sizeof(T));
184 } else if (vaddr + sizeof(T) > local_memory.size()) {
185 LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
186 } else {
187 std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
188 }
189 return ret;
190 }
191
192 template <class T>
193 bool WriteMemory(u64 vaddr, const T value) {
194 if (boost::icl::contains(mapped_ranges, vaddr)) {
195 memory.WriteBlock(vaddr, &value, sizeof(T));
196 } else if (vaddr + sizeof(T) > local_memory.size()) {
197 LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
198 } else {
199 std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
200 }
201 return true;
202 }
203
204private:
205 Core::Memory::Memory& memory;
206 std::vector<u8>& local_memory;
207 IntervalSet& mapped_ranges;
208 JITContextImpl& parent;
209};
210
211class JITContextImpl {
212public:
213 explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
214 callbacks =
215 std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
216 user_config.callbacks = callbacks.get();
217 jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
218 }
219
220 bool LoadNRO(std::span<const u8> data) {
221 local_memory.clear();
222 local_memory.insert(local_memory.end(), data.begin(), data.end());
223
224 if (FixupRelocations()) {
225 InsertHelperFunctions();
226 InsertStack();
227 return true;
228 } else {
229 return false;
230 }
231 }
232
233 bool FixupRelocations() {
234 const VAddr mod_offset{callbacks->MemoryRead32(4)};
235 if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
236 return false;
237 }
238
239 VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
240 VAddr rela_dyn = 0;
241 size_t num_rela = 0;
242 while (true) {
243 const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
244 dynamic_offset += sizeof(Elf64_Dyn);
245
246 if (!dyn.d_tag) {
247 break;
248 }
249 if (dyn.d_tag == DT_RELA) {
250 rela_dyn = dyn.d_un;
251 }
252 if (dyn.d_tag == DT_RELASZ) {
253 num_rela = dyn.d_un / sizeof(Elf64_Rela);
254 }
255 }
256
257 for (size_t i = 0; i < num_rela; i++) {
258 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
259 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
260 continue;
261 }
262 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
263 callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
264 }
265
266 return true;
267 }
268
269 void InsertHelperFunctions() {
270 for (const auto& [name, contents] : HELPER_FUNCTIONS) {
271 helpers[name] = local_memory.size();
272 local_memory.insert(local_memory.end(), contents.begin(), contents.end());
273 }
274 }
275
276 void InsertStack() {
277 const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
278 local_memory.size()};
279 local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
280 top_of_stack = local_memory.size();
281 heap_pointer = top_of_stack;
282 }
283
284 void MapProcessMemory(VAddr dest_address, std::size_t size) {
285 mapped_ranges.add(IntervalType{dest_address, dest_address + size});
286 }
287
288 void PushArgument(const void* data, size_t size) {
289 const size_t num_words = Common::DivCeil(size, sizeof(u64));
290 const size_t current_pos = argument_stack.size();
291 argument_stack.insert(argument_stack.end(), num_words, 0);
292 std::memcpy(argument_stack.data() + current_pos, data, size);
293 }
294
295 void SetupArguments() {
296 for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
297 jit->SetRegister(i, argument_stack[i]);
298 }
299 if (argument_stack.size() > 8) {
300 const VAddr new_sp = Common::AlignDown(
301 top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
302 for (size_t i = 8; i < argument_stack.size(); i++) {
303 callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
304 }
305 jit->SetSP(new_sp);
306 }
307 argument_stack.clear();
308 heap_pointer = top_of_stack;
309 }
310
311 u64 CallFunction(VAddr func) {
312 jit->SetRegister(30, helpers["_stop"]);
313 jit->SetSP(top_of_stack);
314 SetupArguments();
315
316 jit->SetPC(func);
317 jit->Run();
318 return jit->GetRegister(0);
319 }
320
321 VAddr GetHelper(const std::string& name) {
322 return helpers[name];
323 }
324
325 VAddr AddHeap(const void* data, size_t size) {
326 const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
327 if (heap_pointer + num_bytes > local_memory.size()) {
328 local_memory.insert(local_memory.end(),
329 (heap_pointer + num_bytes) - local_memory.size(), 0);
330 }
331 const VAddr location{heap_pointer};
332 std::memcpy(local_memory.data() + location, data, size);
333 heap_pointer += num_bytes;
334 return location;
335 }
336
337 void GetHeap(VAddr location, void* data, size_t size) {
338 std::memcpy(data, local_memory.data() + location, size);
339 }
340
341 std::unique_ptr<DynarmicCallbacks64> callbacks;
342 std::vector<u8> local_memory;
343 std::vector<u64> argument_stack;
344 IntervalSet mapped_ranges;
345 Dynarmic::A64::UserConfig user_config;
346 std::unique_ptr<Dynarmic::A64::Jit> jit;
347 std::map<std::string, VAddr, std::less<>> helpers;
348 Core::Memory::Memory& memory;
349 VAddr top_of_stack;
350 VAddr heap_pointer;
351};
352
353void DynarmicCallbacks64::CallSVC(u32 swi) {
354 switch (swi) {
355 case 0:
356 parent.jit->HaltExecution();
357 break;
358
359 case 1: {
360 // X0 contains a char* for a symbol to resolve
361 std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
362 const auto helper{parent.helpers[name]};
363
364 if (helper != 0) {
365 parent.jit->SetRegister(0, helper);
366 } else {
367 LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
368 parent.jit->SetRegister(0, parent.helpers["_panic"]);
369 }
370 break;
371 }
372
373 case 2:
374 default:
375 LOG_CRITICAL(Service_JIT, "plugin panicked!");
376 parent.jit->HaltExecution();
377 break;
378 }
379}
380
381void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
382 LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
383 parent.jit->HaltExecution();
384}
385
386void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
387 LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
388 parent.jit->HaltExecution();
389}
390
391JITContext::JITContext(Core::Memory::Memory& memory)
392 : impl{std::make_unique<JITContextImpl>(memory)} {}
393
394JITContext::~JITContext() {}
395
396bool JITContext::LoadNRO(std::span<const u8> data) {
397 return impl->LoadNRO(data);
398}
399
400void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
401 impl->MapProcessMemory(dest_address, size);
402}
403
404u64 JITContext::CallFunction(VAddr func) {
405 return impl->CallFunction(func);
406}
407
408void JITContext::PushArgument(const void* data, size_t size) {
409 impl->PushArgument(data, size);
410}
411
412VAddr JITContext::GetHelper(const std::string& name) {
413 return impl->GetHelper(name);
414}
415
416VAddr JITContext::AddHeap(const void* data, size_t size) {
417 return impl->AddHeap(data, size);
418}
419
420void JITContext::GetHeap(VAddr location, void* data, size_t size) {
421 impl->GetHeap(location, data, size);
422}
423
424} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h
new file mode 100644
index 000000000..d8bf76cff
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.h
@@ -0,0 +1,65 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace Service::JIT {
18
19class JITContextImpl;
20
21class JITContext {
22public:
23 explicit JITContext(Core::Memory::Memory& memory);
24 ~JITContext();
25
26 [[nodiscard]] bool LoadNRO(std::span<const u8> data);
27 void MapProcessMemory(VAddr dest_address, std::size_t size);
28
29 template <typename T, typename... Ts>
30 u64 CallFunction(VAddr func, T argument, Ts... rest) {
31 static_assert(std::is_trivially_copyable_v<T>);
32 PushArgument(&argument, sizeof(argument));
33
34 if constexpr (sizeof...(rest) > 0) {
35 return CallFunction(func, rest...);
36 } else {
37 return CallFunction(func);
38 }
39 }
40
41 u64 CallFunction(VAddr func);
42 VAddr GetHelper(const std::string& name);
43
44 template <typename T>
45 VAddr AddHeap(T argument) {
46 return AddHeap(&argument, sizeof(argument));
47 }
48 VAddr AddHeap(const void* data, size_t size);
49
50 template <typename T>
51 T GetHeap(VAddr location) {
52 static_assert(std::is_trivially_copyable_v<T>);
53 T result;
54 GetHeap(location, &result, sizeof(result));
55 return result;
56 }
57 void GetHeap(VAddr location, void* data, size_t size);
58
59private:
60 std::unique_ptr<JITContextImpl> impl;
61
62 void PushArgument(const void* data, size_t size);
63};
64
65} // namespace Service::JIT
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index b8c2c6e51..ff0bbb788 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -17,21 +17,12 @@ namespace Service::KernelHelpers {
17 17
18ServiceContext::ServiceContext(Core::System& system_, std::string name_) 18ServiceContext::ServiceContext(Core::System& system_, std::string name_)
19 : kernel(system_.Kernel()) { 19 : kernel(system_.Kernel()) {
20
21 // Create a resource limit for the process.
22 const auto physical_memory_size =
23 kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
24 auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
25
26 // Create the process. 20 // Create the process.
27 process = Kernel::KProcess::Create(kernel); 21 process = Kernel::KProcess::Create(kernel);
28 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), 22 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
29 Kernel::KProcess::ProcessType::KernelInternal, 23 Kernel::KProcess::ProcessType::KernelInternal,
30 resource_limit) 24 kernel.GetSystemResourceLimit())
31 .IsSuccess()); 25 .IsSuccess());
32
33 // Close reference to our resource limit, as the process opens one.
34 resource_limit->Close();
35} 26}
36 27
37ServiceContext::~ServiceContext() { 28ServiceContext::~ServiceContext() {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9fc7bb1b1..42f9cf811 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -12,7 +12,6 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/k_page_table.h" 14#include "core/hle/kernel/k_page_table.h"
15#include "core/hle/kernel/k_system_control.h"
16#include "core/hle/kernel/svc_results.h" 15#include "core/hle/kernel/svc_results.h"
17#include "core/hle/kernel/svc_types.h" 16#include "core/hle/kernel/svc_types.h"
18#include "core/hle/service/ldr/ldr.h" 17#include "core/hle/service/ldr/ldr.h"
@@ -161,7 +160,8 @@ public:
161 160
162class RelocatableObject final : public ServiceFramework<RelocatableObject> { 161class RelocatableObject final : public ServiceFramework<RelocatableObject> {
163public: 162public:
164 explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { 163 explicit RelocatableObject(Core::System& system_)
164 : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
165 // clang-format off 165 // clang-format off
166 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
167 {0, &RelocatableObject::LoadModule, "LoadModule"}, 167 {0, &RelocatableObject::LoadModule, "LoadModule"},
@@ -288,7 +288,7 @@ public:
288 } 288 }
289 289
290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { 290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
291 constexpr std::size_t padding_size{4 * Kernel::PageSize}; 291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
292 const auto start_info{page_table.QueryInfo(start - 1)}; 292 const auto start_info{page_table.QueryInfo(start - 1)};
293 293
294 if (start_info.state != Kernel::KMemoryState::Free) { 294 if (start_info.state != Kernel::KMemoryState::Free) {
@@ -308,31 +308,69 @@ public:
308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); 308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
309 } 309 }
310 310
311 VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { 311 ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
312 VAddr addr{}; 312 size = Common::AlignUp(size, Kernel::PageSize);
313 const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> 313 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
314 Kernel::PageBits}; 314
315 do { 315 const auto is_region_available = [&](VAddr addr) {
316 addr = page_table.GetAliasCodeRegionStart() + 316 const auto end_addr = addr + size;
317 (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); 317 while (addr < end_addr) {
318 } while (!page_table.IsInsideAddressSpace(addr, size) || 318 if (system.Memory().IsValidVirtualAddress(addr)) {
319 page_table.IsInsideHeapRegion(addr, size) || 319 return false;
320 page_table.IsInsideAliasRegion(addr, size)); 320 }
321 return addr; 321
322 if (!page_table.IsInsideAddressSpace(out_addr, size)) {
323 return false;
324 }
325
326 if (page_table.IsInsideHeapRegion(out_addr, size)) {
327 return false;
328 }
329
330 if (page_table.IsInsideAliasRegion(out_addr, size)) {
331 return false;
332 }
333
334 addr += Kernel::PageSize;
335 }
336 return true;
337 };
338
339 bool succeeded = false;
340 const auto map_region_end =
341 page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
342 while (current_map_addr < map_region_end) {
343 if (is_region_available(current_map_addr)) {
344 succeeded = true;
345 break;
346 }
347 current_map_addr += 0x100000;
348 }
349
350 if (!succeeded) {
351 UNREACHABLE_MSG("Out of address space!");
352 return Kernel::ResultOutOfMemory;
353 }
354
355 out_addr = current_map_addr;
356 current_map_addr += size;
357
358 return ResultSuccess;
322 } 359 }
323 360
324 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, 361 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
325 u64 size) const { 362 auto& page_table{process->PageTable()};
363 VAddr addr{};
364
326 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 365 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
327 auto& page_table{process->PageTable()}; 366 R_TRY(GetAvailableMapRegion(page_table, size, addr));
328 const VAddr addr{GetRandomMapRegion(page_table, size)};
329 const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
330 367
368 const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
331 if (result == Kernel::ResultInvalidCurrentMemory) { 369 if (result == Kernel::ResultInvalidCurrentMemory) {
332 continue; 370 continue;
333 } 371 }
334 372
335 CASCADE_CODE(result); 373 R_TRY(result);
336 374
337 if (ValidateRegionForMap(page_table, addr, size)) { 375 if (ValidateRegionForMap(page_table, addr, size)) {
338 return addr; 376 return addr;
@@ -343,7 +381,7 @@ public:
343 } 381 }
344 382
345 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, 383 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
346 VAddr bss_addr, std::size_t bss_size, std::size_t size) const { 384 VAddr bss_addr, std::size_t bss_size, std::size_t size) {
347 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 385 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
348 auto& page_table{process->PageTable()}; 386 auto& page_table{process->PageTable()};
349 VAddr addr{}; 387 VAddr addr{};
@@ -352,8 +390,12 @@ public:
352 390
353 if (bss_size) { 391 if (bss_size) {
354 auto block_guard = detail::ScopeExit([&] { 392 auto block_guard = detail::ScopeExit([&] {
355 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); 393 page_table.UnmapCodeMemory(
356 page_table.UnmapCodeMemory(addr, nro_addr, nro_size); 394 addr + nro_size, bss_addr, bss_size,
395 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
396 page_table.UnmapCodeMemory(
397 addr, nro_addr, nro_size,
398 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
357 }); 399 });
358 400
359 const ResultCode result{ 401 const ResultCode result{
@@ -533,17 +575,21 @@ public:
533 auto& page_table{system.CurrentProcess()->PageTable()}; 575 auto& page_table{system.CurrentProcess()->PageTable()};
534 576
535 if (info.bss_size != 0) { 577 if (info.bss_size != 0) {
536 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + 578 CASCADE_CODE(page_table.UnmapCodeMemory(
537 info.ro_size + info.data_size, 579 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
538 info.bss_address, info.bss_size)); 580 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
539 } 581 }
540 582
541 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, 583 CASCADE_CODE(page_table.UnmapCodeMemory(
542 info.src_addr + info.text_size + info.ro_size, 584 info.nro_address + info.text_size + info.ro_size,
543 info.data_size)); 585 info.src_addr + info.text_size + info.ro_size, info.data_size,
544 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, 586 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
545 info.src_addr + info.text_size, info.ro_size)); 587 CASCADE_CODE(page_table.UnmapCodeMemory(
546 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); 588 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
590 CASCADE_CODE(page_table.UnmapCodeMemory(
591 info.nro_address, info.src_addr, info.text_size,
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
547 return ResultSuccess; 593 return ResultSuccess;
548 } 594 }
549 595
@@ -597,6 +643,7 @@ public:
597 LOG_WARNING(Service_LDR, "(STUBBED) called"); 643 LOG_WARNING(Service_LDR, "(STUBBED) called");
598 644
599 initialized = true; 645 initialized = true;
646 current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
600 647
601 IPC::ResponseBuilder rb{ctx, 2}; 648 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(ResultSuccess); 649 rb.Push(ResultSuccess);
@@ -607,6 +654,7 @@ private:
607 654
608 std::map<VAddr, NROInfo> nro; 655 std::map<VAddr, NROInfo> nro;
609 std::map<VAddr, std::vector<SHA256Hash>> nrr; 656 std::map<VAddr, std::vector<SHA256Hash>> nrr;
657 VAddr current_map_addr{};
610 658
611 bool IsValidNROHash(const SHA256Hash& hash) const { 659 bool IsValidNROHash(const SHA256Hash& hash) const {
612 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { 660 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 0a57c3cde..188231615 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -12,7 +12,6 @@
12#include "core/hle/service/acc/profile_manager.h" 12#include "core/hle/service/acc/profile_manager.h"
13#include "core/hle/service/mii/mii_manager.h" 13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/mii/raw_data.h" 14#include "core/hle/service/mii/raw_data.h"
15#include "core/hle/service/mii/types.h"
16 15
17namespace Service::Mii { 16namespace Service::Mii {
18 17
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 6999d15b1..5d134c425 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -1,315 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector> 7#include <vector>
9#include "common/bit_field.h" 8
10#include "common/common_funcs.h"
11#include "common/uuid.h"
12#include "core/hle/result.h" 9#include "core/hle/result.h"
13#include "core/hle/service/mii/types.h" 10#include "core/hle/service/mii/types.h"
14 11
15namespace Service::Mii { 12namespace Service::Mii {
16 13
17enum class Source : u32 {
18 Database = 0,
19 Default = 1,
20 Account = 2,
21 Friend = 3,
22};
23
24enum class SourceFlag : u32 {
25 None = 0,
26 Database = 1 << 0,
27 Default = 1 << 1,
28};
29DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
30
31struct MiiInfo {
32 Common::UUID uuid;
33 std::array<char16_t, 11> name;
34 u8 font_region;
35 u8 favorite_color;
36 u8 gender;
37 u8 height;
38 u8 build;
39 u8 type;
40 u8 region_move;
41 u8 faceline_type;
42 u8 faceline_color;
43 u8 faceline_wrinkle;
44 u8 faceline_make;
45 u8 hair_type;
46 u8 hair_color;
47 u8 hair_flip;
48 u8 eye_type;
49 u8 eye_color;
50 u8 eye_scale;
51 u8 eye_aspect;
52 u8 eye_rotate;
53 u8 eye_x;
54 u8 eye_y;
55 u8 eyebrow_type;
56 u8 eyebrow_color;
57 u8 eyebrow_scale;
58 u8 eyebrow_aspect;
59 u8 eyebrow_rotate;
60 u8 eyebrow_x;
61 u8 eyebrow_y;
62 u8 nose_type;
63 u8 nose_scale;
64 u8 nose_y;
65 u8 mouth_type;
66 u8 mouth_color;
67 u8 mouth_scale;
68 u8 mouth_aspect;
69 u8 mouth_y;
70 u8 beard_color;
71 u8 beard_type;
72 u8 mustache_type;
73 u8 mustache_scale;
74 u8 mustache_y;
75 u8 glasses_type;
76 u8 glasses_color;
77 u8 glasses_scale;
78 u8 glasses_y;
79 u8 mole_type;
80 u8 mole_scale;
81 u8 mole_x;
82 u8 mole_y;
83 u8 padding;
84
85 std::u16string Name() const;
86};
87static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
88static_assert(std::has_unique_object_representations_v<MiiInfo>,
89 "All bits of MiiInfo must contribute to its value.");
90
91#pragma pack(push, 4)
92
93struct MiiInfoElement {
94 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
95
96 MiiInfo info{};
97 Source source{};
98};
99static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
100
101struct MiiStoreBitFields {
102 union {
103 u32 word_0{};
104
105 BitField<0, 8, u32> hair_type;
106 BitField<8, 7, u32> height;
107 BitField<15, 1, u32> mole_type;
108 BitField<16, 7, u32> build;
109 BitField<23, 1, HairFlip> hair_flip;
110 BitField<24, 7, u32> hair_color;
111 BitField<31, 1, u32> type;
112 };
113
114 union {
115 u32 word_1{};
116
117 BitField<0, 7, u32> eye_color;
118 BitField<7, 1, Gender> gender;
119 BitField<8, 7, u32> eyebrow_color;
120 BitField<16, 7, u32> mouth_color;
121 BitField<24, 7, u32> beard_color;
122 };
123
124 union {
125 u32 word_2{};
126
127 BitField<0, 7, u32> glasses_color;
128 BitField<8, 6, u32> eye_type;
129 BitField<14, 2, u32> region_move;
130 BitField<16, 6, u32> mouth_type;
131 BitField<22, 2, FontRegion> font_region;
132 BitField<24, 5, u32> eye_y;
133 BitField<29, 3, u32> glasses_scale;
134 };
135
136 union {
137 u32 word_3{};
138
139 BitField<0, 5, u32> eyebrow_type;
140 BitField<5, 3, MustacheType> mustache_type;
141 BitField<8, 5, u32> nose_type;
142 BitField<13, 3, BeardType> beard_type;
143 BitField<16, 5, u32> nose_y;
144 BitField<21, 3, u32> mouth_aspect;
145 BitField<24, 5, u32> mouth_y;
146 BitField<29, 3, u32> eyebrow_aspect;
147 };
148
149 union {
150 u32 word_4{};
151
152 BitField<0, 5, u32> mustache_y;
153 BitField<5, 3, u32> eye_rotate;
154 BitField<8, 5, u32> glasses_y;
155 BitField<13, 3, u32> eye_aspect;
156 BitField<16, 5, u32> mole_x;
157 BitField<21, 3, u32> eye_scale;
158 BitField<24, 5, u32> mole_y;
159 };
160
161 union {
162 u32 word_5{};
163
164 BitField<0, 5, u32> glasses_type;
165 BitField<8, 4, u32> favorite_color;
166 BitField<12, 4, u32> faceline_type;
167 BitField<16, 4, u32> faceline_color;
168 BitField<20, 4, u32> faceline_wrinkle;
169 BitField<24, 4, u32> faceline_makeup;
170 BitField<28, 4, u32> eye_x;
171 };
172
173 union {
174 u32 word_6{};
175
176 BitField<0, 4, u32> eyebrow_scale;
177 BitField<4, 4, u32> eyebrow_rotate;
178 BitField<8, 4, u32> eyebrow_x;
179 BitField<12, 4, u32> eyebrow_y;
180 BitField<16, 4, u32> nose_scale;
181 BitField<20, 4, u32> mouth_scale;
182 BitField<24, 4, u32> mustache_scale;
183 BitField<28, 4, u32> mole_scale;
184 };
185};
186static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
187static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
188 "MiiStoreBitFields is not trivially copyable.");
189
190struct MiiStoreData {
191 using Name = std::array<char16_t, 10>;
192
193 MiiStoreData();
194 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
195 const Common::UUID& user_id);
196
197 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
198 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
199 // not suitable for our uses.
200 struct {
201 std::array<u8, 0x1C> data{};
202 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
203
204 Name name{};
205 Common::UUID uuid{};
206 } data;
207
208 u16 data_crc{};
209 u16 device_crc{};
210};
211static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
212
213struct MiiStoreDataElement {
214 MiiStoreData data{};
215 Source source{};
216};
217static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
218
219struct MiiDatabase {
220 u32 magic{}; // 'NFDB'
221 std::array<MiiStoreData, 0x64> miis{};
222 INSERT_PADDING_BYTES(1);
223 u8 count{};
224 u16 crc{};
225};
226static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
227
228struct RandomMiiValues {
229 std::array<u8, 0xbc> values{};
230};
231static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
232
233struct RandomMiiData4 {
234 Gender gender{};
235 Age age{};
236 Race race{};
237 u32 values_count{};
238 std::array<u32, 47> values{};
239};
240static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
241
242struct RandomMiiData3 {
243 u32 arg_1;
244 u32 arg_2;
245 u32 values_count;
246 std::array<u32, 47> values{};
247};
248static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
249
250struct RandomMiiData2 {
251 u32 arg_1;
252 u32 values_count;
253 std::array<u32, 47> values{};
254};
255static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
256
257struct DefaultMii {
258 u32 face_type{};
259 u32 face_color{};
260 u32 face_wrinkle{};
261 u32 face_makeup{};
262 u32 hair_type{};
263 u32 hair_color{};
264 u32 hair_flip{};
265 u32 eye_type{};
266 u32 eye_color{};
267 u32 eye_scale{};
268 u32 eye_aspect{};
269 u32 eye_rotate{};
270 u32 eye_x{};
271 u32 eye_y{};
272 u32 eyebrow_type{};
273 u32 eyebrow_color{};
274 u32 eyebrow_scale{};
275 u32 eyebrow_aspect{};
276 u32 eyebrow_rotate{};
277 u32 eyebrow_x{};
278 u32 eyebrow_y{};
279 u32 nose_type{};
280 u32 nose_scale{};
281 u32 nose_y{};
282 u32 mouth_type{};
283 u32 mouth_color{};
284 u32 mouth_scale{};
285 u32 mouth_aspect{};
286 u32 mouth_y{};
287 u32 mustache_type{};
288 u32 beard_type{};
289 u32 beard_color{};
290 u32 mustache_scale{};
291 u32 mustache_y{};
292 u32 glasses_type{};
293 u32 glasses_color{};
294 u32 glasses_scale{};
295 u32 glasses_y{};
296 u32 mole_type{};
297 u32 mole_scale{};
298 u32 mole_x{};
299 u32 mole_y{};
300 u32 height{};
301 u32 weight{};
302 Gender gender{};
303 u32 favorite_color{};
304 u32 region{};
305 FontRegion font_region{};
306 u32 type{};
307 INSERT_PADDING_WORDS(5);
308};
309static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
310
311#pragma pack(pop)
312
313// The Mii manager is responsible for loading and storing the Miis to the database in NAND along 14// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
314// with providing an easy interface for HLE emulation of the mii service. 15// with providing an easy interface for HLE emulation of the mii service.
315class MiiManager { 16class MiiManager {
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
index bd90c2162..2e39c0d4f 100644
--- a/src/core/hle/service/mii/raw_data.h
+++ b/src/core/hle/service/mii/raw_data.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8 8
9#include "core/hle/service/mii/mii_manager.h" 9#include "core/hle/service/mii/types.h"
10 10
11namespace Service::Mii::RawData { 11namespace Service::Mii::RawData {
12 12
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
index d65a1055e..5580b8c6a 100644
--- a/src/core/hle/service/mii/types.h
+++ b/src/core/hle/service/mii/types.h
@@ -1,11 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <type_traits>
9
10#include "common/bit_field.h"
7#include "common/common_funcs.h" 11#include "common/common_funcs.h"
8#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/uuid.h"
9 14
10namespace Service::Mii { 15namespace Service::Mii {
11 16
@@ -25,7 +30,11 @@ enum class BeardType : u32 {
25 Beard5, 30 Beard5,
26}; 31};
27 32
28enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache }; 33enum class BeardAndMustacheFlag : u32 {
34 Beard = 1,
35 Mustache,
36 All = Beard | Mustache,
37};
29DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); 38DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
30 39
31enum class FontRegion : u32 { 40enum class FontRegion : u32 {
@@ -64,4 +73,298 @@ enum class Race : u32 {
64 All, 73 All,
65}; 74};
66 75
76enum class Source : u32 {
77 Database = 0,
78 Default = 1,
79 Account = 2,
80 Friend = 3,
81};
82
83enum class SourceFlag : u32 {
84 None = 0,
85 Database = 1 << 0,
86 Default = 1 << 1,
87};
88DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
89
90struct MiiInfo {
91 Common::UUID uuid;
92 std::array<char16_t, 11> name;
93 u8 font_region;
94 u8 favorite_color;
95 u8 gender;
96 u8 height;
97 u8 build;
98 u8 type;
99 u8 region_move;
100 u8 faceline_type;
101 u8 faceline_color;
102 u8 faceline_wrinkle;
103 u8 faceline_make;
104 u8 hair_type;
105 u8 hair_color;
106 u8 hair_flip;
107 u8 eye_type;
108 u8 eye_color;
109 u8 eye_scale;
110 u8 eye_aspect;
111 u8 eye_rotate;
112 u8 eye_x;
113 u8 eye_y;
114 u8 eyebrow_type;
115 u8 eyebrow_color;
116 u8 eyebrow_scale;
117 u8 eyebrow_aspect;
118 u8 eyebrow_rotate;
119 u8 eyebrow_x;
120 u8 eyebrow_y;
121 u8 nose_type;
122 u8 nose_scale;
123 u8 nose_y;
124 u8 mouth_type;
125 u8 mouth_color;
126 u8 mouth_scale;
127 u8 mouth_aspect;
128 u8 mouth_y;
129 u8 beard_color;
130 u8 beard_type;
131 u8 mustache_type;
132 u8 mustache_scale;
133 u8 mustache_y;
134 u8 glasses_type;
135 u8 glasses_color;
136 u8 glasses_scale;
137 u8 glasses_y;
138 u8 mole_type;
139 u8 mole_scale;
140 u8 mole_x;
141 u8 mole_y;
142 u8 padding;
143};
144static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
145static_assert(std::has_unique_object_representations_v<MiiInfo>,
146 "All bits of MiiInfo must contribute to its value.");
147
148#pragma pack(push, 4)
149
150struct MiiInfoElement {
151 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
152
153 MiiInfo info{};
154 Source source{};
155};
156static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
157
158struct MiiStoreBitFields {
159 union {
160 u32 word_0{};
161
162 BitField<0, 8, u32> hair_type;
163 BitField<8, 7, u32> height;
164 BitField<15, 1, u32> mole_type;
165 BitField<16, 7, u32> build;
166 BitField<23, 1, HairFlip> hair_flip;
167 BitField<24, 7, u32> hair_color;
168 BitField<31, 1, u32> type;
169 };
170
171 union {
172 u32 word_1{};
173
174 BitField<0, 7, u32> eye_color;
175 BitField<7, 1, Gender> gender;
176 BitField<8, 7, u32> eyebrow_color;
177 BitField<16, 7, u32> mouth_color;
178 BitField<24, 7, u32> beard_color;
179 };
180
181 union {
182 u32 word_2{};
183
184 BitField<0, 7, u32> glasses_color;
185 BitField<8, 6, u32> eye_type;
186 BitField<14, 2, u32> region_move;
187 BitField<16, 6, u32> mouth_type;
188 BitField<22, 2, FontRegion> font_region;
189 BitField<24, 5, u32> eye_y;
190 BitField<29, 3, u32> glasses_scale;
191 };
192
193 union {
194 u32 word_3{};
195
196 BitField<0, 5, u32> eyebrow_type;
197 BitField<5, 3, MustacheType> mustache_type;
198 BitField<8, 5, u32> nose_type;
199 BitField<13, 3, BeardType> beard_type;
200 BitField<16, 5, u32> nose_y;
201 BitField<21, 3, u32> mouth_aspect;
202 BitField<24, 5, u32> mouth_y;
203 BitField<29, 3, u32> eyebrow_aspect;
204 };
205
206 union {
207 u32 word_4{};
208
209 BitField<0, 5, u32> mustache_y;
210 BitField<5, 3, u32> eye_rotate;
211 BitField<8, 5, u32> glasses_y;
212 BitField<13, 3, u32> eye_aspect;
213 BitField<16, 5, u32> mole_x;
214 BitField<21, 3, u32> eye_scale;
215 BitField<24, 5, u32> mole_y;
216 };
217
218 union {
219 u32 word_5{};
220
221 BitField<0, 5, u32> glasses_type;
222 BitField<8, 4, u32> favorite_color;
223 BitField<12, 4, u32> faceline_type;
224 BitField<16, 4, u32> faceline_color;
225 BitField<20, 4, u32> faceline_wrinkle;
226 BitField<24, 4, u32> faceline_makeup;
227 BitField<28, 4, u32> eye_x;
228 };
229
230 union {
231 u32 word_6{};
232
233 BitField<0, 4, u32> eyebrow_scale;
234 BitField<4, 4, u32> eyebrow_rotate;
235 BitField<8, 4, u32> eyebrow_x;
236 BitField<12, 4, u32> eyebrow_y;
237 BitField<16, 4, u32> nose_scale;
238 BitField<20, 4, u32> mouth_scale;
239 BitField<24, 4, u32> mustache_scale;
240 BitField<28, 4, u32> mole_scale;
241 };
242};
243static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
244static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
245 "MiiStoreBitFields is not trivially copyable.");
246
247struct MiiStoreData {
248 using Name = std::array<char16_t, 10>;
249
250 MiiStoreData();
251 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
252 const Common::UUID& user_id);
253
254 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
255 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
256 // not suitable for our uses.
257 struct {
258 std::array<u8, 0x1C> data{};
259 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
260
261 Name name{};
262 Common::UUID uuid{};
263 } data;
264
265 u16 data_crc{};
266 u16 device_crc{};
267};
268static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
269
270struct MiiStoreDataElement {
271 MiiStoreData data{};
272 Source source{};
273};
274static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
275
276struct MiiDatabase {
277 u32 magic{}; // 'NFDB'
278 std::array<MiiStoreData, 0x64> miis{};
279 INSERT_PADDING_BYTES(1);
280 u8 count{};
281 u16 crc{};
282};
283static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
284
285struct RandomMiiValues {
286 std::array<u8, 0xbc> values{};
287};
288static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
289
290struct RandomMiiData4 {
291 Gender gender{};
292 Age age{};
293 Race race{};
294 u32 values_count{};
295 std::array<u32, 47> values{};
296};
297static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
298
299struct RandomMiiData3 {
300 u32 arg_1;
301 u32 arg_2;
302 u32 values_count;
303 std::array<u32, 47> values{};
304};
305static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
306
307struct RandomMiiData2 {
308 u32 arg_1;
309 u32 values_count;
310 std::array<u32, 47> values{};
311};
312static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
313
314struct DefaultMii {
315 u32 face_type{};
316 u32 face_color{};
317 u32 face_wrinkle{};
318 u32 face_makeup{};
319 u32 hair_type{};
320 u32 hair_color{};
321 u32 hair_flip{};
322 u32 eye_type{};
323 u32 eye_color{};
324 u32 eye_scale{};
325 u32 eye_aspect{};
326 u32 eye_rotate{};
327 u32 eye_x{};
328 u32 eye_y{};
329 u32 eyebrow_type{};
330 u32 eyebrow_color{};
331 u32 eyebrow_scale{};
332 u32 eyebrow_aspect{};
333 u32 eyebrow_rotate{};
334 u32 eyebrow_x{};
335 u32 eyebrow_y{};
336 u32 nose_type{};
337 u32 nose_scale{};
338 u32 nose_y{};
339 u32 mouth_type{};
340 u32 mouth_color{};
341 u32 mouth_scale{};
342 u32 mouth_aspect{};
343 u32 mouth_y{};
344 u32 mustache_type{};
345 u32 beard_type{};
346 u32 beard_color{};
347 u32 mustache_scale{};
348 u32 mustache_y{};
349 u32 glasses_type{};
350 u32 glasses_color{};
351 u32 glasses_scale{};
352 u32 glasses_y{};
353 u32 mole_type{};
354 u32 mole_scale{};
355 u32 mole_x{};
356 u32 mole_y{};
357 u32 height{};
358 u32 weight{};
359 Gender gender{};
360 u32 favorite_color{};
361 u32 region{};
362 FontRegion font_region{};
363 u32 type{};
364 INSERT_PADDING_WORDS(5);
365};
366static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
367
368#pragma pack(pop)
369
67} // namespace Service::Mii 370} // namespace Service::Mii
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 513107715..dab99b675 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -12,6 +12,7 @@
12#include "core/hid/hid_types.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp.h" 16#include "core/hle/service/nfp/nfp.h"
16#include "core/hle/service/nfp/nfp_user.h" 17#include "core/hle/service/nfp/nfp_user.h"
17 18
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 022f13b29..ab652f635 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "core/hle/service/kernel_helpers.h" 11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/mii/mii_manager.h" 12#include "core/hle/service/mii/types.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14 14
15namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index a253dd066..edb576ad3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -5,7 +5,6 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_event.h" 7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/service/kernel_helpers.h" 8#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/nifm/nifm.h" 9#include "core/hle/service/nifm/nifm.h"
11#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
index 36ce46353..3a83d0698 100644
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/ns/pdm_qry.h" 10#include "core/hle/service/ns/pdm_qry.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 12
14namespace Service::NS { 13namespace Service::NS {
15 14
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 68f1e9060..9fad45fe1 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
38void nvdisp_disp0::OnOpen(DeviceFD fd) {} 38void nvdisp_disp0::OnOpen(DeviceFD fd) {}
39void nvdisp_disp0::OnClose(DeviceFD fd) {} 39void nvdisp_disp0::OnClose(DeviceFD fd) {}
40 40
41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
42 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
43 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect) {
44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
45 LOG_TRACE(Service, 45 LOG_TRACE(Service,
46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
47 addr, offset, width, height, stride, format); 47 addr, offset, width, height, stride, format);
48 48
49 const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); 49 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
50 const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); 50 stride, format, transform, crop_rect};
51 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
52 stride, pixel_format, transform_flags, crop_rect};
53 51
54 system.GetPerfStats().EndSystemFrame(); 52 system.GetPerfStats().EndSystemFrame();
55 system.GPU().SwapBuffers(&framebuffer); 53 system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index de01e1d5f..30b5da429 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -9,7 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 12#include "core/hle/service/nvflinger/buffer_transform_flags.h"
13#include "core/hle/service/nvflinger/pixel_format.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -31,8 +32,8 @@ public:
31 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
32 33
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 34 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 35 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 NVFlinger::BufferQueue::BufferTransformFlags transform, 36 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 37 const Common::Rectangle<int>& crop_rect);
37 38
38private: 39private:
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index f9b82b504..44c54c665 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
134 } 134 }
135 135
136 EventState status = events_interface.status[event_id]; 136 EventState status = events_interface.status[event_id];
137 const bool bad_parameter = status != EventState::Free && status != EventState::Registered; 137 const bool bad_parameter = status == EventState::Busy;
138 if (bad_parameter) { 138 if (bad_parameter) {
139 std::memcpy(output.data(), &params, sizeof(params)); 139 std::memcpy(output.data(), &params, sizeof(params));
140 return NvResult::BadParameter; 140 return NvResult::BadParameter;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 898d00a17..f434f6929 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -5,6 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8
9#include "common/common_funcs.h"
8#include "common/common_types.h" 10#include "common/common_types.h"
9#include "common/swap.h" 11#include "common/swap.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a043e386..dde5b1507 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
187 return NvResult::Success; 187 return NvResult::Success;
188} 188}
189 189
190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
191 return { 191 return {
192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
193 Tegra::SubmissionMode::Increasing), 193 Tegra::SubmissionMode::Increasing),
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
198 }; 198 };
199} 199}
200 200
201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { 201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
202 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 203 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 204 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
204 Tegra::SubmissionMode::Increasing), 205 Tegra::SubmissionMode::Increasing),
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
213 return result; 214 return result;
214} 215}
215 216
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, 217static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
217 u32 add_increment) { 218 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 219 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 220 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index f27a82bff..b2e943e45 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 13#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -108,7 +109,7 @@ private:
108 static_assert(sizeof(IoctlGetErrorNotification) == 16, 109 static_assert(sizeof(IoctlGetErrorNotification) == 16,
109 "IoctlGetErrorNotification is incorrect size"); 110 "IoctlGetErrorNotification is incorrect size");
110 111
111 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 112 static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
112 113
113 struct IoctlAllocGpfifoEx { 114 struct IoctlAllocGpfifoEx {
114 u32_le num_entries{}; 115 u32_le num_entries{};
@@ -126,7 +127,7 @@ private:
126 u32_le num_entries{}; // in 127 u32_le num_entries{}; // in
127 u32_le flags{}; // in 128 u32_le flags{}; // in
128 u32_le unk0{}; // in (1 works) 129 u32_le unk0{}; // in (1 works)
129 Fence fence_out{}; // out 130 NvFence fence_out{}; // out
130 u32_le unk1{}; // in 131 u32_le unk1{}; // in
131 u32_le unk2{}; // in 132 u32_le unk2{}; // in
132 u32_le unk3{}; // in 133 u32_le unk3{}; // in
@@ -152,13 +153,13 @@ private:
152 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
153 BitField<8, 1, u32_le> increment; // increment the returned fence 154 BitField<8, 1, u32_le> increment; // increment the returned fence
154 } flags; 155 } flags;
155 Fence fence_out{}; // returned new fence object for others to wait on 156 NvFence fence_out{}; // returned new fence object for others to wait on
156 157
157 u32 AddIncrementValue() const { 158 u32 AddIncrementValue() const {
158 return flags.add_increment.Value() << 1; 159 return flags.add_increment.Value() << 1;
159 } 160 }
160 }; 161 };
161 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
162 "IoctlSubmitGpfifo is incorrect size"); 163 "IoctlSubmitGpfifo is incorrect size");
163 164
164 struct IoctlGetWaitbase { 165 struct IoctlGetWaitbase {
@@ -193,7 +194,7 @@ private:
193 194
194 std::shared_ptr<nvmap> nvmap_dev; 195 std::shared_ptr<nvmap> nvmap_dev;
195 SyncpointManager& syncpoint_manager; 196 SyncpointManager& syncpoint_manager;
196 Fence channel_fence; 197 NvFence channel_fence;
197}; 198};
198 199
199} // namespace Service::Nvidia::Devices 200} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 5ab221fc1..3069c3c80 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -16,17 +16,11 @@ using DeviceFD = s32;
16 16
17constexpr DeviceFD INVALID_NVDRV_FD = -1; 17constexpr DeviceFD INVALID_NVDRV_FD = -1;
18 18
19struct Fence { 19struct NvFence {
20 s32 id; 20 s32 id;
21 u32 value; 21 u32 value;
22}; 22};
23 23static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
24static_assert(sizeof(Fence) == 8, "Fence has wrong size");
25
26struct MultiFence {
27 u32 num_fences;
28 std::array<Fence, 4> fences;
29};
30 24
31enum class NvResult : u32 { 25enum class NvResult : u32 {
32 Success = 0x0, 26 Success = 0x0,
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a5af5b785..11cf6c0d1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -12,6 +12,7 @@
12#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "core/hle/service/nvdrv/syncpoint_manager.h" 14#include "core/hle/service/nvdrv/syncpoint_manager.h"
15#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
16 17
17namespace Core { 18namespace Core {
@@ -37,7 +38,7 @@ class nvdevice;
37/// Represents an Nvidia event 38/// Represents an Nvidia event
38struct NvEvent { 39struct NvEvent {
39 Kernel::KEvent* event{}; 40 Kernel::KEvent* event{};
40 Fence fence{}; 41 NvFence fence{};
41}; 42};
42 43
43struct EventInterface { 44struct EventInterface {
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..8467b50e4 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
26 rb.Push<DeviceFD>(0); 26 rb.Push<DeviceFD>(0);
27 rb.PushEnum(NvResult::NotInitialized); 27 rb.PushEnum(NvResult::NotInitialized);
28 28
29 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 29 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
30 return; 30 return;
31 } 31 }
32 32
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
61 61
62 if (!is_initialized) { 62 if (!is_initialized) {
63 ServiceError(ctx, NvResult::NotInitialized); 63 ServiceError(ctx, NvResult::NotInitialized);
64 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 64 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
65 return; 65 return;
66 } 66 }
67 67
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
87 87
88 if (!is_initialized) { 88 if (!is_initialized) {
89 ServiceError(ctx, NvResult::NotInitialized); 89 ServiceError(ctx, NvResult::NotInitialized);
90 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 90 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
91 return; 91 return;
92 } 92 }
93 93
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
114 114
115 if (!is_initialized) { 115 if (!is_initialized) {
116 ServiceError(ctx, NvResult::NotInitialized); 116 ServiceError(ctx, NvResult::NotInitialized);
117 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 117 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
118 return; 118 return;
119 } 119 }
120 120
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
139 139
140 if (!is_initialized) { 140 if (!is_initialized) {
141 ServiceError(ctx, NvResult::NotInitialized); 141 ServiceError(ctx, NvResult::NotInitialized);
142 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 142 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
143 return; 143 return;
144 } 144 }
145 145
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
170 170
171 if (!is_initialized) { 171 if (!is_initialized) {
172 ServiceError(ctx, NvResult::NotInitialized); 172 ServiceError(ctx, NvResult::NotInitialized);
173 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 173 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
174 return; 174 return;
175 } 175 }
176 176
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
230} 230}
231 231
232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) 232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
233 : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { 233 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
234 static const FunctionInfo functions[] = { 234 static const FunctionInfo functions[] = {
235 {0, &NVDRV::Open, "Open"}, 235 {0, &NVDRV::Open, "Open"},
236 {1, &NVDRV::Ioctl1, "Ioctl"}, 236 {1, &NVDRV::Ioctl1, "Ioctl"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
new file mode 100644
index 000000000..7d0d4d819
--- /dev/null
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -0,0 +1,42 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
6
7#pragma once
8
9#include "common/common_types.h"
10
11namespace Kernel {
12class HLERequestContext;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::android {
17
18enum class TransactionId {
19 RequestBuffer = 1,
20 SetBufferCount = 2,
21 DequeueBuffer = 3,
22 DetachBuffer = 4,
23 DetachNextBuffer = 5,
24 AttachBuffer = 6,
25 QueueBuffer = 7,
26 CancelBuffer = 8,
27 Query = 9,
28 Connect = 10,
29 Disconnect = 11,
30 AllocateBuffers = 13,
31 SetPreallocatedBuffer = 14,
32 GetBufferHistory = 17,
33};
34
35class IBinder {
36public:
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
40};
41
42} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h
new file mode 100644
index 000000000..64b82b851
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item.h
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "core/hle/service/nvflinger/ui/fence.h"
14#include "core/hle/service/nvflinger/window.h"
15
16namespace Service::android {
17
18class GraphicBuffer;
19
20class BufferItem final {
21public:
22 constexpr BufferItem() = default;
23
24 std::shared_ptr<GraphicBuffer> graphic_buffer;
25 Fence fence;
26 Common::Rectangle<s32> crop;
27 NativeWindowTransform transform{};
28 u32 scaling_mode{};
29 s64 timestamp{};
30 bool is_auto_timestamp{};
31 u64 frame_number{};
32
33 // The default value for buf, used to indicate this doesn't correspond to a slot.
34 static constexpr s32 INVALID_BUFFER_SLOT = -1;
35 union {
36 s32 slot{INVALID_BUFFER_SLOT};
37 s32 buf;
38 };
39
40 bool is_droppable{};
41 bool acquire_called{};
42 bool transform_to_display_inverse{};
43 s32 swap_interval{};
44};
45
46} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
new file mode 100644
index 000000000..d7ee5362b
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -0,0 +1,59 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_item_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
12
13namespace Service::android {
14
15BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
16 : ConsumerBase{std::move(consumer_)} {}
17
18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
19 bool wait_for_fence) {
20 if (!item) {
21 return Status::BadValue;
22 }
23
24 std::scoped_lock lock{mutex};
25
26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
27 if (status != Status::NoBufferAvailable) {
28 LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
29 }
30 return status;
31 }
32
33 if (wait_for_fence) {
34 UNIMPLEMENTED();
35 }
36
37 item->graphic_buffer = slots[item->slot].graphic_buffer;
38
39 return Status::NoError;
40}
41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
43 std::scoped_lock lock{mutex};
44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
46 status != Status::NoError) {
47 LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
48 }
49
50 if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
51 status != Status::NoError) {
52 LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
53 return status;
54 }
55
56 return Status::NoError;
57}
58
59} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h
new file mode 100644
index 000000000..536db81e2
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/consumer_base.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19
20class BufferItemConsumer final : public ConsumerBase {
21public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
26};
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
deleted file mode 100644
index 5fead6d1b..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvflinger/buffer_queue.h"
14
15namespace Service::NVFlinger {
16
17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
18 KernelHelpers::ServiceContext& service_context_)
19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
21}
22
23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
26
27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
28 ASSERT(slot < buffer_slots);
29 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
30
31 {
32 std::unique_lock lock{free_buffers_mutex};
33 free_buffers.push_back(slot);
34 }
35 free_buffers_condition.notify_one();
36
37 buffers[slot] = {
38 .slot = slot,
39 .status = Buffer::Status::Free,
40 .igbp_buffer = igbp_buffer,
41 .transform = {},
42 .crop_rect = {},
43 .swap_interval = 0,
44 .multi_fence = {},
45 };
46
47 buffer_wait_event->GetWritableEvent().Signal();
48}
49
50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
51 u32 height) {
52 // Wait for first request before trying to dequeue
53 {
54 std::unique_lock lock{free_buffers_mutex};
55 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
56 }
57
58 if (!is_connect) {
59 // Buffer was disconnected while the thread was blocked, this is most likely due to
60 // emulation being stopped
61 return std::nullopt;
62 }
63
64 std::unique_lock lock{free_buffers_mutex};
65
66 auto f_itr = free_buffers.begin();
67 auto slot = buffers.size();
68
69 while (f_itr != free_buffers.end()) {
70 const Buffer& buffer = buffers[*f_itr];
71 if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
72 buffer.igbp_buffer.height == height) {
73 slot = *f_itr;
74 free_buffers.erase(f_itr);
75 break;
76 }
77 ++f_itr;
78 }
79 if (slot == buffers.size()) {
80 return std::nullopt;
81 }
82 buffers[slot].status = Buffer::Status::Dequeued;
83 return {{buffers[slot].slot, &buffers[slot].multi_fence}};
84}
85
86const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
87 ASSERT(slot < buffers.size());
88 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
89 ASSERT(buffers[slot].slot == slot);
90
91 return buffers[slot].igbp_buffer;
92}
93
94void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
95 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
96 Service::Nvidia::MultiFence& multi_fence) {
97 ASSERT(slot < buffers.size());
98 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
99 ASSERT(buffers[slot].slot == slot);
100
101 buffers[slot].status = Buffer::Status::Queued;
102 buffers[slot].transform = transform;
103 buffers[slot].crop_rect = crop_rect;
104 buffers[slot].swap_interval = swap_interval;
105 buffers[slot].multi_fence = multi_fence;
106 std::unique_lock lock{queue_sequence_mutex};
107 queue_sequence.push_back(slot);
108}
109
110void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
111 ASSERT(slot < buffers.size());
112 ASSERT(buffers[slot].status != Buffer::Status::Free);
113 ASSERT(buffers[slot].slot == slot);
114
115 buffers[slot].status = Buffer::Status::Free;
116 buffers[slot].multi_fence = multi_fence;
117 buffers[slot].swap_interval = 0;
118
119 {
120 std::unique_lock lock{free_buffers_mutex};
121 free_buffers.push_back(slot);
122 }
123 free_buffers_condition.notify_one();
124
125 buffer_wait_event->GetWritableEvent().Signal();
126}
127
128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
129 std::unique_lock lock{queue_sequence_mutex};
130 std::size_t buffer_slot = buffers.size();
131 // Iterate to find a queued buffer matching the requested slot.
132 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
133 const auto slot = static_cast<std::size_t>(queue_sequence.front());
134 ASSERT(slot < buffers.size());
135 if (buffers[slot].status == Buffer::Status::Queued) {
136 ASSERT(buffers[slot].slot == slot);
137 buffer_slot = slot;
138 }
139 queue_sequence.pop_front();
140 }
141 if (buffer_slot == buffers.size()) {
142 return std::nullopt;
143 }
144 buffers[buffer_slot].status = Buffer::Status::Acquired;
145 return {{buffers[buffer_slot]}};
146}
147
148void BufferQueue::ReleaseBuffer(u32 slot) {
149 ASSERT(slot < buffers.size());
150 ASSERT(buffers[slot].status == Buffer::Status::Acquired);
151 ASSERT(buffers[slot].slot == slot);
152
153 buffers[slot].status = Buffer::Status::Free;
154 {
155 std::unique_lock lock{free_buffers_mutex};
156 free_buffers.push_back(slot);
157 }
158 free_buffers_condition.notify_one();
159
160 buffer_wait_event->GetWritableEvent().Signal();
161}
162
163void BufferQueue::Connect() {
164 std::unique_lock lock{queue_sequence_mutex};
165 queue_sequence.clear();
166 is_connect = true;
167}
168
169void BufferQueue::Disconnect() {
170 buffers.fill({});
171 {
172 std::unique_lock lock{queue_sequence_mutex};
173 queue_sequence.clear();
174 }
175 buffer_wait_event->GetWritableEvent().Signal();
176 is_connect = false;
177 free_buffers_condition.notify_one();
178}
179
180u32 BufferQueue::Query(QueryType type) {
181 LOG_WARNING(Service, "(STUBBED) called type={}", type);
182
183 switch (type) {
184 case QueryType::NativeWindowFormat:
185 return static_cast<u32>(PixelFormat::RGBA8888);
186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight:
188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
193 }
194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
195 return 0;
196}
197
198Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
199 return buffer_wait_event->GetWritableEvent();
200}
201
202Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
203 return buffer_wait_event->GetReadableEvent();
204}
205
206} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
deleted file mode 100644
index f2a579133..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ /dev/null
@@ -1,154 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <condition_variable>
8#include <list>
9#include <mutex>
10#include <optional>
11
12#include "common/common_funcs.h"
13#include "common/math_util.h"
14#include "common/swap.h"
15#include "core/hle/kernel/k_event.h"
16#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Kernel {
20class KernelCore;
21class KEvent;
22class KReadableEvent;
23class KWritableEvent;
24} // namespace Kernel
25
26namespace Service::KernelHelpers {
27class ServiceContext;
28} // namespace Service::KernelHelpers
29
30namespace Service::NVFlinger {
31
32constexpr u32 buffer_slots = 0x40;
33struct IGBPBuffer {
34 u32_le magic;
35 u32_le width;
36 u32_le height;
37 u32_le stride;
38 u32_le format;
39 u32_le usage;
40 INSERT_PADDING_WORDS(1);
41 u32_le index;
42 INSERT_PADDING_WORDS(3);
43 u32_le gpu_buffer_id;
44 INSERT_PADDING_WORDS(6);
45 u32_le external_format;
46 INSERT_PADDING_WORDS(10);
47 u32_le nvmap_handle;
48 u32_le offset;
49 INSERT_PADDING_WORDS(60);
50};
51
52static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
53
54class BufferQueue final {
55public:
56 enum class QueryType {
57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
65 };
66
67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
68 KernelHelpers::ServiceContext& service_context_);
69 ~BufferQueue();
70
71 enum class BufferTransformFlags : u32 {
72 /// No transform flags are set
73 Unset = 0x00,
74 /// Flip source image horizontally (around the vertical axis)
75 FlipH = 0x01,
76 /// Flip source image vertically (around the horizontal axis)
77 FlipV = 0x02,
78 /// Rotate source image 90 degrees clockwise
79 Rotate90 = 0x04,
80 /// Rotate source image 180 degrees
81 Rotate180 = 0x03,
82 /// Rotate source image 270 degrees clockwise
83 Rotate270 = 0x07,
84 };
85
86 enum class PixelFormat : u32 {
87 RGBA8888 = 1,
88 RGBX8888 = 2,
89 RGB888 = 3,
90 RGB565 = 4,
91 BGRA8888 = 5,
92 RGBA5551 = 6,
93 RRGBA4444 = 7,
94 };
95
96 struct Buffer {
97 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
98
99 u32 slot;
100 Status status = Status::Free;
101 IGBPBuffer igbp_buffer;
102 BufferTransformFlags transform;
103 Common::Rectangle<int> crop_rect;
104 u32 swap_interval;
105 Service::Nvidia::MultiFence multi_fence;
106 };
107
108 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
109 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
110 u32 height);
111 const IGBPBuffer& RequestBuffer(u32 slot) const;
112 void QueueBuffer(u32 slot, BufferTransformFlags transform,
113 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
114 Service::Nvidia::MultiFence& multi_fence);
115 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
116 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
117 void ReleaseBuffer(u32 slot);
118 void Connect();
119 void Disconnect();
120 u32 Query(QueryType type);
121
122 u32 GetId() const {
123 return id;
124 }
125
126 bool IsConnected() const {
127 return is_connect;
128 }
129
130 Kernel::KWritableEvent& GetWritableBufferWaitEvent();
131
132 Kernel::KReadableEvent& GetBufferWaitEvent();
133
134private:
135 BufferQueue(const BufferQueue&) = delete;
136
137 u32 id{};
138 u64 layer_id{};
139 std::atomic_bool is_connect{};
140
141 std::list<u32> free_buffers;
142 std::array<Buffer, buffer_slots> buffers;
143 std::list<u32> queue_sequence;
144 Kernel::KEvent* buffer_wait_event{};
145
146 std::mutex free_buffers_mutex;
147 std::condition_variable free_buffers_condition;
148
149 std::mutex queue_sequence_mutex;
150
151 KernelHelpers::ServiceContext& service_context;
152};
153
154} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
new file mode 100644
index 000000000..3ab9a8c05
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,231 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6
7#include "common/logging/log.h"
8#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h"
12
13namespace Service::android {
14
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
16 : core{std::move(core_)}, slots{core->slots} {}
17
18BufferQueueConsumer::~BufferQueueConsumer() = default;
19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
21 std::chrono::nanoseconds expected_present) {
22 std::scoped_lock lock{core->mutex};
23
24 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
25 const s32 num_acquired_buffers{
26 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
27 return slot.buffer_state == BufferState::Acquired;
28 }))};
29
30 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
31 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
32 num_acquired_buffers, core->max_acquired_buffer_count);
33 return Status::InvalidOperation;
34 }
35
36 // Check if the queue is empty.
37 if (core->queue.empty()) {
38 return Status::NoBufferAvailable;
39 }
40
41 auto front(core->queue.begin());
42
43 // If expected_present is specified, we may not want to return a buffer yet.
44 if (expected_present.count() != 0) {
45 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
46
47 // The expected_present argument indicates when the buffer is expected to be presented
48 // on-screen.
49 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
50 const auto& buffer_item{core->queue[1]};
51
52 // If entry[1] is timely, drop entry[0] (and repeat).
53 const auto desired_present = buffer_item.timestamp;
54 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
55 desired_present > expected_present.count()) {
56 // This buffer is set to display in the near future, or desired_present is garbage.
57 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
58 expected_present.count());
59 break;
60 }
61
62 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
63 expected_present.count(), core->queue.size());
64
65 if (core->StillTracking(*front)) {
66 // Front buffer is still in mSlots, so mark the slot as free
67 slots[front->slot].buffer_state = BufferState::Free;
68 }
69
70 core->queue.erase(front);
71 front = core->queue.begin();
72 }
73
74 // See if the front buffer is ready to be acquired.
75 const auto desired_present = front->timestamp;
76 if (desired_present > expected_present.count() &&
77 desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
78 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
79 expected_present.count());
80 return Status::PresentLater;
81 }
82
83 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
84 expected_present.count());
85 }
86
87 const auto slot = front->slot;
88 *out_buffer = *front;
89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) {
103 out_buffer->graphic_buffer = nullptr;
104 }
105
106 core->queue.erase(front);
107
108 // We might have freed a slot while dropping old buffers, or the producer may be blocked
109 // waiting for the number of buffers in the queue to decrease.
110 core->SignalDequeueCondition();
111
112 return Status::NoError;
113}
114
115Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
116 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
117 LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
118 return Status::BadValue;
119 }
120
121 std::shared_ptr<IProducerListener> listener;
122 {
123 std::scoped_lock lock{core->mutex};
124
125 // If the frame number has changed because the buffer has been reallocated, we can ignore
126 // this ReleaseBuffer for the old buffer.
127 if (frame_number != slots[slot].frame_number) {
128 return Status::StaleBufferSlot;
129 }
130
131 // Make sure this buffer hasn't been queued while acquired by the consumer.
132 auto current(core->queue.begin());
133 while (current != core->queue.end()) {
134 if (current->slot == slot) {
135 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
136 slot);
137 return Status::BadValue;
138 }
139 ++current;
140 }
141
142 if (slots[slot].buffer_state == BufferState::Acquired) {
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152
153 slots[slot].needs_cleanup_on_release = false;
154
155 return Status::StaleBufferSlot;
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162
163 core->SignalDequeueCondition();
164 }
165
166 // Call back without lock held
167 if (listener != nullptr) {
168 listener->OnBufferReleased();
169 }
170
171 return Status::NoError;
172}
173
174Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
175 bool controlled_by_app) {
176 if (consumer_listener == nullptr) {
177 LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
178 return Status::BadValue;
179 }
180
181 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
182
183 std::scoped_lock lock{core->mutex};
184
185 if (core->is_abandoned) {
186 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
187 return Status::NoInit;
188 }
189
190 core->consumer_listener = consumer_listener;
191 core->consumer_controlled_by_app = controlled_by_app;
192
193 return Status::NoError;
194}
195
196Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
197 if (out_slot_mask == nullptr) {
198 LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
199 return Status::BadValue;
200 }
201
202 std::scoped_lock lock{core->mutex};
203
204 if (core->is_abandoned) {
205 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
206 return Status::NoInit;
207 }
208
209 u64 mask = 0;
210 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
211 if (!slots[s].acquire_called) {
212 mask |= (1ULL << s);
213 }
214 }
215
216 // Remove from the mask queued buffers for which acquire has been called, since the consumer
217 // will not receive their buffer addresses and so must retain their cached information
218 auto current(core->queue.begin());
219 while (current != core->queue.end()) {
220 if (current->acquire_called) {
221 mask &= ~(1ULL << current->slot);
222 }
223 ++current;
224 }
225
226 LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
227 *out_slot_mask = mask;
228 return Status::NoError;
229}
230
231} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
new file mode 100644
index 000000000..8a047fe06
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19class BufferQueueCore;
20class IConsumerListener;
21
22class BufferQueueConsumer final {
23public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer();
26
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status GetReleasedBuffers(u64* out_slot_mask);
31
32private:
33 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots;
35};
36
37} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
new file mode 100644
index 000000000..ec5aabaeb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -0,0 +1,117 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
6
7#include "common/assert.h"
8
9#include "core/hle/service/nvflinger/buffer_queue_core.h"
10
11namespace Service::android {
12
13BufferQueueCore::BufferQueueCore() = default;
14
15BufferQueueCore::~BufferQueueCore() = default;
16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_condition.notify_all();
27}
28
29bool BufferQueueCore::WaitForDequeueCondition() {
30 if (is_shutting_down) {
31 return false;
32 }
33
34 dequeue_condition.wait(mutex);
35
36 return true;
37}
38
39s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
40 // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
41 if (!use_async_buffer) {
42 return max_acquired_buffer_count;
43 }
44
45 if (dequeue_buffer_cannot_block || async) {
46 return max_acquired_buffer_count + 1;
47 }
48
49 return max_acquired_buffer_count;
50}
51
52s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
53 return GetMinUndequeuedBufferCountLocked(async) + 1;
54}
55
56s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
57 const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
58 auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
59
60 if (override_max_buffer_count != 0) {
61 ASSERT(override_max_buffer_count >= min_buffer_count);
62 max_buffer_count = override_max_buffer_count;
63 }
64
65 // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
66 // need to have their slots preserved.
67 for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
68 const auto state = slots[slot].buffer_state;
69 if (state == BufferState::Queued || state == BufferState::Dequeued) {
70 max_buffer_count = slot + 1;
71 }
72 }
73
74 return max_buffer_count;
75}
76
77s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
78 return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
79 [](const auto& slot) { return slot.is_preallocated; }));
80}
81
82void BufferQueueCore::FreeBufferLocked(s32 slot) {
83 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
84
85 slots[slot].graphic_buffer.reset();
86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false;
94 slots[slot].fence = Fence::NoFence();
95}
96
97void BufferQueueCore::FreeAllBuffersLocked() {
98 buffer_has_been_queued = false;
99
100 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
101 FreeBufferLocked(slot);
102 }
103}
104
105bool BufferQueueCore::StillTracking(const BufferItem& item) const {
106 const BufferSlot& slot = slots[item.slot];
107
108 return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
109}
110
111void BufferQueueCore::WaitWhileAllocatingLocked() const {
112 while (is_allocating) {
113 is_allocating_condition.wait(mutex);
114 }
115}
116
117} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
new file mode 100644
index 000000000..e4e0937cb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -0,0 +1,79 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
6
7#pragma once
8
9#include <condition_variable>
10#include <list>
11#include <memory>
12#include <mutex>
13#include <set>
14#include <vector>
15
16#include "core/hle/service/nvflinger/buffer_item.h"
17#include "core/hle/service/nvflinger/buffer_queue_defs.h"
18#include "core/hle/service/nvflinger/pixel_format.h"
19#include "core/hle/service/nvflinger/status.h"
20#include "core/hle/service/nvflinger/window.h"
21
22namespace Service::android {
23
24class IConsumerListener;
25class IProducerListener;
26
27class BufferQueueCore final {
28 friend class BufferQueueProducer;
29 friend class BufferQueueConsumer;
30
31public:
32 static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
33
34 BufferQueueCore();
35 ~BufferQueueCore();
36
37 void NotifyShutdown();
38
39private:
40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition();
42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const;
45 s32 GetMaxBufferCountLocked(bool async) const;
46 s32 GetPreallocatedBufferCountLocked() const;
47 void FreeBufferLocked(s32 slot);
48 void FreeAllBuffersLocked();
49 bool StillTracking(const BufferItem& item) const;
50 void WaitWhileAllocatingLocked() const;
51
52private:
53 mutable std::mutex mutex;
54 bool is_abandoned{};
55 bool consumer_controlled_by_app{};
56 std::shared_ptr<IConsumerListener> consumer_listener;
57 u32 consumer_usage_bit{};
58 NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
59 std::shared_ptr<IProducerListener> connected_producer_listener;
60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition;
64 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
67 u32 default_width{1};
68 u32 default_height{1};
69 s32 default_max_buffer_count{2};
70 const s32 max_acquired_buffer_count{}; // This is always zero on HOS
71 bool buffer_has_been_queued{};
72 u64 frame_counter{};
73 u32 transform_hint{};
74 bool is_allocating{};
75 mutable std::condition_variable_any is_allocating_condition;
76 bool is_shutting_down{};
77};
78
79} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h
new file mode 100644
index 000000000..387d3d36a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/buffer_slot.h"
13
14namespace Service::android::BufferQueueDefs {
15
16// BufferQueue will keep track of at most this value of buffers.
17constexpr s32 NUM_BUFFER_SLOTS = 64;
18
19using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
20
21} // namespace Service::android::BufferQueueDefs
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
new file mode 100644
index 000000000..6f604a88e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,923 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h"
21#include "core/hle/service/nvflinger/parcel.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/nvflinger/window.h"
24#include "core/hle/service/vi/vi.h"
25
26namespace Service::android {
27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32}
33
34BufferQueueProducer::~BufferQueueProducer() {
35 service_context.CloseEvent(buffer_wait_event);
36}
37
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40
41 std::scoped_lock lock{core->mutex};
42
43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
45 return Status::NoInit;
46 }
47 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
48 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
49 BufferQueueDefs::NUM_BUFFER_SLOTS);
50 return Status::BadValue;
51 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
52 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
53 slots[slot].buffer_state);
54 return Status::BadValue;
55 }
56
57 slots[slot].request_buffer_called = true;
58 *buf = slots[slot].graphic_buffer;
59
60 return Status::NoError;
61}
62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65
66 std::shared_ptr<IConsumerListener> listener;
67 {
68 std::scoped_lock lock{core->mutex};
69 core->WaitWhileAllocatingLocked();
70
71 if (core->is_abandoned) {
72 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
73 return Status::NoInit;
74 }
75
76 if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
77 LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
78 BufferQueueDefs::NUM_BUFFER_SLOTS);
79 return Status::BadValue;
80 }
81
82 // There must be no dequeued buffers when changing the buffer count.
83 for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
84 if (slots[s].buffer_state == BufferState::Dequeued) {
85 LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
86 return Status::BadValue;
87 }
88 }
89
90 if (buffer_count == 0) {
91 core->override_max_buffer_count = 0;
92 core->SignalDequeueCondition();
93 return Status::NoError;
94 }
95
96 const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
97 if (buffer_count < min_buffer_slots) {
98 LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
99 buffer_count, min_buffer_slots);
100 return Status::BadValue;
101 }
102
103 // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
104 // release all of its buffer references.
105 if (core->GetPreallocatedBufferCountLocked() <= 0) {
106 core->FreeAllBuffersLocked();
107 }
108
109 core->override_max_buffer_count = buffer_count;
110 core->SignalDequeueCondition();
111 buffer_wait_event->GetWritableEvent().Signal();
112 listener = core->consumer_listener;
113 }
114
115 // Call back without lock held
116 if (listener != nullptr) {
117 listener->OnBuffersReleased();
118 }
119
120 return Status::NoError;
121}
122
123Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
124 Status* return_flags) const {
125 bool try_again = true;
126
127 while (try_again) {
128 if (core->is_abandoned) {
129 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
130 return Status::NoInit;
131 }
132
133 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
134 if (async && core->override_max_buffer_count) {
135 if (core->override_max_buffer_count < max_buffer_count) {
136 LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
137 return Status::BadValue;
138 }
139 }
140
141 // Free up any buffers that are in slots beyond the max buffer count
142 for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
143 ASSERT(slots[s].buffer_state == BufferState::Free);
144 if (slots[s].graphic_buffer != nullptr) {
145 core->FreeBufferLocked(s);
146 *return_flags |= Status::ReleaseAllBuffers;
147 }
148 }
149
150 // Look for a free buffer to give to the client
151 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
152 s32 dequeued_count{};
153 s32 acquired_count{};
154 for (s32 s{}; s < max_buffer_count; ++s) {
155 switch (slots[s].buffer_state) {
156 case BufferState::Dequeued:
157 ++dequeued_count;
158 break;
159 case BufferState::Acquired:
160 ++acquired_count;
161 break;
162 case BufferState::Free:
163 // We return the oldest of the free buffers to avoid stalling the producer if
164 // possible, since the consumer may still have pending reads of in-flight buffers
165 if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
166 slots[s].frame_number < slots[*found].frame_number) {
167 *found = s;
168 }
169 break;
170 default:
171 break;
172 }
173 }
174
175 // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
176 // count
177 if (!core->override_max_buffer_count && dequeued_count) {
178 LOG_ERROR(Service_NVFlinger,
179 "can't dequeue multiple buffers without setting the buffer count");
180 return Status::InvalidOperation;
181 }
182
183 // See whether a buffer has been queued since the last SetBufferCount so we know whether to
184 // perform the min undequeued buffers check below
185 if (core->buffer_has_been_queued) {
186 // Make sure the producer is not trying to dequeue more buffers than allowed
187 const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
188 const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
189 if (new_undequeued_count < min_undequeued_count) {
190 LOG_ERROR(Service_NVFlinger,
191 "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
192 min_undequeued_count, dequeued_count, new_undequeued_count);
193 return Status::InvalidOperation;
194 }
195 }
196
197 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
198 // but we have many buffers in the queue. This can cause us to run out of memory if we
199 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
200 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
201 if (too_many_buffers) {
202 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
203 }
204
205 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
206 // buffer to be acquired or released, or for the max buffer count to change.
207 try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
208 if (try_again) {
209 // Return an error if we're in non-blocking mode (producer and consumer are controlled
210 // by the application).
211 if (core->dequeue_buffer_cannot_block &&
212 (acquired_count <= core->max_acquired_buffer_count)) {
213 return Status::WouldBlock;
214 }
215
216 if (!core->WaitForDequeueCondition()) {
217 // We are no longer running
218 return Status::NoError;
219 }
220 }
221 }
222
223 return Status::NoError;
224}
225
226Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
227 u32 height, PixelFormat format, u32 usage) {
228 LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
229 width, height, format, usage);
230
231 if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
232 LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
233 return Status::BadValue;
234 }
235
236 Status return_flags = Status::NoError;
237 bool attached_by_consumer = false;
238 {
239 std::scoped_lock lock{core->mutex};
240 core->WaitWhileAllocatingLocked();
241
242 if (format == PixelFormat::NoFormat) {
243 format = core->default_buffer_format;
244 }
245
246 // Enable the usage bits the consumer requested
247 usage |= core->consumer_usage_bit;
248
249 s32 found{};
250 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
251 if (status != Status::NoError) {
252 return status;
253 }
254
255 // This should not happen
256 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
257 LOG_ERROR(Service_NVFlinger, "no available buffer slots");
258 return Status::Busy;
259 }
260
261 *out_slot = found;
262
263 attached_by_consumer = slots[found].attached_by_consumer;
264
265 const bool use_default_size = !width && !height;
266 if (use_default_size) {
267 width = core->default_width;
268 height = core->default_height;
269 }
270
271 slots[found].buffer_state = BufferState::Dequeued;
272
273 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
274 if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
275 (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
276 slots[found].acquire_called = false;
277 slots[found].graphic_buffer = nullptr;
278 slots[found].request_buffer_called = false;
279 slots[found].fence = Fence::NoFence();
280
281 return_flags |= Status::BufferNeedsReallocation;
282 }
283
284 *out_fence = slots[found].fence;
285 slots[found].fence = Fence::NoFence();
286 }
287
288 if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
289 LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
290
291 auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
292 if (graphic_buffer == nullptr) {
293 LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
294 return Status::NoMemory;
295 }
296
297 {
298 std::scoped_lock lock{core->mutex};
299
300 if (core->is_abandoned) {
301 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
302 return Status::NoInit;
303 }
304
305 slots[*out_slot].frame_number = UINT32_MAX;
306 slots[*out_slot].graphic_buffer = graphic_buffer;
307 }
308 }
309
310 if (attached_by_consumer) {
311 return_flags |= Status::BufferNeedsReallocation;
312 }
313
314 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
315 slots[*out_slot].frame_number, return_flags);
316
317 return return_flags;
318}
319
320Status BufferQueueProducer::DetachBuffer(s32 slot) {
321 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
322
323 std::scoped_lock lock{core->mutex};
324
325 if (core->is_abandoned) {
326 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
327 return Status::NoInit;
328 }
329
330 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
331 LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
332 BufferQueueDefs::NUM_BUFFER_SLOTS);
333 return Status::BadValue;
334 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
335 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
336 slots[slot].buffer_state);
337 return Status::BadValue;
338 } else if (!slots[slot].request_buffer_called) {
339 LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
340 return Status::BadValue;
341 }
342
343 core->FreeBufferLocked(slot);
344 core->SignalDequeueCondition();
345
346 return Status::NoError;
347}
348
349Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
350 Fence* out_fence) {
351 if (out_buffer == nullptr) {
352 LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
353 return Status::BadValue;
354 } else if (out_fence == nullptr) {
355 LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
356 return Status::BadValue;
357 }
358
359 std::scoped_lock lock{core->mutex};
360 core->WaitWhileAllocatingLocked();
361
362 if (core->is_abandoned) {
363 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
364 return Status::NoInit;
365 }
366
367 // Find the oldest valid slot
368 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
369 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
370 if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
371 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
372 slots[s].frame_number < slots[found].frame_number) {
373 found = s;
374 }
375 }
376 }
377
378 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
379 return Status::NoMemory;
380 }
381
382 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
383
384 *out_buffer = slots[found].graphic_buffer;
385 *out_fence = slots[found].fence;
386
387 core->FreeBufferLocked(found);
388
389 return Status::NoError;
390}
391
392Status BufferQueueProducer::AttachBuffer(s32* out_slot,
393 const std::shared_ptr<GraphicBuffer>& buffer) {
394 if (out_slot == nullptr) {
395 LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
396 return Status::BadValue;
397 } else if (buffer == nullptr) {
398 LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
399 return Status::BadValue;
400 }
401
402 std::scoped_lock lock{core->mutex};
403 core->WaitWhileAllocatingLocked();
404
405 Status return_flags = Status::NoError;
406 s32 found{};
407
408 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
409 if (status != Status::NoError) {
410 return status;
411 }
412
413 // This should not happen
414 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
415 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
416 return Status::Busy;
417 }
418
419 *out_slot = found;
420
421 LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
422
423 slots[*out_slot].graphic_buffer = buffer;
424 slots[*out_slot].buffer_state = BufferState::Dequeued;
425 slots[*out_slot].fence = Fence::NoFence();
426 slots[*out_slot].request_buffer_called = true;
427
428 return return_flags;
429}
430
431Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
432 QueueBufferOutput* output) {
433 s64 timestamp{};
434 bool is_auto_timestamp{};
435 Common::Rectangle<s32> crop;
436 NativeWindowScalingMode scaling_mode{};
437 NativeWindowTransform transform;
438 u32 sticky_transform_{};
439 bool async{};
440 s32 swap_interval{};
441 Fence fence{};
442
443 input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
444 &sticky_transform_, &async, &swap_interval, &fence);
445
446 switch (scaling_mode) {
447 case NativeWindowScalingMode::Freeze:
448 case NativeWindowScalingMode::ScaleToWindow:
449 case NativeWindowScalingMode::ScaleCrop:
450 case NativeWindowScalingMode::NoScaleCrop:
451 break;
452 default:
453 LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
454 return Status::BadValue;
455 }
456
457 std::shared_ptr<IConsumerListener> frame_available_listener;
458 std::shared_ptr<IConsumerListener> frame_replaced_listener;
459 s32 callback_ticket{};
460 BufferItem item;
461
462 {
463 std::scoped_lock lock{core->mutex};
464
465 if (core->is_abandoned) {
466 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
467 return Status::NoInit;
468 }
469
470 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
471 if (async && core->override_max_buffer_count) {
472 if (core->override_max_buffer_count < max_buffer_count) {
473 LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
474 "buffer count override");
475 return Status::BadValue;
476 }
477 }
478
479 if (slot < 0 || slot >= max_buffer_count) {
480 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
481 max_buffer_count);
482 return Status::BadValue;
483 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
484 LOG_ERROR(Service_NVFlinger,
485 "slot {} is not owned by the producer "
486 "(state = {})",
487 slot, slots[slot].buffer_state);
488 return Status::BadValue;
489 } else if (!slots[slot].request_buffer_called) {
490 LOG_ERROR(Service_NVFlinger,
491 "slot {} was queued without requesting "
492 "a buffer",
493 slot);
494 return Status::BadValue;
495 }
496
497 LOG_DEBUG(Service_NVFlinger,
498 "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
499 core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
500 crop.Bottom(), transform, scaling_mode);
501
502 const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
503 Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
504 Common::Rectangle<s32> cropped_rect;
505 [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
506
507 if (cropped_rect != crop) {
508 LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
509 slot);
510 return Status::BadValue;
511 }
512
513 slots[slot].fence = fence;
514 slots[slot].buffer_state = BufferState::Queued;
515 ++core->frame_counter;
516 slots[slot].frame_number = core->frame_counter;
517
518 item.acquire_called = slots[slot].acquire_called;
519 item.graphic_buffer = slots[slot].graphic_buffer;
520 item.crop = crop;
521 item.transform = transform & ~NativeWindowTransform::InverseDisplay;
522 item.transform_to_display_inverse =
523 (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
524 item.scaling_mode = static_cast<u32>(scaling_mode);
525 item.timestamp = timestamp;
526 item.is_auto_timestamp = is_auto_timestamp;
527 item.frame_number = core->frame_counter;
528 item.slot = slot;
529 item.fence = fence;
530 item.is_droppable = core->dequeue_buffer_cannot_block || async;
531 item.swap_interval = swap_interval;
532
533 sticky_transform = sticky_transform_;
534
535 if (core->queue.empty()) {
536 // When the queue is empty, we can simply queue this buffer
537 core->queue.push_back(item);
538 frame_available_listener = core->consumer_listener;
539 } else {
540 // When the queue is not empty, we need to look at the front buffer
541 // state to see if we need to replace it
542 auto front(core->queue.begin());
543
544 if (front->is_droppable) {
545 // If the front queued buffer is still being tracked, we first
546 // mark it as freed
547 if (core->StillTracking(*front)) {
548 slots[front->slot].buffer_state = BufferState::Free;
549 // Reset the frame number of the freed buffer so that it is the first in line to
550 // be dequeued again
551 slots[front->slot].frame_number = 0;
552 }
553 // Overwrite the droppable buffer with the incoming one
554 *front = item;
555 frame_replaced_listener = core->consumer_listener;
556 } else {
557 core->queue.push_back(item);
558 frame_available_listener = core->consumer_listener;
559 }
560 }
561
562 core->buffer_has_been_queued = true;
563 core->SignalDequeueCondition();
564 output->Inflate(core->default_width, core->default_height, core->transform_hint,
565 static_cast<u32>(core->queue.size()));
566
567 // Take a ticket for the callback functions
568 callback_ticket = next_callback_ticket++;
569 }
570
571 // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
572 // consumer shouldn't need it
573 item.graphic_buffer.reset();
574 item.slot = BufferItem::INVALID_BUFFER_SLOT;
575
576 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
577 // ensure that callbacks occur in order
578 {
579 std::scoped_lock lock{callback_mutex};
580 while (callback_ticket != current_callback_ticket) {
581 callback_condition.wait(callback_mutex);
582 }
583
584 if (frame_available_listener != nullptr) {
585 frame_available_listener->OnFrameAvailable(item);
586 } else if (frame_replaced_listener != nullptr) {
587 frame_replaced_listener->OnFrameReplaced(item);
588 }
589
590 ++current_callback_ticket;
591 callback_condition.notify_all();
592 }
593
594 return Status::NoError;
595}
596
597void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
598 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
599
600 std::scoped_lock lock{core->mutex};
601
602 if (core->is_abandoned) {
603 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
604 return;
605 }
606
607 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
608 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
609 BufferQueueDefs::NUM_BUFFER_SLOTS);
610 return;
611 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
612 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
613 slots[slot].buffer_state);
614 return;
615 }
616
617 slots[slot].buffer_state = BufferState::Free;
618 slots[slot].frame_number = 0;
619 slots[slot].fence = fence;
620
621 core->SignalDequeueCondition();
622 buffer_wait_event->GetWritableEvent().Signal();
623}
624
625Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
626 std::scoped_lock lock{core->mutex};
627
628 if (out_value == nullptr) {
629 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
630 return Status::BadValue;
631 }
632
633 if (core->is_abandoned) {
634 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
635 return Status::NoInit;
636 }
637
638 u32 value{};
639 switch (what) {
640 case NativeWindow::Width:
641 value = core->default_width;
642 break;
643 case NativeWindow::Height:
644 value = core->default_height;
645 break;
646 case NativeWindow::Format:
647 value = static_cast<u32>(core->default_buffer_format);
648 break;
649 case NativeWindow::MinUndequeedBuffers:
650 value = core->GetMinUndequeuedBufferCountLocked(false);
651 break;
652 case NativeWindow::StickyTransform:
653 value = sticky_transform;
654 break;
655 case NativeWindow::ConsumerRunningBehind:
656 value = (core->queue.size() > 1);
657 break;
658 case NativeWindow::ConsumerUsageBits:
659 value = core->consumer_usage_bit;
660 break;
661 default:
662 UNREACHABLE();
663 return Status::BadValue;
664 }
665
666 LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
667
668 *out_value = static_cast<s32>(value);
669
670 return Status::NoError;
671}
672
673Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
674 NativeWindowApi api, bool producer_controlled_by_app,
675 QueueBufferOutput* output) {
676 std::scoped_lock lock{core->mutex};
677
678 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
679 producer_controlled_by_app);
680
681 if (core->is_abandoned) {
682 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
683 return Status::NoInit;
684 }
685
686 if (core->consumer_listener == nullptr) {
687 LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
688 return Status::NoInit;
689 }
690
691 if (output == nullptr) {
692 LOG_ERROR(Service_NVFlinger, "output was nullptr");
693 return Status::BadValue;
694 }
695
696 if (core->connected_api != NativeWindowApi::NoConnectedApi) {
697 LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
698 api);
699 return Status::BadValue;
700 }
701
702 Status status = Status::NoError;
703 switch (api) {
704 case NativeWindowApi::Egl:
705 case NativeWindowApi::Cpu:
706 case NativeWindowApi::Media:
707 case NativeWindowApi::Camera:
708 core->connected_api = api;
709 output->Inflate(core->default_width, core->default_height, core->transform_hint,
710 static_cast<u32>(core->queue.size()));
711 core->connected_producer_listener = listener;
712 break;
713 default:
714 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
715 status = Status::BadValue;
716 break;
717 }
718
719 core->buffer_has_been_queued = false;
720 core->dequeue_buffer_cannot_block =
721 core->consumer_controlled_by_app && producer_controlled_by_app;
722
723 return status;
724}
725
726Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
727 LOG_DEBUG(Service_NVFlinger, "api = {}", api);
728
729 Status status = Status::NoError;
730 std::shared_ptr<IConsumerListener> listener;
731
732 {
733 std::scoped_lock lock{core->mutex};
734
735 core->WaitWhileAllocatingLocked();
736
737 if (core->is_abandoned) {
738 // Disconnecting after the surface has been abandoned is a no-op.
739 return Status::NoError;
740 }
741
742 switch (api) {
743 case NativeWindowApi::Egl:
744 case NativeWindowApi::Cpu:
745 case NativeWindowApi::Media:
746 case NativeWindowApi::Camera:
747 if (core->connected_api == api) {
748 core->FreeAllBuffersLocked();
749 core->connected_producer_listener = nullptr;
750 core->connected_api = NativeWindowApi::NoConnectedApi;
751 core->SignalDequeueCondition();
752 buffer_wait_event->GetWritableEvent().Signal();
753 listener = core->consumer_listener;
754 } else {
755 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
756 core->connected_api, api);
757 status = Status::BadValue;
758 }
759 break;
760 default:
761 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
762 status = Status::BadValue;
763 break;
764 }
765 }
766
767 // Call back without lock held
768 if (listener != nullptr) {
769 listener->OnBuffersReleased();
770 }
771
772 return status;
773}
774
775Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
776 const std::shared_ptr<GraphicBuffer>& buffer) {
777 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
778
779 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
780 return Status::BadValue;
781 }
782
783 std::scoped_lock lock{core->mutex};
784
785 slots[slot] = {};
786 slots[slot].graphic_buffer = buffer;
787 slots[slot].frame_number = 0;
788
789 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
790 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
791 if (buffer) {
792 slots[slot].is_preallocated = true;
793
794 core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
795 core->default_width = buffer->Width();
796 core->default_height = buffer->Height();
797 core->default_buffer_format = buffer->Format();
798 }
799
800 core->SignalDequeueCondition();
801 buffer_wait_event->GetWritableEvent().Signal();
802
803 return Status::NoError;
804}
805
806void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
807 Status status{Status::NoError};
808 Parcel parcel_in{ctx.ReadBuffer()};
809 Parcel parcel_out{};
810
811 switch (code) {
812 case TransactionId::Connect: {
813 const auto enable_listener = parcel_in.Read<bool>();
814 const auto api = parcel_in.Read<NativeWindowApi>();
815 const auto producer_controlled_by_app = parcel_in.Read<bool>();
816
817 UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
818
819 std::shared_ptr<IProducerListener> listener;
820 QueueBufferOutput output{};
821
822 status = Connect(listener, api, producer_controlled_by_app, &output);
823
824 parcel_out.Write(output);
825 break;
826 }
827 case TransactionId::SetPreallocatedBuffer: {
828 const auto slot = parcel_in.Read<s32>();
829 const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
830
831 status = SetPreallocatedBuffer(slot, buffer);
832 break;
833 }
834 case TransactionId::DequeueBuffer: {
835 const auto is_async = parcel_in.Read<bool>();
836 const auto width = parcel_in.Read<u32>();
837 const auto height = parcel_in.Read<u32>();
838 const auto pixel_format = parcel_in.Read<PixelFormat>();
839 const auto usage = parcel_in.Read<u32>();
840
841 s32 slot{};
842 Fence fence{};
843
844 status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
845
846 parcel_out.Write(slot);
847 parcel_out.WriteObject(&fence);
848 break;
849 }
850 case TransactionId::RequestBuffer: {
851 const auto slot = parcel_in.Read<s32>();
852
853 std::shared_ptr<GraphicBuffer> buf;
854
855 status = RequestBuffer(slot, &buf);
856
857 parcel_out.WriteObject(buf);
858 break;
859 }
860 case TransactionId::QueueBuffer: {
861 const auto slot = parcel_in.Read<s32>();
862
863 QueueBufferInput input{parcel_in};
864 QueueBufferOutput output;
865
866 status = QueueBuffer(slot, input, &output);
867
868 parcel_out.Write(output);
869 break;
870 }
871 case TransactionId::Query: {
872 const auto what = parcel_in.Read<NativeWindow>();
873
874 s32 value{};
875
876 status = Query(what, &value);
877
878 parcel_out.Write(value);
879 break;
880 }
881 case TransactionId::CancelBuffer: {
882 const auto slot = parcel_in.Read<s32>();
883 const auto fence = parcel_in.ReadFlattened<Fence>();
884
885 CancelBuffer(slot, fence);
886 break;
887 }
888 case TransactionId::Disconnect: {
889 const auto api = parcel_in.Read<NativeWindowApi>();
890
891 status = Disconnect(api);
892 break;
893 }
894 case TransactionId::DetachBuffer: {
895 const auto slot = parcel_in.Read<s32>();
896
897 status = DetachBuffer(slot);
898 break;
899 }
900 case TransactionId::SetBufferCount: {
901 const auto buffer_count = parcel_in.Read<s32>();
902
903 status = SetBufferCount(buffer_count);
904 break;
905 }
906 case TransactionId::GetBufferHistory:
907 LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
908 break;
909 default:
910 ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
911 break;
912 }
913
914 parcel_out.Write(status);
915
916 ctx.WriteBuffer(parcel_out.Serialize());
917}
918
919Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
920 return buffer_wait_event->GetReadableEvent();
921}
922
923} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
new file mode 100644
index 000000000..c4ca68fd3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,83 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
6
7#pragma once
8
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12
13#include "common/common_funcs.h"
14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/binder.h"
16#include "core/hle/service/nvflinger/buffer_queue_defs.h"
17#include "core/hle/service/nvflinger/buffer_slot.h"
18#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
19#include "core/hle/service/nvflinger/pixel_format.h"
20#include "core/hle/service/nvflinger/status.h"
21#include "core/hle/service/nvflinger/window.h"
22
23namespace Kernel {
24class KernelCore;
25class KEvent;
26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel
29
30namespace Service::KernelHelpers {
31class ServiceContext;
32} // namespace Service::KernelHelpers
33
34namespace Service::android {
35
36class BufferQueueCore;
37class IProducerListener;
38
39class BufferQueueProducer final : public IBinder {
40public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
43 ~BufferQueueProducer();
44
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
46
47 Kernel::KReadableEvent& GetNativeHandle() override;
48
49public:
50 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
51 Status SetBufferCount(s32 buffer_count);
52 Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
53 u32 height, PixelFormat format, u32 usage);
54 Status DetachBuffer(s32 slot);
55 Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
56 Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
57 Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
58 void CancelBuffer(s32 slot, const Fence& fence);
59 Status Query(NativeWindow what, s32* out_value);
60 Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
61 bool producer_controlled_by_app, QueueBufferOutput* output);
62
63 Status Disconnect(NativeWindowApi api);
64 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
65
66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
70
71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context;
73
74 std::shared_ptr<BufferQueueCore> core;
75 BufferQueueDefs::SlotsType& slots;
76 u32 sticky_transform{};
77 std::mutex callback_mutex;
78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition;
81};
82
83} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
new file mode 100644
index 000000000..e3ea58910
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13
14namespace Service::android {
15
16class GraphicBuffer;
17
18enum class BufferState : u32 {
19 Free = 0,
20 Dequeued = 1,
21 Queued = 2,
22 Acquired = 3,
23};
24
25struct BufferSlot final {
26 constexpr BufferSlot() = default;
27
28 std::shared_ptr<GraphicBuffer> graphic_buffer;
29 BufferState buffer_state{BufferState::Free};
30 bool request_buffer_called{};
31 u64 frame_number{};
32 Fence fence;
33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{};
36 bool is_preallocated{};
37};
38
39} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h
new file mode 100644
index 000000000..e8e6300e3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h
@@ -0,0 +1,25 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class BufferTransformFlags : u32 {
11 /// No transform flags are set
12 Unset = 0x00,
13 /// Flip source image horizontally (around the vertical axis)
14 FlipH = 0x01,
15 /// Flip source image vertically (around the horizontal axis)
16 FlipV = 0x02,
17 /// Rotate source image 90 degrees clockwise
18 Rotate90 = 0x04,
19 /// Rotate source image 180 degrees
20 Rotate180 = 0x03,
21 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07,
23};
24
25} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
new file mode 100644
index 000000000..30fc21acc
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,133 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_core.h"
12#include "core/hle/service/nvflinger/consumer_base.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
14
15namespace Service::android {
16
17ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {}
19
20ConsumerBase::~ConsumerBase() {
21 std::scoped_lock lock{mutex};
22
23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
24}
25
26void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app);
28}
29
30void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
32
33 slots[slot_index].graphic_buffer = nullptr;
34 slots[slot_index].fence = Fence::NoFence();
35 slots[slot_index].frame_number = 0;
36}
37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 LOG_DEBUG(Service_NVFlinger, "called");
40}
41
42void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
43 LOG_DEBUG(Service_NVFlinger, "called");
44}
45
46void ConsumerBase::OnBuffersReleased() {
47 std::scoped_lock lock{mutex};
48
49 LOG_DEBUG(Service_NVFlinger, "called");
50
51 if (is_abandoned) {
52 // Nothing to do if we're already abandoned.
53 return;
54 }
55
56 u64 mask = 0;
57 consumer->GetReleasedBuffers(&mask);
58 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
59 if (mask & (1ULL << i)) {
60 FreeBufferLocked(i);
61 }
62 }
63}
64
65void ConsumerBase::OnSidebandStreamChanged() {}
66
67Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
68 Status err = consumer->AcquireBuffer(item, present_when);
69 if (err != Status::NoError) {
70 return err;
71 }
72
73 if (item->graphic_buffer != nullptr) {
74 slots[item->slot].graphic_buffer = item->graphic_buffer;
75 }
76
77 slots[item->slot].frame_number = item->frame_number;
78 slots[item->slot].fence = item->fence;
79
80 LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
81
82 return Status::NoError;
83}
84
85Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
86 const std::shared_ptr<GraphicBuffer> graphic_buffer,
87 const Fence& fence) {
88 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
89
90 // If consumer no longer tracks this graphic_buffer, we can safely
91 // drop this fence, as it will never be received by the producer.
92
93 if (!StillTracking(slot, graphic_buffer)) {
94 return Status::NoError;
95 }
96
97 slots[slot].fence = fence;
98
99 return Status::NoError;
100}
101
102Status ConsumerBase::ReleaseBufferLocked(s32 slot,
103 const std::shared_ptr<GraphicBuffer> graphic_buffer) {
104 // If consumer no longer tracks this graphic_buffer (we received a new
105 // buffer on the same slot), the buffer producer is definitely no longer
106 // tracking it.
107
108 if (!StillTracking(slot, graphic_buffer)) {
109 return Status::NoError;
110 }
111
112 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
113 Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
114 if (err == Status::StaleBufferSlot) {
115 FreeBufferLocked(slot);
116 }
117
118 slots[slot].fence = Fence::NoFence();
119
120 return err;
121}
122
123bool ConsumerBase::StillTracking(s32 slot,
124 const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
125 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
126 return false;
127 }
128
129 return (slots[slot].graphic_buffer != nullptr &&
130 slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
131}
132
133} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
new file mode 100644
index 000000000..736080e3a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,60 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
6
7#pragma once
8
9#include <array>
10#include <chrono>
11#include <memory>
12#include <mutex>
13
14#include "common/common_types.h"
15#include "core/hle/service/nvflinger/buffer_queue_defs.h"
16#include "core/hle/service/nvflinger/consumer_listener.h"
17#include "core/hle/service/nvflinger/status.h"
18
19namespace Service::android {
20
21class BufferItem;
22class BufferQueueConsumer;
23
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public:
26 void Connect(bool controlled_by_app);
27
28protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
30 virtual ~ConsumerBase();
31
32 virtual void OnFrameAvailable(const BufferItem& item) override;
33 virtual void OnFrameReplaced(const BufferItem& item) override;
34 virtual void OnBuffersReleased() override;
35 virtual void OnSidebandStreamChanged() override;
36
37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
42 const Fence& fence);
43
44 struct Slot final {
45 std::shared_ptr<GraphicBuffer> graphic_buffer;
46 Fence fence;
47 u64 frame_number{};
48 };
49
50protected:
51 std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
52
53 bool is_abandoned{};
54
55 std::unique_ptr<BufferQueueConsumer> consumer;
56
57 mutable std::mutex mutex;
58};
59
60} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h
new file mode 100644
index 000000000..b6d1c3e9a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_listener.h
@@ -0,0 +1,26 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class BufferItem;
12
13/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
14/// that the consumer may wish to react to.
15class IConsumerListener {
16public:
17 IConsumerListener() = default;
18 virtual ~IConsumerListener() = default;
19
20 virtual void OnFrameAvailable(const BufferItem& item) = 0;
21 virtual void OnFrameReplaced(const BufferItem& item) = 0;
22 virtual void OnBuffersReleased() = 0;
23 virtual void OnSidebandStreamChanged() = 0;
24};
25
26}; // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
new file mode 100644
index 000000000..04068827e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -0,0 +1,18 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
6
7#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
8#include "core/hle/service/nvflinger/parcel.h"
9
10namespace Service::android {
11
12QueueBufferInput::QueueBufferInput(Parcel& parcel) {
13 parcel.ReadFlattened(*this);
14}
15
16QueueBufferOutput::QueueBufferOutput() = default;
17
18} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
new file mode 100644
index 000000000..98d27871c
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -0,0 +1,76 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13#include "core/hle/service/nvflinger/window.h"
14
15namespace Service::android {
16
17class Parcel;
18
19#pragma pack(push, 1)
20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel);
22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
25 u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
26 *timestamp_ = timestamp;
27 *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
28 *crop_ = crop;
29 *scaling_mode_ = scaling_mode;
30 *transform_ = transform;
31 *sticky_transform_ = sticky_transform;
32 *async_ = static_cast<bool>(async);
33 *swap_interval_ = swap_interval;
34 *fence_ = fence;
35 }
36
37private:
38 s64 timestamp{};
39 s32 is_auto_timestamp{};
40 Common::Rectangle<s32> crop{};
41 NativeWindowScalingMode scaling_mode{};
42 NativeWindowTransform transform{};
43 u32 sticky_transform{};
44 s32 async{};
45 s32 swap_interval{};
46 Fence fence{};
47};
48#pragma pack(pop)
49static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
50
51struct QueueBufferOutput final {
52 QueueBufferOutput();
53
54 void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
55 *width_ = width;
56 *height_ = height;
57 *transform_hint_ = transform_hint;
58 *num_pending_buffers_ = num_pending_buffers;
59 }
60
61 void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
62 width = width_;
63 height = height_;
64 transform_hint = transform_hint_;
65 num_pending_buffers = num_pending_buffers_;
66 }
67
68private:
69 u32 width{};
70 u32 height{};
71 u32 transform_hint{};
72 u32 num_pending_buffers{};
73};
74static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
75
76} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
new file mode 100644
index 000000000..094ba2542
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
@@ -0,0 +1,36 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#include <mutex>
5
6#include "common/common_types.h"
7#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
8
9namespace Service::NVFlinger {
10
11HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
12 : service_context(system_, "HosBinderDriverServer") {}
13
14HosBinderDriverServer::~HosBinderDriverServer() {}
15
16u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
17 std::scoped_lock lk{lock};
18
19 last_id++;
20
21 producers[last_id] = std::move(binder);
22
23 return last_id;
24}
25
26android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
27 std::scoped_lock lk{lock};
28
29 if (auto search = producers.find(id); search != producers.end()) {
30 return search->second.get();
31 }
32
33 return {};
34}
35
36} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
new file mode 100644
index 000000000..cbca87fa0
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <unordered_map>
9
10#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/binder.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::NVFlinger {
19
20class HosBinderDriverServer final {
21public:
22 explicit HosBinderDriverServer(Core::System& system_);
23 ~HosBinderDriverServer();
24
25 u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
26
27 android::IBinder* TryGetProducer(u64 id);
28
29private:
30 KernelHelpers::ServiceContext service_context;
31
32 std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
33 std::mutex lock;
34 u64 last_id{};
35};
36
37} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 01e69de30..6fb2cdff1 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <optional> 5#include <optional>
@@ -16,8 +15,11 @@
16#include "core/hle/kernel/k_readable_event.h" 15#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 16#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
18#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
19#include "core/hle/service/nvflinger/buffer_queue.h" 18#include "core/hle/service/nvflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvflinger/buffer_queue_core.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 21#include "core/hle/service/nvflinger/nvflinger.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
21#include "core/hle/service/vi/display/vi_display.h" 23#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
23#include "video_core/gpu.h" 25#include "video_core/gpu.h"
@@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
53 } 55 }
54} 56}
55 57
56NVFlinger::NVFlinger(Core::System& system_) 58NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
57 : system(system_), service_context(system_, "nvflinger") { 59 : system(system_), service_context(system_, "nvflinger"),
58 displays.emplace_back(0, "Default", service_context, system); 60 hos_binder_driver_server(hos_binder_driver_server_) {
59 displays.emplace_back(1, "External", service_context, system); 61 displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
60 displays.emplace_back(2, "Edid", service_context, system); 62 displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
61 displays.emplace_back(3, "Internal", service_context, system); 63 displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
62 displays.emplace_back(4, "Null", service_context, system); 64 displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
65 displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
63 guard = std::make_shared<std::mutex>(); 66 guard = std::make_shared<std::mutex>();
64 67
65 // Schedule the screen composition events 68 // Schedule the screen composition events
@@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_)
83} 86}
84 87
85NVFlinger::~NVFlinger() { 88NVFlinger::~NVFlinger() {
86 for (auto& buffer_queue : buffer_queues) {
87 buffer_queue->Disconnect();
88 }
89 if (!system.IsMulticore()) { 89 if (!system.IsMulticore()) {
90 system.CoreTiming().UnscheduleEvent(composition_event, 0); 90 system.CoreTiming().UnscheduleEvent(composition_event, 0);
91 } 91 }
92
93 for (auto& display : displays) {
94 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
95 display.GetLayer(layer).Core().NotifyShutdown();
96 }
97 }
92} 98}
93 99
94void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 100void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -98,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
98std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 104std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
99 const auto lock_guard = Lock(); 105 const auto lock_guard = Lock();
100 106
101 LOG_DEBUG(Service, "Opening \"{}\" display", name); 107 LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
102 108
103 const auto itr = 109 const auto itr =
104 std::find_if(displays.begin(), displays.end(), 110 std::find_if(displays.begin(), displays.end(),
@@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
125} 131}
126 132
127void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 133void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
128 const u32 buffer_queue_id = next_buffer_queue_id++; 134 const auto buffer_id = next_buffer_queue_id++;
129 buffer_queues.emplace_back( 135 display.CreateLayer(layer_id, buffer_id);
130 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
131 display.CreateLayer(layer_id, *buffer_queues.back());
132} 136}
133 137
134void NVFlinger::CloseLayer(u64 layer_id) { 138void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
147 return std::nullopt; 151 return std::nullopt;
148 } 152 }
149 153
150 return layer->GetBufferQueue().GetId(); 154 return layer->GetBinderId();
151} 155}
152 156
153Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { 157Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
@@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
161 return &display->GetVSyncEvent(); 165 return &display->GetVSyncEvent();
162} 166}
163 167
164BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
165 const auto lock_guard = Lock();
166 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
167 [id](const auto& queue) { return queue->GetId() == id; });
168
169 if (itr == buffer_queues.end()) {
170 return nullptr;
171 }
172
173 return itr->get();
174}
175
176VI::Display* NVFlinger::FindDisplay(u64 display_id) { 168VI::Display* NVFlinger::FindDisplay(u64 display_id) {
177 const auto itr = 169 const auto itr =
178 std::find_if(displays.begin(), displays.end(), 170 std::find_if(displays.begin(), displays.end(),
@@ -227,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
227 auto* layer = display->FindLayer(layer_id); 219 auto* layer = display->FindLayer(layer_id);
228 220
229 if (layer == nullptr) { 221 if (layer == nullptr) {
230 LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); 222 LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
231 CreateLayerAtId(*display, layer_id); 223 CreateLayerAtId(*display, layer_id);
232 return display->FindLayer(layer_id); 224 return display->FindLayer(layer_id);
233 } 225 }
@@ -246,23 +238,22 @@ void NVFlinger::Compose() {
246 238
247 // TODO(Subv): Support more than 1 layer. 239 // TODO(Subv): Support more than 1 layer.
248 VI::Layer& layer = display.GetLayer(0); 240 VI::Layer& layer = display.GetLayer(0);
249 auto& buffer_queue = layer.GetBufferQueue();
250 241
251 // Search for a queued buffer and acquire it 242 android::BufferItem buffer{};
252 auto buffer = buffer_queue.AcquireBuffer(); 243 const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
253 244
254 if (!buffer) { 245 if (status != android::Status::NoError) {
255 continue; 246 continue;
256 } 247 }
257 248
258 const auto& igbp_buffer = buffer->get().igbp_buffer; 249 const auto& igbp_buffer = *buffer.graphic_buffer;
259 250
260 if (!system.IsPoweredOn()) { 251 if (!system.IsPoweredOn()) {
261 return; // We are likely shutting down 252 return; // We are likely shutting down
262 } 253 }
263 254
264 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
265 const auto& multi_fence = buffer->get().multi_fence; 256 const auto& multi_fence = buffer.fence;
266 guard->unlock(); 257 guard->unlock();
267 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 258 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
268 const auto& fence = multi_fence.fences[fence_id]; 259 const auto& fence = multi_fence.fences[fence_id];
@@ -278,12 +269,18 @@ void NVFlinger::Compose() {
278 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 269 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
279 ASSERT(nvdisp); 270 ASSERT(nvdisp);
280 271
281 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, 272 Common::Rectangle<int> crop_rect{
282 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 273 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
283 buffer->get().transform, buffer->get().crop_rect); 274 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
275
276 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
277 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
278 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect);
279
280 swap_interval = buffer.swap_interval;
284 281
285 swap_interval = buffer->get().swap_interval; 282 auto fence = android::Fence::NoFence();
286 buffer_queue.ReleaseBuffer(buffer->get().slot); 283 layer.GetConsumer().ReleaseBuffer(buffer, fence);
287 } 284 }
288} 285}
289 286
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 7935cf773..ed160f6f9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -37,13 +36,16 @@ class Display;
37class Layer; 36class Layer;
38} // namespace Service::VI 37} // namespace Service::VI
39 38
40namespace Service::NVFlinger { 39namespace Service::android {
40class BufferQueueCore;
41class BufferQueueProducer;
42} // namespace Service::android
41 43
42class BufferQueue; 44namespace Service::NVFlinger {
43 45
44class NVFlinger final { 46class NVFlinger final {
45public: 47public:
46 explicit NVFlinger(Core::System& system_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
47 ~NVFlinger(); 49 ~NVFlinger();
48 50
49 /// Sets the NVDrv module instance to use to send buffers to the GPU. 51 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -72,9 +74,6 @@ public:
72 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
73 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); 75 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
74 76
75 /// Obtains a buffer queue identified by the ID.
76 [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
77
78 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 77 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
79 /// finished. 78 /// finished.
80 void Compose(); 79 void Compose();
@@ -82,6 +81,12 @@ public:
82 [[nodiscard]] s64 GetNextTicks() const; 81 [[nodiscard]] s64 GetNextTicks() const;
83 82
84private: 83private:
84 struct Layer {
85 std::unique_ptr<android::BufferQueueCore> core;
86 std::unique_ptr<android::BufferQueueProducer> producer;
87 };
88
89private:
85 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { 90 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
86 return std::unique_lock{*guard}; 91 return std::unique_lock{*guard};
87 } 92 }
@@ -111,7 +116,6 @@ private:
111 std::shared_ptr<Nvidia::Module> nvdrv; 116 std::shared_ptr<Nvidia::Module> nvdrv;
112 117
113 std::list<VI::Display> displays; 118 std::list<VI::Display> displays;
114 std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
115 119
116 /// Id to use for the next layer that is created, this counter is shared among all displays. 120 /// Id to use for the next layer that is created, this counter is shared among all displays.
117 u64 next_layer_id = 1; 121 u64 next_layer_id = 1;
@@ -131,6 +135,8 @@ private:
131 std::jthread vsync_thread; 135 std::jthread vsync_thread;
132 136
133 KernelHelpers::ServiceContext service_context; 137 KernelHelpers::ServiceContext service_context;
138
139 HosBinderDriverServer& hos_binder_driver_server;
134}; 140};
135 141
136} // namespace Service::NVFlinger 142} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
new file mode 100644
index 000000000..aa36e6479
--- /dev/null
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -0,0 +1,172 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "common/alignment.h"
10#include "common/assert.h"
11#include "common/common_types.h"
12
13namespace Service::android {
14
15class Parcel final {
16public:
17 static constexpr std::size_t DefaultBufferSize = 0x40;
18
19 Parcel() : buffer(DefaultBufferSize) {}
20
21 template <typename T>
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 }
30
31 template <typename T>
32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size());
35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4);
39 }
40
41 template <typename T>
42 T Read() {
43 T val;
44 Read(val);
45 return val;
46 }
47
48 template <typename T>
49 void ReadFlattened(T& val) {
50 const auto flattened_size = Read<s64>();
51 ASSERT(sizeof(T) == flattened_size);
52 Read(val);
53 }
54
55 template <typename T>
56 T ReadFlattened() {
57 T val;
58 ReadFlattened(val);
59 return val;
60 }
61
62 template <typename T>
63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size());
66
67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T);
70 return val;
71 }
72
73 template <typename T>
74 const std::shared_ptr<T> ReadObject() {
75 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
76
77 const auto is_valid{Read<bool>()};
78
79 if (is_valid) {
80 auto result = std::make_shared<T>();
81 ReadFlattened(*result);
82 return result;
83 }
84
85 return {};
86 }
87
88 std::u16string ReadInterfaceToken() {
89 [[maybe_unused]] const u32 unknown = Read<u32>();
90 const u32 length = Read<u32>();
91
92 std::u16string token;
93 token.reserve(length + 1);
94
95 for (u32 ch = 0; ch < length + 1; ++ch) {
96 token.push_back(ReadUnaligned<u16>());
97 }
98
99 read_index = Common::AlignUp(read_index, 4);
100
101 return token;
102 }
103
104 template <typename T>
105 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
107
108 if (buffer.size() < write_index + sizeof(T)) {
109 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
110 }
111
112 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
113 write_index += sizeof(T);
114 write_index = Common::AlignUp(write_index, 4);
115 }
116
117 template <typename T>
118 void WriteObject(const T* ptr) {
119 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
120
121 if (!ptr) {
122 Write<u32>(0);
123 return;
124 }
125
126 Write<u32>(1);
127 Write<s64>(sizeof(T));
128 Write(*ptr);
129 }
130
131 template <typename T>
132 void WriteObject(const std::shared_ptr<T> ptr) {
133 WriteObject(ptr.get());
134 }
135
136 void DeserializeHeader() {
137 ASSERT(buffer.size() > sizeof(Header));
138
139 Header header{};
140 std::memcpy(&header, buffer.data(), sizeof(Header));
141
142 read_index = header.data_offset;
143 }
144
145 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0);
147
148 Header header{};
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header));
154
155 return buffer;
156 }
157
158private:
159 struct Header {
160 u32 data_size;
161 u32 data_offset;
162 u32 objects_size;
163 u32 objects_offset;
164 };
165 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
166
167 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0;
169 std::size_t write_index = sizeof(Header);
170};
171
172} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h
new file mode 100644
index 000000000..8a77d8bea
--- /dev/null
+++ b/src/core/hle/service/nvflinger/pixel_format.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class PixelFormat : u32 {
11 NoFormat = 0,
12 Rgba8888 = 1,
13 Rgbx8888 = 2,
14 Rgb888 = 3,
15 Rgb565 = 4,
16 Bgra8888 = 5,
17 Rgba5551 = 6,
18 Rgba4444 = 7,
19};
20
21} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h
new file mode 100644
index 000000000..468e06431
--- /dev/null
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -0,0 +1,16 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class IProducerListener {
12public:
13 virtual void OnBufferReleased() = 0;
14};
15
16} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h
new file mode 100644
index 000000000..a003eda89
--- /dev/null
+++ b/src/core/hle/service/nvflinger/status.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11enum class Status : s32 {
12 None = 0,
13 NoError = 0,
14 StaleBufferSlot = 1,
15 NoBufferAvailable = 2,
16 PresentLater = 3,
17 WouldBlock = -11,
18 NoMemory = -12,
19 Busy = -16,
20 NoInit = -19,
21 BadValue = -22,
22 InvalidOperation = -37,
23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2,
25};
26DECLARE_ENUM_FLAG_OPERATORS(Status);
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h
new file mode 100644
index 000000000..4a74e26a3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/fence.h
@@ -0,0 +1,32 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Service::android {
15
16class Fence {
17public:
18 constexpr Fence() = default;
19
20 static constexpr Fence NoFence() {
21 Fence fence;
22 fence.fences[0].id = -1;
23 return fence;
24 }
25
26public:
27 u32 num_fences{};
28 std::array<Service::Nvidia::NvFence, 4> fences{};
29};
30static_assert(sizeof(Fence) == 36, "Fence has wrong size");
31
32} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
new file mode 100644
index 000000000..7abbf78ba
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
@@ -0,0 +1,100 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2007 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/nvflinger/pixel_format.h"
12
13namespace Service::android {
14
15class GraphicBuffer final {
16public:
17 constexpr GraphicBuffer() = default;
18
19 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
20 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
21 usage{static_cast<s32>(usage_)} {}
22
23 constexpr u32 Width() const {
24 return static_cast<u32>(width);
25 }
26
27 constexpr u32 Height() const {
28 return static_cast<u32>(height);
29 }
30
31 constexpr u32 Stride() const {
32 return static_cast<u32>(stride);
33 }
34
35 constexpr u32 Usage() const {
36 return static_cast<u32>(usage);
37 }
38
39 constexpr PixelFormat Format() const {
40 return format;
41 }
42
43 constexpr u32 BufferId() const {
44 return buffer_id;
45 }
46
47 constexpr PixelFormat ExternalFormat() const {
48 return external_format;
49 }
50
51 constexpr u32 Handle() const {
52 return handle;
53 }
54
55 constexpr u32 Offset() const {
56 return offset;
57 }
58
59 constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
60 u32 usage_) const {
61 if (static_cast<s32>(width_) != width) {
62 return true;
63 }
64
65 if (static_cast<s32>(height_) != height) {
66 return true;
67 }
68
69 if (format_ != format) {
70 return true;
71 }
72
73 if ((static_cast<u32>(usage) & usage_) != usage_) {
74 return true;
75 }
76
77 return false;
78 }
79
80private:
81 u32 magic{};
82 s32 width{};
83 s32 height{};
84 s32 stride{};
85 PixelFormat format{};
86 s32 usage{};
87 INSERT_PADDING_WORDS(1);
88 u32 index{};
89 INSERT_PADDING_WORDS(3);
90 u32 buffer_id{};
91 INSERT_PADDING_WORDS(6);
92 PixelFormat external_format{};
93 INSERT_PADDING_WORDS(10);
94 u32 handle{};
95 u32 offset{};
96 INSERT_PADDING_WORDS(60);
97};
98static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
99
100} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h
new file mode 100644
index 000000000..e26f8160e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/window.h
@@ -0,0 +1,53 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11/// Attributes queryable with Query
12enum class NativeWindow : s32 {
13 Width = 0,
14 Height = 1,
15 Format = 2,
16 MinUndequeedBuffers = 3,
17 QueuesToWindowComposer = 4,
18 ConcreteType = 5,
19 DefaultWidth = 6,
20 DefaultHeight = 7,
21 TransformHint = 8,
22 ConsumerRunningBehind = 9,
23 ConsumerUsageBits = 10,
24 StickyTransform = 11,
25 DefaultDataSpace = 12,
26 BufferAge = 13,
27};
28
29/// Parameter for Connect/Disconnect
30enum class NativeWindowApi : s32 {
31 NoConnectedApi = 0,
32 Egl = 1,
33 Cpu = 2,
34 Media = 3,
35 Camera = 4,
36};
37
38/// Scaling mode parameter for QueueBuffer
39enum class NativeWindowScalingMode : s32 {
40 Freeze = 0,
41 ScaleToWindow = 1,
42 ScaleCrop = 2,
43 NoScaleCrop = 3,
44};
45
46/// Transform parameter for QueueBuffer
47enum class NativeWindowTransform : u32 {
48 None = 0x0,
49 InverseDisplay = 0x08,
50};
51DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
52
53} // namespace Service::android
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index eb1138313..0f59a03c5 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -32,6 +32,7 @@
32#include "core/hle/service/glue/glue.h" 32#include "core/hle/service/glue/glue.h"
33#include "core/hle/service/grc/grc.h" 33#include "core/hle/service/grc/grc.h"
34#include "core/hle/service/hid/hid.h" 34#include "core/hle/service/hid/hid.h"
35#include "core/hle/service/jit/jit.h"
35#include "core/hle/service/lbl/lbl.h" 36#include "core/hle/service/lbl/lbl.h"
36#include "core/hle/service/ldn/ldn.h" 37#include "core/hle/service/ldn/ldn.h"
37#include "core/hle/service/ldr/ldr.h" 38#include "core/hle/service/ldr/ldr.h"
@@ -49,6 +50,7 @@
49#include "core/hle/service/npns/npns.h" 50#include "core/hle/service/npns/npns.h"
50#include "core/hle/service/ns/ns.h" 51#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h" 52#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
52#include "core/hle/service/nvflinger/nvflinger.h" 54#include "core/hle/service/nvflinger/nvflinger.h"
53#include "core/hle/service/olsc/olsc.h" 55#include "core/hle/service/olsc/olsc.h"
54#include "core/hle/service/pcie/pcie.h" 56#include "core/hle/service/pcie/pcie.h"
@@ -90,8 +92,9 @@ namespace Service {
90} 92}
91 93
92ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, 94ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
93 u32 max_sessions_, InvokerFn* handler_invoker_) 95 ServiceThreadType thread_type, u32 max_sessions_,
94 : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, 96 InvokerFn* handler_invoker_)
97 : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
95 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} 98 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
96 99
97ServiceFrameworkBase::~ServiceFrameworkBase() { 100ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -230,7 +233,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
230 233
231/// Initialize Services 234/// Initialize Services
232Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) 235Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
233 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { 236 : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
237 nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
234 238
235 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 239 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
236 // here and pass it into the respective InstallInterfaces functions. 240 // here and pass it into the respective InstallInterfaces functions.
@@ -259,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
259 Glue::InstallInterfaces(system); 263 Glue::InstallInterfaces(system);
260 GRC::InstallInterfaces(*sm, system); 264 GRC::InstallInterfaces(*sm, system);
261 HID::InstallInterfaces(*sm, system); 265 HID::InstallInterfaces(*sm, system);
266 JIT::InstallInterfaces(*sm, system);
262 LBL::InstallInterfaces(*sm, system); 267 LBL::InstallInterfaces(*sm, system);
263 LDN::InstallInterfaces(*sm, system); 268 LDN::InstallInterfaces(*sm, system);
264 LDR::InstallInterfaces(*sm, system); 269 LDR::InstallInterfaces(*sm, system);
@@ -290,7 +295,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
290 SSL::InstallInterfaces(*sm, system); 295 SSL::InstallInterfaces(*sm, system);
291 Time::InstallInterfaces(system); 296 Time::InstallInterfaces(system);
292 USB::InstallInterfaces(*sm, system); 297 USB::InstallInterfaces(*sm, system);
293 VI::InstallInterfaces(*sm, system, *nv_flinger); 298 VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
294 WLAN::InstallInterfaces(*sm, system); 299 WLAN::InstallInterfaces(*sm, system);
295} 300}
296 301
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c9d6b879d..148265218 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -9,7 +9,6 @@
9#include <string> 9#include <string>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
13#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,8 +32,9 @@ class FileSystemController;
33} 32}
34 33
35namespace NVFlinger { 34namespace NVFlinger {
35class HosBinderDriverServer;
36class NVFlinger; 36class NVFlinger;
37} 37} // namespace NVFlinger
38 38
39namespace SM { 39namespace SM {
40class ServiceManager; 40class ServiceManager;
@@ -89,7 +89,7 @@ protected:
89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); 89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
90 90
91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
92 [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { 92 [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
93 return std::scoped_lock{lock_service}; 93 return std::scoped_lock{lock_service};
94 } 94 }
95 95
@@ -113,7 +113,8 @@ private:
113 Kernel::HLERequestContext& ctx); 113 Kernel::HLERequestContext& ctx);
114 114
115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, 115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
116 u32 max_sessions_, InvokerFn* handler_invoker_); 116 ServiceThreadType thread_type, u32 max_sessions_,
117 InvokerFn* handler_invoker_);
117 ~ServiceFrameworkBase() override; 118 ~ServiceFrameworkBase() override;
118 119
119 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 120 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -133,7 +134,7 @@ private:
133 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; 134 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
134 135
135 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 136 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
136 Common::SpinLock lock_service; 137 std::mutex lock_service;
137}; 138};
138 139
139/** 140/**
@@ -175,14 +176,17 @@ protected:
175 /** 176 /**
176 * Initializes the handler with no functions installed. 177 * Initializes the handler with no functions installed.
177 * 178 *
178 * @param system_ The system context to construct this service under. 179 * @param system_ The system context to construct this service under.
179 * @param service_name_ Name of the service. 180 * @param service_name_ Name of the service.
180 * @param max_sessions_ Maximum number of sessions that can be 181 * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
181 * connected to this service at the same time. 182 * it creates a new thread for it, otherwise this uses the default thread.
183 * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
184 * same time.
182 */ 185 */
183 explicit ServiceFramework(Core::System& system_, const char* service_name_, 186 explicit ServiceFramework(Core::System& system_, const char* service_name_,
187 ServiceThreadType thread_type = ServiceThreadType::Default,
184 u32 max_sessions_ = ServerSessionCountMax) 188 u32 max_sessions_ = ServerSessionCountMax)
185 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 189 : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
186 190
187 /// Registers handlers in the service. 191 /// Registers handlers in the service.
188 template <std::size_t N> 192 template <std::size_t N>
@@ -236,6 +240,7 @@ public:
236 ~Services(); 240 ~Services();
237 241
238private: 242private:
243 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
239 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 244 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
240}; 245};
241 246
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index eaa172595..13f5e08ec 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -81,6 +81,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
81 } 81 }
82 82
83 auto* port = Kernel::KPort::Create(kernel); 83 auto* port = Kernel::KPort::Create(kernel);
84 SCOPE_EXIT({ port->Close(); });
85
84 port->Initialize(ServerSessionCountMax, false, name); 86 port->Initialize(ServerSessionCountMax, false, name);
85 auto handler = it->second; 87 auto handler = it->second;
86 port->GetServerPort().SetSessionHandler(std::move(handler)); 88 port->GetServerPort().SetSessionHandler(std::move(handler));
@@ -151,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
151 auto& port = port_result.Unwrap(); 153 auto& port = port_result.Unwrap();
152 SCOPE_EXIT({ port->GetClientPort().Close(); }); 154 SCOPE_EXIT({ port->GetClientPort().Close(); });
153 155
154 server_ports.emplace_back(&port->GetServerPort()); 156 kernel.RegisterServerObject(&port->GetServerPort());
155 157
156 // Create a new session. 158 // Create a new session.
157 Kernel::KClientSession* session{}; 159 Kernel::KClientSession* session{};
@@ -204,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
204} 206}
205 207
206SM::SM(ServiceManager& service_manager_, Core::System& system_) 208SM::SM(ServiceManager& service_manager_, Core::System& system_)
207 : ServiceFramework{system_, "sm:", 4}, 209 : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
208 service_manager{service_manager_}, kernel{system_.Kernel()} { 210 service_manager{service_manager_}, kernel{system_.Kernel()} {
209 RegisterHandlers({ 211 RegisterHandlers({
210 {0, &SM::Initialize, "Initialize"}, 212 {0, &SM::Initialize, "Initialize"},
@@ -222,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
222 }); 224 });
223} 225}
224 226
225SM::~SM() { 227SM::~SM() = default;
226 for (auto& server_port : server_ports) {
227 server_port->Close();
228 }
229}
230 228
231} // namespace Service::SM 229} // namespace Service::SM
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 021eb51b4..f3ff7b27e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -22,7 +22,6 @@ class KClientPort;
22class KClientSession; 22class KClientSession;
23class KernelCore; 23class KernelCore;
24class KPort; 24class KPort;
25class KServerPort;
26class SessionRequestHandler; 25class SessionRequestHandler;
27} // namespace Kernel 26} // namespace Kernel
28 27
@@ -48,7 +47,6 @@ private:
48 ServiceManager& service_manager; 47 ServiceManager& service_manager;
49 bool is_initialized{}; 48 bool is_initialized{};
50 Kernel::KernelCore& kernel; 49 Kernel::KernelCore& kernel;
51 std::vector<Kernel::KServerPort*> server_ports;
52}; 50};
53 51
54class ServiceManager { 52class ServiceManager {
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f83272633..d25b050e2 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -10,7 +10,6 @@
10#include <fmt/format.h> 10#include <fmt/format.h>
11 11
12#include "common/microprofile.h" 12#include "common/microprofile.h"
13#include "common/thread.h"
14#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/k_thread.h" 14#include "core/hle/kernel/k_thread.h"
16#include "core/hle/service/sockets/bsd.h" 15#include "core/hle/service/sockets/bsd.h"
@@ -569,9 +568,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
569 new_descriptor.socket = std::move(result.socket); 568 new_descriptor.socket = std::move(result.socket);
570 new_descriptor.is_connection_based = descriptor.is_connection_based; 569 new_descriptor.is_connection_based = descriptor.is_connection_based;
571 570
572 ASSERT(write_buffer.size() == sizeof(SockAddrIn));
573 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); 571 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
574 std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); 572 const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
573 std::memcpy(write_buffer.data(), &guest_addr_in, length);
575 574
576 return {new_fd, Errno::SUCCESS}; 575 return {new_fd, Errno::SUCCESS};
577} 576}
@@ -690,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
690 case OptName::REUSEADDR: 689 case OptName::REUSEADDR:
691 ASSERT(value == 0 || value == 1); 690 ASSERT(value == 0 || value == 1);
692 return Translate(socket->SetReuseAddr(value != 0)); 691 return Translate(socket->SetReuseAddr(value != 0));
692 case OptName::KEEPALIVE:
693 ASSERT(value == 0 || value == 1);
694 return Translate(socket->SetKeepAlive(value != 0));
693 case OptName::BROADCAST: 695 case OptName::BROADCAST:
694 ASSERT(value == 0 || value == 1); 696 ASSERT(value == 0 || value == 1);
695 return Translate(socket->SetBroadcast(value != 0)); 697 return Translate(socket->SetBroadcast(value != 0));
@@ -838,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
838 rb.PushEnum(bsd_errno); 840 rb.PushEnum(bsd_errno);
839} 841}
840 842
841BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 843BSD::BSD(Core::System& system_, const char* name)
844 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
842 // clang-format off 845 // clang-format off
843 static const FunctionInfo functions[] = { 846 static const FunctionInfo functions[] = {
844 {0, &BSD::RegisterClient, "RegisterClient"}, 847 {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index fb6142c49..a193fb578 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -2,8 +2,28 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6#include <utility>
7#include <vector>
8
9#include "common/string_util.h"
10#include "common/swap.h"
11#include "core/core.h"
5#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/sockets/sfdnsres.h" 13#include "core/hle/service/sockets/sfdnsres.h"
14#include "core/memory.h"
15
16#ifdef _WIN32
17#include <ws2tcpip.h>
18#elif YUZU_UNIX
19#include <arpa/inet.h>
20#include <netdb.h>
21#include <netinet/in.h>
22#include <sys/socket.h>
23#ifndef EAI_NODATA
24#define EAI_NODATA EAI_NONAME
25#endif
26#endif
7 27
8namespace Service::Sockets { 28namespace Service::Sockets {
9 29
@@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
21 {9, nullptr, "CancelRequest"}, 41 {9, nullptr, "CancelRequest"},
22 {10, nullptr, "GetHostByNameRequestWithOptions"}, 42 {10, nullptr, "GetHostByNameRequestWithOptions"},
23 {11, nullptr, "GetHostByAddrRequestWithOptions"}, 43 {11, nullptr, "GetHostByAddrRequestWithOptions"},
24 {12, nullptr, "GetAddrInfoRequestWithOptions"}, 44 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
25 {13, nullptr, "GetNameInfoRequestWithOptions"}, 45 {13, nullptr, "GetNameInfoRequestWithOptions"},
26 {14, nullptr, "ResolverSetOptionRequest"}, 46 {14, nullptr, "ResolverSetOptionRequest"},
27 {15, nullptr, "ResolverGetOptionRequest"}, 47 {15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
31 51
32SFDNSRES::~SFDNSRES() = default; 52SFDNSRES::~SFDNSRES() = default;
33 53
34void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { 54enum class NetDbError : s32 {
55 Internal = -1,
56 Success = 0,
57 HostNotFound = 1,
58 TryAgain = 2,
59 NoRecovery = 3,
60 NoData = 4,
61};
62
63static NetDbError AddrInfoErrorToNetDbError(s32 result) {
64 // Best effort guess to map errors
65 switch (result) {
66 case 0:
67 return NetDbError::Success;
68 case EAI_AGAIN:
69 return NetDbError::TryAgain;
70 case EAI_NODATA:
71 return NetDbError::NoData;
72 default:
73 return NetDbError::HostNotFound;
74 }
75}
76
77static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
78 std::string_view host) {
79 // Adapted from
80 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
81 std::vector<u8> data;
82
83 auto* current = addrinfo;
84 while (current != nullptr) {
85 struct SerializedResponseHeader {
86 u32 magic;
87 s32 flags;
88 s32 family;
89 s32 socket_type;
90 s32 protocol;
91 u32 address_length;
92 };
93 static_assert(sizeof(SerializedResponseHeader) == 0x18,
94 "Response header size must be 0x18 bytes");
95
96 constexpr auto header_size = sizeof(SerializedResponseHeader);
97 const auto addr_size =
98 current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
99 const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
100
101 const auto last_size = data.size();
102 data.resize(last_size + header_size + addr_size + canonname_size);
103
104 // Header in network byte order
105 SerializedResponseHeader header{};
106
107 constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
108 header.magic = htonl(HEADER_MAGIC);
109 header.family = htonl(current->ai_family);
110 header.flags = htonl(current->ai_flags);
111 header.socket_type = htonl(current->ai_socktype);
112 header.protocol = htonl(current->ai_protocol);
113 header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
114
115 auto* header_ptr = data.data() + last_size;
116 std::memcpy(header_ptr, &header, header_size);
117
118 if (header.address_length == 0) {
119 std::memset(header_ptr + header_size, 0, 4);
120 } else {
121 switch (current->ai_family) {
122 case AF_INET: {
123 struct SockAddrIn {
124 s16 sin_family;
125 u16 sin_port;
126 u32 sin_addr;
127 u8 sin_zero[8];
128 };
129
130 SockAddrIn serialized_addr{};
131 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
132 serialized_addr.sin_port = htons(addr.sin_port);
133 serialized_addr.sin_family = htons(addr.sin_family);
134 serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
135 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
136
137 char addr_string_buf[64]{};
138 inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
139 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
140 break;
141 }
142 case AF_INET6: {
143 struct SockAddrIn6 {
144 s16 sin6_family;
145 u16 sin6_port;
146 u32 sin6_flowinfo;
147 u8 sin6_addr[16];
148 u32 sin6_scope_id;
149 };
150
151 SockAddrIn6 serialized_addr{};
152 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
153 serialized_addr.sin6_family = htons(addr.sin6_family);
154 serialized_addr.sin6_port = htons(addr.sin6_port);
155 serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
156 serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
157 std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
158 sizeof(SockAddrIn6::sin6_addr));
159 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
160
161 char addr_string_buf[64]{};
162 inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
163 LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
164 break;
165 }
166 default:
167 std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
168 break;
169 }
170 }
171 if (current->ai_canonname) {
172 std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
173 } else {
174 *(header_ptr + header_size + addr_size) = 0;
175 }
176
177 current = current->ai_next;
178 }
179
180 // 4-byte sentinel value
181 data.push_back(0);
182 data.push_back(0);
183 data.push_back(0);
184 data.push_back(0);
185
186 return data;
187}
188
189static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
35 struct Parameters { 190 struct Parameters {
36 u8 use_nsd_resolve; 191 u8 use_nsd_resolve;
37 u32 unknown; 192 u32 unknown;
@@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
42 const auto parameters = rp.PopRaw<Parameters>(); 197 const auto parameters = rp.PopRaw<Parameters>();
43 198
44 LOG_WARNING(Service, 199 LOG_WARNING(Service,
45 "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", 200 "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
46 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); 201 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
47 202
48 IPC::ResponseBuilder rb{ctx, 2}; 203 const auto host_buffer = ctx.ReadBuffer(0);
204 const std::string host = Common::StringFromBuffer(host_buffer);
205
206 const auto service_buffer = ctx.ReadBuffer(1);
207 const std::string service = Common::StringFromBuffer(service_buffer);
208
209 addrinfo* addrinfo;
210 // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
211 s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
212
213 u32 data_size = 0;
214 if (result_code == 0 && addrinfo != nullptr) {
215 const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
216 data_size = static_cast<u32>(data.size());
217 freeaddrinfo(addrinfo);
218
219 ctx.WriteBuffer(data, 0);
220 }
221
222 return std::make_pair(data_size, result_code);
223}
224
225void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
226 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
227
228 IPC::ResponseBuilder rb{ctx, 4};
229 rb.Push(ResultSuccess);
230 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
231 rb.Push(result_code); // errno
232 rb.Push(data_size); // serialized size
233}
234
235void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
236 // Additional options are ignored
237 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
238
239 IPC::ResponseBuilder rb{ctx, 5};
49 rb.Push(ResultSuccess); 240 rb.Push(ResultSuccess);
241 rb.Push(data_size); // serialized size
242 rb.Push(result_code); // errno
243 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
244 rb.Push(0);
50} 245}
51 246
52} // namespace Service::Sockets 247} // namespace Service::Sockets \ No newline at end of file
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index 5d3b4dc2d..f0c57377d 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -19,6 +19,7 @@ public:
19 19
20private: 20private:
21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); 21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
22 void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
22}; 23};
23 24
24} // namespace Service::Sockets 25} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 02dbbae40..d69c86431 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -46,6 +46,7 @@ enum class Protocol : u32 {
46 46
47enum class OptName : u32 { 47enum class OptName : u32 {
48 REUSEADDR = 0x4, 48 REUSEADDR = 0x4,
49 KEEPALIVE = 0x8,
49 BROADCAST = 0x20, 50 BROADCAST = 0x20,
50 LINGER = 0x80, 51 LINGER = 0x80,
51 SNDBUF = 0x1001, 52 SNDBUF = 0x1001,
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b7705c02a..558022511 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -13,14 +13,34 @@
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvflinger/buffer_item_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
16#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
17#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
18 23
19namespace Service::VI { 24namespace Service::VI {
20 25
21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 26struct BufferQueue {
22 Core::System& system_) 27 std::shared_ptr<android::BufferQueueCore> core;
23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} { 28 std::unique_ptr<android::BufferQueueProducer> producer;
29 std::unique_ptr<android::BufferQueueConsumer> consumer;
30};
31
32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) {
33 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
34 return {buffer_queue_core,
35 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core),
36 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
37}
38
39Display::Display(u64 id, std::string name_,
40 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
41 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
42 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
43 service_context{service_context_} {
24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); 44 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
25} 45}
26 46
@@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() {
44 vsync_event->GetWritableEvent().Signal(); 64 vsync_event->GetWritableEvent().Signal();
45} 65}
46 66
47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 67void Display::CreateLayer(u64 layer_id, u32 binder_id) {
48 // TODO(Subv): Support more than 1 layer.
49 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 68 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
50 69
51 layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); 70 auto [core, producer, consumer] = CreateBufferQueue(service_context);
71
72 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
73 buffer_item_consumer->Connect(false);
74
75 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
76 std::move(buffer_item_consumer)));
77
78 hos_binder_driver_server.RegisterProducer(std::move(producer));
52} 79}
53 80
54void Display::CloseLayer(u64 layer_id) { 81void Display::CloseLayer(u64 layer_id) {
55 std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); 82 std::erase_if(layers,
83 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
56} 84}
57 85
58Layer* Display::FindLayer(u64 layer_id) { 86Layer* Display::FindLayer(u64 layer_id) {
59 const auto itr = 87 const auto itr =
60 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 88 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
61 return layer->GetID() == layer_id; 89 return layer->GetLayerId() == layer_id;
62 }); 90 });
63 91
64 if (itr == layers.end()) { 92 if (itr == layers.end()) {
@@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) {
70 98
71const Layer* Display::FindLayer(u64 layer_id) const { 99const Layer* Display::FindLayer(u64 layer_id) const {
72 const auto itr = 100 const auto itr =
73 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 101 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
74 return layer->GetID() == layer_id; 102 return layer->GetLayerId() == layer_id;
75 }); 103 });
76 104
77 if (itr == layers.end()) { 105 if (itr == layers.end()) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 329f4ba86..e93d084ee 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -15,12 +15,17 @@ namespace Kernel {
15class KEvent; 15class KEvent;
16} 16}
17 17
18namespace Service::NVFlinger { 18namespace Service::android {
19class BufferQueue; 19class BufferQueueProducer;
20} 20}
21
21namespace Service::KernelHelpers { 22namespace Service::KernelHelpers {
22class ServiceContext; 23class ServiceContext;
23} // namespace Service::KernelHelpers 24}
25
26namespace Service::NVFlinger {
27class HosBinderDriverServer;
28}
24 29
25namespace Service::VI { 30namespace Service::VI {
26 31
@@ -35,12 +40,13 @@ public:
35 /// Constructs a display with a given unique ID and name. 40 /// Constructs a display with a given unique ID and name.
36 /// 41 ///
37 /// @param id The unique ID for this display. 42 /// @param id The unique ID for this display.
43 /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
38 /// @param service_context_ The ServiceContext for the owning service. 44 /// @param service_context_ The ServiceContext for the owning service.
39 /// @param name_ The name for this display. 45 /// @param name_ The name for this display.
40 /// @param system_ The global system instance. 46 /// @param system_ The global system instance.
41 /// 47 ///
42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 48 Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
43 Core::System& system_); 49 KernelHelpers::ServiceContext& service_context_, Core::System& system_);
44 ~Display(); 50 ~Display();
45 51
46 /// Gets the unique ID assigned to this display. 52 /// Gets the unique ID assigned to this display.
@@ -64,6 +70,10 @@ public:
64 /// Gets a layer for this display based off an index. 70 /// Gets a layer for this display based off an index.
65 const Layer& GetLayer(std::size_t index) const; 71 const Layer& GetLayer(std::size_t index) const;
66 72
73 std::size_t GetNumLayers() const {
74 return layers.size();
75 }
76
67 /// Gets the readable vsync event. 77 /// Gets the readable vsync event.
68 Kernel::KReadableEvent& GetVSyncEvent(); 78 Kernel::KReadableEvent& GetVSyncEvent();
69 79
@@ -72,10 +82,10 @@ public:
72 82
73 /// Creates and adds a layer to this display with the given ID. 83 /// Creates and adds a layer to this display with the given ID.
74 /// 84 ///
75 /// @param layer_id The ID to assign to the created layer. 85 /// @param layer_id The ID to assign to the created layer.
76 /// @param buffer_queue The buffer queue for the layer instance to use. 86 /// @param binder_id The ID assigned to the buffer queue.
77 /// 87 ///
78 void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); 88 void CreateLayer(u64 layer_id, u32 binder_id);
79 89
80 /// Closes and removes a layer from this display with the given ID. 90 /// Closes and removes a layer from this display with the given ID.
81 /// 91 ///
@@ -104,9 +114,10 @@ public:
104private: 114private:
105 u64 display_id; 115 u64 display_id;
106 std::string name; 116 std::string name;
117 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
107 KernelHelpers::ServiceContext& service_context; 118 KernelHelpers::ServiceContext& service_context;
108 119
109 std::vector<std::shared_ptr<Layer>> layers; 120 std::vector<std::unique_ptr<Layer>> layers;
110 Kernel::KEvent* vsync_event{}; 121 Kernel::KEvent* vsync_event{};
111}; 122};
112 123
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9bc382587..93858e91f 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -6,7 +6,11 @@
6 6
7namespace Service::VI { 7namespace Service::VI {
8 8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} 9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
10 android::BufferQueueProducer& binder_,
11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
13 consumer_)} {}
10 14
11Layer::~Layer() = default; 15Layer::~Layer() = default;
12 16
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index ebdd85505..c28b14450 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -4,11 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace Service::NVFlinger { 11namespace Service::android {
10class BufferQueue; 12class BufferItemConsumer;
11} 13class BufferQueueCore;
14class BufferQueueProducer;
15} // namespace Service::android
12 16
13namespace Service::VI { 17namespace Service::VI {
14 18
@@ -17,10 +21,13 @@ class Layer {
17public: 21public:
18 /// Constructs a layer with a given ID and buffer queue. 22 /// Constructs a layer with a given ID and buffer queue.
19 /// 23 ///
20 /// @param id The ID to assign to this layer. 24 /// @param layer_id_ The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use. 25 /// @param binder_id_ The binder ID to assign to this layer.
26 /// @param binder_ The buffer producer queue for this layer to use.
22 /// 27 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue); 28 Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
29 android::BufferQueueProducer& binder_,
30 std::shared_ptr<android::BufferItemConsumer>&& consumer_);
24 ~Layer(); 31 ~Layer();
25 32
26 Layer(const Layer&) = delete; 33 Layer(const Layer&) = delete;
@@ -30,23 +37,47 @@ public:
30 Layer& operator=(Layer&&) = delete; 37 Layer& operator=(Layer&&) = delete;
31 38
32 /// Gets the ID for this layer. 39 /// Gets the ID for this layer.
33 u64 GetID() const { 40 u64 GetLayerId() const {
34 return layer_id; 41 return layer_id;
35 } 42 }
36 43
44 /// Gets the binder ID for this layer.
45 u32 GetBinderId() const {
46 return binder_id;
47 }
48
37 /// Gets a reference to the buffer queue this layer is using. 49 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() { 50 android::BufferQueueProducer& GetBufferQueue() {
39 return buffer_queue; 51 return binder;
40 } 52 }
41 53
42 /// Gets a const reference to the buffer queue this layer is using. 54 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const { 55 const android::BufferQueueProducer& GetBufferQueue() const {
44 return buffer_queue; 56 return binder;
57 }
58
59 android::BufferItemConsumer& GetConsumer() {
60 return *consumer;
61 }
62
63 const android::BufferItemConsumer& GetConsumer() const {
64 return *consumer;
65 }
66
67 android::BufferQueueCore& Core() {
68 return core;
69 }
70
71 const android::BufferQueueCore& Core() const {
72 return core;
45 } 73 }
46 74
47private: 75private:
48 u64 layer_id; 76 const u64 layer_id;
49 NVFlinger::BufferQueue& buffer_queue; 77 const u32 binder_id;
78 android::BufferQueueCore& core;
79 android::BufferQueueProducer& binder;
80 std::shared_ptr<android::BufferItemConsumer> consumer;
50}; 81};
51 82
52} // namespace Service::VI 83} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 75ee3e5e4..a3436c8ea 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -22,8 +22,11 @@
22#include "core/hle/kernel/k_readable_event.h" 22#include "core/hle/kernel/k_readable_event.h"
23#include "core/hle/kernel/k_thread.h" 23#include "core/hle/kernel/k_thread.h"
24#include "core/hle/service/nvdrv/nvdata.h" 24#include "core/hle/service/nvdrv/nvdata.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/binder.h"
26#include "core/hle/service/nvflinger/buffer_queue_producer.h"
27#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 28#include "core/hle/service/nvflinger/nvflinger.h"
29#include "core/hle/service/nvflinger/parcel.h"
27#include "core/hle/service/service.h" 30#include "core/hle/service/service.h"
28#include "core/hle/service/vi/vi.h" 31#include "core/hle/service/vi/vi.h"
29#include "core/hle/service/vi/vi_m.h" 32#include "core/hle/service/vi/vi_m.h"
@@ -57,447 +60,25 @@ struct DisplayInfo {
57}; 60};
58static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); 61static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
59 62
60class Parcel { 63class NativeWindow final {
61public: 64public:
62 // This default size was chosen arbitrarily. 65 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
63 static constexpr std::size_t DefaultBufferSize = 0x40;
64 Parcel() : buffer(DefaultBufferSize) {}
65 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
66 virtual ~Parcel() = default;
67
68 template <typename T>
69 T Read() {
70 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
71 ASSERT(read_index + sizeof(T) <= buffer.size());
72
73 T val;
74 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
75 read_index += sizeof(T);
76 read_index = Common::AlignUp(read_index, 4);
77 return val;
78 }
79
80 template <typename T>
81 T ReadUnaligned() {
82 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
83 ASSERT(read_index + sizeof(T) <= buffer.size());
84
85 T val;
86 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
87 read_index += sizeof(T);
88 return val;
89 }
90
91 std::vector<u8> ReadBlock(std::size_t length) {
92 ASSERT(read_index + length <= buffer.size());
93 const u8* const begin = buffer.data() + read_index;
94 const u8* const end = begin + length;
95 std::vector<u8> data(begin, end);
96 read_index += length;
97 read_index = Common::AlignUp(read_index, 4);
98 return data;
99 }
100
101 std::u16string ReadInterfaceToken() {
102 [[maybe_unused]] const u32 unknown = Read<u32_le>();
103 const u32 length = Read<u32_le>();
104
105 std::u16string token{};
106
107 for (u32 ch = 0; ch < length + 1; ++ch) {
108 token.push_back(ReadUnaligned<u16_le>());
109 }
110
111 read_index = Common::AlignUp(read_index, 4);
112
113 return token;
114 }
115
116 template <typename T>
117 void Write(const T& val) {
118 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
119
120 if (buffer.size() < write_index + sizeof(T)) {
121 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
122 }
123
124 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
125 write_index += sizeof(T);
126 write_index = Common::AlignUp(write_index, 4);
127 }
128
129 template <typename T>
130 void WriteObject(const T& val) {
131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
132
133 const u32_le size = static_cast<u32>(sizeof(val));
134 Write(size);
135 // TODO(Subv): Support file descriptors.
136 Write<u32_le>(0); // Fd count.
137 Write(val);
138 }
139
140 void Deserialize() {
141 ASSERT(buffer.size() > sizeof(Header));
142
143 Header header{};
144 std::memcpy(&header, buffer.data(), sizeof(Header));
145
146 read_index = header.data_offset;
147 DeserializeData();
148 }
149
150 std::vector<u8> Serialize() {
151 ASSERT(read_index == 0);
152 write_index = sizeof(Header);
153
154 SerializeData();
155
156 Header header{};
157 header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
158 header.data_offset = sizeof(Header);
159 header.objects_size = 4;
160 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
161 std::memcpy(buffer.data(), &header, sizeof(Header));
162
163 return buffer;
164 }
165
166protected:
167 virtual void SerializeData() {}
168
169 virtual void DeserializeData() {}
170
171private:
172 struct Header {
173 u32_le data_size;
174 u32_le data_offset;
175 u32_le objects_size;
176 u32_le objects_offset;
177 };
178 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
179
180 std::vector<u8> buffer;
181 std::size_t read_index = 0;
182 std::size_t write_index = 0;
183};
184
185class NativeWindow : public Parcel {
186public:
187 explicit NativeWindow(u32 id) {
188 data.id = id;
189 }
190 ~NativeWindow() override = default;
191
192protected:
193 void SerializeData() override {
194 Write(data);
195 }
196
197private:
198 struct Data {
199 u32_le magic = 2;
200 u32_le process_id = 1;
201 u32_le id;
202 INSERT_PADDING_WORDS(3);
203 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
204 INSERT_PADDING_WORDS(2);
205 };
206 static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
207
208 Data data{};
209};
210
211class IGBPConnectRequestParcel : public Parcel {
212public:
213 explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
214 Deserialize();
215 }
216
217 void DeserializeData() override {
218 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
219 data = Read<Data>();
220 }
221
222 struct Data {
223 u32_le unk;
224 u32_le api;
225 u32_le producer_controlled_by_app;
226 };
227
228 Data data;
229};
230
231class IGBPConnectResponseParcel : public Parcel {
232public:
233 explicit IGBPConnectResponseParcel(u32 width, u32 height) {
234 data.width = width;
235 data.height = height;
236 }
237 ~IGBPConnectResponseParcel() override = default;
238
239protected:
240 void SerializeData() override {
241 Write(data);
242 }
243
244private:
245 struct Data {
246 u32_le width;
247 u32_le height;
248 u32_le transform_hint;
249 u32_le num_pending_buffers;
250 u32_le status;
251 };
252 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
253
254 Data data{};
255};
256
257/// Represents a parcel containing one int '0' as its data
258/// Used by DetachBuffer and Disconnect
259class IGBPEmptyResponseParcel : public Parcel {
260protected:
261 void SerializeData() override {
262 Write(data);
263 }
264
265private:
266 struct Data {
267 u32_le unk_0{};
268 };
269
270 Data data{};
271};
272
273class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
274public:
275 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
276 : Parcel(std::move(buffer_)) {
277 Deserialize();
278 }
279
280 void DeserializeData() override {
281 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
282 data = Read<Data>();
283 if (data.contains_object != 0) {
284 buffer_container = Read<BufferContainer>();
285 }
286 }
287
288 struct Data {
289 u32_le slot;
290 u32_le contains_object;
291 };
292
293 struct BufferContainer {
294 u32_le graphic_buffer_length;
295 INSERT_PADDING_WORDS(1);
296 NVFlinger::IGBPBuffer buffer{};
297 };
298
299 Data data{};
300 BufferContainer buffer_container{};
301};
302
303class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
304protected:
305 void SerializeData() override {
306 // TODO(Subv): Find out what this means
307 Write<u32>(0);
308 }
309};
310
311class IGBPCancelBufferRequestParcel : public Parcel {
312public:
313 explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
314 Deserialize();
315 }
316
317 void DeserializeData() override {
318 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
319 data = Read<Data>();
320 }
321
322 struct Data {
323 u32_le slot;
324 Service::Nvidia::MultiFence multi_fence;
325 };
326
327 Data data;
328};
329
330class IGBPCancelBufferResponseParcel : public Parcel {
331protected:
332 void SerializeData() override {
333 Write<u32>(0); // Success
334 }
335};
336
337class IGBPDequeueBufferRequestParcel : public Parcel {
338public:
339 explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
340 Deserialize();
341 }
342
343 void DeserializeData() override {
344 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
345 data = Read<Data>();
346 }
347
348 struct Data {
349 u32_le pixel_format;
350 u32_le width;
351 u32_le height;
352 u32_le get_frame_timestamps;
353 u32_le usage;
354 };
355
356 Data data;
357};
358
359class IGBPDequeueBufferResponseParcel : public Parcel {
360public:
361 explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
362 : slot(slot_), multi_fence(multi_fence_) {}
363
364protected:
365 void SerializeData() override {
366 Write(slot);
367 Write<u32_le>(1);
368 WriteObject(multi_fence);
369 Write<u32_le>(0);
370 }
371
372 u32_le slot;
373 Service::Nvidia::MultiFence multi_fence;
374};
375
376class IGBPRequestBufferRequestParcel : public Parcel {
377public:
378 explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
379 Deserialize();
380 }
381
382 void DeserializeData() override {
383 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
384 slot = Read<u32_le>();
385 }
386
387 u32_le slot;
388};
389
390class IGBPRequestBufferResponseParcel : public Parcel {
391public:
392 explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
393 ~IGBPRequestBufferResponseParcel() override = default;
394
395protected:
396 void SerializeData() override {
397 // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
398 // try to read an IGBPBuffer object from the parcel.
399 Write<u32_le>(1);
400 WriteObject(buffer);
401 Write<u32_le>(0);
402 }
403
404 NVFlinger::IGBPBuffer buffer;
405};
406
407class IGBPQueueBufferRequestParcel : public Parcel {
408public:
409 explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
410 Deserialize();
411 }
412
413 void DeserializeData() override {
414 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
415 data = Read<Data>();
416 }
417
418 struct Data {
419 u32_le slot;
420 INSERT_PADDING_WORDS(3);
421 u32_le timestamp;
422 s32_le is_auto_timestamp;
423 s32_le crop_top;
424 s32_le crop_left;
425 s32_le crop_right;
426 s32_le crop_bottom;
427 s32_le scaling_mode;
428 NVFlinger::BufferQueue::BufferTransformFlags transform;
429 u32_le sticky_transform;
430 INSERT_PADDING_WORDS(1);
431 u32_le swap_interval;
432 Service::Nvidia::MultiFence multi_fence;
433
434 Common::Rectangle<int> GetCropRect() const {
435 return {crop_left, crop_top, crop_right, crop_bottom};
436 }
437 };
438 static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
439
440 Data data;
441};
442
443class IGBPQueueBufferResponseParcel : public Parcel {
444public:
445 explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
446 data.width = width;
447 data.height = height;
448 }
449 ~IGBPQueueBufferResponseParcel() override = default;
450
451protected:
452 void SerializeData() override {
453 Write(data);
454 }
455
456private:
457 struct Data {
458 u32_le width;
459 u32_le height;
460 u32_le transform_hint;
461 u32_le num_pending_buffers;
462 u32_le status;
463 };
464 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
465
466 Data data{};
467};
468
469class IGBPQueryRequestParcel : public Parcel {
470public:
471 explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
472 Deserialize();
473 }
474
475 void DeserializeData() override {
476 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
477 type = Read<u32_le>();
478 }
479
480 u32 type;
481};
482
483class IGBPQueryResponseParcel : public Parcel {
484public:
485 explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
486 ~IGBPQueryResponseParcel() override = default;
487
488protected:
489 void SerializeData() override {
490 Write(value);
491 }
492 66
493private: 67private:
494 u32_le value; 68 const u32 magic = 2;
69 const u32 process_id = 1;
70 const u32 id;
71 INSERT_PADDING_WORDS(3);
72 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
73 INSERT_PADDING_WORDS(2);
495}; 74};
75static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
496 76
497class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
498public: 78public:
499 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
500 : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { 80 : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
81 server(server_) {
501 static const FunctionInfo functions[] = { 82 static const FunctionInfo functions[] = {
502 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 83 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
503 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 84 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +89,16 @@ public:
508 } 89 }
509 90
510private: 91private:
511 enum class TransactionId {
512 RequestBuffer = 1,
513 SetBufferCount = 2,
514 DequeueBuffer = 3,
515 DetachBuffer = 4,
516 DetachNextBuffer = 5,
517 AttachBuffer = 6,
518 QueueBuffer = 7,
519 CancelBuffer = 8,
520 Query = 9,
521 Connect = 10,
522 Disconnect = 11,
523
524 AllocateBuffers = 13,
525 SetPreallocatedBuffer = 14,
526
527 GetBufferHistory = 17
528 };
529
530 void TransactParcel(Kernel::HLERequestContext& ctx) { 92 void TransactParcel(Kernel::HLERequestContext& ctx) {
531 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
532 const u32 id = rp.Pop<u32>(); 94 const u32 id = rp.Pop<u32>();
533 const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); 95 const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
534 const u32 flags = rp.Pop<u32>(); 96 const u32 flags = rp.Pop<u32>();
535 97
536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 98 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
537 transaction, flags); 99 transaction, flags);
538 100
539 auto& buffer_queue = *nv_flinger.FindBufferQueue(id); 101 server.TryGetProducer(id)->Transact(ctx, transaction, flags);
540
541 switch (transaction) {
542 case TransactionId::Connect: {
543 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
544 IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
545 static_cast<u32>(DisplayResolution::UndockedHeight)};
546
547 buffer_queue.Connect();
548
549 ctx.WriteBuffer(response.Serialize());
550 break;
551 }
552 case TransactionId::SetPreallocatedBuffer: {
553 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
554
555 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
556
557 IGBPSetPreallocatedBufferResponseParcel response{};
558 ctx.WriteBuffer(response.Serialize());
559 break;
560 }
561 case TransactionId::DequeueBuffer: {
562 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
563 const u32 width{request.data.width};
564 const u32 height{request.data.height};
565
566 do {
567 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
568 // Buffer is available
569 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
570 ctx.WriteBuffer(response.Serialize());
571 break;
572 }
573 } while (buffer_queue.IsConnected());
574
575 break;
576 }
577 case TransactionId::RequestBuffer: {
578 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
579
580 auto& buffer = buffer_queue.RequestBuffer(request.slot);
581 IGBPRequestBufferResponseParcel response{buffer};
582 ctx.WriteBuffer(response.Serialize());
583
584 break;
585 }
586 case TransactionId::QueueBuffer: {
587 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
588
589 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
590 request.data.GetCropRect(), request.data.swap_interval,
591 request.data.multi_fence);
592
593 IGBPQueueBufferResponseParcel response{1280, 720};
594 ctx.WriteBuffer(response.Serialize());
595 break;
596 }
597 case TransactionId::Query: {
598 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
599
600 const u32 value =
601 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
602
603 IGBPQueryResponseParcel response{value};
604 ctx.WriteBuffer(response.Serialize());
605 break;
606 }
607 case TransactionId::CancelBuffer: {
608 IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
609
610 buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
611
612 IGBPCancelBufferResponseParcel response{};
613 ctx.WriteBuffer(response.Serialize());
614 break;
615 }
616 case TransactionId::Disconnect: {
617 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
618 const auto buffer = ctx.ReadBuffer();
619
620 buffer_queue.Disconnect();
621
622 IGBPEmptyResponseParcel response{};
623 ctx.WriteBuffer(response.Serialize());
624 break;
625 }
626 case TransactionId::DetachBuffer: {
627 const auto buffer = ctx.ReadBuffer();
628
629 IGBPEmptyResponseParcel response{};
630 ctx.WriteBuffer(response.Serialize());
631 break;
632 }
633 case TransactionId::SetBufferCount: {
634 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
635 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
636
637 IGBPEmptyResponseParcel response{};
638 ctx.WriteBuffer(response.Serialize());
639 break;
640 }
641 case TransactionId::GetBufferHistory: {
642 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
643 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
644
645 IGBPEmptyResponseParcel response{};
646 ctx.WriteBuffer(response.Serialize());
647 break;
648 }
649 default:
650 ASSERT_MSG(false, "Unimplemented");
651 }
652 102
653 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(ResultSuccess); 104 rb.Push(ResultSuccess);
@@ -674,13 +124,13 @@ private:
674 124
675 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 125 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
676 126
677 // TODO(Subv): Find out what this actually is.
678 IPC::ResponseBuilder rb{ctx, 2, 1}; 127 IPC::ResponseBuilder rb{ctx, 2, 1};
679 rb.Push(ResultSuccess); 128 rb.Push(ResultSuccess);
680 rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); 129 rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
681 } 130 }
682 131
683 NVFlinger::NVFlinger& nv_flinger; 132private:
133 NVFlinger::HosBinderDriverServer& server;
684}; 134};
685 135
686class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 136class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -937,7 +387,40 @@ private:
937 387
938class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 388class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
939public: 389public:
940 explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 390 IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
391 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
392 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
393 hos_binder_driver_server{hos_binder_driver_server_} {
394
395 static const FunctionInfo functions[] = {
396 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
397 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
398 {102, &IApplicationDisplayService::GetManagerDisplayService,
399 "GetManagerDisplayService"},
400 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
401 "GetIndirectDisplayTransactionService"},
402 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
403 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
404 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
405 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
406 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
407 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
408 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
409 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
410 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
411 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
412 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
413 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
414 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
415 "GetIndirectLayerImageMap"},
416 {2451, nullptr, "GetIndirectLayerImageCropMap"},
417 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
418 "GetIndirectLayerImageRequiredMemoryInfo"},
419 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
420 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
421 };
422 RegisterHandlers(functions);
423 }
941 424
942private: 425private:
943 enum class ConvertedScaleMode : u64 { 426 enum class ConvertedScaleMode : u64 {
@@ -961,7 +444,7 @@ private:
961 444
962 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 445 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
963 rb.Push(ResultSuccess); 446 rb.Push(ResultSuccess);
964 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 447 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
965 } 448 }
966 449
967 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { 450 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +468,7 @@ private:
985 468
986 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 469 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
987 rb.Push(ResultSuccess); 470 rb.Push(ResultSuccess);
988 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 471 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
989 } 472 }
990 473
991 void OpenDisplay(Kernel::HLERequestContext& ctx) { 474 void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1089,7 +572,7 @@ private:
1089 void ListDisplays(Kernel::HLERequestContext& ctx) { 572 void ListDisplays(Kernel::HLERequestContext& ctx) {
1090 LOG_WARNING(Service_VI, "(STUBBED) called"); 573 LOG_WARNING(Service_VI, "(STUBBED) called");
1091 574
1092 DisplayInfo display_info; 575 const DisplayInfo display_info;
1093 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 576 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1094 IPC::ResponseBuilder rb{ctx, 4}; 577 IPC::ResponseBuilder rb{ctx, 4};
1095 rb.Push(ResultSuccess); 578 rb.Push(ResultSuccess);
@@ -1124,8 +607,8 @@ private:
1124 return; 607 return;
1125 } 608 }
1126 609
1127 NativeWindow native_window{*buffer_queue_id}; 610 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1128 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 611 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1129 612
1130 IPC::ResponseBuilder rb{ctx, 4}; 613 IPC::ResponseBuilder rb{ctx, 4};
1131 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -1170,8 +653,8 @@ private:
1170 return; 653 return;
1171 } 654 }
1172 655
1173 NativeWindow native_window{*buffer_queue_id}; 656 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1174 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 657 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1175 658
1176 IPC::ResponseBuilder rb{ctx, 6}; 659 IPC::ResponseBuilder rb{ctx, 6};
1177 rb.Push(ResultSuccess); 660 rb.Push(ResultSuccess);
@@ -1287,39 +770,9 @@ private:
1287 } 770 }
1288 771
1289 NVFlinger::NVFlinger& nv_flinger; 772 NVFlinger::NVFlinger& nv_flinger;
773 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
1290}; 774};
1291 775
1292IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
1293 NVFlinger::NVFlinger& nv_flinger_)
1294 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
1295 static const FunctionInfo functions[] = {
1296 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1297 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
1298 {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
1299 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
1300 "GetIndirectDisplayTransactionService"},
1301 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
1302 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
1303 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
1304 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
1305 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
1306 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
1307 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
1308 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
1309 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
1310 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
1311 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
1312 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
1313 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
1314 {2451, nullptr, "GetIndirectLayerImageCropMap"},
1315 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
1316 "GetIndirectLayerImageRequiredMemoryInfo"},
1317 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
1318 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
1319 };
1320 RegisterHandlers(functions);
1321}
1322
1323static bool IsValidServiceAccess(Permission permission, Policy policy) { 776static bool IsValidServiceAccess(Permission permission, Policy policy) {
1324 if (permission == Permission::User) { 777 if (permission == Permission::User) {
1325 return policy == Policy::User; 778 return policy == Policy::User;
@@ -1333,7 +786,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1333} 786}
1334 787
1335void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 788void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
1336 NVFlinger::NVFlinger& nv_flinger, Permission permission) { 789 NVFlinger::NVFlinger& nv_flinger,
790 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
791 Permission permission) {
1337 IPC::RequestParser rp{ctx}; 792 IPC::RequestParser rp{ctx};
1338 const auto policy = rp.PopEnum<Policy>(); 793 const auto policy = rp.PopEnum<Policy>();
1339 794
@@ -1346,14 +801,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
1346 801
1347 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 802 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1348 rb.Push(ResultSuccess); 803 rb.Push(ResultSuccess);
1349 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); 804 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
1350} 805}
1351 806
1352void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 807void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
1353 NVFlinger::NVFlinger& nv_flinger) { 808 NVFlinger::NVFlinger& nv_flinger,
1354 std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); 809 NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
1355 std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); 810 std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
1356 std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); 811 ->InstallAsService(service_manager);
812 std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
813 ->InstallAsService(service_manager);
814 std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
815 ->InstallAsService(service_manager);
1357} 816}
1358 817
1359} // namespace Service::VI 818} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 2fd7f8e61..d68f2646b 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -15,8 +15,9 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::SM { 22namespace Service::SM {
22class ServiceManager; 23class ServiceManager;
@@ -47,11 +48,14 @@ enum class Policy {
47 48
48namespace detail { 49namespace detail {
49void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 50void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
50 NVFlinger::NVFlinger& nv_flinger, Permission permission); 51 NVFlinger::NVFlinger& nv_flinger,
52 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
53 Permission permission);
51} // namespace detail 54} // namespace detail
52 55
53/// Registers all VI services with the specified service manager. 56/// Registers all VI services with the specified service manager.
54void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 57void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
55 NVFlinger::NVFlinger& nv_flinger); 58 NVFlinger::NVFlinger& nv_flinger,
59 NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
56 60
57} // namespace Service::VI 61} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 87db1c416..be0255f3d 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 16 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_M::~VI_M() = default;
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::Manager);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index d79c41beb..efbd34e09 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_M final : public ServiceFramework<VI_M> { 24class VI_M final : public ServiceFramework<VI_M> {
24public: 25public:
25 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_M() override; 28 ~VI_M() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 5cd22f7df..7996a6811 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 16 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_S::~VI_S() = default;
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::System);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 5f1f8f290..3812c5061 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_S final : public ServiceFramework<VI_S> { 24class VI_S final : public ServiceFramework<VI_S> {
24public: 25public:
25 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_S() override; 28 ~VI_S() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 0079d51f0..57c888313 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 16 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_U::~VI_U() = default;
22void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::User);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 8e3885c73..b08e56576 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_U final : public ServiceFramework<VI_U> { 24class VI_U final : public ServiceFramework<VI_U> {
24public: 25public:
25 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_U() override; 28 ~VI_U() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index a3e0664b9..0784a165d 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) {
600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); 600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
601} 601}
602 602
603Errno Socket::SetKeepAlive(bool enable) {
604 return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
605}
606
603Errno Socket::SetBroadcast(bool enable) { 607Errno Socket::SetBroadcast(bool enable) {
604 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); 608 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
605} 609}
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
index a44393325..caaefce7c 100644
--- a/src/core/network/sockets.h
+++ b/src/core/network/sockets.h
@@ -8,7 +8,6 @@
8#include <utility> 8#include <utility>
9 9
10#if defined(_WIN32) 10#if defined(_WIN32)
11#include <winsock.h>
12#elif !YUZU_UNIX 11#elif !YUZU_UNIX
13#error "Platform not implemented" 12#error "Platform not implemented"
14#endif 13#endif
@@ -68,6 +67,8 @@ public:
68 67
69 Errno SetReuseAddr(bool enable); 68 Errno SetReuseAddr(bool enable);
70 69
70 Errno SetKeepAlive(bool enable);
71
71 Errno SetBroadcast(bool enable); 72 Errno SetBroadcast(bool enable);
72 73
73 Errno SetSndBuf(u32 value); 74 Errno SetSndBuf(u32 value);
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 52c43c857..6ef459b7a 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -53,13 +53,13 @@ PerfStats::~PerfStats() {
53} 53}
54 54
55void PerfStats::BeginSystemFrame() { 55void PerfStats::BeginSystemFrame() {
56 std::lock_guard lock{object_mutex}; 56 std::scoped_lock lock{object_mutex};
57 57
58 frame_begin = Clock::now(); 58 frame_begin = Clock::now();
59} 59}
60 60
61void PerfStats::EndSystemFrame() { 61void PerfStats::EndSystemFrame() {
62 std::lock_guard lock{object_mutex}; 62 std::scoped_lock lock{object_mutex};
63 63
64 auto frame_end = Clock::now(); 64 auto frame_end = Clock::now();
65 const auto frame_time = frame_end - frame_begin; 65 const auto frame_time = frame_end - frame_begin;
@@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
79} 79}
80 80
81double PerfStats::GetMeanFrametime() const { 81double PerfStats::GetMeanFrametime() const {
82 std::lock_guard lock{object_mutex}; 82 std::scoped_lock lock{object_mutex};
83 83
84 if (current_index <= IgnoreFrames) { 84 if (current_index <= IgnoreFrames) {
85 return 0; 85 return 0;
@@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
91} 91}
92 92
93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
94 std::lock_guard lock{object_mutex}; 94 std::scoped_lock lock{object_mutex};
95 95
96 const auto now = Clock::now(); 96 const auto now = Clock::now();
97 // Walltime elapsed since stats were reset 97 // Walltime elapsed since stats were reset
@@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
120} 120}
121 121
122double PerfStats::GetLastFrameTimeScale() const { 122double PerfStats::GetLastFrameTimeScale() const {
123 std::lock_guard lock{object_mutex}; 123 std::scoped_lock lock{object_mutex};
124 124
125 constexpr double FRAME_LENGTH = 1.0 / 60; 125 constexpr double FRAME_LENGTH = 1.0 / 60;
126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index d4becdc0a..89faed6ee 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -8,7 +8,6 @@
8 8
9#include <fmt/chrono.h> 9#include <fmt/chrono.h>
10#include <fmt/format.h> 10#include <fmt/format.h>
11#include <fmt/ostream.h>
12#include <nlohmann/json.hpp> 11#include <nlohmann/json.hpp>
13 12
14#include "common/fs/file.h" 13#include "common/fs/file.h"
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 032c71aff..c81dc0e52 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
80} 80}
81 81
82void Freezer::Clear() { 82void Freezer::Clear() {
83 std::lock_guard lock{entries_mutex}; 83 std::scoped_lock lock{entries_mutex};
84 84
85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); 85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
86 86
@@ -88,7 +88,7 @@ void Freezer::Clear() {
88} 88}
89 89
90u64 Freezer::Freeze(VAddr address, u32 width) { 90u64 Freezer::Freeze(VAddr address, u32 width) {
91 std::lock_guard lock{entries_mutex}; 91 std::scoped_lock lock{entries_mutex};
92 92
93 const auto current_value = MemoryReadWidth(memory, width, address); 93 const auto current_value = MemoryReadWidth(memory, width, address);
94 entries.push_back({address, width, current_value}); 94 entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
101} 101}
102 102
103void Freezer::Unfreeze(VAddr address) { 103void Freezer::Unfreeze(VAddr address) {
104 std::lock_guard lock{entries_mutex}; 104 std::scoped_lock lock{entries_mutex};
105 105
106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
107 107
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
109} 109}
110 110
111bool Freezer::IsFrozen(VAddr address) const { 111bool Freezer::IsFrozen(VAddr address) const {
112 std::lock_guard lock{entries_mutex}; 112 std::scoped_lock lock{entries_mutex};
113 113
114 return FindEntry(address) != entries.cend(); 114 return FindEntry(address) != entries.cend();
115} 115}
116 116
117void Freezer::SetFrozenValue(VAddr address, u64 value) { 117void Freezer::SetFrozenValue(VAddr address, u64 value) {
118 std::lock_guard lock{entries_mutex}; 118 std::scoped_lock lock{entries_mutex};
119 119
120 const auto iter = FindEntry(address); 120 const auto iter = FindEntry(address);
121 121
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
132} 132}
133 133
134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
135 std::lock_guard lock{entries_mutex}; 135 std::scoped_lock lock{entries_mutex};
136 136
137 const auto iter = FindEntry(address); 137 const auto iter = FindEntry(address);
138 138
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144} 144}
145 145
146std::vector<Freezer::Entry> Freezer::GetEntries() const { 146std::vector<Freezer::Entry> Freezer::GetEntries() const {
147 std::lock_guard lock{entries_mutex}; 147 std::scoped_lock lock{entries_mutex};
148 148
149 return entries; 149 return entries;
150} 150}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
165 return; 165 return;
166 } 166 }
167 167
168 std::lock_guard lock{entries_mutex}; 168 std::scoped_lock lock{entries_mutex};
169 169
170 for (const auto& entry : entries) { 170 for (const auto& entry : entries) {
171 LOG_DEBUG(Common_Memory, 171 LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
178} 178}
179 179
180void Freezer::FillEntryReads() { 180void Freezer::FillEntryReads() {
181 std::lock_guard lock{entries_mutex}; 181 std::scoped_lock lock{entries_mutex};
182 182
183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); 183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
184 184
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 155caae42..8abd41726 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -524,4 +524,20 @@ Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& para
524 return Common::Input::ButtonNames::Invalid; 524 return Common::Input::ButtonNames::Invalid;
525} 525}
526 526
527bool GCAdapter::IsStickInverted(const Common::ParamPackage& params) {
528 if (!params.Has("port")) {
529 return false;
530 }
531
532 const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0));
533 const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0));
534 if (x_axis != PadAxes::StickY && x_axis != PadAxes::SubstickY) {
535 return false;
536 }
537 if (y_axis != PadAxes::StickX && y_axis != PadAxes::SubstickX) {
538 return false;
539 }
540 return true;
541}
542
527} // namespace InputCommon 543} // namespace InputCommon
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 7ce1912a3..894ab65a4 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <mutex>
10#include <stop_token> 9#include <stop_token>
11#include <string> 10#include <string>
12#include <thread> 11#include <thread>
@@ -36,6 +35,8 @@ public:
36 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; 35 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
37 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; 36 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
38 37
38 bool IsStickInverted(const Common::ParamPackage& params) override;
39
39private: 40private:
40 enum class PadButton { 41 enum class PadButton {
41 Undefined = 0x0000, 42 Undefined = 0x0000,
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index c17ea305e..a5c63e74a 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -62,7 +62,7 @@ public:
62 62
63 bool UpdateMotion(SDL_ControllerSensorEvent event) { 63 bool UpdateMotion(SDL_ControllerSensorEvent event) {
64 constexpr float gravity_constant = 9.80665f; 64 constexpr float gravity_constant = 9.80665f;
65 std::lock_guard lock{mutex}; 65 std::scoped_lock lock{mutex};
66 const u64 time_difference = event.timestamp - last_motion_update; 66 const u64 time_difference = event.timestamp - last_motion_update;
67 last_motion_update = event.timestamp; 67 last_motion_update = event.timestamp;
68 switch (event.sensor) { 68 switch (event.sensor) {
@@ -241,7 +241,7 @@ private:
241}; 241};
242 242
243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { 243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
244 std::lock_guard lock{joystick_map_mutex}; 244 std::scoped_lock lock{joystick_map_mutex};
245 const auto it = joystick_map.find(guid); 245 const auto it = joystick_map.find(guid);
246 246
247 if (it != joystick_map.end()) { 247 if (it != joystick_map.end()) {
@@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
263 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 263 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
264 const std::string guid = GetGUID(sdl_joystick); 264 const std::string guid = GetGUID(sdl_joystick);
265 265
266 std::lock_guard lock{joystick_map_mutex}; 266 std::scoped_lock lock{joystick_map_mutex};
267 const auto map_it = joystick_map.find(guid); 267 const auto map_it = joystick_map.find(guid);
268 268
269 if (map_it == joystick_map.end()) { 269 if (map_it == joystick_map.end()) {
@@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
297 297
298 const std::string guid = GetGUID(sdl_joystick); 298 const std::string guid = GetGUID(sdl_joystick);
299 299
300 std::lock_guard lock{joystick_map_mutex}; 300 std::scoped_lock lock{joystick_map_mutex};
301 if (joystick_map.find(guid) == joystick_map.end()) { 301 if (joystick_map.find(guid) == joystick_map.end()) {
302 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 302 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
303 PreSetController(joystick->GetPadIdentifier()); 303 PreSetController(joystick->GetPadIdentifier());
@@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
326void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { 326void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
327 const std::string guid = GetGUID(sdl_joystick); 327 const std::string guid = GetGUID(sdl_joystick);
328 328
329 std::lock_guard lock{joystick_map_mutex}; 329 std::scoped_lock lock{joystick_map_mutex};
330 // This call to guid is safe since the joystick is guaranteed to be in the map 330 // This call to guid is safe since the joystick is guaranteed to be in the map
331 const auto& joystick_guid_list = joystick_map[guid]; 331 const auto& joystick_guid_list = joystick_map[guid];
332 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 332 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
@@ -392,7 +392,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
392} 392}
393 393
394void SDLDriver::CloseJoysticks() { 394void SDLDriver::CloseJoysticks() {
395 std::lock_guard lock{joystick_map_mutex}; 395 std::scoped_lock lock{joystick_map_mutex};
396 joystick_map.clear(); 396 joystick_map.clear();
397} 397}
398 398
@@ -934,4 +934,37 @@ u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const {
934 return direction; 934 return direction;
935} 935}
936 936
937bool SDLDriver::IsStickInverted(const Common::ParamPackage& params) {
938 if (!params.Has("guid") || !params.Has("port")) {
939 return false;
940 }
941 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
942 if (joystick == nullptr) {
943 return false;
944 }
945 auto* controller = joystick->GetSDLGameController();
946 if (controller == nullptr) {
947 return false;
948 }
949
950 const auto& axis_x = params.Get("axis_x", 0);
951 const auto& axis_y = params.Get("axis_y", 0);
952 const auto& binding_left_x =
953 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
954 const auto& binding_right_x =
955 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
956 const auto& binding_left_y =
957 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
958 const auto& binding_right_y =
959 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
960
961 if (axis_x != binding_left_y.value.axis && axis_x != binding_right_y.value.axis) {
962 return false;
963 }
964 if (axis_y != binding_left_x.value.axis && axis_y != binding_right_x.value.axis) {
965 return false;
966 }
967 return true;
968}
969
937} // namespace InputCommon 970} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 4cde3606f..dcd0d1e64 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -58,6 +58,8 @@ public:
58 std::string GetHatButtonName(u8 direction_value) const override; 58 std::string GetHatButtonName(u8 direction_value) const override;
59 u8 GetHatButtonId(const std::string& direction_name) const override; 59 u8 GetHatButtonId(const std::string& direction_name) const override;
60 60
61 bool IsStickInverted(const Common::ParamPackage& params) override;
62
61 Common::Input::VibrationError SetRumble( 63 Common::Input::VibrationError SetRumble(
62 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 64 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
63 65
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 9780ead10..825262a07 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -547,6 +547,22 @@ Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& para
547 return Common::Input::ButtonNames::Invalid; 547 return Common::Input::ButtonNames::Invalid;
548} 548}
549 549
550bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
551 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
552 return false;
553 }
554
555 const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0));
556 const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0));
557 if (x_axis != PadAxes::LeftStickY && x_axis != PadAxes::RightStickY) {
558 return false;
559 }
560 if (y_axis != PadAxes::LeftStickX && y_axis != PadAxes::RightStickX) {
561 return false;
562 }
563 return true;
564}
565
550void TestCommunication(const std::string& host, u16 port, 566void TestCommunication(const std::string& host, u16 port,
551 const std::function<void()>& success_callback, 567 const std::function<void()>& success_callback,
552 const std::function<void()>& failure_callback) { 568 const std::function<void()>& failure_callback) {
diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h
index c7cc7d846..dece2a45b 100644
--- a/src/input_common/drivers/udp_client.h
+++ b/src/input_common/drivers/udp_client.h
@@ -64,6 +64,8 @@ public:
64 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; 64 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
65 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; 65 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
66 66
67 bool IsStickInverted(const Common::ParamPackage& params) override;
68
67private: 69private:
68 enum class PadButton { 70 enum class PadButton {
69 Undefined = 0x0000, 71 Undefined = 0x0000,
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
index 2d5d54ddb..597f51cd3 100644
--- a/src/input_common/helpers/udp_protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -8,9 +8,17 @@
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10 10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
14#endif
15
11#include <boost/crc.hpp> 16#include <boost/crc.hpp>
12 17
13#include "common/bit_field.h" 18#ifdef _MSC_VER
19#pragma warning(pop)
20#endif
21
14#include "common/swap.h" 22#include "common/swap.h"
15 23
16namespace InputCommon::CemuhookUDP { 24namespace InputCommon::CemuhookUDP {
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 7adf7e3d7..a16cca33d 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -3,43 +3,42 @@
3// Refer to the license.txt file included 3// Refer to the license.txt file included
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/param_package.h"
7#include "input_common/input_engine.h" 6#include "input_common/input_engine.h"
8 7
9namespace InputCommon { 8namespace InputCommon {
10 9
11void InputEngine::PreSetController(const PadIdentifier& identifier) { 10void InputEngine::PreSetController(const PadIdentifier& identifier) {
12 std::lock_guard lock{mutex}; 11 std::scoped_lock lock{mutex};
13 controller_list.try_emplace(identifier); 12 controller_list.try_emplace(identifier);
14} 13}
15 14
16void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { 15void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
17 std::lock_guard lock{mutex}; 16 std::scoped_lock lock{mutex};
18 ControllerData& controller = controller_list.at(identifier); 17 ControllerData& controller = controller_list.at(identifier);
19 controller.buttons.try_emplace(button, false); 18 controller.buttons.try_emplace(button, false);
20} 19}
21 20
22void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { 21void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
23 std::lock_guard lock{mutex}; 22 std::scoped_lock lock{mutex};
24 ControllerData& controller = controller_list.at(identifier); 23 ControllerData& controller = controller_list.at(identifier);
25 controller.hat_buttons.try_emplace(button, u8{0}); 24 controller.hat_buttons.try_emplace(button, u8{0});
26} 25}
27 26
28void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { 27void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
29 std::lock_guard lock{mutex}; 28 std::scoped_lock lock{mutex};
30 ControllerData& controller = controller_list.at(identifier); 29 ControllerData& controller = controller_list.at(identifier);
31 controller.axes.try_emplace(axis, 0.0f); 30 controller.axes.try_emplace(axis, 0.0f);
32} 31}
33 32
34void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { 33void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
35 std::lock_guard lock{mutex}; 34 std::scoped_lock lock{mutex};
36 ControllerData& controller = controller_list.at(identifier); 35 ControllerData& controller = controller_list.at(identifier);
37 controller.motions.try_emplace(motion); 36 controller.motions.try_emplace(motion);
38} 37}
39 38
40void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { 39void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
41 { 40 {
42 std::lock_guard lock{mutex}; 41 std::scoped_lock lock{mutex};
43 ControllerData& controller = controller_list.at(identifier); 42 ControllerData& controller = controller_list.at(identifier);
44 if (!configuring) { 43 if (!configuring) {
45 controller.buttons.insert_or_assign(button, value); 44 controller.buttons.insert_or_assign(button, value);
@@ -50,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va
50 49
51void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { 50void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
52 { 51 {
53 std::lock_guard lock{mutex}; 52 std::scoped_lock lock{mutex};
54 ControllerData& controller = controller_list.at(identifier); 53 ControllerData& controller = controller_list.at(identifier);
55 if (!configuring) { 54 if (!configuring) {
56 controller.hat_buttons.insert_or_assign(button, value); 55 controller.hat_buttons.insert_or_assign(button, value);
@@ -61,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v
61 60
62void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { 61void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
63 { 62 {
64 std::lock_guard lock{mutex}; 63 std::scoped_lock lock{mutex};
65 ControllerData& controller = controller_list.at(identifier); 64 ControllerData& controller = controller_list.at(identifier);
66 if (!configuring) { 65 if (!configuring) {
67 controller.axes.insert_or_assign(axis, value); 66 controller.axes.insert_or_assign(axis, value);
@@ -72,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
72 71
73void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { 72void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
74 { 73 {
75 std::lock_guard lock{mutex}; 74 std::scoped_lock lock{mutex};
76 ControllerData& controller = controller_list.at(identifier); 75 ControllerData& controller = controller_list.at(identifier);
77 if (!configuring) { 76 if (!configuring) {
78 controller.battery = value; 77 controller.battery = value;
@@ -83,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
83 82
84void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { 83void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
85 { 84 {
86 std::lock_guard lock{mutex}; 85 std::scoped_lock lock{mutex};
87 ControllerData& controller = controller_list.at(identifier); 86 ControllerData& controller = controller_list.at(identifier);
88 if (!configuring) { 87 if (!configuring) {
89 controller.motions.insert_or_assign(motion, value); 88 controller.motions.insert_or_assign(motion, value);
@@ -93,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
93} 92}
94 93
95bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { 94bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
96 std::lock_guard lock{mutex}; 95 std::scoped_lock lock{mutex};
97 const auto controller_iter = controller_list.find(identifier); 96 const auto controller_iter = controller_list.find(identifier);
98 if (controller_iter == controller_list.cend()) { 97 if (controller_iter == controller_list.cend()) {
99 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 98 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -110,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
110} 109}
111 110
112bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { 111bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
113 std::lock_guard lock{mutex}; 112 std::scoped_lock lock{mutex};
114 const auto controller_iter = controller_list.find(identifier); 113 const auto controller_iter = controller_list.find(identifier);
115 if (controller_iter == controller_list.cend()) { 114 if (controller_iter == controller_list.cend()) {
116 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 115 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -127,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
127} 126}
128 127
129f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { 128f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
130 std::lock_guard lock{mutex}; 129 std::scoped_lock lock{mutex};
131 const auto controller_iter = controller_list.find(identifier); 130 const auto controller_iter = controller_list.find(identifier);
132 if (controller_iter == controller_list.cend()) { 131 if (controller_iter == controller_list.cend()) {
133 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 132 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -144,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
144} 143}
145 144
146Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { 145Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
147 std::lock_guard lock{mutex}; 146 std::scoped_lock lock{mutex};
148 const auto controller_iter = controller_list.find(identifier); 147 const auto controller_iter = controller_list.find(identifier);
149 if (controller_iter == controller_list.cend()) { 148 if (controller_iter == controller_list.cend()) {
150 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 149 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -156,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
156} 155}
157 156
158BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { 157BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
159 std::lock_guard lock{mutex}; 158 std::scoped_lock lock{mutex};
160 const auto controller_iter = controller_list.find(identifier); 159 const auto controller_iter = controller_list.find(identifier);
161 if (controller_iter == controller_list.cend()) { 160 if (controller_iter == controller_list.cend()) {
162 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 161 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -187,7 +186,7 @@ void InputEngine::ResetAnalogState() {
187} 186}
188 187
189void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { 188void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
190 std::lock_guard lock{mutex_callback}; 189 std::scoped_lock lock{mutex_callback};
191 for (const auto& poller_pair : callback_list) { 190 for (const auto& poller_pair : callback_list) {
192 const InputIdentifier& poller = poller_pair.second; 191 const InputIdentifier& poller = poller_pair.second;
193 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { 192 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
@@ -215,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
215} 214}
216 215
217void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { 216void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
218 std::lock_guard lock{mutex_callback}; 217 std::scoped_lock lock{mutex_callback};
219 for (const auto& poller_pair : callback_list) { 218 for (const auto& poller_pair : callback_list) {
220 const InputIdentifier& poller = poller_pair.second; 219 const InputIdentifier& poller = poller_pair.second;
221 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { 220 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
@@ -244,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
244} 243}
245 244
246void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { 245void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
247 std::lock_guard lock{mutex_callback}; 246 std::scoped_lock lock{mutex_callback};
248 for (const auto& poller_pair : callback_list) { 247 for (const auto& poller_pair : callback_list) {
249 const InputIdentifier& poller = poller_pair.second; 248 const InputIdentifier& poller = poller_pair.second;
250 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { 249 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
@@ -271,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
271 270
272void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, 271void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
273 [[maybe_unused]] Common::Input::BatteryLevel value) { 272 [[maybe_unused]] Common::Input::BatteryLevel value) {
274 std::lock_guard lock{mutex_callback}; 273 std::scoped_lock lock{mutex_callback};
275 for (const auto& poller_pair : callback_list) { 274 for (const auto& poller_pair : callback_list) {
276 const InputIdentifier& poller = poller_pair.second; 275 const InputIdentifier& poller = poller_pair.second;
277 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { 276 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
@@ -285,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
285 284
286void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 285void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
287 const BasicMotion& value) { 286 const BasicMotion& value) {
288 std::lock_guard lock{mutex_callback}; 287 std::scoped_lock lock{mutex_callback};
289 for (const auto& poller_pair : callback_list) { 288 for (const auto& poller_pair : callback_list) {
290 const InputIdentifier& poller = poller_pair.second; 289 const InputIdentifier& poller = poller_pair.second;
291 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { 290 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
@@ -347,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const {
347} 346}
348 347
349int InputEngine::SetCallback(InputIdentifier input_identifier) { 348int InputEngine::SetCallback(InputIdentifier input_identifier) {
350 std::lock_guard lock{mutex_callback}; 349 std::scoped_lock lock{mutex_callback};
351 callback_list.insert_or_assign(last_callback_key, std::move(input_identifier)); 350 callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
352 return last_callback_key++; 351 return last_callback_key++;
353} 352}
354 353
355void InputEngine::SetMappingCallback(MappingCallback callback) { 354void InputEngine::SetMappingCallback(MappingCallback callback) {
356 std::lock_guard lock{mutex_callback}; 355 std::scoped_lock lock{mutex_callback};
357 mapping_callback = std::move(callback); 356 mapping_callback = std::move(callback);
358} 357}
359 358
360void InputEngine::DeleteCallback(int key) { 359void InputEngine::DeleteCallback(int key) {
361 std::lock_guard lock{mutex_callback}; 360 std::scoped_lock lock{mutex_callback};
362 const auto& iterator = callback_list.find(key); 361 const auto& iterator = callback_list.find(key);
363 if (iterator == callback_list.end()) { 362 if (iterator == callback_list.end()) {
364 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 363 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index f44e0799b..f94b7669c 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -157,6 +157,11 @@ public:
157 return 0; 157 return 0;
158 } 158 }
159 159
160 /// Returns true if axis of a stick aren't mapped in the correct direction
161 virtual bool IsStickInverted([[maybe_unused]] const Common::ParamPackage& params) {
162 return false;
163 }
164
160 void PreSetController(const PadIdentifier& identifier); 165 void PreSetController(const PadIdentifier& identifier);
161 void PreSetButton(const PadIdentifier& identifier, int button); 166 void PreSetButton(const PadIdentifier& identifier, int button);
162 void PreSetHatButton(const PadIdentifier& identifier, int button); 167 void PreSetHatButton(const PadIdentifier& identifier, int button);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index a4d7ed645..21834fb6b 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <thread>
7#include "common/input.h" 6#include "common/input.h"
8#include "common/param_package.h" 7#include "common/param_package.h"
9#include "input_common/drivers/gc_adapter.h" 8#include "input_common/drivers/gc_adapter.h"
@@ -242,6 +241,28 @@ struct InputSubsystem::Impl {
242 return Common::Input::ButtonNames::Invalid; 241 return Common::Input::ButtonNames::Invalid;
243 } 242 }
244 243
244 bool IsStickInverted(const Common::ParamPackage& params) {
245 const std::string engine = params.Get("engine", "");
246 if (engine == mouse->GetEngineName()) {
247 return mouse->IsStickInverted(params);
248 }
249 if (engine == gcadapter->GetEngineName()) {
250 return gcadapter->IsStickInverted(params);
251 }
252 if (engine == udp_client->GetEngineName()) {
253 return udp_client->IsStickInverted(params);
254 }
255 if (engine == tas_input->GetEngineName()) {
256 return tas_input->IsStickInverted(params);
257 }
258#ifdef HAVE_SDL2
259 if (engine == sdl->GetEngineName()) {
260 return sdl->IsStickInverted(params);
261 }
262#endif
263 return false;
264 }
265
245 bool IsController(const Common::ParamPackage& params) { 266 bool IsController(const Common::ParamPackage& params) {
246 const std::string engine = params.Get("engine", ""); 267 const std::string engine = params.Get("engine", "");
247 if (engine == mouse->GetEngineName()) { 268 if (engine == mouse->GetEngineName()) {
@@ -385,6 +406,13 @@ bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
385 return impl->IsController(params); 406 return impl->IsController(params);
386} 407}
387 408
409bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
410 if (params.Has("axis_x") && params.Has("axis_y")) {
411 return impl->IsStickInverted(params);
412 }
413 return false;
414}
415
388void InputSubsystem::ReloadInputDevices() { 416void InputSubsystem::ReloadInputDevices() {
389 impl->udp_client.get()->ReloadSockets(); 417 impl->udp_client.get()->ReloadSockets();
390} 418}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index baf107e0f..147c310c4 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -119,6 +119,9 @@ public:
119 /// Returns true if device is a controller. 119 /// Returns true if device is a controller.
120 [[nodiscard]] bool IsController(const Common::ParamPackage& params) const; 120 [[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
121 121
122 /// Returns true if axis of a stick aren't mapped in the correct direction
123 [[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const;
124
122 /// Reloads the input devices. 125 /// Reloads the input devices.
123 void ReloadInputDevices(); 126 void ReloadInputDevices();
124 127
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
index 4cff70fe4..8603c6be2 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
index 356640471..e4b6b6f31 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
index f0fd94a28..44b363b50 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index 86287ee3f..affe35be7 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -2,11 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
11 8
12#ifdef _MSC_VER 9#ifdef _MSC_VER
diff --git a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
index 0401953f7..af88b7dfa 100644
--- a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/bindings.h" 5#include "shader_recompiler/backend/bindings.h"
8#include "shader_recompiler/backend/glasm/emit_glasm.h" 6#include "shader_recompiler/backend/glasm/emit_glasm.h"
9#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 7#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
index 201e428c1..4e98d3ea0 100644
--- a/src/shader_recompiler/backend/glasm/reg_alloc.cpp
+++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
@@ -2,11 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string>
6
7#include <fmt/format.h> 5#include <fmt/format.h>
8 6
9#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
10#include "shader_recompiler/backend/glasm/reg_alloc.h" 7#include "shader_recompiler/backend/glasm/reg_alloc.h"
11#include "shader_recompiler/exception.h" 8#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
index 8a9faa394..0f204495c 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
@@ -4,7 +4,6 @@
4 4
5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
6#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h"
8 7
9namespace Shader::Backend::GLSL { 8namespace Shader::Backend::GLSL {
10void EmitBarrier(EmitContext& ctx) { 9void EmitBarrier(EmitContext& ctx) {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 0c1fbc7b1..282668b36 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -35,6 +35,15 @@ std::string_view OutputVertexIndex(EmitContext& ctx) {
35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; 35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
36} 36}
37 37
38std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
39 if (binding.IsImmediate()) {
40 return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
41 } else {
42 const auto binding_var{ctx.var_alloc.Consume(binding)};
43 return fmt::format("GetCbufIndirect({},{})", binding_var, index);
44 }
45}
46
38void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, 47void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
39 const IR::Value& offset, u32 num_bits, std::string_view cast = {}, 48 const IR::Value& offset, u32 num_bits, std::string_view cast = {},
40 std::string_view bit_offset = {}) { 49 std::string_view bit_offset = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
55 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32())) 64 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
56 : fmt::format("[({}>>2)%4]", offset_var)}; 65 : fmt::format("[({}>>2)%4]", offset_var)};
57 66
58 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; 67 const auto cbuf{ChooseCbuf(ctx, binding, index)};
59 const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)}; 68 const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
60 const auto extraction{num_bits == 32 ? cbuf_cast 69 const auto extraction{num_bits == 32 ? cbuf_cast
61 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast, 70 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
62 bit_offset, num_bits)}; 71 bit_offset, num_bits)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 149
141void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 150void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
142 const IR::Value& offset) { 151 const IR::Value& offset) {
143 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
144 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"}; 152 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
145 if (offset.IsImmediate()) { 153 if (offset.IsImmediate()) {
154 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
146 static constexpr u32 cbuf_size{0x10000}; 155 static constexpr u32 cbuf_size{0x10000};
147 const u32 u32_offset{offset.U32()}; 156 const u32 u32_offset{offset.U32()};
148 const s32 signed_offset{static_cast<s32>(offset.U32())}; 157 const s32 signed_offset{static_cast<s32>(offset.U32())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
162 return; 171 return;
163 } 172 }
164 const auto offset_var{ctx.var_alloc.Consume(offset)}; 173 const auto offset_var{ctx.var_alloc.Consume(offset)};
174 const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
165 if (!ctx.profile.has_gl_component_indexing_bug) { 175 if (!ctx.profile.has_gl_component_indexing_bug) {
166 ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst, 176 ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
167 cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var); 177 offset_var, cast, cbuf, offset_var);
168 return; 178 return;
169 } 179 }
170 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)}; 180 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
171 const auto cbuf_offset{fmt::format("{}>>2", offset_var)}; 181 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
172 for (u32 swizzle = 0; swizzle < 4; ++swizzle) { 182 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
173 ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset, 183 ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
174 swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var, 184 cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
175 "xyzw"[(swizzle + 1) % 4]);
176 } 185 }
177} 186}
178 187
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
index c86465e8b..6f61560f1 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/exception.h" 7#include "shader_recompiler/exception.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
index b0d85be99..64322cdbf 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
index 742fec9cf..162a8c461 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
index 74ae345e5..f8aa4ecd6 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index fcf620b79..8b94ea90a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
index cace1db85..9367524e8 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9 7
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index e816a93ec..17266f40d 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 DefineConstantBuffers(bindings); 361 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect();
362 DefineStorageBuffers(bindings); 363 DefineStorageBuffers(bindings);
363 SetupImages(bindings); 364 SetupImages(bindings);
364 SetupTextures(bindings); 365 SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
436 } 437 }
437} 438}
438 439
440void EmitContext::DefineConstantBufferIndirect() {
441 if (!info.uses_cbuf_indirect) {
442 return;
443 }
444
445 header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
446 header += "GetCbufIndirect(uint binding, uint offset){"
447 "switch(binding){"
448 "default:";
449
450 for (const auto& desc : info.constant_buffer_descriptors) {
451 header +=
452 fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
453 }
454
455 header += "}}";
456}
457
439void EmitContext::DefineStorageBuffers(Bindings& bindings) { 458void EmitContext::DefineStorageBuffers(Bindings& bindings) {
440 if (info.storage_buffers_descriptors.empty()) { 459 if (info.storage_buffers_descriptors.empty()) {
441 return; 460 return;
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d29..2b13db6e6 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
@@ -162,6 +162,7 @@ public:
162private: 162private:
163 void SetupExtensions(); 163 void SetupExtensions();
164 void DefineConstantBuffers(Bindings& bindings); 164 void DefineConstantBuffers(Bindings& bindings);
165 void DefineConstantBufferIndirect();
165 void DefineStorageBuffers(Bindings& bindings); 166 void DefineStorageBuffers(Bindings& bindings);
166 void DefineGenericOutput(size_t index, u32 invocations); 167 void DefineGenericOutput(size_t index, u32 invocations);
167 void DefineHelperFunctions(); 168 void DefineHelperFunctions();
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index b412957c7..2b360e073 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -22,7 +22,7 @@ constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
22struct RescalingLayout { 22struct RescalingLayout {
23 alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures; 23 alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
24 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; 24 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
25 alignas(16) u32 down_factor; 25 u32 down_factor;
26}; 26};
27constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); 27constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
28constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); 28constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index d3cbb14a9..cb47d253c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
index 9ce95a41b..6ecaa3937 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
@@ -2,10 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8#include "shader_recompiler/frontend/ir/modifiers.h"
9 7
10namespace Shader::Backend::SPIRV { 8namespace Shader::Backend::SPIRV {
11namespace { 9namespace {
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 02d1e63f7..831c6f158 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
index 5c3e1ee2b..812c3668d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -2,10 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8#include "shader_recompiler/frontend/ir/modifiers.h"
9 7
10namespace Shader::Backend::SPIRV { 8namespace Shader::Backend::SPIRV {
11 9
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 8ea730c80..aa7082978 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -2,10 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
5#include <tuple> 6#include <tuple>
6#include <utility> 7#include <utility>
7 8
8#include "shader_recompiler/backend/spirv/emit_spirv.h"
9#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 9#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
10#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 10#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
11 11
@@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
123} 123}
124 124
125Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, 125Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size,
126 const IR::Value& binding, const IR::Value& offset) { 126 const IR::Value& binding, const IR::Value& offset, const Id indirect_func) {
127 Id buffer_offset;
128 const Id uniform_type{ctx.uniform_types.*member_ptr};
129 if (offset.IsImmediate()) {
130 // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
131 const Id imm_offset{ctx.Const(offset.U32() / element_size)};
132 buffer_offset = imm_offset;
133 } else if (element_size > 1) {
134 const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
135 const Id shift{ctx.Const(log2_element_size)};
136 buffer_offset = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
137 } else {
138 buffer_offset = ctx.Def(offset);
139 }
127 if (!binding.IsImmediate()) { 140 if (!binding.IsImmediate()) {
128 throw NotImplementedException("Constant buffer indexing"); 141 return ctx.OpFunctionCall(result_type, indirect_func, ctx.Def(binding), buffer_offset);
129 } 142 }
130 const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; 143 const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
131 const Id uniform_type{ctx.uniform_types.*member_ptr}; 144 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, buffer_offset)};
132 if (!offset.IsImmediate()) {
133 Id index{ctx.Def(offset)};
134 if (element_size > 1) {
135 const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
136 const Id shift{ctx.Const(log2_element_size)};
137 index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
138 }
139 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
140 return ctx.OpLoad(result_type, access_chain);
141 }
142 // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
143 const Id imm_offset{ctx.Const(offset.U32() / element_size)};
144 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
145 return ctx.OpLoad(result_type, access_chain); 145 return ctx.OpLoad(result_type, access_chain);
146} 146}
147 147
148Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 148Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
149 return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset); 149 return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset,
150 ctx.load_const_func_u32);
150} 151}
151 152
152Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 153Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
153 return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset); 154 return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset,
155 ctx.load_const_func_u32x4);
154} 156}
155 157
156Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { 158Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
@@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
201 203
202Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 204Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
203 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { 205 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
204 const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; 206 const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
207 ctx.load_const_func_u8)};
205 return ctx.OpUConvert(ctx.U32[1], load); 208 return ctx.OpUConvert(ctx.U32[1], load);
206 } 209 }
207 Id element{}; 210 Id element{};
@@ -217,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
217 220
218Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 221Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
219 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { 222 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
220 const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)}; 223 const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
224 ctx.load_const_func_u8)};
221 return ctx.OpSConvert(ctx.U32[1], load); 225 return ctx.OpSConvert(ctx.U32[1], load);
222 } 226 }
223 Id element{}; 227 Id element{};
@@ -233,8 +237,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
233 237
234Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 238Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
235 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { 239 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
236 const Id load{ 240 const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
237 GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)}; 241 ctx.load_const_func_u16)};
238 return ctx.OpUConvert(ctx.U32[1], load); 242 return ctx.OpUConvert(ctx.U32[1], load);
239 } 243 }
240 Id element{}; 244 Id element{};
@@ -250,8 +254,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
250 254
251Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 255Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
252 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { 256 if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
253 const Id load{ 257 const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
254 GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)}; 258 ctx.load_const_func_u16)};
255 return ctx.OpSConvert(ctx.U32[1], load); 259 return ctx.OpSConvert(ctx.U32[1], load);
256 } 260 }
257 Id element{}; 261 Id element{};
@@ -276,7 +280,8 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
276 280
277Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 281Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
278 if (ctx.profile.support_descriptor_aliasing) { 282 if (ctx.profile.support_descriptor_aliasing) {
279 return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset); 283 return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset,
284 ctx.load_const_func_f32);
280 } else { 285 } else {
281 const Id vector{GetCbufU32x4(ctx, binding, offset)}; 286 const Id vector{GetCbufU32x4(ctx, binding, offset)};
282 return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); 287 return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u));
@@ -285,8 +290,8 @@ Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
285 290
286Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 291Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
287 if (ctx.profile.support_descriptor_aliasing) { 292 if (ctx.profile.support_descriptor_aliasing) {
288 return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, 293 return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset,
289 offset); 294 ctx.load_const_func_u32x2);
290 } else { 295 } else {
291 const Id vector{GetCbufU32x4(ctx, binding, offset)}; 296 const Id vector{GetCbufU32x4(ctx, binding, offset)};
292 return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), 297 return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u),
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 1eca3aa85..d1afd47b8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
index 832de2452..137a0e257 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
index 0cdc46495..9f65fa269 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
index a96190bc6..727ac2027 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 44521f539..45a384e46 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
index 47745f7ee..74b6efe01 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
index 48caf1ffc..ce55cd31c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
index 330c9052c..b57c66828 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
index b5766fc52..00c6e86e2 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 7034228bf..905c735ad 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 5#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 6#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
8 7
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index cd90c084a..9c83cd2e4 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -4,8 +4,8 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bit>
7#include <climits> 8#include <climits>
8#include <string_view>
9 9
10#include <boost/container/static_vector.hpp> 10#include <boost/container/static_vector.hpp>
11 11
@@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
464 DefineSharedMemory(program); 464 DefineSharedMemory(program);
465 DefineSharedMemoryFunctions(program); 465 DefineSharedMemoryFunctions(program);
466 DefineConstantBuffers(program.info, uniform_binding); 466 DefineConstantBuffers(program.info, uniform_binding);
467 DefineConstantBufferIndirectFunctions(program.info);
467 DefineStorageBuffers(program.info, storage_binding); 468 DefineStorageBuffers(program.info, storage_binding);
468 DefineTextureBuffers(program.info, texture_binding); 469 DefineTextureBuffers(program.info, texture_binding);
469 DefineImageBuffers(program.info, image_binding); 470 DefineImageBuffers(program.info, image_binding);
@@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
993 } 994 }
994 return; 995 return;
995 } 996 }
996 IR::Type types{info.used_constant_buffer_types}; 997 IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
997 if (True(types & IR::Type::U8)) { 998 if (True(types & IR::Type::U8)) {
998 if (profile.support_int8) { 999 if (profile.support_int8) {
999 DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); 1000 DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
@@ -1027,6 +1028,63 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
1027 binding += static_cast<u32>(info.constant_buffer_descriptors.size()); 1028 binding += static_cast<u32>(info.constant_buffer_descriptors.size());
1028} 1029}
1029 1030
1031void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
1032 if (!info.uses_cbuf_indirect) {
1033 return;
1034 }
1035 const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) {
1036 const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])};
1037 const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)};
1038 const Id binding{OpFunctionParameter(U32[1])};
1039 const Id offset{OpFunctionParameter(U32[1])};
1040
1041 AddLabel();
1042
1043 const Id merge_label{OpLabel()};
1044 const Id uniform_type{uniform_types.*member_ptr};
1045
1046 std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels;
1047 std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals;
1048 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1049 buf_labels[i] = OpLabel();
1050 buf_literals[i] = Sirit::Literal{i};
1051 }
1052 OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
1053 OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
1054 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1055 AddLabel(buf_labels[i]);
1056 const Id cbuf{cbufs[i].*member_ptr};
1057 const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
1058 const Id result{OpLoad(buffer_type, access_chain)};
1059 OpReturnValue(result);
1060 }
1061 AddLabel(merge_label);
1062 OpUnreachable();
1063 OpFunctionEnd();
1064 return func;
1065 }};
1066 IR::Type types{info.used_indirect_cbuf_types};
1067 bool supports_aliasing = profile.support_descriptor_aliasing;
1068 if (supports_aliasing && True(types & IR::Type::U8)) {
1069 load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
1070 }
1071 if (supports_aliasing && True(types & IR::Type::U16)) {
1072 load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
1073 }
1074 if (supports_aliasing && True(types & IR::Type::F32)) {
1075 load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
1076 }
1077 if (supports_aliasing && True(types & IR::Type::U32)) {
1078 load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
1079 }
1080 if (supports_aliasing && True(types & IR::Type::U32x2)) {
1081 load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
1082 }
1083 if (!supports_aliasing || True(types & IR::Type::U32x4)) {
1084 load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
1085 }
1086}
1087
1030void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { 1088void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
1031 if (info.storage_buffers_descriptors.empty()) { 1089 if (info.storage_buffers_descriptors.empty()) {
1032 return; 1090 return;
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index f87138f7e..b9115a405 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <string_view>
9 8
10#include <sirit/sirit.h> 9#include <sirit/sirit.h>
11 10
@@ -294,6 +293,13 @@ public:
294 293
295 std::vector<Id> interfaces; 294 std::vector<Id> interfaces;
296 295
296 Id load_const_func_u8{};
297 Id load_const_func_u16{};
298 Id load_const_func_u32{};
299 Id load_const_func_f32{};
300 Id load_const_func_u32x2{};
301 Id load_const_func_u32x4{};
302
297private: 303private:
298 void DefineCommonTypes(const Info& info); 304 void DefineCommonTypes(const Info& info);
299 void DefineCommonConstants(); 305 void DefineCommonConstants();
@@ -302,6 +308,7 @@ private:
302 void DefineSharedMemory(const IR::Program& program); 308 void DefineSharedMemory(const IR::Program& program);
303 void DefineSharedMemoryFunctions(const IR::Program& program); 309 void DefineSharedMemoryFunctions(const IR::Program& program);
304 void DefineConstantBuffers(const Info& info, u32& binding); 310 void DefineConstantBuffers(const Info& info, u32& binding);
311 void DefineConstantBufferIndirectFunctions(const Info& info);
305 void DefineStorageBuffers(const Info& info, u32& binding); 312 void DefineStorageBuffers(const Info& info, u32& binding);
306 void DefineTextureBuffers(const Info& info, u32& binding); 313 void DefineTextureBuffers(const Info& info, u32& binding);
307 void DefineImageBuffers(const Info& info, u32& binding); 314 void DefineImageBuffers(const Info& info, u32& binding);
diff --git a/src/shader_recompiler/exception.h b/src/shader_recompiler/exception.h
index d98b6029b..a6aecde3e 100644
--- a/src/shader_recompiler/exception.h
+++ b/src/shader_recompiler/exception.h
@@ -6,7 +6,6 @@
6 6
7#include <exception> 7#include <exception>
8#include <string> 8#include <string>
9#include <string_view>
10#include <utility> 9#include <utility>
11 10
12#include "common/logging/formatter.h" 11#include "common/logging/formatter.h"
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
index 974efa4a0..a1472cb76 100644
--- a/src/shader_recompiler/frontend/ir/basic_block.cpp
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -5,9 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <initializer_list> 6#include <initializer_list>
7#include <map> 7#include <map>
8#include <memory>
9 8
10#include "common/bit_cast.h"
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "shader_recompiler/frontend/ir/basic_block.h" 10#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/value.h" 11#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/frontend/ir/condition.h b/src/shader_recompiler/frontend/ir/condition.h
index aa8597c60..2f8c10e71 100644
--- a/src/shader_recompiler/frontend/ir/condition.h
+++ b/src/shader_recompiler/frontend/ir/condition.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <compare>
8#include <string> 7#include <string>
9 8
10#include <fmt/format.h> 9#include <fmt/format.h>
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 631446cf7..4a2564f47 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -326,6 +326,11 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
326 phi_args.emplace_back(predecessor, value); 326 phi_args.emplace_back(predecessor, value);
327} 327}
328 328
329void Inst::ErasePhiOperand(size_t index) {
330 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
331 phi_args.erase(operand_it);
332}
333
329void Inst::OrderPhiArgs() { 334void Inst::OrderPhiArgs() {
330 if (op != Opcode::Phi) { 335 if (op != Opcode::Phi) {
331 throw LogicError("{} is not a Phi instruction", op); 336 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.cpp b/src/shader_recompiler/frontend/ir/opcodes.cpp
index 24d024ad7..5baa6792f 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.cpp
+++ b/src/shader_recompiler/frontend/ir/opcodes.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6
7#include "shader_recompiler/frontend/ir/opcodes.h" 5#include "shader_recompiler/frontend/ir/opcodes.h"
8 6
9namespace Shader::IR { 7namespace Shader::IR {
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index 9ab108292..85f7aac02 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -6,7 +6,6 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array> 8#include <array>
9#include <string_view>
10 9
11#include <fmt/format.h> 10#include <fmt/format.h>
12 11
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index d365ea1bc..0248d9c6e 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/frontend/ir/opcodes.h"
6#include "shader_recompiler/frontend/ir/value.h" 5#include "shader_recompiler/frontend/ir/value.h"
7 6
8namespace Shader::IR { 7namespace Shader::IR {
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 947579852..14f6e55bc 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -179,9 +179,13 @@ public:
179 179
180 /// Get a pointer to the block of a phi argument. 180 /// Get a pointer to the block of a phi argument.
181 [[nodiscard]] Block* PhiBlock(size_t index) const; 181 [[nodiscard]] Block* PhiBlock(size_t index) const;
182
182 /// Add phi operand to a phi instruction. 183 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 184 void AddPhiOperand(Block* predecessor, const Value& value);
184 185
186 // Erase the phi operand at the given index.
187 void ErasePhiOperand(size_t index);
188
185 /// Orders the Phi arguments from farthest away to nearest. 189 /// Orders the Phi arguments from farthest away to nearest.
186 void OrderPhiArgs(); 190 void OrderPhiArgs();
187 191
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
index a6bd3e196..7e91fac46 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <compare>
8#include <optional> 7#include <optional>
9#include <span> 8#include <span>
10#include <string> 9#include <string>
@@ -15,6 +14,7 @@
15 14
16#include "shader_recompiler/environment.h" 15#include "shader_recompiler/environment.h"
17#include "shader_recompiler/frontend/ir/condition.h" 16#include "shader_recompiler/frontend/ir/condition.h"
17#include "shader_recompiler/frontend/ir/reg.h"
18#include "shader_recompiler/frontend/maxwell/instruction.h" 18#include "shader_recompiler/frontend/maxwell/instruction.h"
19#include "shader_recompiler/frontend/maxwell/location.h" 19#include "shader_recompiler/frontend/maxwell/location.h"
20#include "shader_recompiler/frontend/maxwell/opcodes.h" 20#include "shader_recompiler/frontend/maxwell/opcodes.h"
diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp
index 972f677dc..e688e648b 100644
--- a/src/shader_recompiler/frontend/maxwell/decode.cpp
+++ b/src/shader_recompiler/frontend/maxwell/decode.cpp
@@ -6,7 +6,6 @@
6#include <array> 6#include <array>
7#include <bit> 7#include <bit>
8#include <memory> 8#include <memory>
9#include <string_view>
10 9
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
diff --git a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h
index eee5102fa..2a23f7abf 100644
--- a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h
+++ b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h
@@ -6,7 +6,6 @@
6 6
7#include <optional> 7#include <optional>
8 8
9#include "common/bit_field.h"
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "shader_recompiler/environment.h" 10#include "shader_recompiler/environment.h"
12#include "shader_recompiler/frontend/ir/reg.h" 11#include "shader_recompiler/frontend/ir/reg.h"
diff --git a/src/shader_recompiler/frontend/maxwell/instruction.h b/src/shader_recompiler/frontend/maxwell/instruction.h
index 743d68d61..57fd531f2 100644
--- a/src/shader_recompiler/frontend/maxwell/instruction.h
+++ b/src/shader_recompiler/frontend/maxwell/instruction.h
@@ -7,7 +7,6 @@
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/flow_test.h" 9#include "shader_recompiler/frontend/ir/flow_test.h"
10#include "shader_recompiler/frontend/ir/reg.h"
11 10
12namespace Shader::Maxwell { 11namespace Shader::Maxwell {
13 12
diff --git a/src/shader_recompiler/frontend/maxwell/location.h b/src/shader_recompiler/frontend/maxwell/location.h
index 26d29eae2..17107f082 100644
--- a/src/shader_recompiler/frontend/maxwell/location.h
+++ b/src/shader_recompiler/frontend/maxwell/location.h
@@ -4,9 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <compare>
8#include <iterator>
9
10#include <fmt/format.h> 7#include <fmt/format.h>
11 8
12#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 69eeaa3e6..7bad628aa 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -8,7 +8,6 @@
8#include <unordered_map> 8#include <unordered_map>
9#include <utility> 9#include <utility>
10#include <vector> 10#include <vector>
11#include <version>
12 11
13#include <fmt/format.h> 12#include <fmt/format.h>
14 13
@@ -17,7 +16,6 @@
17#include "shader_recompiler/environment.h" 16#include "shader_recompiler/environment.h"
18#include "shader_recompiler/frontend/ir/basic_block.h" 17#include "shader_recompiler/frontend/ir/basic_block.h"
19#include "shader_recompiler/frontend/ir/ir_emitter.h" 18#include "shader_recompiler/frontend/ir/ir_emitter.h"
20#include "shader_recompiler/frontend/maxwell/decode.h"
21#include "shader_recompiler/frontend/maxwell/structured_control_flow.h" 19#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
22#include "shader_recompiler/frontend/maxwell/translate/translate.h" 20#include "shader_recompiler/frontend/maxwell/translate/translate.h"
23#include "shader_recompiler/host_translate_info.h" 21#include "shader_recompiler/host_translate_info.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
index fb3f00d3f..d26d0982b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
@@ -4,7 +4,6 @@
4 4
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/frontend/maxwell/opcodes.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9 8
10namespace Shader::Maxwell { 9namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
index 86e433e41..d92d4e929 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp
@@ -4,8 +4,6 @@
4 4
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/frontend/ir/modifiers.h"
8#include "shader_recompiler/frontend/maxwell/opcodes.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10 8
11namespace Shader::Maxwell { 9namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h b/src/shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h
index 214d0af3c..24f041a4f 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9 8
10namespace Shader::Maxwell { 9namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
index c2443c886..62d20ebe4 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 6#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8 7
9namespace Shader::Maxwell { 8namespace Shader::Maxwell {
@@ -12,9 +11,13 @@ void ExitFragment(TranslatorVisitor& v) {
12 const ProgramHeader sph{v.env.SPH()}; 11 const ProgramHeader sph{v.env.SPH()};
13 IR::Reg src_reg{IR::Reg::R0}; 12 IR::Reg src_reg{IR::Reg::R0};
14 for (u32 render_target = 0; render_target < 8; ++render_target) { 13 for (u32 render_target = 0; render_target < 8; ++render_target) {
14 if (!sph.ps.HasOutputComponents(render_target)) {
15 continue;
16 }
15 const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)}; 17 const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)};
16 for (u32 component = 0; component < 4; ++component) { 18 for (u32 component = 0; component < 4; ++component) {
17 if (!mask[component]) { 19 if (!mask[component]) {
20 ++src_reg;
18 continue; 21 continue;
19 } 22 }
20 v.ir.SetFragColor(render_target, component, v.F(src_reg)); 23 v.ir.SetFragColor(render_target, component, v.F(src_reg));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
index 2f8605619..bb8512400 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
@@ -5,7 +5,6 @@
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/exception.h" 7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcodes.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10 9
11namespace Shader::Maxwell { 10namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h
index 59da56a7e..c5e80a559 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h
@@ -6,8 +6,6 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "shader_recompiler/exception.h" 8#include "shader_recompiler/exception.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h"
10#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
11#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
12 10
13namespace Shader::Maxwell { 11namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set.cpp
index cca5b831f..01bc9c49f 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
5#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h" 6#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"
6 7
7namespace Shader::Maxwell { 8namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
index b3931dae3..2b9c4fc0f 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
5#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h" 6#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"
6 7
7namespace Shader::Maxwell { 8namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
index 2300088e3..8007a4d46 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
@@ -11,10 +11,20 @@ namespace Shader::Maxwell {
11using namespace LDC; 11using namespace LDC;
12namespace { 12namespace {
13std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, 13std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index,
14 const IR::U32& reg, const IR::U32& imm) { 14 const IR::U32& reg, const IR::U32& imm_offset) {
15 switch (mode) { 15 switch (mode) {
16 case Mode::Default: 16 case Mode::Default:
17 return {imm_index, ir.IAdd(reg, imm)}; 17 return {imm_index, ir.IAdd(reg, imm_offset)};
18 case Mode::IS: {
19 // Segmented addressing mode
20 // Ra+imm_offset points into a flat mapping of const buffer
21 // address space
22 const IR::U32 address{ir.IAdd(reg, imm_offset)};
23 const IR::U32 index{ir.BitFieldExtract(address, ir.Imm32(16), ir.Imm32(16))};
24 const IR::U32 offset{ir.BitFieldExtract(address, ir.Imm32(0), ir.Imm32(16))};
25
26 return {ir.IAdd(index, imm_index), offset};
27 }
18 default: 28 default:
19 break; 29 break;
20 } 30 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
index 924fb7a40..00d1d8438 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
@@ -6,7 +6,6 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/exception.h" 7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/ir_emitter.h" 8#include "shader_recompiler/frontend/ir/ir_emitter.h"
9#include "shader_recompiler/frontend/maxwell/opcodes.h"
10#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
11 10
12namespace Shader::Maxwell { 11namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
index 36c5cff2f..4792cd4a5 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
@@ -5,7 +5,6 @@
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/exception.h" 7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcodes.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10 9
11namespace Shader::Maxwell { 10namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
index e0fe47912..f3c7ceb57 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
@@ -13,59 +13,535 @@ namespace {
13// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table) 13// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table)
14IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c, 14IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c,
15 u64 ttbl) { 15 u64 ttbl) {
16 IR::U32 r{ir.Imm32(0)}; 16 switch (ttbl) {
17 const IR::U32 not_a{ir.BitwiseNot(a)}; 17 // generated code, do not edit manually
18 const IR::U32 not_b{ir.BitwiseNot(b)}; 18 case 0:
19 const IR::U32 not_c{ir.BitwiseNot(c)}; 19 return ir.Imm32(0);
20 if (ttbl & 0x01) { 20 case 1:
21 // r |= ~a & ~b & ~c; 21 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c)));
22 const auto lhs{ir.BitwiseAnd(not_a, not_b)}; 22 case 2:
23 const auto rhs{ir.BitwiseAnd(lhs, not_c)}; 23 return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
24 r = ir.BitwiseOr(r, rhs); 24 case 3:
25 return ir.BitwiseNot(ir.BitwiseOr(a, b));
26 case 4:
27 return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
28 case 5:
29 return ir.BitwiseNot(ir.BitwiseOr(a, c));
30 case 6:
31 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
32 case 7:
33 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c)));
34 case 8:
35 return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
36 case 9:
37 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c)));
38 case 10:
39 return ir.BitwiseAnd(c, ir.BitwiseNot(a));
40 case 11:
41 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b)));
42 case 12:
43 return ir.BitwiseAnd(b, ir.BitwiseNot(a));
44 case 13:
45 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c)));
46 case 14:
47 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
48 case 15:
49 return ir.BitwiseNot(a);
50 case 16:
51 return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
52 case 17:
53 return ir.BitwiseNot(ir.BitwiseOr(b, c));
54 case 18:
55 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
56 case 19:
57 return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c)));
58 case 20:
59 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
60 case 21:
61 return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
62 case 22:
63 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
64 case 23:
65 return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)),
66 ir.BitwiseNot(a));
67 case 24:
68 return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
69 case 25:
70 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)));
71 case 26:
72 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
73 case 27:
74 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
75 case 28:
76 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
77 case 29:
78 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
79 case 30:
80 return ir.BitwiseXor(a, ir.BitwiseOr(b, c));
81 case 31:
82 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)));
83 case 32:
84 return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
85 case 33:
86 return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
87 case 34:
88 return ir.BitwiseAnd(c, ir.BitwiseNot(b));
89 case 35:
90 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
91 case 36:
92 return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c));
93 case 37:
94 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)));
95 case 38:
96 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
97 case 39:
98 return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
99 case 40:
100 return ir.BitwiseAnd(c, ir.BitwiseXor(a, b));
101 case 41:
102 return ir.BitwiseXor(ir.BitwiseOr(a, b),
103 ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)));
104 case 42:
105 return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
106 case 43:
107 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)),
108 ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
109 case 44:
110 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b));
111 case 45:
112 return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
113 case 46:
114 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c));
115 case 47:
116 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a));
117 case 48:
118 return ir.BitwiseAnd(a, ir.BitwiseNot(b));
119 case 49:
120 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c)));
121 case 50:
122 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
123 case 51:
124 return ir.BitwiseNot(b);
125 case 52:
126 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
127 case 53:
128 return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
129 case 54:
130 return ir.BitwiseXor(b, ir.BitwiseOr(a, c));
131 case 55:
132 return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)));
133 case 56:
134 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b));
135 case 57:
136 return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
137 case 58:
138 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c));
139 case 59:
140 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b));
141 case 60:
142 return ir.BitwiseXor(a, b);
143 case 61:
144 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b));
145 case 62:
146 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b));
147 case 63:
148 return ir.BitwiseNot(ir.BitwiseAnd(a, b));
149 case 64:
150 return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
151 case 65:
152 return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
153 case 66:
154 return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c));
155 case 67:
156 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)));
157 case 68:
158 return ir.BitwiseAnd(b, ir.BitwiseNot(c));
159 case 69:
160 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
161 case 70:
162 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
163 case 71:
164 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
165 case 72:
166 return ir.BitwiseAnd(b, ir.BitwiseXor(a, c));
167 case 73:
168 return ir.BitwiseXor(ir.BitwiseOr(a, c),
169 ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)));
170 case 74:
171 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c));
172 case 75:
173 return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
174 case 76:
175 return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
176 case 77:
177 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)),
178 ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
179 case 78:
180 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c));
181 case 79:
182 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a));
183 case 80:
184 return ir.BitwiseAnd(a, ir.BitwiseNot(c));
185 case 81:
186 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b)));
187 case 82:
188 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
189 case 83:
190 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
191 case 84:
192 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
193 case 85:
194 return ir.BitwiseNot(c);
195 case 86:
196 return ir.BitwiseXor(c, ir.BitwiseOr(a, b));
197 case 87:
198 return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)));
199 case 88:
200 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c));
201 case 89:
202 return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
203 case 90:
204 return ir.BitwiseXor(a, c);
205 case 91:
206 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c));
207 case 92:
208 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b));
209 case 93:
210 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c));
211 case 94:
212 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c));
213 case 95:
214 return ir.BitwiseNot(ir.BitwiseAnd(a, c));
215 case 96:
216 return ir.BitwiseAnd(a, ir.BitwiseXor(b, c));
217 case 97:
218 return ir.BitwiseXor(ir.BitwiseOr(b, c),
219 ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)));
220 case 98:
221 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c));
222 case 99:
223 return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
224 case 100:
225 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c));
226 case 101:
227 return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
228 case 102:
229 return ir.BitwiseXor(b, c);
230 case 103:
231 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c));
232 case 104:
233 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b)));
234 case 105:
235 return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
236 case 106:
237 return ir.BitwiseXor(c, ir.BitwiseAnd(a, b));
238 case 107:
239 return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)),
240 ir.BitwiseXor(a, ir.BitwiseNot(b)));
241 case 108:
242 return ir.BitwiseXor(b, ir.BitwiseAnd(a, c));
243 case 109:
244 return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)),
245 ir.BitwiseXor(a, ir.BitwiseNot(c)));
246 case 110:
247 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
248 case 111:
249 return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
250 case 112:
251 return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
252 case 113:
253 return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)),
254 ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
255 case 114:
256 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c));
257 case 115:
258 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b));
259 case 116:
260 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b));
261 case 117:
262 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c));
263 case 118:
264 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
265 case 119:
266 return ir.BitwiseNot(ir.BitwiseAnd(b, c));
267 case 120:
268 return ir.BitwiseXor(a, ir.BitwiseAnd(b, c));
269 case 121:
270 return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)),
271 ir.BitwiseXor(b, ir.BitwiseNot(c)));
272 case 122:
273 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
274 case 123:
275 return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
276 case 124:
277 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
278 case 125:
279 return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
280 case 126:
281 return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
282 case 127:
283 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)));
284 case 128:
285 return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c));
286 case 129:
287 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
288 case 130:
289 return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
290 case 131:
291 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
292 case 132:
293 return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
294 case 133:
295 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
296 case 134:
297 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
298 case 135:
299 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
300 case 136:
301 return ir.BitwiseAnd(b, c);
302 case 137:
303 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
304 case 138:
305 return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
306 case 139:
307 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
308 case 140:
309 return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
310 case 141:
311 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c)));
312 case 142:
313 return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
314 case 143:
315 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
316 case 144:
317 return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
318 case 145:
319 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
320 case 146:
321 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
322 case 147:
323 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
324 case 148:
325 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
326 case 149:
327 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
328 case 150:
329 return ir.BitwiseXor(a, ir.BitwiseXor(b, c));
330 case 151:
331 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)),
332 ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
333 case 152:
334 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
335 case 153:
336 return ir.BitwiseXor(b, ir.BitwiseNot(c));
337 case 154:
338 return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
339 case 155:
340 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)));
341 case 156:
342 return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
343 case 157:
344 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)));
345 case 158:
346 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c)));
347 case 159:
348 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c)));
349 case 160:
350 return ir.BitwiseAnd(a, c);
351 case 161:
352 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
353 case 162:
354 return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
355 case 163:
356 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
357 case 164:
358 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
359 case 165:
360 return ir.BitwiseXor(a, ir.BitwiseNot(c));
361 case 166:
362 return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
363 case 167:
364 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)));
365 case 168:
366 return ir.BitwiseAnd(c, ir.BitwiseOr(a, b));
367 case 169:
368 return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
369 case 170:
370 return c;
371 case 171:
372 return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
373 case 172:
374 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
375 case 173:
376 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c)));
377 case 174:
378 return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
379 case 175:
380 return ir.BitwiseOr(c, ir.BitwiseNot(a));
381 case 176:
382 return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
383 case 177:
384 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c)));
385 case 178:
386 return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
387 case 179:
388 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
389 case 180:
390 return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
391 case 181:
392 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)));
393 case 182:
394 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c)));
395 case 183:
396 return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c)));
397 case 184:
398 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
399 case 185:
400 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c)));
401 case 186:
402 return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
403 case 187:
404 return ir.BitwiseOr(c, ir.BitwiseNot(b));
405 case 188:
406 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b));
407 case 189:
408 return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
409 case 190:
410 return ir.BitwiseOr(c, ir.BitwiseXor(a, b));
411 case 191:
412 return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
413 case 192:
414 return ir.BitwiseAnd(a, b);
415 case 193:
416 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
417 case 194:
418 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
419 case 195:
420 return ir.BitwiseXor(a, ir.BitwiseNot(b));
421 case 196:
422 return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
423 case 197:
424 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c)));
425 case 198:
426 return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
427 case 199:
428 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)));
429 case 200:
430 return ir.BitwiseAnd(b, ir.BitwiseOr(a, c));
431 case 201:
432 return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
433 case 202:
434 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
435 case 203:
436 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
437 case 204:
438 return b;
439 case 205:
440 return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
441 case 206:
442 return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
443 case 207:
444 return ir.BitwiseOr(b, ir.BitwiseNot(a));
445 case 208:
446 return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
447 case 209:
448 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c)));
449 case 210:
450 return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
451 case 211:
452 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)));
453 case 212:
454 return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
455 case 213:
456 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
457 case 214:
458 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b)));
459 case 215:
460 return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b)));
461 case 216:
462 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
463 case 217:
464 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
465 case 218:
466 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c));
467 case 219:
468 return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
469 case 220:
470 return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
471 case 221:
472 return ir.BitwiseOr(b, ir.BitwiseNot(c));
473 case 222:
474 return ir.BitwiseOr(b, ir.BitwiseXor(a, c));
475 case 223:
476 return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
477 case 224:
478 return ir.BitwiseAnd(a, ir.BitwiseOr(b, c));
479 case 225:
480 return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
481 case 226:
482 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
483 case 227:
484 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
485 case 228:
486 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
487 case 229:
488 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
489 case 230:
490 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c));
491 case 231:
492 return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
493 case 232:
494 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
495 case 233:
496 return ir.BitwiseOr(ir.BitwiseAnd(a, b),
497 ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)));
498 case 234:
499 return ir.BitwiseOr(c, ir.BitwiseAnd(a, b));
500 case 235:
501 return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
502 case 236:
503 return ir.BitwiseOr(b, ir.BitwiseAnd(a, c));
504 case 237:
505 return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
506 case 238:
507 return ir.BitwiseOr(b, c);
508 case 239:
509 return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
510 case 240:
511 return a;
512 case 241:
513 return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
514 case 242:
515 return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
516 case 243:
517 return ir.BitwiseOr(a, ir.BitwiseNot(b));
518 case 244:
519 return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
520 case 245:
521 return ir.BitwiseOr(a, ir.BitwiseNot(c));
522 case 246:
523 return ir.BitwiseOr(a, ir.BitwiseXor(b, c));
524 case 247:
525 return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
526 case 248:
527 return ir.BitwiseOr(a, ir.BitwiseAnd(b, c));
528 case 249:
529 return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
530 case 250:
531 return ir.BitwiseOr(a, c);
532 case 251:
533 return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
534 case 252:
535 return ir.BitwiseOr(a, b);
536 case 253:
537 return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
538 case 254:
539 return ir.BitwiseOr(a, ir.BitwiseOr(b, c));
540 case 255:
541 return ir.Imm32(0xFFFFFFFF);
542 // end of generated code
25 } 543 }
26 if (ttbl & 0x02) { 544 throw NotImplementedException("LOP3 with out of range ttbl");
27 // r |= ~a & ~b & c;
28 const auto lhs{ir.BitwiseAnd(not_a, not_b)};
29 const auto rhs{ir.BitwiseAnd(lhs, c)};
30 r = ir.BitwiseOr(r, rhs);
31 }
32 if (ttbl & 0x04) {
33 // r |= ~a & b & ~c;
34 const auto lhs{ir.BitwiseAnd(not_a, b)};
35 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
36 r = ir.BitwiseOr(r, rhs);
37 }
38 if (ttbl & 0x08) {
39 // r |= ~a & b & c;
40 const auto lhs{ir.BitwiseAnd(not_a, b)};
41 const auto rhs{ir.BitwiseAnd(lhs, c)};
42 r = ir.BitwiseOr(r, rhs);
43 }
44 if (ttbl & 0x10) {
45 // r |= a & ~b & ~c;
46 const auto lhs{ir.BitwiseAnd(a, not_b)};
47 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
48 r = ir.BitwiseOr(r, rhs);
49 }
50 if (ttbl & 0x20) {
51 // r |= a & ~b & c;
52 const auto lhs{ir.BitwiseAnd(a, not_b)};
53 const auto rhs{ir.BitwiseAnd(lhs, c)};
54 r = ir.BitwiseOr(r, rhs);
55 }
56 if (ttbl & 0x40) {
57 // r |= a & b & ~c;
58 const auto lhs{ir.BitwiseAnd(a, b)};
59 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
60 r = ir.BitwiseOr(r, rhs);
61 }
62 if (ttbl & 0x80) {
63 // r |= a & b & c;
64 const auto lhs{ir.BitwiseAnd(a, b)};
65 const auto rhs{ir.BitwiseAnd(lhs, c)};
66 r = ir.BitwiseOr(r, rhs);
67 }
68 return r;
69} 545}
70 546
71IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) { 547IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py
new file mode 100644
index 000000000..8f547c266
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py
@@ -0,0 +1,92 @@
1# Copyright © 2022 degasus <markus@selfnet.de>
2# This work is free. You can redistribute it and/or modify it under the
3# terms of the Do What The Fuck You Want To Public License, Version 2,
4# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
5
6from itertools import product
7
8# The primitive instructions
9OPS = {
10 'ir.BitwiseAnd({}, {})' : (2, 1, lambda a,b: a&b),
11 'ir.BitwiseOr({}, {})' : (2, 1, lambda a,b: a|b),
12 'ir.BitwiseXor({}, {})' : (2, 1, lambda a,b: a^b),
13 'ir.BitwiseNot({})' : (1, 0.1, lambda a: (~a) & 255), # Only tiny cost, as this can often inlined in other instructions
14}
15
16# Our database of combination of instructions
17optimized_calls = {}
18def cmp(lhs, rhs):
19 if lhs is None: # new entry
20 return True
21 if lhs[3] > rhs[3]: # costs
22 return True
23 if lhs[3] < rhs[3]: # costs
24 return False
25 if len(lhs[0]) > len(rhs[0]): # string len
26 return True
27 if len(lhs[0]) < len(rhs[0]): # string len
28 return False
29 if lhs[0] > rhs[0]: # string sorting
30 return True
31 if lhs[0] < rhs[0]: # string sorting
32 return False
33 assert lhs == rhs, "redundant instruction, bug in brute force"
34 return False
35def register(imm, instruction, count, latency):
36 # Use the sum of instruction count and latency as costs to evaluate which combination is best
37 costs = count + latency
38
39 old = optimized_calls.get(imm, None)
40 new = (instruction, count, latency, costs)
41
42 # Update if new or better
43 if cmp(old, new):
44 optimized_calls[imm] = new
45 return True
46
47 return False
48
49# Constants: 0, 1 (for free)
50register(0, 'ir.Imm32(0)', 0, 0)
51register(255, 'ir.Imm32(0xFFFFFFFF)', 0, 0)
52
53# Inputs: a, b, c (for free)
54ta = 0xF0
55tb = 0xCC
56tc = 0xAA
57inputs = {
58 ta : 'a',
59 tb : 'b',
60 tc : 'c',
61}
62for imm, instruction in inputs.items():
63 register(imm, instruction, 0, 0)
64 register((~imm) & 255, 'ir.BitwiseNot({})'.format(instruction), 0.099, 0.099) # slightly cheaper NEG on inputs
65
66# Try to combine two values from the db with an instruction.
67# If it is better than the old method, update it.
68while True:
69 registered = 0
70 calls_copy = optimized_calls.copy()
71 for OP, (argc, cost, f) in OPS.items():
72 for args in product(calls_copy.items(), repeat=argc):
73 # unpack(transponse) the arrays
74 imm = [arg[0] for arg in args]
75 value = [arg[1][0] for arg in args]
76 count = [arg[1][1] for arg in args]
77 latency = [arg[1][2] for arg in args]
78
79 registered += register(
80 f(*imm),
81 OP.format(*value),
82 sum(count) + cost,
83 max(latency) + cost)
84 if registered == 0:
85 # No update at all? So terminate
86 break
87
88# Hacky output. Please improve me to output valid C++ instead.
89s = """ case {imm}:
90 return {op};"""
91for imm in range(256):
92 print(s.format(imm=imm, op=optimized_calls[imm][0]))
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
index 6bb08db8a..c317e14c9 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
@@ -5,7 +5,6 @@
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "shader_recompiler/exception.h" 7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcodes.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10 9
11namespace Shader::Maxwell { 10namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp
index 63b588ad4..100c94c19 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/surface_atomic_operations.cpp
@@ -2,9 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <bit>
7
8#include "common/bit_field.h" 5#include "common/bit_field.h"
9#include "common/common_types.h" 6#include "common/common_types.h"
10#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
index 154e7f1a1..00dbcd86f 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <utility>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather.cpp
index 218cbc1a8..959c63ba9 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
index 34efa2d50..86e68a830 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <utility>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
index c3fe3ffda..6f4feff11 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
index 983058303..60732215b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
@@ -134,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
134 multisample = v.X(meta_reg++); 132 multisample = v.X(meta_reg++);
135 } 133 }
136 if (tld.clamp != 0) { 134 if (tld.clamp != 0) {
137 throw NotImplementedException("TLD.CL - CLAMP is not implmented"); 135 throw NotImplementedException("TLD.CL - CLAMP is not implemented");
138 } 136 }
139 IR::TextureInstInfo info{}; 137 IR::TextureInstInfo info{};
140 info.type.Assign(GetType(tld.type)); 138 info.type.Assign(GetType(tld.type));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
index aea3c0e62..f89ce1b68 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 7#include "shader_recompiler/frontend/ir/modifiers.h"
@@ -83,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
83 } const tmml{insn}; 81 } const tmml{insn};
84 82
85 if ((tmml.mask & 0b1100) != 0) { 83 if ((tmml.mask & 0b1100) != 0) {
86 throw NotImplementedException("TMML BA results are not implmented"); 84 throw NotImplementedException("TMML BA results are not implemented");
87 } 85 }
88 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)}; 86 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)};
89 87
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 0459e5473..72131301c 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
@@ -6,7 +6,6 @@
6 6
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
11 10
12namespace Shader::Maxwell { 11namespace Shader::Maxwell {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp
index 550fed55c..f98f66940 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
6
7#include "common/bit_field.h" 5#include "common/bit_field.h"
8#include "common/common_types.h" 6#include "common/common_types.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" 7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 248ad3ced..b22725584 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -212,11 +212,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
212 } 212 }
213 Optimization::SsaRewritePass(program); 213 Optimization::SsaRewritePass(program);
214 214
215 Optimization::ConstantPropagationPass(program);
216
215 Optimization::GlobalMemoryToStorageBufferPass(program); 217 Optimization::GlobalMemoryToStorageBufferPass(program);
216 Optimization::TexturePass(env, program); 218 Optimization::TexturePass(env, program);
217 219
218 Optimization::ConstantPropagationPass(program);
219
220 if (Settings::values.resolution_info.active) { 220 if (Settings::values.resolution_info.active) {
221 Optimization::RescalingPass(program); 221 Optimization::RescalingPass(program);
222 } 222 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h
index eac83da9d..7b024c627 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.h
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.h
@@ -8,10 +8,13 @@
8#include "shader_recompiler/frontend/ir/basic_block.h" 8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/program.h" 9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h" 10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11#include "shader_recompiler/host_translate_info.h"
12#include "shader_recompiler/object_pool.h" 11#include "shader_recompiler/object_pool.h"
13#include "shader_recompiler/runtime_info.h" 12#include "shader_recompiler/runtime_info.h"
14 13
14namespace Shader {
15struct HostTranslateInfo;
16}
17
15namespace Shader::Maxwell { 18namespace Shader::Maxwell {
16 19
17[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, 20[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index bfd2ae650..16278faab 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -29,6 +29,41 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
29 }); 29 });
30} 30}
31 31
32void AddRegisterIndexedLdc(Info& info) {
33 info.uses_cbuf_indirect = true;
34
35 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
36 AddConstantBufferDescriptor(info, i, 1);
37
38 // The shader can use any possible access size
39 info.constant_buffer_used_sizes[i] = 0x10'000;
40 }
41}
42
43u32 GetElementSize(IR::Type& used_type, Shader::IR::Opcode opcode) {
44 switch (opcode) {
45 case IR::Opcode::GetCbufU8:
46 case IR::Opcode::GetCbufS8:
47 used_type |= IR::Type::U8;
48 return 1;
49 case IR::Opcode::GetCbufU16:
50 case IR::Opcode::GetCbufS16:
51 used_type |= IR::Type::U16;
52 return 2;
53 case IR::Opcode::GetCbufU32:
54 used_type |= IR::Type::U32;
55 return 4;
56 case IR::Opcode::GetCbufF32:
57 used_type |= IR::Type::F32;
58 return 4;
59 case IR::Opcode::GetCbufU32x2:
60 used_type |= IR::Type::U32x2;
61 return 8;
62 default:
63 throw InvalidArgument("Invalid opcode {}", opcode);
64 }
65}
66
32void GetPatch(Info& info, IR::Patch patch) { 67void GetPatch(Info& info, IR::Patch patch) {
33 if (!IR::IsGeneric(patch)) { 68 if (!IR::IsGeneric(patch)) {
34 throw NotImplementedException("Reading non-generic patch {}", patch); 69 throw NotImplementedException("Reading non-generic patch {}", patch);
@@ -463,42 +498,18 @@ void VisitUsages(Info& info, IR::Inst& inst) {
463 case IR::Opcode::GetCbufU32x2: { 498 case IR::Opcode::GetCbufU32x2: {
464 const IR::Value index{inst.Arg(0)}; 499 const IR::Value index{inst.Arg(0)};
465 const IR::Value offset{inst.Arg(1)}; 500 const IR::Value offset{inst.Arg(1)};
466 if (!index.IsImmediate()) { 501 if (index.IsImmediate()) {
467 throw NotImplementedException("Constant buffer with non-immediate index"); 502 AddConstantBufferDescriptor(info, index.U32(), 1);
468 } 503 u32 element_size = GetElementSize(info.used_constant_buffer_types, inst.GetOpcode());
469 AddConstantBufferDescriptor(info, index.U32(), 1); 504 u32& size{info.constant_buffer_used_sizes[index.U32()]};
470 u32 element_size{}; 505 if (offset.IsImmediate()) {
471 switch (inst.GetOpcode()) { 506 size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
472 case IR::Opcode::GetCbufU8: 507 } else {
473 case IR::Opcode::GetCbufS8: 508 size = 0x10'000;
474 info.used_constant_buffer_types |= IR::Type::U8; 509 }
475 element_size = 1;
476 break;
477 case IR::Opcode::GetCbufU16:
478 case IR::Opcode::GetCbufS16:
479 info.used_constant_buffer_types |= IR::Type::U16;
480 element_size = 2;
481 break;
482 case IR::Opcode::GetCbufU32:
483 info.used_constant_buffer_types |= IR::Type::U32;
484 element_size = 4;
485 break;
486 case IR::Opcode::GetCbufF32:
487 info.used_constant_buffer_types |= IR::Type::F32;
488 element_size = 4;
489 break;
490 case IR::Opcode::GetCbufU32x2:
491 info.used_constant_buffer_types |= IR::Type::U32x2;
492 element_size = 8;
493 break;
494 default:
495 break;
496 }
497 u32& size{info.constant_buffer_used_sizes[index.U32()]};
498 if (offset.IsImmediate()) {
499 size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
500 } else { 510 } else {
501 size = 0x10'000; 511 AddRegisterIndexedLdc(info);
512 GetElementSize(info.used_indirect_cbuf_types, inst.GetOpcode());
502 } 513 }
503 break; 514 break;
504 } 515 }
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index c134a12bc..2a14e7f12 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -8,7 +8,6 @@
8#include <type_traits> 8#include <type_traits>
9 9
10#include "common/bit_cast.h" 10#include "common/bit_cast.h"
11#include "common/bit_util.h"
12#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
13#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
index 400836301..6697fde85 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -2,24 +2,104 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
7#include <boost/container/small_vector.hpp>
8
5#include "shader_recompiler/frontend/ir/basic_block.h" 9#include "shader_recompiler/frontend/ir/basic_block.h"
6#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h" 11#include "shader_recompiler/ir_opt/passes.h"
8 12
9namespace Shader::Optimization { 13namespace Shader::Optimization {
10 14namespace {
11void DeadCodeEliminationPass(IR::Program& program) { 15template <bool TEST_USES>
16void DeadInstElimination(IR::Block* const block) {
12 // We iterate over the instructions in reverse order. 17 // We iterate over the instructions in reverse order.
13 // This is because removing an instruction reduces the number of uses for earlier instructions. 18 // This is because removing an instruction reduces the number of uses for earlier instructions.
14 for (IR::Block* const block : program.post_order_blocks) { 19 auto it{block->end()};
15 auto it{block->end()}; 20 while (it != block->begin()) {
16 while (it != block->begin()) { 21 --it;
17 --it; 22 if constexpr (TEST_USES) {
18 if (!it->HasUses() && !it->MayHaveSideEffects()) { 23 if (it->HasUses() || it->MayHaveSideEffects()) {
19 it->Invalidate(); 24 continue;
20 it = block->Instructions().erase(it); 25 }
26 }
27 it->Invalidate();
28 it = block->Instructions().erase(it);
29 }
30}
31
32void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
33 for (IR::Block* const block : program.blocks) {
34 for (IR::Inst& phi : *block) {
35 if (!IR::IsPhi(phi)) {
36 continue;
37 }
38 for (size_t i = 0; i < phi.NumArgs(); ++i) {
39 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
40 continue;
41 }
42 // Phi operand at this index is an unreachable block
43 phi.ErasePhiOperand(i);
44 --i;
45 }
46 }
47 }
48}
49
50void DeadBranchElimination(IR::Program& program) {
51 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
52 const auto begin_it{program.syntax_list.begin()};
53 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
54 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
55 continue;
56 }
57 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
58 const IR::U1 cond{cond_ref->Arg(0)};
59 if (!cond.IsImmediate()) {
60 continue;
61 }
62 if (cond.U1()) {
63 continue;
64 }
65 // False immediate condition. Remove condition ref, erase the entire branch.
66 cond_ref->Invalidate();
67 // Account for nested if-statements within the if(false) branch
68 u32 nested_ifs{1u};
69 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
70 node_it = program.syntax_list.erase(node_it);
71 switch (node_it->type) {
72 case IR::AbstractSyntaxNode::Type::If:
73 ++nested_ifs;
74 break;
75 case IR::AbstractSyntaxNode::Type::EndIf:
76 --nested_ifs;
77 break;
78 case IR::AbstractSyntaxNode::Type::Block: {
79 IR::Block* const block{node_it->data.block};
80 DeadInstElimination<false>(block);
81 dead_blocks.push_back(block);
82 break;
83 }
84 default:
85 break;
21 } 86 }
22 } 87 }
88 // Erase EndIf node of the if(false) branch
89 node_it = program.syntax_list.erase(node_it);
90 // Account for loop increment
91 --node_it;
92 }
93 if (!dead_blocks.empty()) {
94 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
95 }
96}
97} // namespace
98
99void DeadCodeEliminationPass(IR::Program& program) {
100 DeadBranchElimination(program);
101 for (IR::Block* const block : program.post_order_blocks) {
102 DeadInstElimination<true>(block);
23 } 103 }
24} 104}
25 105
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index 38592afd0..3cc1cc07a 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -2,10 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <compare>
7#include <optional> 5#include <optional>
8#include <queue>
9 6
10#include <boost/container/flat_set.hpp> 7#include <boost/container/flat_set.hpp>
11#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
@@ -334,7 +331,8 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
334/// Tries to track the storage buffer address used by a global memory instruction 331/// Tries to track the storage buffer address used by a global memory instruction
335std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { 332std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) {
336 const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { 333 const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> {
337 if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { 334 if (inst->GetOpcode() != IR::Opcode::GetCbufU32 &&
335 inst->GetOpcode() != IR::Opcode::GetCbufU32x2) {
338 return std::nullopt; 336 return std::nullopt;
339 } 337 }
340 const IR::Value index{inst->Arg(0)}; 338 const IR::Value index{inst->Arg(0)};
diff --git a/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp b/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp
index 773e1f961..622f94fc7 100644
--- a/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp
+++ b/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp
@@ -2,9 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/ir/value.h" 5#include "shader_recompiler/frontend/ir/value.h"
9#include "shader_recompiler/ir_opt/passes.h" 6#include "shader_recompiler/ir_opt/passes.h"
10 7
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index f877c7ba0..16ea3d80a 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -4,10 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <span>
8
9#include "shader_recompiler/environment.h" 7#include "shader_recompiler/environment.h"
10#include "shader_recompiler/frontend/ir/basic_block.h"
11#include "shader_recompiler/frontend/ir/program.h" 8#include "shader_recompiler/frontend/ir/program.h"
12 9
13namespace Shader::Optimization { 10namespace Shader::Optimization {
diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
index c28500dd1..75679c793 100644
--- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp
+++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/alignment.h"
6#include "common/settings.h" 5#include "common/settings.h"
7#include "shader_recompiler/environment.h" 6#include "shader_recompiler/environment.h"
8#include "shader_recompiler/frontend/ir/ir_emitter.h" 7#include "shader_recompiler/frontend/ir/ir_emitter.h"
@@ -183,6 +182,31 @@ void ScaleIntegerComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_s
183 } 182 }
184} 183}
185 184
185void ScaleIntegerOffsetComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled,
186 size_t index) {
187 const IR::Value composite{inst.Arg(index)};
188 if (composite.IsEmpty()) {
189 return;
190 }
191 const auto info{inst.Flags<IR::TextureInstInfo>()};
192 const IR::U32 x{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 0)})};
193 const IR::U32 y{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 1)})};
194 switch (info.type) {
195 case TextureType::ColorArray2D:
196 case TextureType::Color2D:
197 inst.SetArg(index, ir.CompositeConstruct(x, y));
198 break;
199 case TextureType::Color1D:
200 case TextureType::ColorArray1D:
201 case TextureType::Color3D:
202 case TextureType::ColorCube:
203 case TextureType::ColorArrayCube:
204 case TextureType::Buffer:
205 // Nothing to patch here
206 break;
207 }
208}
209
186void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) { 210void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) {
187 const auto info{inst.Flags<IR::TextureInstInfo>()}; 211 const auto info{inst.Flags<IR::TextureInstInfo>()};
188 const IR::Value coord{inst.Arg(1)}; 212 const IR::Value coord{inst.Arg(1)};
@@ -220,7 +244,7 @@ void SubScaleImageFetch(IR::Block& block, IR::Inst& inst) {
220 const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; 244 const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))};
221 SubScaleCoord(ir, inst, is_scaled); 245 SubScaleCoord(ir, inst, is_scaled);
222 // Scale ImageFetch offset 246 // Scale ImageFetch offset
223 ScaleIntegerComposite(ir, inst, is_scaled, 2); 247 ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2);
224} 248}
225 249
226void SubScaleImageRead(IR::Block& block, IR::Inst& inst) { 250void SubScaleImageRead(IR::Block& block, IR::Inst& inst) {
@@ -242,7 +266,7 @@ void PatchImageFetch(IR::Block& block, IR::Inst& inst) {
242 const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; 266 const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))};
243 ScaleIntegerComposite(ir, inst, is_scaled, 1); 267 ScaleIntegerComposite(ir, inst, is_scaled, 1);
244 // Scale ImageFetch offset 268 // Scale ImageFetch offset
245 ScaleIntegerComposite(ir, inst, is_scaled, 2); 269 ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2);
246} 270}
247 271
248void PatchImageRead(IR::Block& block, IR::Inst& inst) { 272void PatchImageRead(IR::Block& block, IR::Inst& inst) {
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
index 87aa09358..928557acb 100644
--- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
+++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
@@ -20,7 +20,6 @@
20#include <vector> 20#include <vector>
21 21
22#include <boost/container/flat_map.hpp> 22#include <boost/container/flat_map.hpp>
23#include <boost/container/flat_set.hpp>
24 23
25#include "shader_recompiler/frontend/ir/basic_block.h" 24#include "shader_recompiler/frontend/ir/basic_block.h"
26#include "shader_recompiler/frontend/ir/opcodes.h" 25#include "shader_recompiler/frontend/ir/opcodes.h"
diff --git a/src/shader_recompiler/program_header.h b/src/shader_recompiler/program_header.h
index bd6c2bfb5..0cd6597ef 100644
--- a/src/shader_recompiler/program_header.h
+++ b/src/shader_recompiler/program_header.h
@@ -196,6 +196,11 @@ struct ProgramHeader {
196 return {(bits & 1) != 0, (bits & 2) != 0, (bits & 4) != 0, (bits & 8) != 0}; 196 return {(bits & 1) != 0, (bits & 2) != 0, (bits & 4) != 0, (bits & 8) != 0};
197 } 197 }
198 198
199 [[nodiscard]] bool HasOutputComponents(u32 rt) const noexcept {
200 const u32 bits{omap.target >> (rt * 4)};
201 return (bits & 0xf) != 0;
202 }
203
199 [[nodiscard]] std::array<PixelImap, 4> GenericInputMap(u32 attribute) const { 204 [[nodiscard]] std::array<PixelImap, 4> GenericInputMap(u32 attribute) const {
200 const auto& vector{imap_generic_vector[attribute]}; 205 const auto& vector{imap_generic_vector[attribute]};
201 return {vector.x, vector.y, vector.z, vector.w}; 206 return {vector.x, vector.y, vector.z, vector.w};
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index f3f83a258..722e9d729 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <bitset>
9#include <optional> 8#include <optional>
10#include <vector> 9#include <vector>
11 10
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 9f375c30e..a3a09c71c 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -105,6 +105,7 @@ struct ImageDescriptor {
105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
106 106
107struct Info { 107struct Info {
108 static constexpr size_t MAX_INDIRECT_CBUFS{14};
108 static constexpr size_t MAX_CBUFS{18}; 109 static constexpr size_t MAX_CBUFS{18};
109 static constexpr size_t MAX_SSBOS{32}; 110 static constexpr size_t MAX_SSBOS{32};
110 111
@@ -173,9 +174,11 @@ struct Info {
173 bool uses_atomic_image_u32{}; 174 bool uses_atomic_image_u32{};
174 bool uses_shadow_lod{}; 175 bool uses_shadow_lod{};
175 bool uses_rescaling_uniform{}; 176 bool uses_rescaling_uniform{};
177 bool uses_cbuf_indirect{};
176 178
177 IR::Type used_constant_buffer_types{}; 179 IR::Type used_constant_buffer_types{};
178 IR::Type used_storage_buffer_types{}; 180 IR::Type used_storage_buffer_types{};
181 IR::Type used_indirect_cbuf_types{};
179 182
180 u32 constant_buffer_mask{}; 183 u32 constant_buffer_mask{};
181 std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{}; 184 std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{};
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index be2113f5a..10975884b 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -212,7 +212,7 @@ public:
212 void FlushCachedWrites() noexcept { 212 void FlushCachedWrites() noexcept {
213 flags &= ~BufferFlagBits::CachedWrites; 213 flags &= ~BufferFlagBits::CachedWrites;
214 const u64 num_words = NumWords(); 214 const u64 num_words = NumWords();
215 const u64* const cached_words = Array<Type::CachedCPU>(); 215 u64* const cached_words = Array<Type::CachedCPU>();
216 u64* const untracked_words = Array<Type::Untracked>(); 216 u64* const untracked_words = Array<Type::Untracked>();
217 u64* const cpu_words = Array<Type::CPU>(); 217 u64* const cpu_words = Array<Type::CPU>();
218 for (u64 word_index = 0; word_index < num_words; ++word_index) { 218 for (u64 word_index = 0; word_index < num_words; ++word_index) {
@@ -220,6 +220,7 @@ public:
220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); 220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
221 untracked_words[word_index] |= cached_bits; 221 untracked_words[word_index] |= cached_bits;
222 cpu_words[word_index] |= cached_bits; 222 cpu_words[word_index] |= cached_bits;
223 cached_words[word_index] = 0;
223 } 224 }
224 } 225 }
225 226
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index fa26eb8b0..3f2bf6294 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -11,7 +11,6 @@
11#include <mutex> 11#include <mutex>
12#include <numeric> 12#include <numeric>
13#include <span> 13#include <span>
14#include <unordered_map>
15#include <vector> 14#include <vector>
16 15
17#include <boost/container/small_vector.hpp> 16#include <boost/container/small_vector.hpp>
@@ -22,7 +21,6 @@
22#include "common/literals.h" 21#include "common/literals.h"
23#include "common/lru_cache.h" 22#include "common/lru_cache.h"
24#include "common/microprofile.h" 23#include "common/microprofile.h"
25#include "common/scope_exit.h"
26#include "common/settings.h" 24#include "common/settings.h"
27#include "core/memory.h" 25#include "core/memory.h"
28#include "video_core/buffer_cache/buffer_base.h" 26#include "video_core/buffer_cache/buffer_base.h"
@@ -78,8 +76,9 @@ class BufferCache {
78 76
79 static constexpr BufferId NULL_BUFFER_ID{0}; 77 static constexpr BufferId NULL_BUFFER_ID{0};
80 78
81 static constexpr u64 EXPECTED_MEMORY = 512_MiB; 79 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
82 static constexpr u64 CRITICAL_MEMORY = 1_GiB; 80 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
81 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
83 82
84 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 83 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
85 84
@@ -438,6 +437,8 @@ private:
438 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; 437 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
439 u64 frame_tick = 0; 438 u64 frame_tick = 0;
440 u64 total_used_memory = 0; 439 u64 total_used_memory = 0;
440 u64 minimum_memory = 0;
441 u64 critical_memory = 0;
441 442
442 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; 443 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
443}; 444};
@@ -453,11 +454,30 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
453 // Ensure the first slot is used for the null buffer 454 // Ensure the first slot is used for the null buffer
454 void(slot_buffers.insert(runtime, NullBufferParams{})); 455 void(slot_buffers.insert(runtime, NullBufferParams{}));
455 common_ranges.clear(); 456 common_ranges.clear();
457
458 if (!runtime.CanReportMemoryUsage()) {
459 minimum_memory = DEFAULT_EXPECTED_MEMORY;
460 critical_memory = DEFAULT_CRITICAL_MEMORY;
461 return;
462 }
463
464 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
465 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
466 const s64 min_spacing_critical = device_memory - 1_GiB;
467 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
468 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
469 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
470 minimum_memory = static_cast<u64>(
471 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
472 DEFAULT_EXPECTED_MEMORY));
473 critical_memory = static_cast<u64>(
474 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
475 DEFAULT_CRITICAL_MEMORY));
456} 476}
457 477
458template <class P> 478template <class P>
459void BufferCache<P>::RunGarbageCollector() { 479void BufferCache<P>::RunGarbageCollector() {
460 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; 480 const bool aggressive_gc = total_used_memory >= critical_memory;
461 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; 481 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
462 int num_iterations = aggressive_gc ? 64 : 32; 482 int num_iterations = aggressive_gc ? 64 : 32;
463 const auto clean_up = [this, &num_iterations](BufferId buffer_id) { 483 const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
@@ -488,7 +508,11 @@ void BufferCache<P>::TickFrame() {
488 const bool skip_preferred = hits * 256 < shots * 251; 508 const bool skip_preferred = hits * 256 < shots * 251;
489 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; 509 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
490 510
491 if (total_used_memory >= EXPECTED_MEMORY) { 511 // If we can obtain the memory info, use it instead of the estimate.
512 if (runtime.CanReportMemoryUsage()) {
513 total_used_memory = runtime.GetDeviceMemoryUsage();
514 }
515 if (total_used_memory >= minimum_memory) {
492 RunGarbageCollector(); 516 RunGarbageCollector();
493 } 517 }
494 ++frame_tick; 518 ++frame_tick;
@@ -1287,7 +1311,20 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1287 const GPUVAddr gpu_addr_begin = array.StartAddress(); 1311 const GPUVAddr gpu_addr_begin = array.StartAddress();
1288 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; 1312 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1;
1289 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1313 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
1290 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1314 u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1315 if (address_size >= 64_MiB) {
1316 // Reported vertex buffer size is very large, cap to mapped buffer size
1317 GPUVAddr submapped_addr_end = gpu_addr_begin;
1318
1319 const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)};
1320 if (ranges.size() > 0) {
1321 const auto& [addr, size] = *ranges.begin();
1322 submapped_addr_end = addr + size;
1323 }
1324
1325 address_size =
1326 std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin));
1327 }
1291 const u32 size = address_size; // TODO: Analyze stride and number of vertices 1328 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1292 if (array.enable == 0 || size == 0 || !cpu_addr) { 1329 if (array.enable == 0 || size == 0 || !cpu_addr) {
1293 vertex_buffers[index] = NULL_BINDING; 1330 vertex_buffers[index] = NULL_BINDING;
@@ -1469,19 +1506,27 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
1469 overlap_ids.push_back(overlap_id); 1506 overlap_ids.push_back(overlap_id);
1470 overlap.Pick(); 1507 overlap.Pick();
1471 const VAddr overlap_cpu_addr = overlap.CpuAddr(); 1508 const VAddr overlap_cpu_addr = overlap.CpuAddr();
1472 if (overlap_cpu_addr < begin) { 1509 const bool expands_left = overlap_cpu_addr < begin;
1510 if (expands_left) {
1473 cpu_addr = begin = overlap_cpu_addr; 1511 cpu_addr = begin = overlap_cpu_addr;
1474 } 1512 }
1475 end = std::max(end, overlap_cpu_addr + overlap.SizeBytes()); 1513 const VAddr overlap_end = overlap_cpu_addr + overlap.SizeBytes();
1476 1514 const bool expands_right = overlap_end > end;
1515 if (overlap_end > end) {
1516 end = overlap_end;
1517 }
1477 stream_score += overlap.StreamScore(); 1518 stream_score += overlap.StreamScore();
1478 if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) { 1519 if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) {
1479 // When this memory region has been joined a bunch of times, we assume it's being used 1520 // When this memory region has been joined a bunch of times, we assume it's being used
1480 // as a stream buffer. Increase the size to skip constantly recreating buffers. 1521 // as a stream buffer. Increase the size to skip constantly recreating buffers.
1481 has_stream_leap = true; 1522 has_stream_leap = true;
1482 begin -= PAGE_SIZE * 256; 1523 if (expands_right) {
1483 cpu_addr = begin; 1524 begin -= PAGE_SIZE * 256;
1484 end += PAGE_SIZE * 256; 1525 cpu_addr = begin;
1526 }
1527 if (expands_left) {
1528 end += PAGE_SIZE * 256;
1529 }
1485 } 1530 }
1486 } 1531 }
1487 return OverlapResult{ 1532 return OverlapResult{
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index a8c4b4415..8dd840558 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -23,11 +23,9 @@
23#include "command_classes/nvdec.h" 23#include "command_classes/nvdec.h"
24#include "command_classes/vic.h" 24#include "command_classes/vic.h"
25#include "video_core/cdma_pusher.h" 25#include "video_core/cdma_pusher.h"
26#include "video_core/command_classes/nvdec_common.h"
27#include "video_core/command_classes/sync_manager.h" 26#include "video_core/command_classes/sync_manager.h"
28#include "video_core/engines/maxwell_3d.h" 27#include "video_core/engines/maxwell_3d.h"
29#include "video_core/gpu.h" 28#include "video_core/gpu.h"
30#include "video_core/memory_manager.h"
31 29
32namespace Tegra { 30namespace Tegra {
33CDmaPusher::CDmaPusher(GPU& gpu_) 31CDmaPusher::CDmaPusher(GPU& gpu_)
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index 87b49d6ea..cb1d16b71 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
13namespace Tegra { 14namespace Tegra {
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 04d0f3a2f..40f7755e8 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstdio>
7#include <fstream> 6#include <fstream>
8#include <vector> 7#include <vector>
9#include "common/assert.h" 8#include "common/assert.h"
@@ -57,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi
57 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; 56 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
58 return PREFERRED_CPU_FMT; 57 return PREFERRED_CPU_FMT;
59} 58}
59
60// List all the currently available hwcontext in ffmpeg
61std::vector<AVHWDeviceType> ListSupportedContexts() {
62 std::vector<AVHWDeviceType> contexts{};
63 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
64 do {
65 current_device_type = av_hwdevice_iterate_types(current_device_type);
66 contexts.push_back(current_device_type);
67 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
68 return contexts;
69}
70
60} // namespace 71} // namespace
61 72
62void AVFrameDeleter(AVFrame* ptr) { 73void AVFrameDeleter(AVFrame* ptr) {
@@ -77,17 +88,6 @@ Codec::~Codec() {
77 av_buffer_unref(&av_gpu_decoder); 88 av_buffer_unref(&av_gpu_decoder);
78} 89}
79 90
80// List all the currently available hwcontext in ffmpeg
81static std::vector<AVHWDeviceType> ListSupportedContexts() {
82 std::vector<AVHWDeviceType> contexts{};
83 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
84 do {
85 current_device_type = av_hwdevice_iterate_types(current_device_type);
86 contexts.push_back(current_device_type);
87 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
88 return contexts;
89}
90
91bool Codec::CreateGpuAvDevice() { 91bool Codec::CreateGpuAvDevice() {
92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; 92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
93 static const auto supported_contexts = ListSupportedContexts(); 93 static const auto supported_contexts = ListSupportedContexts();
@@ -97,6 +97,8 @@ bool Codec::CreateGpuAvDevice() {
97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); 97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
98 continue; 98 continue;
99 } 99 }
100 // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
101 av_buffer_unref(&av_gpu_decoder);
100 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); 102 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
101 if (hwdevice_res < 0) { 103 if (hwdevice_res < 0) {
102 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", 104 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
@@ -128,15 +130,19 @@ bool Codec::CreateGpuAvDevice() {
128 av_codec->name, av_hwdevice_get_type_name(type)); 130 av_codec->name, av_hwdevice_get_type_name(type));
129 break; 131 break;
130 } 132 }
131 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { 133 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
132 av_codec_ctx->pix_fmt = config->pix_fmt; 134#if defined(__unix__)
133 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { 135 // Some linux decoding backends are reported to crash with this config method
136 // TODO(ameerj): Properly support this method
137 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
134 // skip zero-copy decoders, we don't currently support them 138 // skip zero-copy decoders, we don't currently support them
135 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", 139 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
136 av_hwdevice_get_type_name(type), config->methods); 140 av_hwdevice_get_type_name(type), config->methods);
137 continue; 141 continue;
138 } 142 }
143#endif
139 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); 144 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
145 av_codec_ctx->pix_fmt = config->pix_fmt;
140 return true; 146 return true;
141 } 147 }
142 } 148 }
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index de5672155..661673b4e 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string_view> 8#include <string_view>
9#include <queue> 9#include <queue>
10#include "common/common_types.h" 10
11#include "video_core/command_classes/nvdec_common.h" 11#include "video_core/command_classes/nvdec_common.h"
12 12
13extern "C" { 13extern "C" {
diff --git a/src/video_core/command_classes/codecs/vp8.cpp b/src/video_core/command_classes/codecs/vp8.cpp
index 32ad0ec16..2f280cb7c 100644
--- a/src/video_core/command_classes/codecs/vp8.cpp
+++ b/src/video_core/command_classes/codecs/vp8.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <vector> 5#include <vector>
7 6
8#include "video_core/command_classes/codecs/vp8.h" 7#include "video_core/command_classes/codecs/vp8.h"
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 3b1ed4b3a..af1290016 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstring>
9#include <vector> 8#include <vector>
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h
index 7e94799dd..736d2fd0c 100644
--- a/src/video_core/command_classes/host1x.h
+++ b/src/video_core/command_classes/host1x.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h" 7#include "common/common_types.h"
10 8
11namespace Tegra { 9namespace Tegra {
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 8d28bd884..0b51e402a 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -6,7 +6,6 @@
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/memory.h"
10#include "video_core/dma_pusher.h" 9#include "video_core/dma_pusher.h"
11#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
12#include "video_core/gpu.h" 11#include "video_core/gpu.h"
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index d76c5ed56..4c0568c4c 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -9,7 +9,6 @@
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "video_core/engines/engine_interface.h" 12#include "video_core/engines/engine_interface.h"
14#include "video_core/gpu.h" 13#include "video_core/gpu.h"
15 14
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 5a1c12076..f7ff92c57 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -10,7 +10,6 @@
10#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/memory_manager.h" 11#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h" 12#include "video_core/rasterizer_interface.h"
13#include "video_core/renderer_base.h"
14#include "video_core/textures/decoders.h" 13#include "video_core/textures/decoders.h"
15 14
16namespace Tegra::Engines { 15namespace Tegra::Engines {
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index f8b8d06ac..c6b8adb56 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -12,7 +12,6 @@
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/engines/engine_interface.h" 13#include "video_core/engines/engine_interface.h"
14#include "video_core/engines/engine_upload.h" 14#include "video_core/engines/engine_upload.h"
15#include "video_core/gpu.h"
16#include "video_core/textures/texture.h" 15#include "video_core/textures/texture.h"
17 16
18namespace Core { 17namespace Core {
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 8aed16caa..f930e02b6 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -9,8 +9,6 @@
9#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/memory_manager.h" 10#include "video_core/memory_manager.h"
11#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
12#include "video_core/renderer_base.h"
13#include "video_core/textures/decoders.h"
14 12
15namespace Tegra::Engines { 13namespace Tegra::Engines {
16 14
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 949e2fae1..4cb4a3d2d 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -6,13 +6,11 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <vector>
10#include "common/bit_field.h" 9#include "common/bit_field.h"
11#include "common/common_funcs.h" 10#include "common/common_funcs.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "video_core/engines/engine_interface.h" 12#include "video_core/engines/engine_interface.h"
14#include "video_core/engines/engine_upload.h" 13#include "video_core/engines/engine_upload.h"
15#include "video_core/gpu.h"
16 14
17namespace Core { 15namespace Core {
18class System; 16class System;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 5d6d217bb..7399e760f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -7,6 +7,7 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "video_core/dirty_flags.h"
10#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
@@ -195,7 +196,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
195 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: 196 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
196 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: 197 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
197 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: 198 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
198 return StartCBData(method); 199 return ProcessCBData(argument);
199 case MAXWELL3D_REG_INDEX(cb_bind[0]): 200 case MAXWELL3D_REG_INDEX(cb_bind[0]):
200 return ProcessCBBind(0); 201 return ProcessCBBind(0);
201 case MAXWELL3D_REG_INDEX(cb_bind[1]): 202 case MAXWELL3D_REG_INDEX(cb_bind[1]):
@@ -208,6 +209,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
208 return ProcessCBBind(4); 209 return ProcessCBBind(4);
209 case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): 210 case MAXWELL3D_REG_INDEX(draw.vertex_end_gl):
210 return DrawArrays(); 211 return DrawArrays();
212 case MAXWELL3D_REG_INDEX(small_index):
213 regs.index_array.count = regs.small_index.count;
214 regs.index_array.first = regs.small_index.first;
215 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
216 return DrawArrays();
217 case MAXWELL3D_REG_INDEX(small_index_2):
218 regs.index_array.count = regs.small_index_2.count;
219 regs.index_array.first = regs.small_index_2.first;
220 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
221 return DrawArrays();
222 case MAXWELL3D_REG_INDEX(topology_override):
223 use_topology_override = true;
224 return;
211 case MAXWELL3D_REG_INDEX(clear_buffers): 225 case MAXWELL3D_REG_INDEX(clear_buffers):
212 return ProcessClearBuffers(); 226 return ProcessClearBuffers();
213 case MAXWELL3D_REG_INDEX(query.query_get): 227 case MAXWELL3D_REG_INDEX(query.query_get):
@@ -248,14 +262,6 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
248} 262}
249 263
250void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 264void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
251 if (method == cb_data_state.current) {
252 regs.reg_array[method] = method_argument;
253 ProcessCBData(method_argument);
254 return;
255 } else if (cb_data_state.current != null_cb_data) {
256 FinishCBData();
257 }
258
259 // It is an error to write to a register other than the current macro's ARG register before it 265 // It is an error to write to a register other than the current macro's ARG register before it
260 // has finished execution. 266 // has finished execution.
261 if (executing_macro != 0) { 267 if (executing_macro != 0) {
@@ -302,7 +308,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
302 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: 308 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
303 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: 309 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
304 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: 310 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
305 ProcessCBMultiData(method, base_start, amount); 311 ProcessCBMultiData(base_start, amount);
306 break; 312 break;
307 default: 313 default:
308 for (std::size_t i = 0; i < amount; i++) { 314 for (std::size_t i = 0; i < amount; i++) {
@@ -360,6 +366,35 @@ void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
360 } 366 }
361} 367}
362 368
369void Maxwell3D::ProcessTopologyOverride() {
370 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
371 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
372
373 PrimitiveTopology topology{};
374
375 switch (regs.topology_override) {
376 case PrimitiveTopologyOverride::None:
377 topology = regs.draw.topology;
378 break;
379 case PrimitiveTopologyOverride::Points:
380 topology = PrimitiveTopology::Points;
381 break;
382 case PrimitiveTopologyOverride::Lines:
383 topology = PrimitiveTopology::Lines;
384 break;
385 case PrimitiveTopologyOverride::LineStrip:
386 topology = PrimitiveTopology::LineStrip;
387 break;
388 default:
389 topology = static_cast<PrimitiveTopology>(regs.topology_override);
390 break;
391 }
392
393 if (use_topology_override) {
394 regs.draw.topology.Assign(topology);
395 }
396}
397
363void Maxwell3D::FlushMMEInlineDraw() { 398void Maxwell3D::FlushMMEInlineDraw() {
364 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), 399 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
365 regs.vertex_buffer.count); 400 regs.vertex_buffer.count);
@@ -370,6 +405,8 @@ void Maxwell3D::FlushMMEInlineDraw() {
370 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, 405 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
371 "Illegal combination of instancing parameters"); 406 "Illegal combination of instancing parameters");
372 407
408 ProcessTopologyOverride();
409
373 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; 410 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
374 if (ShouldExecute()) { 411 if (ShouldExecute()) {
375 rasterizer->Draw(is_indexed, true); 412 rasterizer->Draw(is_indexed, true);
@@ -529,6 +566,8 @@ void Maxwell3D::DrawArrays() {
529 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, 566 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
530 "Illegal combination of instancing parameters"); 567 "Illegal combination of instancing parameters");
531 568
569 ProcessTopologyOverride();
570
532 if (regs.draw.instance_next) { 571 if (regs.draw.instance_next) {
533 // Increment the current instance *before* drawing. 572 // Increment the current instance *before* drawing.
534 state.current_instance += 1; 573 state.current_instance += 1;
@@ -587,46 +626,7 @@ void Maxwell3D::ProcessCBBind(size_t stage_index) {
587 rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); 626 rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size);
588} 627}
589 628
590void Maxwell3D::ProcessCBData(u32 value) { 629void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
591 const u32 id = cb_data_state.id;
592 cb_data_state.buffer[id][cb_data_state.counter] = value;
593 // Increment the current buffer position.
594 regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
595 cb_data_state.counter++;
596}
597
598void Maxwell3D::StartCBData(u32 method) {
599 constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
600 cb_data_state.start_pos = regs.const_buffer.cb_pos;
601 cb_data_state.id = method - first_cb_data;
602 cb_data_state.current = method;
603 cb_data_state.counter = 0;
604 ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]);
605}
606
607void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) {
608 if (cb_data_state.current != method) {
609 if (cb_data_state.current != null_cb_data) {
610 FinishCBData();
611 }
612 constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
613 cb_data_state.start_pos = regs.const_buffer.cb_pos;
614 cb_data_state.id = method - first_cb_data;
615 cb_data_state.current = method;
616 cb_data_state.counter = 0;
617 }
618 const std::size_t id = cb_data_state.id;
619 const std::size_t size = amount;
620 std::size_t i = 0;
621 for (; i < size; i++) {
622 cb_data_state.buffer[id][cb_data_state.counter] = start_base[i];
623 cb_data_state.counter++;
624 }
625 // Increment the current buffer position.
626 regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount;
627}
628
629void Maxwell3D::FinishCBData() {
630 // Write the input value to the current const buffer at the current position. 630 // Write the input value to the current const buffer at the current position.
631 const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); 631 const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
632 ASSERT(buffer_address != 0); 632 ASSERT(buffer_address != 0);
@@ -634,14 +634,16 @@ void Maxwell3D::FinishCBData() {
634 // Don't allow writing past the end of the buffer. 634 // Don't allow writing past the end of the buffer.
635 ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); 635 ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size);
636 636
637 const GPUVAddr address{buffer_address + cb_data_state.start_pos}; 637 const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
638 const std::size_t size = regs.const_buffer.cb_pos - cb_data_state.start_pos; 638 const size_t copy_size = amount * sizeof(u32);
639 memory_manager.WriteBlock(address, start_base, copy_size);
639 640
640 const u32 id = cb_data_state.id; 641 // Increment the current buffer position.
641 memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size); 642 regs.const_buffer.cb_pos += static_cast<u32>(copy_size);
643}
642 644
643 cb_data_state.id = null_cb_data; 645void Maxwell3D::ProcessCBData(u32 value) {
644 cb_data_state.current = null_cb_data; 646 ProcessCBMultiData(&value, 1);
645} 647}
646 648
647Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 649Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index dc9df6c8b..d36dc3daa 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -10,7 +10,6 @@
10#include <limits> 10#include <limits>
11#include <optional> 11#include <optional>
12#include <type_traits> 12#include <type_traits>
13#include <unordered_map>
14#include <vector> 13#include <vector>
15 14
16#include "common/assert.h" 15#include "common/assert.h"
@@ -367,6 +366,22 @@ public:
367 Patches = 0xe, 366 Patches = 0xe,
368 }; 367 };
369 368
369 // Constants as from NVC0_3D_UNK1970_D3D
370 // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598
371 enum class PrimitiveTopologyOverride : u32 {
372 None = 0x0,
373 Points = 0x1,
374 Lines = 0x2,
375 LineStrip = 0x3,
376 Triangles = 0x4,
377 TriangleStrip = 0x5,
378 LinesAdjacency = 0xa,
379 LineStripAdjacency = 0xb,
380 TrianglesAdjacency = 0xc,
381 TriangleStripAdjacency = 0xd,
382 Patches = 0xe,
383 };
384
370 enum class IndexFormat : u32 { 385 enum class IndexFormat : u32 {
371 UnsignedByte = 0x0, 386 UnsignedByte = 0x0,
372 UnsignedShort = 0x1, 387 UnsignedShort = 0x1,
@@ -1200,7 +1215,17 @@ public:
1200 } 1215 }
1201 } index_array; 1216 } index_array;
1202 1217
1203 INSERT_PADDING_WORDS_NOINIT(0x7); 1218 union {
1219 BitField<0, 16, u32> first;
1220 BitField<16, 16, u32> count;
1221 } small_index;
1222
1223 union {
1224 BitField<0, 16, u32> first;
1225 BitField<16, 16, u32> count;
1226 } small_index_2;
1227
1228 INSERT_PADDING_WORDS_NOINIT(0x5);
1204 1229
1205 INSERT_PADDING_WORDS_NOINIT(0x1F); 1230 INSERT_PADDING_WORDS_NOINIT(0x1F);
1206 1231
@@ -1244,7 +1269,11 @@ public:
1244 BitField<11, 1, u32> depth_clamp_disabled; 1269 BitField<11, 1, u32> depth_clamp_disabled;
1245 } view_volume_clip_control; 1270 } view_volume_clip_control;
1246 1271
1247 INSERT_PADDING_WORDS_NOINIT(0x1F); 1272 INSERT_PADDING_WORDS_NOINIT(0xC);
1273
1274 PrimitiveTopologyOverride topology_override;
1275
1276 INSERT_PADDING_WORDS_NOINIT(0x12);
1248 1277
1249 u32 depth_bounds_enable; 1278 u32 depth_bounds_enable;
1250 1279
@@ -1520,10 +1549,8 @@ private:
1520 void ProcessSyncPoint(); 1549 void ProcessSyncPoint();
1521 1550
1522 /// Handles a write to the CB_DATA[i] register. 1551 /// Handles a write to the CB_DATA[i] register.
1523 void StartCBData(u32 method);
1524 void ProcessCBData(u32 value); 1552 void ProcessCBData(u32 value);
1525 void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount); 1553 void ProcessCBMultiData(const u32* start_base, u32 amount);
1526 void FinishCBData();
1527 1554
1528 /// Handles a write to the CB_BIND register. 1555 /// Handles a write to the CB_BIND register.
1529 void ProcessCBBind(size_t stage_index); 1556 void ProcessCBBind(size_t stage_index);
@@ -1531,6 +1558,9 @@ private:
1531 /// Handles a write to the VERTEX_END_GL register, triggering a draw. 1558 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
1532 void DrawArrays(); 1559 void DrawArrays();
1533 1560
1561 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
1562 void ProcessTopologyOverride();
1563
1534 // Handles a instance drawcall from MME 1564 // Handles a instance drawcall from MME
1535 void StepInstance(MMEDrawMode expected_mode, u32 count); 1565 void StepInstance(MMEDrawMode expected_mode, u32 count);
1536 1566
@@ -1555,20 +1585,10 @@ private:
1555 /// Interpreter for the macro codes uploaded to the GPU. 1585 /// Interpreter for the macro codes uploaded to the GPU.
1556 std::unique_ptr<MacroEngine> macro_engine; 1586 std::unique_ptr<MacroEngine> macro_engine;
1557 1587
1558 static constexpr u32 null_cb_data = 0xFFFFFFFF;
1559 struct CBDataState {
1560 static constexpr size_t inline_size = 0x4000;
1561 std::array<std::array<u32, inline_size>, 16> buffer;
1562 u32 current{null_cb_data};
1563 u32 id{null_cb_data};
1564 u32 start_pos{};
1565 u32 counter{};
1566 };
1567 CBDataState cb_data_state;
1568
1569 Upload::State upload_state; 1588 Upload::State upload_state;
1570 1589
1571 bool execute_on{true}; 1590 bool execute_on{true};
1591 bool use_topology_override{false};
1572}; 1592};
1573 1593
1574#define ASSERT_REG_POSITION(field_name, position) \ 1594#define ASSERT_REG_POSITION(field_name, position) \
@@ -1685,6 +1705,7 @@ ASSERT_REG_POSITION(draw, 0x585);
1685ASSERT_REG_POSITION(primitive_restart, 0x591); 1705ASSERT_REG_POSITION(primitive_restart, 0x591);
1686ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); 1706ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1);
1687ASSERT_REG_POSITION(index_array, 0x5F2); 1707ASSERT_REG_POSITION(index_array, 0x5F2);
1708ASSERT_REG_POSITION(small_index, 0x5F9);
1688ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); 1709ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
1689ASSERT_REG_POSITION(instanced_arrays, 0x620); 1710ASSERT_REG_POSITION(instanced_arrays, 0x620);
1690ASSERT_REG_POSITION(vp_point_size, 0x644); 1711ASSERT_REG_POSITION(vp_point_size, 0x644);
@@ -1694,6 +1715,7 @@ ASSERT_REG_POSITION(cull_face, 0x648);
1694ASSERT_REG_POSITION(pixel_center_integer, 0x649); 1715ASSERT_REG_POSITION(pixel_center_integer, 0x649);
1695ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); 1716ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
1696ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); 1717ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
1718ASSERT_REG_POSITION(topology_override, 0x65C);
1697ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); 1719ASSERT_REG_POSITION(depth_bounds_enable, 0x66F);
1698ASSERT_REG_POSITION(logic_op, 0x671); 1720ASSERT_REG_POSITION(logic_op, 0x671);
1699ASSERT_REG_POSITION(clear_buffers, 0x674); 1721ASSERT_REG_POSITION(clear_buffers, 0x674);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 67388d980..1fc1358bc 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -53,7 +53,6 @@ void MaxwellDMA::Launch() {
53 53
54 // TODO(Subv): Perform more research and implement all features of this engine. 54 // TODO(Subv): Perform more research and implement all features of this engine.
55 const LaunchDMA& launch = regs.launch_dma; 55 const LaunchDMA& launch = regs.launch_dma;
56 ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
57 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); 56 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
58 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); 57 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
59 ASSERT(regs.dst_params.origin.x == 0); 58 ASSERT(regs.dst_params.origin.x == 0);
@@ -79,6 +78,7 @@ void MaxwellDMA::Launch() {
79 CopyPitchToBlockLinear(); 78 CopyPitchToBlockLinear();
80 } 79 }
81 } 80 }
81 ReleaseSemaphore();
82} 82}
83 83
84void MaxwellDMA::CopyPitchToPitch() { 84void MaxwellDMA::CopyPitchToPitch() {
@@ -244,4 +244,22 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
244 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 244 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
245} 245}
246 246
247void MaxwellDMA::ReleaseSemaphore() {
248 const auto type = regs.launch_dma.semaphore_type;
249 const GPUVAddr address = regs.semaphore.address;
250 switch (type) {
251 case LaunchDMA::SemaphoreType::NONE:
252 break;
253 case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE:
254 memory_manager.Write<u32>(address, regs.semaphore.payload);
255 break;
256 case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE:
257 memory_manager.Write<u64>(address, static_cast<u64>(regs.semaphore.payload));
258 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
259 break;
260 default:
261 UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value()));
262 }
263}
264
247} // namespace Tegra::Engines 265} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index a04514425..9d0c77793 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -8,10 +8,8 @@
8#include <cstddef> 8#include <cstddef>
9#include <vector> 9#include <vector>
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "video_core/engines/engine_interface.h" 12#include "video_core/engines/engine_interface.h"
14#include "video_core/gpu.h"
15 13
16namespace Core { 14namespace Core {
17class System; 15class System;
@@ -224,6 +222,8 @@ private:
224 222
225 void FastCopyBlockLinearToPitch(); 223 void FastCopyBlockLinearToPitch();
226 224
225 void ReleaseSemaphore();
226
227 Core::System& system; 227 Core::System& system;
228 228
229 MemoryManager& memory_manager; 229 MemoryManager& memory_manager;
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 34dc6c596..f80d62c80 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -8,8 +8,6 @@
8#include <queue> 8#include <queue>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "core/core.h"
13#include "video_core/delayed_destruction_ring.h" 11#include "video_core/delayed_destruction_ring.h"
14#include "video_core/gpu.h" 12#include "video_core/gpu.h"
15#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index b1d455e30..93349bb78 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -6,41 +6,22 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/math_util.h" 8#include "common/math_util.h"
9#include "core/hle/service/nvflinger/buffer_transform_flags.h"
10#include "core/hle/service/nvflinger/pixel_format.h"
9 11
10namespace Tegra { 12namespace Tegra {
13
11/** 14/**
12 * Struct describing framebuffer configuration 15 * Struct describing framebuffer configuration
13 */ 16 */
14struct FramebufferConfig { 17struct FramebufferConfig {
15 enum class PixelFormat : u32 {
16 A8B8G8R8_UNORM = 1,
17 RGB565_UNORM = 4,
18 B8G8R8A8_UNORM = 5,
19 };
20
21 enum class TransformFlags : u32 {
22 /// No transform flags are set
23 Unset = 0x00,
24 /// Flip source image horizontally (around the vertical axis)
25 FlipH = 0x01,
26 /// Flip source image vertically (around the horizontal axis)
27 FlipV = 0x02,
28 /// Rotate source image 90 degrees clockwise
29 Rotate90 = 0x04,
30 /// Rotate source image 180 degrees
31 Rotate180 = 0x03,
32 /// Rotate source image 270 degrees clockwise
33 Rotate270 = 0x07,
34 };
35
36 VAddr address{}; 18 VAddr address{};
37 u32 offset{}; 19 u32 offset{};
38 u32 width{}; 20 u32 width{};
39 u32 height{}; 21 u32 height{};
40 u32 stride{}; 22 u32 stride{};
41 PixelFormat pixel_format{}; 23 Service::android::PixelFormat pixel_format{};
42 24 Service::android::BufferTransformFlags transform_flags{};
43 TransformFlags transform_flags{};
44 Common::Rectangle<int> crop_rect; 25 Common::Rectangle<int> crop_rect;
45}; 26};
46 27
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ba9ba082f..789af452d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -230,7 +230,7 @@ struct GPU::Impl {
230 void IncrementSyncPoint(u32 syncpoint_id) { 230 void IncrementSyncPoint(u32 syncpoint_id) {
231 auto& syncpoint = syncpoints.at(syncpoint_id); 231 auto& syncpoint = syncpoints.at(syncpoint_id);
232 syncpoint++; 232 syncpoint++;
233 std::lock_guard lock{sync_mutex}; 233 std::scoped_lock lock{sync_mutex};
234 sync_cv.notify_all(); 234 sync_cv.notify_all();
235 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 235 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
236 if (!interrupt.empty()) { 236 if (!interrupt.empty()) {
@@ -252,7 +252,7 @@ struct GPU::Impl {
252 } 252 }
253 253
254 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { 254 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
255 std::lock_guard lock{sync_mutex}; 255 std::scoped_lock lock{sync_mutex};
256 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 256 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
257 bool contains = std::any_of(interrupt.begin(), interrupt.end(), 257 bool contains = std::any_of(interrupt.begin(), interrupt.end(),
258 [value](u32 in_value) { return in_value == value; }); 258 [value](u32 in_value) { return in_value == value; });
@@ -263,7 +263,7 @@ struct GPU::Impl {
263 } 263 }
264 264
265 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { 265 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
266 std::lock_guard lock{sync_mutex}; 266 std::scoped_lock lock{sync_mutex};
267 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 267 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
268 const auto iter = 268 const auto iter =
269 std::find_if(interrupt.begin(), interrupt.end(), 269 std::find_if(interrupt.begin(), interrupt.end(),
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 26b8ea233..97c029140 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -8,6 +8,7 @@
8 8
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/nvdrv/nvdata.h"
11#include "video_core/cdma_pusher.h" 12#include "video_core/cdma_pusher.h"
12#include "video_core/framebuffer_config.h" 13#include "video_core/framebuffer_config.h"
13 14
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 9547f277a..4e8999915 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -56,7 +56,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
56 if (next.block) { 56 if (next.block) {
57 // We have to lock the write_lock to ensure that the condition_variable wait not get a 57 // We have to lock the write_lock to ensure that the condition_variable wait not get a
58 // race between the check and the lock itself. 58 // race between the check and the lock itself.
59 std::lock_guard lk(state.write_lock); 59 std::scoped_lock lk{state.write_lock};
60 state.cv.notify_all(); 60 state.cv.notify_all();
61 } 61 }
62 } 62 }
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index fd3e41434..190fc6aea 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -14,9 +14,11 @@ set(SHADER_FILES
14 convert_d24s8_to_abgr8.frag 14 convert_d24s8_to_abgr8.frag
15 convert_depth_to_float.frag 15 convert_depth_to_float.frag
16 convert_float_to_depth.frag 16 convert_float_to_depth.frag
17 convert_s8d24_to_abgr8.frag
17 full_screen_triangle.vert 18 full_screen_triangle.vert
18 fxaa.frag 19 fxaa.frag
19 fxaa.vert 20 fxaa.vert
21 opengl_convert_s8d24.comp
20 opengl_copy_bc4.comp 22 opengl_copy_bc4.comp
21 opengl_present.frag 23 opengl_present.frag
22 opengl_present.vert 24 opengl_present.vert
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
new file mode 100644
index 000000000..c8a1683b8
--- /dev/null
+++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 450
6
7layout(binding = 0) uniform sampler2D depth_tex;
8layout(binding = 1) uniform isampler2D stencil_tex;
9
10layout(location = 0) out vec4 color;
11
12void main() {
13 ivec2 coord = ivec2(gl_FragCoord.xy);
14 uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
15 uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
16
17 highp uint depth_val =
18 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
19 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
20 highp uvec4 components =
21 uvec4((uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu, stencil_val);
22 color.rgba = vec4(components) / (exp2(8.0) - 1.0);
23}
diff --git a/src/video_core/host_shaders/opengl_convert_s8d24.comp b/src/video_core/host_shaders/opengl_convert_s8d24.comp
new file mode 100644
index 000000000..83e1ab176
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_convert_s8d24.comp
@@ -0,0 +1,18 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 430 core
6
7layout(local_size_x = 16, local_size_y = 8) in;
8
9layout(binding = 0, rgba8ui) restrict uniform uimage2D destination;
10layout(location = 0) uniform uvec3 size;
11
12void main() {
13 if (any(greaterThanEqual(gl_GlobalInvocationID, size))) {
14 return;
15 }
16 uvec4 components = imageLoad(destination, ivec2(gl_GlobalInvocationID.xy));
17 imageStore(destination, ivec2(gl_GlobalInvocationID.xy), components.wxyz);
18}
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 4ff3fa268..722ebd9ad 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
12#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
13#include "core/memory.h" 13#include "core/memory.h"
14#include "video_core/gpu.h"
15#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
16#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
17#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 392f82eb7..8a84bcfa9 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -18,9 +18,7 @@
18 18
19#include "common/assert.h" 19#include "common/assert.h"
20#include "common/settings.h" 20#include "common/settings.h"
21#include "core/core.h"
22#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
23#include "video_core/gpu.h"
24#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
25#include "video_core/rasterizer_interface.h" 23#include "video_core/rasterizer_interface.h"
26 24
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index a99c33c37..9756a81d6 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
8#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
9 8
@@ -27,6 +26,10 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 26 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
28} 27}
29 28
29bool RendererBase::IsScreenshotPending() const {
30 return renderer_settings.screenshot_requested;
31}
32
30void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback, 33void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
31 const Layout::FramebufferLayout& layout) { 34 const Layout::FramebufferLayout& layout) {
32 if (renderer_settings.screenshot_requested) { 35 if (renderer_settings.screenshot_requested) {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index c5f974080..30d19b178 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -83,6 +83,9 @@ public:
83 /// Refreshes the settings common to all renderers 83 /// Refreshes the settings common to all renderers
84 void RefreshBaseSettings(); 84 void RefreshBaseSettings();
85 85
86 /// Returns true if a screenshot is being processed
87 bool IsScreenshotPending() const;
88
86 /// Request a screenshot of the next frame 89 /// Request a screenshot of the next frame
87 void RequestScreenshot(void* data, std::function<void(bool)> callback, 90 void RequestScreenshot(void* data, std::function<void(bool)> callback,
88 const Layout::FramebufferLayout& layout); 91 const Layout::FramebufferLayout& layout);
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index d4dd10bb6..f1f7b384b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -135,6 +135,20 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
135 buffer.Create(); 135 buffer.Create();
136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY); 136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY);
137 } 137 }
138
139 device_access_memory = [this]() -> u64 {
140 if (device.CanReportMemoryUsage()) {
141 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
142 }
143 return 2_GiB; // Return minimum requirements
144 }();
145}
146
147u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
148 if (device.CanReportMemoryUsage()) {
149 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
150 }
151 return 2_GiB;
138} 152}
139 153
140void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 154void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 060d36427..a8699f28c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -7,9 +7,7 @@
7#include <array> 7#include <array>
8#include <span> 8#include <span>
9 9
10#include "common/alignment.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "common/dynamic_library.h"
13#include "video_core/buffer_cache/buffer_cache.h" 11#include "video_core/buffer_cache/buffer_cache.h"
14#include "video_core/rasterizer_interface.h" 12#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_opengl/gl_device.h" 13#include "video_core/renderer_opengl/gl_device.h"
@@ -91,6 +89,8 @@ public:
91 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, 89 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
92 VideoCore::Surface::PixelFormat format); 90 VideoCore::Surface::PixelFormat format);
93 91
92 u64 GetDeviceMemoryUsage() const;
93
94 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { 94 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
95 const GLuint handle = fast_uniforms[stage][binding_index].handle; 95 const GLuint handle = fast_uniforms[stage][binding_index].handle;
96 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); 96 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
@@ -153,6 +153,14 @@ public:
153 use_storage_buffers = use_storage_buffers_; 153 use_storage_buffers = use_storage_buffers_;
154 } 154 }
155 155
156 u64 GetDeviceLocalMemory() const {
157 return device_access_memory;
158 }
159
160 bool CanReportMemoryUsage() const {
161 return device.CanReportMemoryUsage();
162 }
163
156private: 164private:
157 static constexpr std::array PABO_LUT{ 165 static constexpr std::array PABO_LUT{
158 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 166 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
@@ -186,6 +194,8 @@ private:
186 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms; 194 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms;
187 195
188 u32 index_buffer_offset = 0; 196 u32 index_buffer_offset = 0;
197
198 u64 device_access_memory;
189}; 199};
190 200
191struct BufferCacheParams { 201struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 50c676365..b0d183b46 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <type_traits> 8#include <type_traits>
9#include <utility>
10 9
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "shader_recompiler/shader_info.h" 11#include "shader_recompiler/shader_info.h"
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index e62912a22..597301eeb 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -6,8 +6,6 @@
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <cstdlib> 8#include <cstdlib>
9#include <cstring>
10#include <limits>
11#include <optional> 9#include <optional>
12#include <span> 10#include <span>
13#include <stdexcept> 11#include <stdexcept>
@@ -15,13 +13,15 @@
15 13
16#include <glad/glad.h> 14#include <glad/glad.h>
17 15
16#include "common/literals.h"
18#include "common/logging/log.h" 17#include "common/logging/log.h"
19#include "common/scope_exit.h"
20#include "common/settings.h" 18#include "common/settings.h"
21#include "shader_recompiler/stage.h" 19#include "shader_recompiler/stage.h"
22#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
23#include "video_core/renderer_opengl/gl_resource_manager.h" 21#include "video_core/renderer_opengl/gl_resource_manager.h"
24 22
23using namespace Common::Literals;
24
25namespace OpenGL { 25namespace OpenGL {
26namespace { 26namespace {
27constexpr std::array LIMIT_UBOS = { 27constexpr std::array LIMIT_UBOS = {
@@ -168,6 +168,7 @@ Device::Device() {
168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; 168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; 169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
170 need_fastmath_off = is_nvidia; 170 need_fastmath_off = is_nvidia;
171 can_report_memory = GLAD_GL_NVX_gpu_memory_info;
171 172
172 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive 173 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
173 // uniform buffers as "push constants" 174 // uniform buffers as "push constants"
@@ -279,4 +280,10 @@ void main() {
279})"); 280})");
280} 281}
281 282
283u64 Device::GetCurrentDedicatedVideoMemory() const {
284 GLint cur_avail_mem_kb = 0;
285 glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb);
286 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
287}
288
282} // namespace OpenGL 289} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 95c2e8d38..9bb0b9148 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -20,6 +20,8 @@ public:
20 20
21 [[nodiscard]] std::string GetVendorName() const; 21 [[nodiscard]] std::string GetVendorName() const;
22 22
23 u64 GetCurrentDedicatedVideoMemory() const;
24
23 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept { 25 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept {
24 return max_uniform_buffers[static_cast<size_t>(stage)]; 26 return max_uniform_buffers[static_cast<size_t>(stage)];
25 } 27 }
@@ -168,6 +170,10 @@ public:
168 return vendor_name == "ATI Technologies Inc."; 170 return vendor_name == "ATI Technologies Inc.";
169 } 171 }
170 172
173 bool CanReportMemoryUsage() const {
174 return can_report_memory;
175 }
176
171private: 177private:
172 static bool TestVariableAoffi(); 178 static bool TestVariableAoffi();
173 static bool TestPreciseBug(); 179 static bool TestPreciseBug();
@@ -210,6 +216,7 @@ private:
210 bool need_fastmath_off{}; 216 bool need_fastmath_off{};
211 bool has_cbuf_ftou_bug{}; 217 bool has_cbuf_ftou_bug{};
212 bool has_bool_ref_bug{}; 218 bool has_bool_ref_bug{};
219 bool can_report_memory{};
213 220
214 std::string vendor_name; 221 std::string vendor_name;
215}; 222};
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index f8495896c..fd40966d5 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -243,10 +243,6 @@ GraphicsPipeline::GraphicsPipeline(
243 case Settings::ShaderBackend::GLASM: 243 case Settings::ShaderBackend::GLASM:
244 if (!sources[stage].empty()) { 244 if (!sources[stage].empty()) {
245 assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); 245 assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage));
246 if (in_parallel) {
247 // Make sure program is built before continuing when building in parallel
248 glGetString(GL_PROGRAM_ERROR_STRING_NV);
249 }
250 } 246 }
251 break; 247 break;
252 case Settings::ShaderBackend::SPIRV: 248 case Settings::ShaderBackend::SPIRV:
@@ -256,20 +252,18 @@ GraphicsPipeline::GraphicsPipeline(
256 break; 252 break;
257 } 253 }
258 } 254 }
259 if (in_parallel && backend != Settings::ShaderBackend::GLASM) { 255 if (in_parallel) {
260 // Make sure programs have built if we are building shaders in parallel 256 std::scoped_lock lock{built_mutex};
261 for (OGLProgram& program : source_programs) { 257 built_fence.Create();
262 if (program.handle != 0) { 258 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
263 GLint status{}; 259 glFlush();
264 glGetProgramiv(program.handle, GL_LINK_STATUS, &status); 260 built_condvar.notify_one();
265 } 261 } else {
266 } 262 is_built = true;
267 } 263 }
268 if (shader_notify) { 264 if (shader_notify) {
269 shader_notify->MarkShaderComplete(); 265 shader_notify->MarkShaderComplete();
270 } 266 }
271 is_built = true;
272 built_condvar.notify_one();
273 }}; 267 }};
274 if (thread_worker) { 268 if (thread_worker) {
275 thread_worker->QueueWork(std::move(func)); 269 thread_worker->QueueWork(std::move(func));
@@ -440,7 +434,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
440 buffer_cache.UpdateGraphicsBuffers(is_indexed); 434 buffer_cache.UpdateGraphicsBuffers(is_indexed);
441 buffer_cache.BindHostGeometryBuffers(is_indexed); 435 buffer_cache.BindHostGeometryBuffers(is_indexed);
442 436
443 if (!is_built.load(std::memory_order::relaxed)) { 437 if (!IsBuilt()) {
444 WaitForBuild(); 438 WaitForBuild();
445 } 439 }
446 const bool use_assembly{assembly_programs[0].handle != 0}; 440 const bool use_assembly{assembly_programs[0].handle != 0};
@@ -585,8 +579,26 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
585} 579}
586 580
587void GraphicsPipeline::WaitForBuild() { 581void GraphicsPipeline::WaitForBuild() {
588 std::unique_lock lock{built_mutex}; 582 if (built_fence.handle == 0) {
589 built_condvar.wait(lock, [this] { return is_built.load(std::memory_order::relaxed); }); 583 std::unique_lock lock{built_mutex};
584 built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
585 }
586 ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
587 is_built = true;
588}
589
590bool GraphicsPipeline::IsBuilt() noexcept {
591 if (is_built) {
592 return true;
593 }
594 if (built_fence.handle == 0) {
595 return false;
596 }
597 // Timeout of zero means this is non-blocking
598 const auto sync_status = glClientWaitSync(built_fence.handle, 0, 0);
599 ASSERT(sync_status != GL_WAIT_FAILED);
600 is_built = sync_status != GL_TIMEOUT_EXPIRED;
601 return is_built;
590} 602}
591 603
592} // namespace OpenGL 604} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 4e28d9a42..4f8049717 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -14,7 +14,6 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "shader_recompiler/shader_info.h" 15#include "shader_recompiler/shader_info.h"
16#include "video_core/engines/maxwell_3d.h" 16#include "video_core/engines/maxwell_3d.h"
17#include "video_core/memory_manager.h"
18#include "video_core/renderer_opengl/gl_buffer_cache.h" 17#include "video_core/renderer_opengl/gl_buffer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 18#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_texture_cache.h" 19#include "video_core/renderer_opengl/gl_texture_cache.h"
@@ -100,9 +99,7 @@ public:
100 return writes_global_memory; 99 return writes_global_memory;
101 } 100 }
102 101
103 [[nodiscard]] bool IsBuilt() const noexcept { 102 [[nodiscard]] bool IsBuilt() noexcept;
104 return is_built.load(std::memory_order::relaxed);
105 }
106 103
107 template <typename Spec> 104 template <typename Spec>
108 static auto MakeConfigureSpecFunc() { 105 static auto MakeConfigureSpecFunc() {
@@ -154,7 +151,8 @@ private:
154 151
155 std::mutex built_mutex; 152 std::mutex built_mutex;
156 std::condition_variable built_condvar; 153 std::condition_variable built_condvar;
157 std::atomic_bool is_built{false}; 154 OGLSync built_fence{};
155 bool is_built{false};
158}; 156};
159 157
160} // namespace OpenGL 158} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index acebbf5f4..9e6c50055 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -3,15 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring>
7#include <memory> 6#include <memory>
8#include <unordered_map>
9#include <utility> 7#include <utility>
10#include <vector> 8#include <vector>
11 9
12#include <glad/glad.h> 10#include <glad/glad.h>
13 11
14#include "common/assert.h"
15#include "core/core.h" 12#include "core/core.h"
16#include "video_core/engines/maxwell_3d.h" 13#include "video_core/engines/maxwell_3d.h"
17#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 142412a8e..8ef79753f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -6,9 +6,7 @@
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
8#include <memory> 8#include <memory>
9#include <string>
10#include <string_view> 9#include <string_view>
11#include <tuple>
12#include <utility> 10#include <utility>
13 11
14#include <glad/glad.h> 12#include <glad/glad.h>
@@ -17,8 +15,9 @@
17#include "common/logging/log.h" 15#include "common/logging/log.h"
18#include "common/math_util.h" 16#include "common/math_util.h"
19#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "common/scope_exit.h"
20#include "common/settings.h" 19#include "common/settings.h"
21#include "core/memory.h" 20
22#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
23#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
24#include "video_core/memory_manager.h" 23#include "video_core/memory_manager.h"
@@ -212,6 +211,7 @@ void RasterizerOpenGL::Clear() {
212void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 211void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
213 MICROPROFILE_SCOPE(OpenGL_Drawing); 212 MICROPROFILE_SCOPE(OpenGL_Drawing);
214 213
214 SCOPE_EXIT({ gpu.TickWork(); });
215 query_cache.UpdateCounters(); 215 query_cache.UpdateCounters();
216 216
217 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 217 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -267,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
267 267
268 ++num_queued_commands; 268 ++num_queued_commands;
269 has_written_global_memory |= pipeline->WritesGlobalMemory(); 269 has_written_global_memory |= pipeline->WritesGlobalMemory();
270
271 gpu.TickWork();
272} 270}
273 271
274void RasterizerOpenGL::DispatchCompute() { 272void RasterizerOpenGL::DispatchCompute() {
@@ -522,6 +520,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
522 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); 520 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different");
523 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); 521 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different");
524 522
523 screen_info.texture.width = image_view->size.width;
524 screen_info.texture.height = image_view->size.height;
525 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); 525 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
526 screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format); 526 screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format);
527 return true; 527 return true;
@@ -559,12 +559,19 @@ void RasterizerOpenGL::SyncViewport() {
559 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; 559 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports;
560 const bool dirty_clip_control = flags[Dirty::ClipControl]; 560 const bool dirty_clip_control = flags[Dirty::ClipControl];
561 561
562 if (dirty_clip_control || flags[Dirty::FrontFace]) { 562 if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) {
563 flags[Dirty::FrontFace] = false; 563 flags[Dirty::FrontFace] = false;
564 564
565 GLenum mode = MaxwellToGL::FrontFace(regs.front_face); 565 GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
566 bool flip_faces = false;
566 if (regs.screen_y_control.triangle_rast_flip != 0 && 567 if (regs.screen_y_control.triangle_rast_flip != 0 &&
567 regs.viewport_transform[0].scale_y < 0.0f) { 568 regs.viewport_transform[0].scale_y < 0.0f) {
569 flip_faces = !flip_faces;
570 }
571 if (regs.viewport_transform[0].scale_z < 0.0f) {
572 flip_faces = !flip_faces;
573 }
574 if (flip_faces) {
568 switch (mode) { 575 switch (mode) {
569 case GL_CW: 576 case GL_CW:
570 mode = GL_CCW; 577 mode = GL_CCW;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 98f6fd342..c79461d59 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -5,20 +5,14 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
9#include <cstddef> 8#include <cstddef>
10#include <memory>
11#include <optional> 9#include <optional>
12#include <tuple>
13#include <utility>
14 10
15#include <boost/container/static_vector.hpp> 11#include <boost/container/static_vector.hpp>
16 12
17#include <glad/glad.h> 13#include <glad/glad.h>
18 14
19#include "common/common_types.h" 15#include "common/common_types.h"
20#include "video_core/engines/const_buffer_info.h"
21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
23#include "video_core/rasterizer_accelerated.h" 17#include "video_core/rasterizer_accelerated.h"
24#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
@@ -26,12 +20,8 @@
26#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
27#include "video_core/renderer_opengl/gl_fence_manager.h" 21#include "video_core/renderer_opengl/gl_fence_manager.h"
28#include "video_core/renderer_opengl/gl_query_cache.h" 22#include "video_core/renderer_opengl/gl_query_cache.h"
29#include "video_core/renderer_opengl/gl_resource_manager.h"
30#include "video_core/renderer_opengl/gl_shader_cache.h" 23#include "video_core/renderer_opengl/gl_shader_cache.h"
31#include "video_core/renderer_opengl/gl_shader_manager.h"
32#include "video_core/renderer_opengl/gl_state_tracker.h"
33#include "video_core/renderer_opengl/gl_texture_cache.h" 24#include "video_core/renderer_opengl/gl_texture_cache.h"
34#include "video_core/textures/texture.h"
35 25
36namespace Core::Memory { 26namespace Core::Memory {
37class Memory; 27class Memory;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 5e7101d28..f6839a657 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -3,9 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view> 5#include <string_view>
6#include <utility>
7#include <glad/glad.h> 6#include <glad/glad.h>
8#include "common/common_types.h"
9#include "common/microprofile.h" 7#include "common/microprofile.h"
10#include "video_core/renderer_opengl/gl_resource_manager.h" 8#include "video_core/renderer_opengl/gl_resource_manager.h"
11#include "video_core/renderer_opengl/gl_shader_util.h" 9#include "video_core/renderer_opengl/gl_shader_util.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index f71e01a34..05c5e702c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -14,10 +14,8 @@
14#include "common/fs/fs.h" 14#include "common/fs/fs.h"
15#include "common/fs/path_util.h" 15#include "common/fs/path_util.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/scope_exit.h"
18#include "common/settings.h" 17#include "common/settings.h"
19#include "common/thread_worker.h" 18#include "common/thread_worker.h"
20#include "core/core.h"
21#include "shader_recompiler/backend/glasm/emit_glasm.h" 19#include "shader_recompiler/backend/glasm/emit_glasm.h"
22#include "shader_recompiler/backend/glsl/emit_glsl.h" 20#include "shader_recompiler/backend/glsl/emit_glsl.h"
23#include "shader_recompiler/backend/spirv/emit_spirv.h" 21#include "shader_recompiler/backend/spirv/emit_spirv.h"
@@ -29,7 +27,6 @@
29#include "video_core/engines/maxwell_3d.h" 27#include "video_core/engines/maxwell_3d.h"
30#include "video_core/memory_manager.h" 28#include "video_core/memory_manager.h"
31#include "video_core/renderer_opengl/gl_rasterizer.h" 29#include "video_core/renderer_opengl/gl_rasterizer.h"
32#include "video_core/renderer_opengl/gl_resource_manager.h"
33#include "video_core/renderer_opengl/gl_shader_cache.h" 30#include "video_core/renderer_opengl/gl_shader_cache.h"
34#include "video_core/renderer_opengl/gl_shader_util.h" 31#include "video_core/renderer_opengl/gl_shader_util.h"
35#include "video_core/renderer_opengl/gl_state_tracker.h" 32#include "video_core/renderer_opengl/gl_state_tracker.h"
@@ -261,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
261 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 258 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
262 ctx->pools.ReleaseContents(); 259 ctx->pools.ReleaseContents();
263 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 260 auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
264 std::lock_guard lock{state.mutex}; 261 std::scoped_lock lock{state.mutex};
265 if (pipeline) { 262 if (pipeline) {
266 compute_cache.emplace(key, std::move(pipeline)); 263 compute_cache.emplace(key, std::move(pipeline));
267 } 264 }
@@ -283,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
283 } 280 }
284 ctx->pools.ReleaseContents(); 281 ctx->pools.ReleaseContents();
285 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 282 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
286 std::lock_guard lock{state.mutex}; 283 std::scoped_lock lock{state.mutex};
287 if (pipeline) { 284 if (pipeline) {
288 graphics_cache.emplace(key, std::move(pipeline)); 285 graphics_cache.emplace(key, std::move(pipeline));
289 } 286 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index a34110b37..06d4b38bb 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -4,18 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <filesystem> 7#include <filesystem>
9#include <stop_token> 8#include <stop_token>
10#include <unordered_map> 9#include <unordered_map>
11 10
12#include <glad/glad.h>
13
14#include "common/common_types.h" 11#include "common/common_types.h"
15#include "common/thread_worker.h" 12#include "common/thread_worker.h"
16#include "shader_recompiler/frontend/ir/value.h"
17#include "shader_recompiler/host_translate_info.h" 13#include "shader_recompiler/host_translate_info.h"
18#include "shader_recompiler/object_pool.h"
19#include "shader_recompiler/profile.h" 14#include "shader_recompiler/profile.h"
20#include "video_core/renderer_opengl/gl_compute_pipeline.h" 15#include "video_core/renderer_opengl/gl_compute_pipeline.h"
21#include "video_core/renderer_opengl/gl_graphics_pipeline.h" 16#include "video_core/renderer_opengl/gl_graphics_pipeline.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index d432072ad..129966e72 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -6,7 +6,6 @@
6#include <vector> 6#include <vector>
7#include <glad/glad.h> 7#include <glad/glad.h>
8 8
9#include "common/assert.h"
10#include "common/logging/log.h" 9#include "common/logging/log.h"
11#include "common/settings.h" 10#include "common/settings.h"
12#include "video_core/renderer_opengl/gl_shader_util.h" 11#include "video_core/renderer_opengl/gl_shader_util.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 4e1a2a8e1..a64ef37dc 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -5,14 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <span> 7#include <span>
8#include <string>
9#include <string_view> 8#include <string_view>
10#include <vector>
11 9
12#include <glad/glad.h> 10#include <glad/glad.h>
13 11
14#include "common/assert.h"
15#include "common/logging/log.h"
16#include "video_core/renderer_opengl/gl_resource_manager.h" 12#include "video_core/renderer_opengl/gl_resource_manager.h"
17 13
18namespace OpenGL { 14namespace OpenGL {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 5864c7c07..550ed6d36 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -9,7 +9,6 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/core.h"
13#include "video_core/dirty_flags.h" 12#include "video_core/dirty_flags.h"
14#include "video_core/engines/maxwell_3d.h" 13#include "video_core/engines/maxwell_3d.h"
15 14
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h
index 2e67922a6..f0cb29dca 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.h
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
9#include <span> 8#include <span>
10#include <utility> 9#include <utility>
11 10
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 3c1f79a27..f8c6e5c7e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -182,6 +182,26 @@ GLenum AttachmentType(PixelFormat format) {
182 } 182 }
183} 183}
184 184
185GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
186 switch (source) {
187 case SwizzleSource::Zero:
188 return GL_ZERO;
189 case SwizzleSource::R:
190 return GL_ALPHA;
191 case SwizzleSource::G:
192 return GL_BLUE;
193 case SwizzleSource::B:
194 return GL_GREEN;
195 case SwizzleSource::A:
196 return GL_RED;
197 case SwizzleSource::OneInt:
198 case SwizzleSource::OneFloat:
199 return GL_ONE;
200 }
201 UNREACHABLE_MSG("Invalid swizzle source={}", source);
202 return GL_NONE;
203}
204
185void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) { 205void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) {
186 switch (format) { 206 switch (format) {
187 case PixelFormat::D24_UNORM_S8_UINT: 207 case PixelFormat::D24_UNORM_S8_UINT:
@@ -192,6 +212,12 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
192 TextureMode(format, swizzle[0] == SwizzleSource::R)); 212 TextureMode(format, swizzle[0] == SwizzleSource::R));
193 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 213 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
194 break; 214 break;
215 case PixelFormat::A5B5G5R1_UNORM: {
216 std::array<GLint, 4> gl_swizzle;
217 std::ranges::transform(swizzle, gl_swizzle.begin(), ConvertA5B5G5R1_UNORM);
218 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
219 return;
220 }
195 default: 221 default:
196 break; 222 break;
197 } 223 }
@@ -409,8 +435,8 @@ ImageBufferMap::~ImageBufferMap() {
409 435
410TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager, 436TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager,
411 StateTracker& state_tracker_) 437 StateTracker& state_tracker_)
412 : device{device_}, state_tracker{state_tracker_}, 438 : device{device_}, state_tracker{state_tracker_}, util_shaders(program_manager),
413 util_shaders(program_manager), resolution{Settings::values.resolution_info} { 439 format_conversion_pass{util_shaders}, resolution{Settings::values.resolution_info} {
414 static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D}; 440 static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D};
415 for (size_t i = 0; i < TARGETS.size(); ++i) { 441 for (size_t i = 0; i < TARGETS.size(); ++i) {
416 const GLenum target = TARGETS[i]; 442 const GLenum target = TARGETS[i];
@@ -484,6 +510,13 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
484 rescale_read_fbos[i].Create(); 510 rescale_read_fbos[i].Create();
485 } 511 }
486 } 512 }
513
514 device_access_memory = [this]() -> u64 {
515 if (device.CanReportMemoryUsage()) {
516 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
517 }
518 return 2_GiB; // Return minimum requirements
519 }();
487} 520}
488 521
489TextureCacheRuntime::~TextureCacheRuntime() = default; 522TextureCacheRuntime::~TextureCacheRuntime() = default;
@@ -500,13 +533,11 @@ ImageBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
500 return download_buffers.RequestMap(size, false); 533 return download_buffers.RequestMap(size, false);
501} 534}
502 535
503u64 TextureCacheRuntime::GetDeviceLocalMemory() const { 536u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
504 if (GLAD_GL_NVX_gpu_memory_info) { 537 if (device.CanReportMemoryUsage()) {
505 GLint cur_avail_mem_kb = 0; 538 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
506 glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb);
507 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
508 } 539 }
509 return 2_GiB; // Return minimum requirements 540 return 2_GiB;
510} 541}
511 542
512void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image, 543void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
@@ -686,6 +717,7 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
686 } 717 }
687 if (IsConverted(runtime->device, info.format, info.type)) { 718 if (IsConverted(runtime->device, info.format, info.type)) {
688 flags |= ImageFlagBits::Converted; 719 flags |= ImageFlagBits::Converted;
720 flags |= ImageFlagBits::CostlyLoad;
689 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; 721 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
690 gl_format = GL_RGBA; 722 gl_format = GL_RGBA;
691 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 723 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
@@ -1319,6 +1351,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1319 1351
1320Framebuffer::~Framebuffer() = default; 1352Framebuffer::~Framebuffer() = default;
1321 1353
1354FormatConversionPass::FormatConversionPass(UtilShaders& util_shaders_)
1355 : util_shaders{util_shaders_} {}
1356
1322void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, 1357void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
1323 std::span<const VideoCommon::ImageCopy> copies) { 1358 std::span<const VideoCommon::ImageCopy> copies) {
1324 const GLenum dst_target = ImageTarget(dst_image.info); 1359 const GLenum dst_target = ImageTarget(dst_image.info);
@@ -1351,6 +1386,12 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
1351 dst_origin.z, region.width, region.height, region.depth, 1386 dst_origin.z, region.width, region.height, region.depth,
1352 dst_image.GlFormat(), dst_image.GlType(), nullptr); 1387 dst_image.GlFormat(), dst_image.GlType(), nullptr);
1353 } 1388 }
1389
1390 // Swap component order of S8D24 to ABGR8 reinterprets
1391 if (src_image.info.format == PixelFormat::D24_UNORM_S8_UINT &&
1392 dst_image.info.format == PixelFormat::A8B8G8R8_UNORM) {
1393 util_shaders.ConvertS8D24(dst_image, copies);
1394 }
1354} 1395}
1355 1396
1356} // namespace OpenGL 1397} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 7f425631f..672fa8dde 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -10,6 +10,7 @@
10#include <glad/glad.h> 10#include <glad/glad.h>
11 11
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
14#include "video_core/renderer_opengl/util_shaders.h" 15#include "video_core/renderer_opengl/util_shaders.h"
15#include "video_core/texture_cache/image_view_base.h" 16#include "video_core/texture_cache/image_view_base.h"
@@ -21,7 +22,6 @@ struct ResolutionScalingInfo;
21 22
22namespace OpenGL { 23namespace OpenGL {
23 24
24class Device;
25class ProgramManager; 25class ProgramManager;
26class StateTracker; 26class StateTracker;
27 27
@@ -55,13 +55,14 @@ struct FormatProperties {
55 55
56class FormatConversionPass { 56class FormatConversionPass {
57public: 57public:
58 FormatConversionPass() = default; 58 explicit FormatConversionPass(UtilShaders& util_shaders);
59 ~FormatConversionPass() = default; 59 ~FormatConversionPass() = default;
60 60
61 void ConvertImage(Image& dst_image, Image& src_image, 61 void ConvertImage(Image& dst_image, Image& src_image,
62 std::span<const VideoCommon::ImageCopy> copies); 62 std::span<const VideoCommon::ImageCopy> copies);
63 63
64private: 64private:
65 UtilShaders& util_shaders;
65 OGLBuffer intermediate_pbo; 66 OGLBuffer intermediate_pbo;
66 size_t pbo_size{}; 67 size_t pbo_size{};
67}; 68};
@@ -83,7 +84,15 @@ public:
83 84
84 ImageBufferMap DownloadStagingBuffer(size_t size); 85 ImageBufferMap DownloadStagingBuffer(size_t size);
85 86
86 u64 GetDeviceLocalMemory() const; 87 u64 GetDeviceLocalMemory() const {
88 return device_access_memory;
89 }
90
91 u64 GetDeviceMemoryUsage() const;
92
93 bool CanReportMemoryUsage() const {
94 return device.CanReportMemoryUsage();
95 }
87 96
88 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) { 97 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
89 return true; 98 return true;
@@ -172,6 +181,7 @@ private:
172 std::array<OGLFramebuffer, 4> rescale_draw_fbos; 181 std::array<OGLFramebuffer, 4> rescale_draw_fbos;
173 std::array<OGLFramebuffer, 4> rescale_read_fbos; 182 std::array<OGLFramebuffer, 4> rescale_read_fbos;
174 const Settings::ResolutionScalingInfo& resolution; 183 const Settings::ResolutionScalingInfo& resolution;
184 u64 device_access_memory;
175}; 185};
176 186
177class Image : public VideoCommon::ImageBase { 187class Image : public VideoCommon::ImageBase {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index db5bf1d30..03adf3d4c 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -30,6 +30,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM 30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT 31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM 32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
33 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
33 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM 34 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
34 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM 35 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM
35 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT 36 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT
@@ -87,6 +88,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
87 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 88 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
88 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB 89 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
89 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM 90 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
91 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM
90 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB 92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
91 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB 93 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB 94 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f81c1b233..3a3c213bb 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstddef> 6#include <cstddef>
7#include <cstdlib> 7#include <cstdlib>
8#include <cstring>
9#include <memory> 8#include <memory>
10 9
11#include <glad/glad.h> 10#include <glad/glad.h>
@@ -15,11 +14,9 @@
15#include "common/microprofile.h" 14#include "common/microprofile.h"
16#include "common/settings.h" 15#include "common/settings.h"
17#include "common/telemetry.h" 16#include "common/telemetry.h"
18#include "core/core.h"
19#include "core/core_timing.h" 17#include "core/core_timing.h"
20#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
21#include "core/memory.h" 19#include "core/memory.h"
22#include "core/perf_stats.h"
23#include "core/telemetry_session.h" 20#include "core/telemetry_session.h"
24#include "video_core/host_shaders/fxaa_frag.h" 21#include "video_core/host_shaders/fxaa_frag.h"
25#include "video_core/host_shaders/fxaa_vert.h" 22#include "video_core/host_shaders/fxaa_vert.h"
@@ -211,6 +208,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
211 // Framebuffer orientation handling 208 // Framebuffer orientation handling
212 framebuffer_transform_flags = framebuffer.transform_flags; 209 framebuffer_transform_flags = framebuffer.transform_flags;
213 framebuffer_crop_rect = framebuffer.crop_rect; 210 framebuffer_crop_rect = framebuffer.crop_rect;
211 framebuffer_width = framebuffer.width;
212 framebuffer_height = framebuffer.height;
214 213
215 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; 214 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
216 screen_info.was_accelerated = 215 screen_info.was_accelerated =
@@ -326,12 +325,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
326 325
327 GLint internal_format; 326 GLint internal_format;
328 switch (framebuffer.pixel_format) { 327 switch (framebuffer.pixel_format) {
329 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 328 case Service::android::PixelFormat::Rgba8888:
330 internal_format = GL_RGBA8; 329 internal_format = GL_RGBA8;
331 texture.gl_format = GL_RGBA; 330 texture.gl_format = GL_RGBA;
332 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 331 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
333 break; 332 break;
334 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 333 case Service::android::PixelFormat::Rgb565:
335 internal_format = GL_RGB565; 334 internal_format = GL_RGB565;
336 texture.gl_format = GL_RGB; 335 texture.gl_format = GL_RGB;
337 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; 336 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
@@ -467,8 +466,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
467 const auto& texcoords = screen_info.display_texcoords; 466 const auto& texcoords = screen_info.display_texcoords;
468 auto left = texcoords.left; 467 auto left = texcoords.left;
469 auto right = texcoords.right; 468 auto right = texcoords.right;
470 if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) { 469 if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
471 if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { 470 if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
472 // Flip the framebuffer vertically 471 // Flip the framebuffer vertically
473 left = texcoords.right; 472 left = texcoords.right;
474 right = texcoords.left; 473 right = texcoords.left;
@@ -483,9 +482,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
483 ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented"); 482 ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
484 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); 483 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
485 484
485 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
486 f32 scale_v =
487 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
488
486 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 489 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
487 // (e.g. handheld mode) on a 1920x1080 framebuffer. 490 // (e.g. handheld mode) on a 1920x1080 framebuffer.
488 f32 scale_u = 1.f, scale_v = 1.f;
489 if (framebuffer_crop_rect.GetWidth() > 0) { 491 if (framebuffer_crop_rect.GetWidth() > 0) {
490 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 492 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
491 static_cast<f32>(screen_info.texture.width); 493 static_cast<f32>(screen_info.texture.width);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cda333cad..ae9558a33 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -8,10 +8,12 @@
8#include <glad/glad.h> 8#include <glad/glad.h>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11
11#include "video_core/renderer_base.h" 12#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 13#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_rasterizer.h" 14#include "video_core/renderer_opengl/gl_rasterizer.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_manager.h"
15#include "video_core/renderer_opengl/gl_state_tracker.h" 17#include "video_core/renderer_opengl/gl_state_tracker.h"
16 18
17namespace Core { 19namespace Core {
@@ -44,7 +46,7 @@ struct TextureInfo {
44 GLsizei height; 46 GLsizei height;
45 GLenum gl_format; 47 GLenum gl_format;
46 GLenum gl_type; 48 GLenum gl_type;
47 Tegra::FramebufferConfig::PixelFormat pixel_format; 49 Service::android::PixelFormat pixel_format;
48}; 50};
49 51
50/// Structure used for storing information about the display target for the Switch screen 52/// Structure used for storing information about the display target for the Switch screen
@@ -133,8 +135,10 @@ private:
133 std::vector<u8> gl_framebuffer_data; 135 std::vector<u8> gl_framebuffer_data;
134 136
135 /// Used for transforming the framebuffer orientation 137 /// Used for transforming the framebuffer orientation
136 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; 138 Service::android::BufferTransformFlags framebuffer_transform_flags{};
137 Common::Rectangle<int> framebuffer_crop_rect; 139 Common::Rectangle<int> framebuffer_crop_rect;
140 u32 framebuffer_width;
141 u32 framebuffer_height;
138}; 142};
139 143
140} // namespace OpenGL 144} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 897c380b3..04c482a09 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -13,6 +13,7 @@
13#include "video_core/host_shaders/astc_decoder_comp.h" 13#include "video_core/host_shaders/astc_decoder_comp.h"
14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" 14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" 15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
16#include "video_core/host_shaders/opengl_convert_s8d24_comp.h"
16#include "video_core/host_shaders/opengl_copy_bc4_comp.h" 17#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
17#include "video_core/host_shaders/pitch_unswizzle_comp.h" 18#include "video_core/host_shaders/pitch_unswizzle_comp.h"
18#include "video_core/renderer_opengl/gl_shader_manager.h" 19#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -50,7 +51,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
50 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), 51 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
51 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), 52 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
52 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), 53 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
53 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { 54 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
55 convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) {
54 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); 56 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
55 swizzle_table_buffer.Create(); 57 swizzle_table_buffer.Create();
56 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); 58 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
@@ -248,6 +250,26 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
248 program_manager.RestoreGuestCompute(); 250 program_manager.RestoreGuestCompute();
249} 251}
250 252
253void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) {
254 static constexpr GLuint BINDING_DESTINATION = 0;
255 static constexpr GLuint LOC_SIZE = 0;
256
257 program_manager.BindComputeProgram(convert_s8d24_program.handle);
258 for (const ImageCopy& copy : copies) {
259 ASSERT(copy.src_subresource.base_layer == 0);
260 ASSERT(copy.src_subresource.num_layers == 1);
261 ASSERT(copy.dst_subresource.base_layer == 0);
262 ASSERT(copy.dst_subresource.num_layers == 1);
263
264 glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth);
265 glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(),
266 copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI);
267 glDispatchCompute(Common::DivCeil(copy.extent.width, 16u),
268 Common::DivCeil(copy.extent.height, 8u), copy.extent.depth);
269 }
270 program_manager.RestoreGuestCompute();
271}
272
251GLenum StoreFormat(u32 bytes_per_block) { 273GLenum StoreFormat(u32 bytes_per_block) {
252 switch (bytes_per_block) { 274 switch (bytes_per_block) {
253 case 1: 275 case 1:
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 5de95ea7a..5c132e67f 100644
--- a/src/video_core/renderer_opengl/util_shaders.h
+++ b/src/video_core/renderer_opengl/util_shaders.h
@@ -39,6 +39,8 @@ public:
39 void CopyBC4(Image& dst_image, Image& src_image, 39 void CopyBC4(Image& dst_image, Image& src_image,
40 std::span<const VideoCommon::ImageCopy> copies); 40 std::span<const VideoCommon::ImageCopy> copies);
41 41
42 void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies);
43
42private: 44private:
43 ProgramManager& program_manager; 45 ProgramManager& program_manager;
44 46
@@ -49,6 +51,7 @@ private:
49 OGLProgram block_linear_unswizzle_3d_program; 51 OGLProgram block_linear_unswizzle_3d_program;
50 OGLProgram pitch_unswizzle_program; 52 OGLProgram pitch_unswizzle_program;
51 OGLProgram copy_bc4_program; 53 OGLProgram copy_bc4_program;
54 OGLProgram convert_s8d24_program;
52}; 55};
53 56
54GLenum StoreFormat(u32 bytes_per_block); 57GLenum StoreFormat(u32 bytes_per_block);
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 2c3914459..abda1c490 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -9,6 +9,7 @@
9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" 9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" 11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
12#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
13#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" 14#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 15#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -366,16 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
366 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 367 PipelineLayoutCreateInfo(two_textures_set_layout.address()))),
367 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 368 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
368 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 369 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
370 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
369 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 371 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
370 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 372 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
371 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 373 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
372 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), 374 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
375 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
373 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), 376 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
374 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { 377 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
375 if (device.IsExtShaderStencilExportSupported()) {
376 blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV);
377 }
378}
379 378
380BlitImageHelper::~BlitImageHelper() = default; 379BlitImageHelper::~BlitImageHelper() = default;
381 380
@@ -474,6 +473,13 @@ void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
474 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); 473 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
475} 474}
476 475
476void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
477 ImageView& src_image_view) {
478 ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
479 convert_s8d24_to_abgr8_frag);
480 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
481}
482
477void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 483void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
478 const ImageView& src_image_view) { 484 const ImageView& src_image_view) {
479 const VkPipelineLayout layout = *one_texture_pipeline_layout; 485 const VkPipelineLayout layout = *one_texture_pipeline_layout;
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 85e7dca5b..29ee0f67a 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <compare>
8
9#include "video_core/engines/fermi_2d.h" 7#include "video_core/engines/fermi_2d.h"
10#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 8#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
11#include "video_core/texture_cache/types.h" 9#include "video_core/texture_cache/types.h"
@@ -56,6 +54,8 @@ public:
56 54
57 void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); 55 void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
58 56
57 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
58
59private: 59private:
60 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 60 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
61 const ImageView& src_image_view); 61 const ImageView& src_image_view);
@@ -99,6 +99,7 @@ private:
99 vk::ShaderModule convert_float_to_depth_frag; 99 vk::ShaderModule convert_float_to_depth_frag;
100 vk::ShaderModule convert_abgr8_to_d24s8_frag; 100 vk::ShaderModule convert_abgr8_to_d24s8_frag;
101 vk::ShaderModule convert_d24s8_to_abgr8_frag; 101 vk::ShaderModule convert_d24s8_to_abgr8_frag;
102 vk::ShaderModule convert_s8d24_to_abgr8_frag;
102 vk::Sampler linear_sampler; 103 vk::Sampler linear_sampler;
103 vk::Sampler nearest_sampler; 104 vk::Sampler nearest_sampler;
104 105
@@ -112,6 +113,7 @@ private:
112 vk::Pipeline convert_r16_to_d16_pipeline; 113 vk::Pipeline convert_r16_to_d16_pipeline;
113 vk::Pipeline convert_abgr8_to_d24s8_pipeline; 114 vk::Pipeline convert_abgr8_to_d24s8_pipeline;
114 vk::Pipeline convert_d24s8_to_abgr8_pipeline; 115 vk::Pipeline convert_d24s8_to_abgr8_pipeline;
116 vk::Pipeline convert_s8d24_to_abgr8_pipeline;
115}; 117};
116 118
117} // namespace Vulkan 119} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index d70153df3..c2259ac5f 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -4,9 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <tuple>
8
9#include <boost/functional/hash.hpp>
10 7
11#include "common/bit_cast.h" 8#include "common/bit_cast.h"
12#include "common/cityhash.h" 9#include "common/cityhash.h"
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 1c136c410..a2c6d0e6c 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -127,6 +127,7 @@ struct FormatTuple {
127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
130 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
130 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 131 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
131 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM 132 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM
132 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT 133 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT
@@ -184,6 +185,7 @@ struct FormatTuple {
184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB 185 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB 186 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM 187 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
188 {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM
187 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB 189 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
188 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB 190 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
189 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB 191 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 8a9616039..1c1f420f2 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
8#include "shader_recompiler/stage.h" 7#include "shader_recompiler/stage.h"
9#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
10#include "video_core/surface.h" 9#include "video_core/surface.h"
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 11c160570..c25d469e6 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -8,7 +8,6 @@
8 8
9#include <boost/container/small_vector.hpp> 9#include <boost/container/small_vector.hpp>
10 10
11#include "common/assert.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "shader_recompiler/backend/spirv/emit_spirv.h" 12#include "shader_recompiler/backend/spirv/emit_spirv.h"
14#include "shader_recompiler/shader_info.h" 13#include "shader_recompiler/shader_info.h"
@@ -16,7 +15,6 @@
16#include "video_core/renderer_vulkan/vk_update_descriptor.h" 15#include "video_core/renderer_vulkan/vk_update_descriptor.h"
17#include "video_core/texture_cache/texture_cache.h" 16#include "video_core/texture_cache/texture_cache.h"
18#include "video_core/texture_cache/types.h" 17#include "video_core/texture_cache/types.h"
19#include "video_core/textures/texture.h"
20#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
21 19
22namespace Vulkan { 20namespace Vulkan {
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
index bfec931a6..7ccadf084 100644
--- a/src/video_core/renderer_vulkan/pipeline_statistics.cpp
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
@@ -57,7 +57,7 @@ void PipelineStatistics::Collect(VkPipeline pipeline) {
57 stage_stats.basic_block_count = GetUint64(statistic); 57 stage_stats.basic_block_count = GetUint64(statistic);
58 } 58 }
59 } 59 }
60 std::lock_guard lock{mutex}; 60 std::scoped_lock lock{mutex};
61 collected_stats.push_back(stage_stats); 61 collected_stats.push_back(stage_stats);
62 } 62 }
63} 63}
@@ -66,7 +66,7 @@ void PipelineStatistics::Report() const {
66 double num{}; 66 double num{};
67 Stats total; 67 Stats total;
68 { 68 {
69 std::lock_guard lock{mutex}; 69 std::scoped_lock lock{mutex};
70 for (const Stats& stats : collected_stats) { 70 for (const Stats& stats : collected_stats) {
71 total.code_size += stats.code_size; 71 total.code_size += stats.code_size;
72 total.register_count += stats.register_count; 72 total.register_count += stats.register_count;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 74822814d..ef57fdfa4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -13,16 +13,15 @@
13#include <fmt/format.h> 13#include <fmt/format.h>
14 14
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/scope_exit.h"
16#include "common/settings.h" 17#include "common/settings.h"
17#include "common/telemetry.h" 18#include "common/telemetry.h"
18#include "core/core.h"
19#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/frontend/emu_window.h" 20#include "core/frontend/emu_window.h"
21#include "core/telemetry_session.h" 21#include "core/telemetry_session.h"
22#include "video_core/gpu.h" 22#include "video_core/gpu.h"
23#include "video_core/renderer_vulkan/renderer_vulkan.h" 23#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 24#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_master_semaphore.h"
26#include "video_core/renderer_vulkan/vk_rasterizer.h" 25#include "video_core/renderer_vulkan/vk_rasterizer.h"
27#include "video_core/renderer_vulkan/vk_scheduler.h" 26#include "video_core/renderer_vulkan/vk_scheduler.h"
28#include "video_core/renderer_vulkan/vk_state_tracker.h" 27#include "video_core/renderer_vulkan/vk_state_tracker.h"
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 6dc985109..9680108b6 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <vector>
10 9
11#include "common/dynamic_library.h" 10#include "common/dynamic_library.h"
12#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 621a6a071..b866e9103 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -6,7 +6,6 @@
6#include <array> 6#include <array>
7#include <cstring> 7#include <cstring>
8#include <memory> 8#include <memory>
9#include <tuple>
10#include <vector> 9#include <vector>
11 10
12#include "common/assert.h" 11#include "common/assert.h"
@@ -28,7 +27,6 @@
28#include "video_core/renderer_vulkan/renderer_vulkan.h" 27#include "video_core/renderer_vulkan/renderer_vulkan.h"
29#include "video_core/renderer_vulkan/vk_blit_screen.h" 28#include "video_core/renderer_vulkan/vk_blit_screen.h"
30#include "video_core/renderer_vulkan/vk_fsr.h" 29#include "video_core/renderer_vulkan/vk_fsr.h"
31#include "video_core/renderer_vulkan/vk_master_semaphore.h"
32#include "video_core/renderer_vulkan/vk_scheduler.h" 30#include "video_core/renderer_vulkan/vk_scheduler.h"
33#include "video_core/renderer_vulkan/vk_shader_util.h" 31#include "video_core/renderer_vulkan/vk_shader_util.h"
34#include "video_core/renderer_vulkan/vk_swapchain.h" 32#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -96,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
96 94
97VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 95VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
98 switch (framebuffer.pixel_format) { 96 switch (framebuffer.pixel_format) {
99 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 97 case Service::android::PixelFormat::Rgba8888:
100 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 98 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
101 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 99 case Service::android::PixelFormat::Rgb565:
102 return VK_FORMAT_R5G6B5_UNORM_PACK16; 100 return VK_FORMAT_R5G6B5_UNORM_PACK16;
103 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 101 case Service::android::PixelFormat::Bgra8888:
104 return VK_FORMAT_B8G8R8A8_UNORM; 102 return VK_FORMAT_B8G8R8A8_UNORM;
105 default: 103 default:
106 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
@@ -1392,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
1392 auto right = texcoords.right; 1390 auto right = texcoords.right;
1393 1391
1394 switch (framebuffer_transform_flags) { 1392 switch (framebuffer_transform_flags) {
1395 case Tegra::FramebufferConfig::TransformFlags::Unset: 1393 case Service::android::BufferTransformFlags::Unset:
1396 break; 1394 break;
1397 case Tegra::FramebufferConfig::TransformFlags::FlipV: 1395 case Service::android::BufferTransformFlags::FlipV:
1398 // Flip the framebuffer vertically 1396 // Flip the framebuffer vertically
1399 left = texcoords.right; 1397 left = texcoords.right;
1400 right = texcoords.left; 1398 right = texcoords.left;
@@ -1408,8 +1406,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
1408 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); 1406 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0);
1409 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); 1407 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
1410 1408
1411 f32 scale_u = 1.0f; 1409 f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width);
1412 f32 scale_v = 1.0f; 1410 f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height);
1411
1413 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 1412 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
1414 // (e.g. handheld mode) on a 1920x1080 framebuffer. 1413 // (e.g. handheld mode) on a 1920x1080 framebuffer.
1415 if (!fsr) { 1414 if (!fsr) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5ffd93499..def838c34 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -141,6 +141,18 @@ StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size) {
141 return staging_pool.Request(size, MemoryUsage::Download); 141 return staging_pool.Request(size, MemoryUsage::Download);
142} 142}
143 143
144u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
145 return device.GetDeviceLocalMemory();
146}
147
148u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
149 return device.GetDeviceMemoryUsage();
150}
151
152bool BufferCacheRuntime::CanReportMemoryUsage() const {
153 return device.CanReportMemoryUsage();
154}
155
144void BufferCacheRuntime::Finish() { 156void BufferCacheRuntime::Finish() {
145 scheduler.Finish(); 157 scheduler.Finish();
146} 158}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 1ee0d8420..d7fdd18ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -65,6 +65,12 @@ public:
65 65
66 void Finish(); 66 void Finish();
67 67
68 u64 GetDeviceLocalMemory() const;
69
70 u64 GetDeviceMemoryUsage() const;
71
72 bool CanReportMemoryUsage() const;
73
68 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 74 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
69 75
70 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 76 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 3e96c0f60..713794410 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -2,12 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <array>
6#include <memory> 6#include <memory>
7#include <optional> 7#include <optional>
8#include <utility> 8#include <utility>
9 9
10#include "common/alignment.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "common/div_ceil.h" 12#include "common/div_ceil.h"
@@ -22,7 +21,6 @@
22#include "video_core/renderer_vulkan/vk_update_descriptor.h" 21#include "video_core/renderer_vulkan/vk_update_descriptor.h"
23#include "video_core/texture_cache/accelerated_swizzle.h" 22#include "video_core/texture_cache/accelerated_swizzle.h"
24#include "video_core/texture_cache/types.h" 23#include "video_core/texture_cache/types.h"
25#include "video_core/textures/astc.h"
26#include "video_core/textures/decoders.h" 24#include "video_core/textures/decoders.h"
27#include "video_core/vulkan_common/vulkan_device.h" 25#include "video_core/vulkan_common/vulkan_device.h"
28#include "video_core/vulkan_common/vulkan_wrapper.h" 26#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -292,7 +290,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
292 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 290 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
293 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, 291 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
294 }; 292 };
295 const std::array push_constants{base_vertex, index_shift}; 293 const std::array<u32, 2> push_constants{base_vertex, index_shift};
296 const VkDescriptorSet set = descriptor_allocator.Commit(); 294 const VkDescriptorSet set = descriptor_allocator.Commit();
297 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); 295 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
298 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); 296 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index de36bcdb7..97b3594c2 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -77,7 +77,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
77 if (pipeline_statistics) { 77 if (pipeline_statistics) {
78 pipeline_statistics->Collect(*pipeline); 78 pipeline_statistics->Collect(*pipeline);
79 } 79 }
80 std::lock_guard lock{build_mutex}; 80 std::scoped_lock lock{build_mutex};
81 is_built = true; 81 is_built = true;
82 build_condvar.notify_one(); 82 build_condvar.notify_one();
83 if (shader_notify) { 83 if (shader_notify) {
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index 8c4b0a301..c64bd9a06 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -11,7 +11,6 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/thread_worker.h" 12#include "common/thread_worker.h"
13#include "shader_recompiler/shader_info.h" 13#include "shader_recompiler/shader_info.h"
14#include "video_core/memory_manager.h"
15#include "video_core/renderer_vulkan/vk_buffer_cache.h" 14#include "video_core/renderer_vulkan/vk_buffer_cache.h"
16#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 15#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
17#include "video_core/renderer_vulkan/vk_texture_cache.h" 16#include "video_core/renderer_vulkan/vk_texture_cache.h"
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 3bec48d14..0c1098c8f 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -9,7 +9,6 @@
9#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
10#include "video_core/renderer_vulkan/vk_texture_cache.h" 10#include "video_core/renderer_vulkan/vk_texture_cache.h"
11#include "video_core/vulkan_common/vulkan_device.h" 11#include "video_core/vulkan_common/vulkan_device.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 12
14namespace Vulkan { 13namespace Vulkan {
15 14
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 2f8322d29..cf9f4adbf 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -9,7 +9,6 @@
9#include "video_core/fence_manager.h" 9#include "video_core/fence_manager.h"
10#include "video_core/renderer_vulkan/vk_buffer_cache.h" 10#include "video_core/renderer_vulkan/vk_buffer_cache.h"
11#include "video_core/renderer_vulkan/vk_texture_cache.h" 11#include "video_core/renderer_vulkan/vk_texture_cache.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 12
14namespace Core { 13namespace Core {
15class System; 14class System;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index d514b71d0..8959d6059 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -258,7 +258,7 @@ GraphicsPipeline::GraphicsPipeline(
258 pipeline_statistics->Collect(*pipeline); 258 pipeline_statistics->Collect(*pipeline);
259 } 259 }
260 260
261 std::lock_guard lock{build_mutex}; 261 std::scoped_lock lock{build_mutex};
262 is_built = true; 262 is_built = true;
263 build_condvar.notify_one(); 263 build_condvar.notify_one();
264 if (shader_notify) { 264 if (shader_notify) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index a633b73e5..336d1e9dc 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -16,13 +16,11 @@
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/thread_worker.h" 17#include "common/thread_worker.h"
18#include "core/core.h" 18#include "core/core.h"
19#include "core/memory.h"
20#include "shader_recompiler/backend/spirv/emit_spirv.h" 19#include "shader_recompiler/backend/spirv/emit_spirv.h"
21#include "shader_recompiler/environment.h" 20#include "shader_recompiler/environment.h"
22#include "shader_recompiler/frontend/maxwell/control_flow.h" 21#include "shader_recompiler/frontend/maxwell/control_flow.h"
23#include "shader_recompiler/frontend/maxwell/translate_program.h" 22#include "shader_recompiler/frontend/maxwell/translate_program.h"
24#include "shader_recompiler/program_header.h" 23#include "shader_recompiler/program_header.h"
25#include "video_core/dirty_flags.h"
26#include "video_core/engines/kepler_compute.h" 24#include "video_core/engines/kepler_compute.h"
27#include "video_core/engines/maxwell_3d.h" 25#include "video_core/engines/maxwell_3d.h"
28#include "video_core/memory_manager.h" 26#include "video_core/memory_manager.h"
@@ -406,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
406 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 404 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
407 ShaderPools pools; 405 ShaderPools pools;
408 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; 406 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
409 std::lock_guard lock{state.mutex}; 407 std::scoped_lock lock{state.mutex};
410 if (pipeline) { 408 if (pipeline) {
411 compute_cache.emplace(key, std::move(pipeline)); 409 compute_cache.emplace(key, std::move(pipeline));
412 } 410 }
@@ -436,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
436 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), 434 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
437 state.statistics.get(), false)}; 435 state.statistics.get(), false)};
438 436
439 std::lock_guard lock{state.mutex}; 437 std::scoped_lock lock{state.mutex};
440 graphics_cache.emplace(key, std::move(pipeline)); 438 graphics_cache.emplace(key, std::move(pipeline));
441 ++state.built; 439 ++state.built;
442 if (state.has_loaded) { 440 if (state.has_loaded) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 4c135b5dd..579e25a4a 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -7,11 +7,9 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <filesystem> 9#include <filesystem>
10#include <iosfwd>
11#include <memory> 10#include <memory>
12#include <type_traits> 11#include <type_traits>
13#include <unordered_map> 12#include <unordered_map>
14#include <utility>
15#include <vector> 13#include <vector>
16 14
17#include "common/common_types.h" 15#include "common/common_types.h"
@@ -29,7 +27,6 @@
29#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 27#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
30#include "video_core/renderer_vulkan/vk_texture_cache.h" 28#include "video_core/renderer_vulkan/vk_texture_cache.h"
31#include "video_core/shader_cache.h" 29#include "video_core/shader_cache.h"
32#include "video_core/vulkan_common/vulkan_wrapper.h"
33 30
34namespace Core { 31namespace Core {
35class System; 32class System;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 2227d9197..fa87d37f8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -6,15 +6,12 @@
6#include <array> 6#include <array>
7#include <memory> 7#include <memory>
8#include <mutex> 8#include <mutex>
9#include <vector>
10 9
11#include "common/alignment.h"
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/logging/log.h" 11#include "common/logging/log.h"
14#include "common/microprofile.h" 12#include "common/microprofile.h"
15#include "common/scope_exit.h" 13#include "common/scope_exit.h"
16#include "common/settings.h" 14#include "common/settings.h"
17#include "core/core.h"
18#include "video_core/engines/kepler_compute.h" 15#include "video_core/engines/kepler_compute.h"
19#include "video_core/engines/maxwell_3d.h" 16#include "video_core/engines/maxwell_3d.h"
20#include "video_core/renderer_vulkan/blit_image.h" 17#include "video_core/renderer_vulkan/blit_image.h"
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 5af2e275b..c25036fb3 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -5,10 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <bitset>
9#include <memory>
10#include <utility>
11#include <vector>
12 8
13#include <boost/container/static_vector.hpp> 9#include <boost/container/static_vector.hpp>
14 10
@@ -17,14 +13,12 @@
17#include "video_core/rasterizer_accelerated.h" 13#include "video_core/rasterizer_accelerated.h"
18#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
19#include "video_core/renderer_vulkan/blit_image.h" 15#include "video_core/renderer_vulkan/blit_image.h"
20#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
21#include "video_core/renderer_vulkan/vk_buffer_cache.h" 16#include "video_core/renderer_vulkan/vk_buffer_cache.h"
22#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 17#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
23#include "video_core/renderer_vulkan/vk_fence_manager.h" 18#include "video_core/renderer_vulkan/vk_fence_manager.h"
24#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 19#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
25#include "video_core/renderer_vulkan/vk_query_cache.h" 20#include "video_core/renderer_vulkan/vk_query_cache.h"
26#include "video_core/renderer_vulkan/vk_render_pass_cache.h" 21#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
27#include "video_core/renderer_vulkan/vk_scheduler.h"
28#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 22#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
29#include "video_core/renderer_vulkan/vk_texture_cache.h" 23#include "video_core/renderer_vulkan/vk_texture_cache.h"
30#include "video_core/renderer_vulkan/vk_update_descriptor.h" 24#include "video_core/renderer_vulkan/vk_update_descriptor.h"
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
index 451ffe019..d22bb6694 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -36,7 +36,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
36RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} 36RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
37 37
38VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { 38VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
39 std::lock_guard lock{mutex}; 39 std::scoped_lock lock{mutex};
40 const auto [pair, is_new] = cache.try_emplace(key); 40 const auto [pair, is_new] = cache.try_emplace(key);
41 if (!is_new) { 41 if (!is_new) {
42 return *pair->second; 42 return *pair->second;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 7d9d4f7ba..6a9416457 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -4,7 +4,6 @@
4 4
5#include <memory> 5#include <memory>
6#include <mutex> 6#include <mutex>
7#include <optional>
8#include <thread> 7#include <thread>
9#include <utility> 8#include <utility>
10 9
@@ -74,7 +73,7 @@ void VKScheduler::DispatchWork() {
74 return; 73 return;
75 } 74 }
76 { 75 {
77 std::lock_guard lock{work_mutex}; 76 std::scoped_lock lock{work_mutex};
78 work_queue.push(std::move(chunk)); 77 work_queue.push(std::move(chunk));
79 } 78 }
80 work_cv.notify_one(); 79 work_cv.notify_one();
@@ -158,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
158 if (has_submit) { 157 if (has_submit) {
159 AllocateWorkerCommandBuffer(); 158 AllocateWorkerCommandBuffer();
160 } 159 }
161 std::lock_guard reserve_lock{reserve_mutex}; 160 std::scoped_lock reserve_lock{reserve_mutex};
162 chunk_reserve.push_back(std::move(work)); 161 chunk_reserve.push_back(std::move(work));
163 } while (!stop_token.stop_requested()); 162 } while (!stop_token.stop_requested());
164} 163}
@@ -283,7 +282,7 @@ void VKScheduler::EndRenderPass() {
283} 282}
284 283
285void VKScheduler::AcquireNewChunk() { 284void VKScheduler::AcquireNewChunk() {
286 std::lock_guard lock{reserve_mutex}; 285 std::scoped_lock lock{reserve_mutex};
287 if (chunk_reserve.empty()) { 286 if (chunk_reserve.empty()) {
288 chunk = std::make_unique<CommandChunk>(); 287 chunk = std::make_unique<CommandChunk>();
289 return; 288 return;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index e69aa136b..25c5e6ca1 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8#include <condition_variable> 7#include <condition_variable>
9#include <cstddef> 8#include <cstddef>
10#include <memory> 9#include <memory>
diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp
index aaad4f292..e8e339f3c 100644
--- a/src/video_core/renderer_vulkan/vk_shader_util.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp
@@ -3,9 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <memory>
7 6
8#include "common/assert.h"
9#include "common/common_types.h" 7#include "common/common_types.h"
10#include "video_core/renderer_vulkan/vk_shader_util.h" 8#include "video_core/renderer_vulkan/vk_shader_util.h"
11#include "video_core/vulkan_common/vulkan_device.h" 9#include "video_core/vulkan_common/vulkan_device.h"
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 5d5329abf..64a58304b 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -118,7 +118,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
118 .image = nullptr, 118 .image = nullptr,
119 .buffer = *stream_buffer, 119 .buffer = *stream_buffer,
120 }; 120 };
121 const auto memory_properties = device.GetPhysical().GetMemoryProperties(); 121 const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
122 VkMemoryAllocateInfo stream_memory_info{ 122 VkMemoryAllocateInfo stream_memory_info{
123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
124 .pNext = make_dedicated ? &dedicated_info : nullptr, 124 .pNext = make_dedicated ? &dedicated_info : nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index c00913f55..1e597f98c 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <iterator>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "core/core.h" 10#include "core/core.h"
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 40a149832..8240c83e1 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -8,7 +8,6 @@
8#include <limits> 8#include <limits>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/core.h"
12#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
13#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
14 13
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 8972a6921..ce744f4ca 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -7,11 +7,9 @@
7#include <limits> 7#include <limits>
8#include <vector> 8#include <vector>
9 9
10#include "common/assert.h"
11#include "common/logging/log.h" 10#include "common/logging/log.h"
12#include "common/settings.h" 11#include "common/settings.h"
13#include "core/core.h" 12#include "core/core.h"
14#include "core/frontend/framebuffer_layout.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_swapchain.h" 14#include "video_core/renderer_vulkan/vk_swapchain.h"
17#include "video_core/vulkan_common/vulkan_device.h" 15#include "video_core/vulkan_common/vulkan_device.h"
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 0f62779de..49691ce0c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -438,6 +438,32 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
438 } 438 }
439} 439}
440 440
441[[nodiscard]] SwizzleSource SwapGreenRed(SwizzleSource value) {
442 switch (value) {
443 case SwizzleSource::R:
444 return SwizzleSource::G;
445 case SwizzleSource::G:
446 return SwizzleSource::R;
447 default:
448 return value;
449 }
450}
451
452[[nodiscard]] SwizzleSource SwapSpecial(SwizzleSource value) {
453 switch (value) {
454 case SwizzleSource::A:
455 return SwizzleSource::R;
456 case SwizzleSource::R:
457 return SwizzleSource::A;
458 case SwizzleSource::G:
459 return SwizzleSource::B;
460 case SwizzleSource::B:
461 return SwizzleSource::G;
462 default:
463 return value;
464 }
465}
466
441void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, 467void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
442 VkImageAspectFlags aspect_mask, bool is_initialized, 468 VkImageAspectFlags aspect_mask, bool is_initialized,
443 std::span<const VkBufferImageCopy> copies) { 469 std::span<const VkBufferImageCopy> copies) {
@@ -554,14 +580,25 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
554 }; 580 };
555} 581}
556 582
557[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { 583void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
584 bool emulate_bgr565) {
558 switch (format) { 585 switch (format) {
559 case PixelFormat::A1B5G5R5_UNORM: 586 case PixelFormat::A1B5G5R5_UNORM:
560 return true; 587 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
588 break;
561 case PixelFormat::B5G6R5_UNORM: 589 case PixelFormat::B5G6R5_UNORM:
562 return emulate_bgr565; 590 if (emulate_bgr565) {
591 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
592 }
593 break;
594 case PixelFormat::A5B5G5R1_UNORM:
595 std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
596 break;
597 case PixelFormat::R4G4_UNORM:
598 std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
599 break;
563 default: 600 default:
564 return false; 601 break;
565 } 602 }
566} 603}
567 604
@@ -781,11 +818,6 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
781 !device.IsExtShaderStencilExportSupported()) { 818 !device.IsExtShaderStencilExportSupported()) {
782 return true; 819 return true;
783 } 820 }
784 if (VideoCore::Surface::GetFormatType(src.info.format) ==
785 VideoCore::Surface::SurfaceType::DepthStencil &&
786 !device.IsExtShaderStencilExportSupported()) {
787 return true;
788 }
789 if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT || 821 if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT ||
790 src.info.format == PixelFormat::D32_FLOAT_S8_UINT) { 822 src.info.format == PixelFormat::D32_FLOAT_S8_UINT) {
791 return true; 823 return true;
@@ -1070,6 +1102,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
1070 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { 1102 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
1071 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); 1103 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
1072 } 1104 }
1105 if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
1106 return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
1107 }
1073 break; 1108 break;
1074 case PixelFormat::R32_FLOAT: 1109 case PixelFormat::R32_FLOAT:
1075 if (src_view.format == PixelFormat::D32_FLOAT) { 1110 if (src_view.format == PixelFormat::D32_FLOAT) {
@@ -1191,6 +1226,14 @@ u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
1191 return device.GetDeviceLocalMemory(); 1226 return device.GetDeviceLocalMemory();
1192} 1227}
1193 1228
1229u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
1230 return device.GetDeviceMemoryUsage();
1231}
1232
1233bool TextureCacheRuntime::CanReportMemoryUsage() const {
1234 return device.CanReportMemoryUsage();
1235}
1236
1194void TextureCacheRuntime::TickFrame() {} 1237void TextureCacheRuntime::TickFrame() {}
1195 1238
1196Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, 1239Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1205,6 +1248,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1205 } else { 1248 } else {
1206 flags |= VideoCommon::ImageFlagBits::Converted; 1249 flags |= VideoCommon::ImageFlagBits::Converted;
1207 } 1250 }
1251 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1208 } 1252 }
1209 if (runtime->device.HasDebuggingToolAttached()) { 1253 if (runtime->device.HasDebuggingToolAttached()) {
1210 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1254 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
@@ -1444,8 +1488,7 @@ bool Image::BlitScaleHelper(bool scale_up) {
1444 1488
1445 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, 1489 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region,
1446 src_region, operation, BLIT_OPERATION); 1490 src_region, operation, BLIT_OPERATION);
1447 } else if (!runtime->device.IsBlitDepthStencilSupported() && 1491 } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1448 aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1449 if (!blit_framebuffer) { 1492 if (!blit_framebuffer) {
1450 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); 1493 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent);
1451 } 1494 }
@@ -1490,9 +1533,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1490 }; 1533 };
1491 if (!info.IsRenderTarget()) { 1534 if (!info.IsRenderTarget()) {
1492 swizzle = info.Swizzle(); 1535 swizzle = info.Swizzle();
1493 if (IsFormatFlipped(format, device->MustEmulateBGR565())) { 1536 TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
1494 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
1495 }
1496 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { 1537 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
1497 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 1538 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
1498 } 1539 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index c81130dd2..cb15b4a1c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -55,6 +55,10 @@ public:
55 55
56 u64 GetDeviceLocalMemory() const; 56 u64 GetDeviceLocalMemory() const;
57 57
58 u64 GetDeviceMemoryUsage() const;
59
60 bool CanReportMemoryUsage() const;
61
58 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 62 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
59 const Region2D& dst_region, const Region2D& src_region, 63 const Region2D& dst_region, const Region2D& src_region,
60 Tegra::Engines::Fermi2D::Filter filter, 64 Tegra::Engines::Fermi2D::Filter filter,
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index 0df3a7fe9..89f1b508d 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -5,7 +5,6 @@
5#include <variant> 5#include <variant>
6#include <boost/container/static_vector.hpp> 6#include <boost/container/static_vector.hpp>
7 7
8#include "common/assert.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
11#include "video_core/renderer_vulkan/vk_update_descriptor.h" 10#include "video_core/renderer_vulkan/vk_update_descriptor.h"
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index d7de4c490..971a4eb34 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8 8
9#include "common/common_types.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h" 9#include "video_core/vulkan_common/vulkan_wrapper.h"
11 10
12namespace Vulkan { 11namespace Vulkan {
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index 87636857d..75031767a 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -25,7 +25,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) {
25} 25}
26 26
27void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { 27void ShaderCache::OnCPUWrite(VAddr addr, size_t size) {
28 std::lock_guard lock{invalidation_mutex}; 28 std::scoped_lock lock{invalidation_mutex};
29 InvalidatePagesInRegion(addr, size); 29 InvalidatePagesInRegion(addr, size);
30} 30}
31 31
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 3e673c437..3c22124c4 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bit>
7#include <filesystem> 6#include <filesystem>
8#include <fstream> 7#include <fstream>
9#include <memory> 8#include <memory>
diff --git a/src/video_core/shader_notify.cpp b/src/video_core/shader_notify.cpp
index bcaf5f575..ce8925896 100644
--- a/src/video_core/shader_notify.cpp
+++ b/src/video_core/shader_notify.cpp
@@ -4,7 +4,6 @@
4 4
5#include <atomic> 5#include <atomic>
6#include <chrono> 6#include <chrono>
7#include <optional>
8 7
9#include "video_core/shader_notify.h" 8#include "video_core/shader_notify.h"
10 9
diff --git a/src/video_core/shader_notify.h b/src/video_core/shader_notify.h
index 4d8d52071..538cda28a 100644
--- a/src/video_core/shader_notify.h
+++ b/src/video_core/shader_notify.h
@@ -6,7 +6,6 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <chrono> 8#include <chrono>
9#include <optional>
10 9
11namespace VideoCore { 10namespace VideoCore {
12class ShaderNotify { 11class ShaderNotify {
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index a36015c8c..5f428d35d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -190,13 +190,13 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
190 } 190 }
191} 191}
192 192
193PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { 193PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) {
194 switch (format) { 194 switch (format) {
195 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 195 case Service::android::PixelFormat::Rgba8888:
196 return PixelFormat::A8B8G8R8_UNORM; 196 return PixelFormat::A8B8G8R8_UNORM;
197 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 197 case Service::android::PixelFormat::Rgb565:
198 return PixelFormat::R5G6B5_UNORM; 198 return PixelFormat::R5G6B5_UNORM;
199 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 199 case Service::android::PixelFormat::Bgra8888:
200 return PixelFormat::B8G8R8A8_UNORM; 200 return PixelFormat::B8G8R8A8_UNORM;
201 default: 201 default:
202 UNIMPLEMENTED_MSG("Unimplemented format={}", format); 202 UNIMPLEMENTED_MSG("Unimplemented format={}", format);
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 33e8d24ab..86fea61ae 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -25,6 +25,7 @@ enum class PixelFormat {
25 A2B10G10R10_UNORM, 25 A2B10G10R10_UNORM,
26 A2B10G10R10_UINT, 26 A2B10G10R10_UINT,
27 A1B5G5R5_UNORM, 27 A1B5G5R5_UNORM,
28 A5B5G5R1_UNORM,
28 R8_UNORM, 29 R8_UNORM,
29 R8_SNORM, 30 R8_SNORM,
30 R8_SINT, 31 R8_SINT,
@@ -82,6 +83,7 @@ enum class PixelFormat {
82 BC3_SRGB, 83 BC3_SRGB,
83 BC7_SRGB, 84 BC7_SRGB,
84 A4B4G4R4_UNORM, 85 A4B4G4R4_UNORM,
86 R4G4_UNORM,
85 ASTC_2D_4X4_SRGB, 87 ASTC_2D_4X4_SRGB,
86 ASTC_2D_8X8_SRGB, 88 ASTC_2D_8X8_SRGB,
87 ASTC_2D_8X5_SRGB, 89 ASTC_2D_8X5_SRGB,
@@ -156,6 +158,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
156 1, // A2B10G10R10_UNORM 158 1, // A2B10G10R10_UNORM
157 1, // A2B10G10R10_UINT 159 1, // A2B10G10R10_UINT
158 1, // A1B5G5R5_UNORM 160 1, // A1B5G5R5_UNORM
161 1, // A5B5G5R1_UNORM
159 1, // R8_UNORM 162 1, // R8_UNORM
160 1, // R8_SNORM 163 1, // R8_SNORM
161 1, // R8_SINT 164 1, // R8_SINT
@@ -213,6 +216,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
213 4, // BC3_SRGB 216 4, // BC3_SRGB
214 4, // BC7_SRGB 217 4, // BC7_SRGB
215 1, // A4B4G4R4_UNORM 218 1, // A4B4G4R4_UNORM
219 1, // R4G4_UNORM
216 4, // ASTC_2D_4X4_SRGB 220 4, // ASTC_2D_4X4_SRGB
217 8, // ASTC_2D_8X8_SRGB 221 8, // ASTC_2D_8X8_SRGB
218 8, // ASTC_2D_8X5_SRGB 222 8, // ASTC_2D_8X5_SRGB
@@ -256,6 +260,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
256 1, // A2B10G10R10_UNORM 260 1, // A2B10G10R10_UNORM
257 1, // A2B10G10R10_UINT 261 1, // A2B10G10R10_UINT
258 1, // A1B5G5R5_UNORM 262 1, // A1B5G5R5_UNORM
263 1, // A5B5G5R1_UNORM
259 1, // R8_UNORM 264 1, // R8_UNORM
260 1, // R8_SNORM 265 1, // R8_SNORM
261 1, // R8_SINT 266 1, // R8_SINT
@@ -313,6 +318,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
313 4, // BC3_SRGB 318 4, // BC3_SRGB
314 4, // BC7_SRGB 319 4, // BC7_SRGB
315 1, // A4B4G4R4_UNORM 320 1, // A4B4G4R4_UNORM
321 1, // R4G4_UNORM
316 4, // ASTC_2D_4X4_SRGB 322 4, // ASTC_2D_4X4_SRGB
317 8, // ASTC_2D_8X8_SRGB 323 8, // ASTC_2D_8X8_SRGB
318 5, // ASTC_2D_8X5_SRGB 324 5, // ASTC_2D_8X5_SRGB
@@ -356,6 +362,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
356 32, // A2B10G10R10_UNORM 362 32, // A2B10G10R10_UNORM
357 32, // A2B10G10R10_UINT 363 32, // A2B10G10R10_UINT
358 16, // A1B5G5R5_UNORM 364 16, // A1B5G5R5_UNORM
365 16, // A5B5G5R1_UNORM
359 8, // R8_UNORM 366 8, // R8_UNORM
360 8, // R8_SNORM 367 8, // R8_SNORM
361 8, // R8_SINT 368 8, // R8_SINT
@@ -413,6 +420,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
413 128, // BC3_SRGB 420 128, // BC3_SRGB
414 128, // BC7_UNORM 421 128, // BC7_UNORM
415 16, // A4B4G4R4_UNORM 422 16, // A4B4G4R4_UNORM
423 8, // R4G4_UNORM
416 128, // ASTC_2D_4X4_SRGB 424 128, // ASTC_2D_4X4_SRGB
417 128, // ASTC_2D_8X8_SRGB 425 128, // ASTC_2D_8X8_SRGB
418 128, // ASTC_2D_8X5_SRGB 426 128, // ASTC_2D_8X5_SRGB
@@ -460,7 +468,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
460 468
461PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); 469PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
462 470
463PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); 471PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);
464 472
465SurfaceType GetFormatType(PixelFormat pixel_format); 473SurfaceType GetFormatType(PixelFormat pixel_format);
466 474
diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h
index 3a03b786f..318bd5214 100644
--- a/src/video_core/texture_cache/descriptor_table.h
+++ b/src/video_core/texture_cache/descriptor_table.h
@@ -9,7 +9,6 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/div_ceil.h" 11#include "common/div_ceil.h"
12#include "common/logging/log.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
14#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
15 14
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index afa807d5d..20e64a7c2 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -63,6 +63,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
63 return PixelFormat::A1B5G5R5_UNORM; 63 return PixelFormat::A1B5G5R5_UNORM;
64 case Hash(TextureFormat::A4B4G4R4, UNORM): 64 case Hash(TextureFormat::A4B4G4R4, UNORM):
65 return PixelFormat::A4B4G4R4_UNORM; 65 return PixelFormat::A4B4G4R4_UNORM;
66 case Hash(TextureFormat::G4R4, UNORM):
67 return PixelFormat::R4G4_UNORM;
68 case Hash(TextureFormat::A5B5G5R1, UNORM):
69 return PixelFormat::A5B5G5R1_UNORM;
66 case Hash(TextureFormat::R8, UNORM): 70 case Hash(TextureFormat::R8, UNORM):
67 return PixelFormat::R8_UNORM; 71 return PixelFormat::R8_UNORM;
68 case Hash(TextureFormat::R8, SNORM): 72 case Hash(TextureFormat::R8, SNORM):
@@ -143,6 +147,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
143 return PixelFormat::S8_UINT_D24_UNORM; 147 return PixelFormat::S8_UINT_D24_UNORM;
144 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): 148 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
145 return PixelFormat::S8_UINT_D24_UNORM; 149 return PixelFormat::S8_UINT_D24_UNORM;
150 case Hash(TextureFormat::D24S8, UNORM, UINT, UINT, UINT, LINEAR):
151 return PixelFormat::D24_UNORM_S8_UINT;
146 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): 152 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
147 return PixelFormat::D32_FLOAT_S8_UINT; 153 return PixelFormat::D32_FLOAT_S8_UINT;
148 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): 154 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index b2c81057b..6f5afc5a9 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -38,6 +38,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
38 return "A2B10G10R10_UINT"; 38 return "A2B10G10R10_UINT";
39 case PixelFormat::A1B5G5R5_UNORM: 39 case PixelFormat::A1B5G5R5_UNORM:
40 return "A1B5G5R5_UNORM"; 40 return "A1B5G5R5_UNORM";
41 case PixelFormat::A5B5G5R1_UNORM:
42 return "A5B5G5R1_UNORM";
41 case PixelFormat::R8_UNORM: 43 case PixelFormat::R8_UNORM:
42 return "R8_UNORM"; 44 return "R8_UNORM";
43 case PixelFormat::R8_SNORM: 45 case PixelFormat::R8_SNORM:
@@ -152,6 +154,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
152 return "BC7_SRGB"; 154 return "BC7_SRGB";
153 case PixelFormat::A4B4G4R4_UNORM: 155 case PixelFormat::A4B4G4R4_UNORM:
154 return "A4B4G4R4_UNORM"; 156 return "A4B4G4R4_UNORM";
157 case PixelFormat::R4G4_UNORM:
158 return "R4G4_UNORM";
155 case PixelFormat::ASTC_2D_4X4_SRGB: 159 case PixelFormat::ASTC_2D_4X4_SRGB:
156 return "ASTC_2D_4X4_SRGB"; 160 return "ASTC_2D_4X4_SRGB";
157 case PixelFormat::ASTC_2D_8X8_SRGB: 161 case PixelFormat::ASTC_2D_8X8_SRGB:
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 89c111c00..dd0106432 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -33,11 +33,12 @@ enum class ImageFlagBits : u32 {
33 ///< garbage collection priority 33 ///< garbage collection priority
34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage 34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage
35 ///< collection 35 ///< collection
36 CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back.
36 37
37 // Rescaler 38 // Rescaler
38 Rescaled = 1 << 12, 39 Rescaled = 1 << 13,
39 CheckingRescalable = 1 << 13, 40 CheckingRescalable = 1 << 14,
40 IsRescalable = 1 << 14, 41 IsRescalable = 1 << 15,
41}; 42};
42DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 43DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
43 44
diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h
index 0cb227d69..f8f13e84c 100644
--- a/src/video_core/texture_cache/render_targets.h
+++ b/src/video_core/texture_cache/render_targets.h
@@ -6,7 +6,6 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <span> 8#include <span>
9#include <utility>
10 9
11#include "common/bit_cast.h" 10#include "common/bit_cast.h"
12#include "video_core/texture_cache/types.h" 11#include "video_core/texture_cache/types.h"
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 50df06409..6aabaef7b 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array>
9#include <bit> 8#include <bit>
10#include <concepts>
11#include <numeric> 9#include <numeric>
12#include <type_traits> 10#include <type_traits>
13#include <utility> 11#include <utility>
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 198bb0cfb..8fef74117 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -50,14 +50,20 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
50 void(slot_samplers.insert(runtime, sampler_descriptor)); 50 void(slot_samplers.insert(runtime, sampler_descriptor));
51 51
52 if constexpr (HAS_DEVICE_MEMORY_INFO) { 52 if constexpr (HAS_DEVICE_MEMORY_INFO) {
53 const auto device_memory = runtime.GetDeviceLocalMemory(); 53 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
54 const u64 possible_expected_memory = (device_memory * 4) / 10; 54 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
55 const u64 possible_critical_memory = (device_memory * 7) / 10; 55 const s64 min_spacing_critical = device_memory - 1_GiB;
56 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB); 56 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
57 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB); 57 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
58 minimum_memory = 0; 58 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
59 expected_memory = static_cast<u64>(
60 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
61 DEFAULT_EXPECTED_MEMORY));
62 critical_memory = static_cast<u64>(
63 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
64 DEFAULT_CRITICAL_MEMORY));
65 minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2);
59 } else { 66 } else {
60 // On OpenGL we can be more conservatives as the driver takes care.
61 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; 67 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
62 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; 68 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
63 minimum_memory = 0; 69 minimum_memory = 0;
@@ -66,18 +72,21 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
66 72
67template <class P> 73template <class P>
68void TextureCache<P>::RunGarbageCollector() { 74void TextureCache<P>::RunGarbageCollector() {
69 const bool high_priority_mode = total_used_memory >= expected_memory; 75 bool high_priority_mode = total_used_memory >= expected_memory;
70 const bool aggressive_mode = total_used_memory >= critical_memory; 76 bool aggressive_mode = total_used_memory >= critical_memory;
71 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL; 77 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
72 size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10); 78 size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
73 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { 79 const auto clean_up = [this, &num_iterations, &high_priority_mode,
80 &aggressive_mode](ImageId image_id) {
74 if (num_iterations == 0) { 81 if (num_iterations == 0) {
75 return true; 82 return true;
76 } 83 }
77 --num_iterations; 84 --num_iterations;
78 auto& image = slot_images[image_id]; 85 auto& image = slot_images[image_id];
79 const bool must_download = image.IsSafeDownload(); 86 const bool must_download =
80 if (!high_priority_mode && must_download) { 87 image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
88 if (!high_priority_mode &&
89 (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
81 return false; 90 return false;
82 } 91 }
83 if (must_download) { 92 if (must_download) {
@@ -92,6 +101,18 @@ void TextureCache<P>::RunGarbageCollector() {
92 } 101 }
93 UnregisterImage(image_id); 102 UnregisterImage(image_id);
94 DeleteImage(image_id, image.scale_tick > frame_tick + 5); 103 DeleteImage(image_id, image.scale_tick > frame_tick + 5);
104 if (total_used_memory < critical_memory) {
105 if (aggressive_mode) {
106 // Sink the aggresiveness.
107 num_iterations >>= 2;
108 aggressive_mode = false;
109 return false;
110 }
111 if (high_priority_mode && total_used_memory < expected_memory) {
112 num_iterations >>= 1;
113 high_priority_mode = false;
114 }
115 }
95 return false; 116 return false;
96 }; 117 };
97 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); 118 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
@@ -99,6 +120,10 @@ void TextureCache<P>::RunGarbageCollector() {
99 120
100template <class P> 121template <class P>
101void TextureCache<P>::TickFrame() { 122void TextureCache<P>::TickFrame() {
123 // If we can obtain the memory info, use it instead of the estimate.
124 if (runtime.CanReportMemoryUsage()) {
125 total_used_memory = runtime.GetDeviceMemoryUsage();
126 }
102 if (total_used_memory > minimum_memory) { 127 if (total_used_memory > minimum_memory) {
103 RunGarbageCollector(); 128 RunGarbageCollector();
104 } 129 }
@@ -106,6 +131,7 @@ void TextureCache<P>::TickFrame() {
106 sentenced_framebuffers.Tick(); 131 sentenced_framebuffers.Tick();
107 sentenced_image_view.Tick(); 132 sentenced_image_view.Tick();
108 runtime.TickFrame(); 133 runtime.TickFrame();
134 critical_gc = 0;
109 ++frame_tick; 135 ++frame_tick;
110} 136}
111 137
@@ -343,7 +369,7 @@ template <bool has_blacklists>
343void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, 369void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
344 std::span<ImageViewId> cached_image_view_ids, 370 std::span<ImageViewId> cached_image_view_ids,
345 std::span<ImageViewInOut> views) { 371 std::span<ImageViewInOut> views) {
346 bool has_blacklisted; 372 bool has_blacklisted = false;
347 do { 373 do {
348 has_deleted_images = false; 374 has_deleted_images = false;
349 if constexpr (has_blacklists) { 375 if constexpr (has_blacklists) {
@@ -1052,6 +1078,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1052 1078
1053 for (const ImageId overlap_id : overlap_ids) { 1079 for (const ImageId overlap_id : overlap_ids) {
1054 Image& overlap = slot_images[overlap_id]; 1080 Image& overlap = slot_images[overlap_id];
1081 if (True(overlap.flags & ImageFlagBits::GpuModified)) {
1082 new_image.flags |= ImageFlagBits::GpuModified;
1083 }
1055 if (overlap.info.num_samples != new_image.info.num_samples) { 1084 if (overlap.info.num_samples != new_image.info.num_samples) {
1056 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); 1085 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
1057 } else { 1086 } else {
@@ -1414,6 +1443,10 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1414 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1443 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1415 } 1444 }
1416 total_used_memory += Common::AlignUp(tentative_size, 1024); 1445 total_used_memory += Common::AlignUp(tentative_size, 1024);
1446 if (total_used_memory > critical_memory && critical_gc < GC_EMERGENCY_COUNTS) {
1447 RunGarbageCollector();
1448 critical_gc++;
1449 }
1417 image.lru_index = lru_cache.Insert(image_id, frame_tick); 1450 image.lru_index = lru_cache.Insert(image_id, frame_tick);
1418 1451
1419 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1452 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
@@ -1704,6 +1737,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1704 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); 1737 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
1705 aliased_images.push_back(&aliased); 1738 aliased_images.push_back(&aliased);
1706 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); 1739 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
1740 if (True(aliased_image.flags & ImageFlagBits::GpuModified)) {
1741 image.flags |= ImageFlagBits::GpuModified;
1742 }
1707 } 1743 }
1708 } 1744 }
1709 if (aliased_images.empty()) { 1745 if (aliased_images.empty()) {
@@ -1725,7 +1761,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1725 }); 1761 });
1726 const auto& resolution = Settings::values.resolution_info; 1762 const auto& resolution = Settings::values.resolution_info;
1727 for (const AliasedImage* const aliased : aliased_images) { 1763 for (const AliasedImage* const aliased : aliased_images) {
1728 if (!resolution.active | !any_rescaled) { 1764 if (!resolution.active || !any_rescaled) {
1729 CopyImage(image_id, aliased->id, aliased->copies); 1765 CopyImage(image_id, aliased->id, aliased->copies);
1730 continue; 1766 continue;
1731 } 1767 }
@@ -1736,19 +1772,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1736 continue; 1772 continue;
1737 } 1773 }
1738 ScaleUp(aliased_image); 1774 ScaleUp(aliased_image);
1739 1775 CopyImage(image_id, aliased->id, aliased->copies);
1740 const bool both_2d{image.info.type == ImageType::e2D &&
1741 aliased_image.info.type == ImageType::e2D};
1742 auto copies = aliased->copies;
1743 for (auto copy : copies) {
1744 copy.extent.width = std::max<u32>(
1745 (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1);
1746 if (both_2d) {
1747 copy.extent.height = std::max<u32>(
1748 (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1);
1749 }
1750 }
1751 CopyImage(image_id, aliased->id, copies);
1752 } 1776 }
1753} 1777}
1754 1778
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 7107887a6..b1324edf3 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -22,7 +22,6 @@
22#include "video_core/texture_cache/image_base.h" 22#include "video_core/texture_cache/image_base.h"
23#include "video_core/texture_cache/image_info.h" 23#include "video_core/texture_cache/image_info.h"
24#include "video_core/texture_cache/image_view_base.h" 24#include "video_core/texture_cache/image_view_base.h"
25#include "video_core/texture_cache/image_view_info.h"
26#include "video_core/texture_cache/render_targets.h" 25#include "video_core/texture_cache/render_targets.h"
27#include "video_core/texture_cache/slot_vector.h" 26#include "video_core/texture_cache/slot_vector.h"
28#include "video_core/texture_cache/types.h" 27#include "video_core/texture_cache/types.h"
@@ -60,8 +59,10 @@ class TextureCache {
60 /// True when the API can provide info about the memory of the device. 59 /// True when the API can provide info about the memory of the device.
61 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 60 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
62 61
63 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; 62 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
64 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; 63 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
64 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
65 static constexpr size_t GC_EMERGENCY_COUNTS = 2;
65 66
66 using Runtime = typename P::Runtime; 67 using Runtime = typename P::Runtime;
67 using Image = typename P::Image; 68 using Image = typename P::Image;
@@ -373,6 +374,7 @@ private:
373 u64 minimum_memory; 374 u64 minimum_memory;
374 u64 expected_memory; 375 u64 expected_memory;
375 u64 critical_memory; 376 u64 critical_memory;
377 size_t critical_gc;
376 378
377 SlotVector<Image> slot_images; 379 SlotVector<Image> slot_images;
378 SlotVector<ImageMapView> slot_map_views; 380 SlotVector<ImageMapView> slot_map_views;
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 7af52de2e..f13669ea5 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -9,10 +9,8 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/surface.h" 12#include "video_core/surface.h"
14#include "video_core/texture_cache/image_base.h" 13#include "video_core/texture_cache/image_base.h"
15#include "video_core/texture_cache/image_view_base.h"
16#include "video_core/texture_cache/types.h" 14#include "video_core/texture_cache/types.h"
17#include "video_core/textures/texture.h" 15#include "video_core/textures/texture.h"
18 16
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 25161df1f..28e4beafd 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -16,6 +16,7 @@
16// <http://gamma.cs.unc.edu/FasTC/> 16// <http://gamma.cs.unc.edu/FasTC/>
17 17
18#include <algorithm> 18#include <algorithm>
19#include <bit>
19#include <cassert> 20#include <cassert>
20#include <cstring> 21#include <cstring>
21#include <span> 22#include <span>
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index 14d2beec0..564ae1e36 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -4,9 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <bit>
8#include "common/common_types.h"
9
10namespace Tegra::Texture::ASTC { 7namespace Tegra::Texture::ASTC {
11 8
12void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 9void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 24e943e4c..6dae23049 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -6,7 +6,6 @@
6#include <cmath> 6#include <cmath>
7#include <cstring> 7#include <cstring>
8#include <span> 8#include <span>
9#include <utility>
10 9
11#include "common/alignment.h" 10#include "common/alignment.h"
12#include "common/assert.h" 11#include "common/assert.h"
@@ -14,7 +13,6 @@
14#include "common/div_ceil.h" 13#include "common/div_ceil.h"
15#include "video_core/gpu.h" 14#include "video_core/gpu.h"
16#include "video_core/textures/decoders.h" 15#include "video_core/textures/decoders.h"
17#include "video_core/textures/texture.h"
18 16
19namespace Tegra::Texture { 17namespace Tegra::Texture {
20namespace { 18namespace {
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index 06954963d..f56b4b9f9 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <array> 5#include <array>
7 6
8#include "common/cityhash.h" 7#include "common/cityhash.h"
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 329bf4def..2f2594585 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -50,6 +50,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
50 gpu->BindRenderer(std::move(renderer)); 50 gpu->BindRenderer(std::move(renderer));
51 return gpu; 51 return gpu;
52 } catch (const std::runtime_error& exception) { 52 } catch (const std::runtime_error& exception) {
53 scope.Cancel();
53 LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); 54 LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
54 return nullptr; 55 return nullptr;
55 } 56 }
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
index eae1891dd..55c115081 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.h
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
@@ -4,20 +4,22 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <filesystem>
8#include <mutex>
9#include <span> 7#include <span>
10#include <string>
11#include <vector>
12 8
13#include "common/common_types.h" 9#include "common/common_types.h"
14#include "common/dynamic_library.h"
15#include "video_core/vulkan_common/vulkan_wrapper.h"
16 10
17#ifdef HAS_NSIGHT_AFTERMATH 11#ifdef HAS_NSIGHT_AFTERMATH
12#include <filesystem>
13#include <mutex>
14
15// Vulkan headers must be included before Aftermath
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18#include <GFSDK_Aftermath_Defines.h> 18#include <GFSDK_Aftermath_Defines.h>
19#include <GFSDK_Aftermath_GpuCrashDump.h> 19#include <GFSDK_Aftermath_GpuCrashDump.h>
20#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> 20#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
21
22#include "common/dynamic_library.h"
21#endif 23#endif
22 24
23namespace Vulkan { 25namespace Vulkan {
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index effde73c9..bd05a1f84 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -6,19 +6,20 @@
6#include <bitset> 6#include <bitset>
7#include <chrono> 7#include <chrono>
8#include <optional> 8#include <optional>
9#include <string_view>
10#include <thread> 9#include <thread>
11#include <unordered_set> 10#include <unordered_set>
12#include <utility> 11#include <utility>
13#include <vector> 12#include <vector>
14 13
15#include "common/assert.h" 14#include "common/assert.h"
15#include "common/literals.h"
16#include "common/settings.h" 16#include "common/settings.h"
17#include "video_core/vulkan_common/nsight_aftermath_tracker.h" 17#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
18#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
19#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
20 20
21namespace Vulkan { 21namespace Vulkan {
22using namespace Common::Literals;
22namespace { 23namespace {
23namespace Alternatives { 24namespace Alternatives {
24constexpr std::array STENCIL8_UINT{ 25constexpr std::array STENCIL8_UINT{
@@ -44,6 +45,12 @@ constexpr std::array B5G6R5_UNORM_PACK16{
44 VK_FORMAT_R5G6B5_UNORM_PACK16, 45 VK_FORMAT_R5G6B5_UNORM_PACK16,
45 VK_FORMAT_UNDEFINED, 46 VK_FORMAT_UNDEFINED,
46}; 47};
48
49constexpr std::array R4G4_UNORM_PACK8{
50 VK_FORMAT_R8_UNORM,
51 VK_FORMAT_UNDEFINED,
52};
53
47} // namespace Alternatives 54} // namespace Alternatives
48 55
49enum class NvidiaArchitecture { 56enum class NvidiaArchitecture {
@@ -94,6 +101,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
94 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); 101 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
95 case VK_FORMAT_B5G6R5_UNORM_PACK16: 102 case VK_FORMAT_B5G6R5_UNORM_PACK16:
96 return Alternatives::B5G6R5_UNORM_PACK16.data(); 103 return Alternatives::B5G6R5_UNORM_PACK16.data();
104 case VK_FORMAT_R4G4_UNORM_PACK8:
105 return Alternatives::R4G4_UNORM_PACK8.data();
97 default: 106 default:
98 return nullptr; 107 return nullptr;
99 } 108 }
@@ -121,6 +130,8 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
121 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 130 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
122 VK_FORMAT_R5G6B5_UNORM_PACK16, 131 VK_FORMAT_R5G6B5_UNORM_PACK16,
123 VK_FORMAT_B5G6R5_UNORM_PACK16, 132 VK_FORMAT_B5G6R5_UNORM_PACK16,
133 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
134 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
124 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 135 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
125 VK_FORMAT_A2B10G10R10_UINT_PACK32, 136 VK_FORMAT_A2B10G10R10_UINT_PACK32,
126 VK_FORMAT_A1R5G5B5_UNORM_PACK16, 137 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
@@ -159,7 +170,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
159 VK_FORMAT_R16G16B16A16_SFLOAT, 170 VK_FORMAT_R16G16B16A16_SFLOAT,
160 VK_FORMAT_B8G8R8A8_UNORM, 171 VK_FORMAT_B8G8R8A8_UNORM,
161 VK_FORMAT_B8G8R8A8_SRGB, 172 VK_FORMAT_B8G8R8A8_SRGB,
173 VK_FORMAT_R4G4_UNORM_PACK8,
162 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 174 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
175 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
163 VK_FORMAT_D32_SFLOAT, 176 VK_FORMAT_D32_SFLOAT,
164 VK_FORMAT_D16_UNORM, 177 VK_FORMAT_D16_UNORM,
165 VK_FORMAT_S8_UINT, 178 VK_FORMAT_S8_UINT,
@@ -597,6 +610,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
597 } 610 }
598 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); 611 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
599 612
613 is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
614 is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
615 is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
616 properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
617
600 CollectPhysicalMemoryInfo(); 618 CollectPhysicalMemoryInfo();
601 CollectTelemetryParameters(); 619 CollectTelemetryParameters();
602 CollectToolingInfo(); 620 CollectToolingInfo();
@@ -615,6 +633,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
615 khr_push_descriptor = false; 633 khr_push_descriptor = false;
616 break; 634 break;
617 } 635 }
636 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
637 if (nv_major_version >= 510) {
638 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
639 cant_blit_msaa = true;
640 }
618 } 641 }
619 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; 642 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
620 if (ext_extended_dynamic_state && is_radv) { 643 if (ext_extended_dynamic_state && is_radv) {
@@ -725,7 +748,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
725} 748}
726 749
727void Device::ReportLoss() const { 750void Device::ReportLoss() const {
728 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 751 LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
729 752
730 // Wait for the log to flush and for Nsight Aftermath to dump the results 753 // Wait for the log to flush and for Nsight Aftermath to dump the results
731 std::this_thread::sleep_for(std::chrono::seconds{15}); 754 std::this_thread::sleep_for(std::chrono::seconds{15});
@@ -986,6 +1009,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
986 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, 1009 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
987 false); 1010 false);
988 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); 1011 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
1012 test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
989 if (Settings::values.enable_nsight_aftermath) { 1013 if (Settings::values.enable_nsight_aftermath) {
990 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 1014 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
991 true); 1015 true);
@@ -998,7 +1022,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
998 VkPhysicalDeviceFeatures2KHR features{}; 1022 VkPhysicalDeviceFeatures2KHR features{};
999 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 1023 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
1000 1024
1001 VkPhysicalDeviceProperties2KHR physical_properties; 1025 VkPhysicalDeviceProperties2KHR physical_properties{};
1002 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 1026 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
1003 1027
1004 if (has_khr_shader_float16_int8) { 1028 if (has_khr_shader_float16_int8) {
@@ -1268,15 +1292,50 @@ void Device::CollectTelemetryParameters() {
1268 vendor_name = driver.driverName; 1292 vendor_name = driver.driverName;
1269} 1293}
1270 1294
1295u64 Device::GetDeviceMemoryUsage() const {
1296 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget;
1297 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1298 budget.pNext = nullptr;
1299 physical.GetMemoryProperties(&budget);
1300 u64 result{};
1301 for (const size_t heap : valid_heap_memory) {
1302 result += budget.heapUsage[heap];
1303 }
1304 return result;
1305}
1306
1271void Device::CollectPhysicalMemoryInfo() { 1307void Device::CollectPhysicalMemoryInfo() {
1272 const auto mem_properties = physical.GetMemoryProperties(); 1308 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
1309 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1310 const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr);
1311 const auto& mem_properties = mem_info.memoryProperties;
1273 const size_t num_properties = mem_properties.memoryHeapCount; 1312 const size_t num_properties = mem_properties.memoryHeapCount;
1274 device_access_memory = 0; 1313 device_access_memory = 0;
1314 u64 device_initial_usage = 0;
1315 u64 local_memory = 0;
1275 for (size_t element = 0; element < num_properties; ++element) { 1316 for (size_t element = 0; element < num_properties; ++element) {
1276 if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { 1317 const bool is_heap_local =
1277 device_access_memory += mem_properties.memoryHeaps[element].size; 1318 (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
1319 if (!is_integrated && !is_heap_local) {
1320 continue;
1278 } 1321 }
1322 valid_heap_memory.push_back(element);
1323 if (is_heap_local) {
1324 local_memory += mem_properties.memoryHeaps[element].size;
1325 }
1326 if (ext_memory_budget) {
1327 device_initial_usage += budget.heapUsage[element];
1328 device_access_memory += budget.heapBudget[element];
1329 continue;
1330 }
1331 device_access_memory += mem_properties.memoryHeaps[element].size;
1332 }
1333 if (!is_integrated) {
1334 return;
1279 } 1335 }
1336 const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
1337 device_access_memory = static_cast<u64>(std::max<s64>(
1338 std::min<s64>(available_memory - 8_GiB, 4_GiB), static_cast<s64>(local_memory)));
1280} 1339}
1281 1340
1282void Device::CollectToolingInfo() { 1341void Device::CollectToolingInfo() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 34b1add16..2d709d069 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -6,7 +6,6 @@
6 6
7#include <span> 7#include <span>
8#include <string> 8#include <string>
9#include <string_view>
10#include <unordered_map> 9#include <unordered_map>
11#include <vector> 10#include <vector>
12 11
@@ -342,6 +341,12 @@ public:
342 return device_access_memory; 341 return device_access_memory;
343 } 342 }
344 343
344 bool CanReportMemoryUsage() const {
345 return ext_memory_budget;
346 }
347
348 u64 GetDeviceMemoryUsage() const;
349
345 u32 GetSetsPerPool() const { 350 u32 GetSetsPerPool() const {
346 return sets_per_pool; 351 return sets_per_pool;
347 } 352 }
@@ -422,6 +427,9 @@ private:
422 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list 427 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
423 ///< topologies. 428 ///< topologies.
424 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. 429 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
430 bool is_integrated{}; ///< Is GPU an iGPU.
431 bool is_virtual{}; ///< Is GPU a virtual GPU.
432 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
425 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 433 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
426 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 434 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
427 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 435 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -446,6 +454,7 @@ private:
446 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. 454 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
447 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. 455 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
448 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. 456 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
457 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
449 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 458 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
450 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit 459 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
451 bool has_renderdoc{}; ///< Has RenderDoc attached 460 bool has_renderdoc{}; ///< Has RenderDoc attached
@@ -457,6 +466,7 @@ private:
457 // Telemetry parameters 466 // Telemetry parameters
458 std::string vendor_name; ///< Device's driver name. 467 std::string vendor_name; ///< Device's driver name.
459 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. 468 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions.
469 std::vector<size_t> valid_heap_memory; ///< Heaps used.
460 470
461 /// Format properties dictionary. 471 /// Format properties dictionary.
462 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 472 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index bfd6e6add..662694f16 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -2,11 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <future> 5#include <future>
7#include <optional> 6#include <optional>
8#include <span> 7#include <span>
9#include <utility>
10#include <vector> 8#include <vector>
11 9
12#include "common/common_types.h" 10#include "common/common_types.h"
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp
index 22833fa56..d69de05ef 100644
--- a/src/video_core/vulkan_common/vulkan_library.cpp
+++ b/src/video_core/vulkan_common/vulkan_library.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib>
6#include <string> 5#include <string>
7 6
8#include "common/dynamic_library.h" 7#include "common/dynamic_library.h"
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 300a61205..e6e97b332 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -227,7 +227,7 @@ void MemoryCommit::Release() {
227} 227}
228 228
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, 230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
231 export_allocations{export_allocations_}, 231 export_allocations{export_allocations_},
232 buffer_image_granularity{ 232 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} 233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index 86e8ed119..338daf5ba 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <span> 8#include <span>
9#include <utility>
10#include <vector> 9#include <vector>
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index a9faa4807..742cc39da 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -3,10 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <exception>
7#include <memory> 6#include <memory>
8#include <optional> 7#include <optional>
9#include <string_view>
10#include <utility> 8#include <utility>
11#include <vector> 9#include <vector>
12 10
@@ -239,8 +237,8 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
239 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && 237 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) &&
240 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && 238 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) &&
241 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && 239 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) &&
242 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && 240 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) &&
243 X(vkGetPhysicalDeviceQueueFamilyProperties); 241 X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties);
244#undef X 242#undef X
245} 243}
246 244
@@ -928,9 +926,12 @@ std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR(
928 return modes; 926 return modes;
929} 927}
930 928
931VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { 929VkPhysicalDeviceMemoryProperties2 PhysicalDevice::GetMemoryProperties(
932 VkPhysicalDeviceMemoryProperties properties; 930 void* next_structures) const noexcept {
933 dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); 931 VkPhysicalDeviceMemoryProperties2 properties{};
932 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
933 properties.pNext = next_structures;
934 dld->vkGetPhysicalDeviceMemoryProperties2(physical_device, &properties);
934 return properties; 935 return properties;
935} 936}
936 937
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index b7ae01c6c..0a5f9931c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <exception> 7#include <exception>
8#include <iterator>
9#include <limits> 8#include <limits>
10#include <memory> 9#include <memory>
11#include <optional> 10#include <optional>
@@ -173,6 +172,7 @@ struct InstanceDispatch {
173 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; 172 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
174 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; 173 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
175 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; 174 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
175 PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{};
176 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; 176 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
177 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; 177 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
178 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; 178 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
@@ -951,7 +951,8 @@ public:
951 951
952 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; 952 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const;
953 953
954 VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; 954 VkPhysicalDeviceMemoryProperties2 GetMemoryProperties(
955 void* next_structures = nullptr) const noexcept;
955 956
956private: 957private:
957 VkPhysicalDevice physical_device = nullptr; 958 VkPhysicalDevice physical_device = nullptr;
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index b1e02c57a..58b0c2f10 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <cstdlib>
7#include <mutex> 6#include <mutex>
8#include <string> 7#include <string>
9 8
@@ -33,7 +32,7 @@ constexpr std::size_t TIMEOUT_SECONDS = 30;
33struct Client::Impl { 32struct Client::Impl {
34 Impl(std::string host, std::string username, std::string token) 33 Impl(std::string host, std::string username, std::string token)
35 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { 34 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
36 std::lock_guard lock{jwt_cache.mutex}; 35 std::scoped_lock lock{jwt_cache.mutex};
37 if (this->username == jwt_cache.username && this->token == jwt_cache.token) { 36 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
38 jwt = jwt_cache.jwt; 37 jwt = jwt_cache.jwt;
39 } 38 }
@@ -148,7 +147,7 @@ struct Client::Impl {
148 if (result.result_code != WebResult::Code::Success) { 147 if (result.result_code != WebResult::Code::Success) {
149 LOG_ERROR(WebService, "UpdateJWT failed"); 148 LOG_ERROR(WebService, "UpdateJWT failed");
150 } else { 149 } else {
151 std::lock_guard lock{jwt_cache.mutex}; 150 std::scoped_lock lock{jwt_cache.mutex};
152 jwt_cache.username = username; 151 jwt_cache.username = username;
153 jwt_cache.token = token; 152 jwt_cache.token = token;
154 jwt_cache.jwt = jwt = result.returned_data; 153 jwt_cache.jwt = jwt = result.returned_data;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 30902101d..2ee21f751 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -99,6 +99,9 @@ add_executable(yuzu
99 configuration/configure_profile_manager.cpp 99 configuration/configure_profile_manager.cpp
100 configuration/configure_profile_manager.h 100 configuration/configure_profile_manager.h
101 configuration/configure_profile_manager.ui 101 configuration/configure_profile_manager.ui
102 configuration/configure_ringcon.cpp
103 configuration/configure_ringcon.h
104 configuration/configure_ringcon.ui
102 configuration/configure_network.cpp 105 configuration/configure_network.cpp
103 configuration/configure_network.h 106 configuration/configure_network.h
104 configuration/configure_network.ui 107 configuration/configure_network.ui
@@ -293,7 +296,7 @@ if (YUZU_USE_QT_WEB_ENGINE)
293endif () 296endif ()
294 297
295if(UNIX AND NOT APPLE) 298if(UNIX AND NOT APPLE)
296 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 299 install(TARGETS yuzu)
297endif() 300endif()
298 301
299if (YUZU_USE_BUNDLED_QT) 302if (YUZU_USE_BUNDLED_QT)
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 27d81cd13..2f7ddc7f3 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -87,7 +87,7 @@
87&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; 87&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
88p, li { white-space: pre-wrap; } 88p, li { white-space: pre-wrap; }
89&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt; 89&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
90&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt; 90&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.&lt;/span&gt;&lt;/p&gt;
91&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt; 91&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
92&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 92&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
93 </property> 93 </property>
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 4104928d1..90a27e573 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -6,7 +6,6 @@
6#include <thread> 6#include <thread>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/param_package.h"
10#include "common/string_util.h" 9#include "common/string_util.h"
11#include "core/core.h" 10#include "core/core.h"
12#include "core/hid/emulated_controller.h" 11#include "core/hid/emulated_controller.h"
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 4cd8f7784..44502883c 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -10,6 +10,7 @@
10#include <QLineEdit> 10#include <QLineEdit>
11#include <QScrollArea> 11#include <QScrollArea>
12#include <QStandardItemModel> 12#include <QStandardItemModel>
13#include <QTreeView>
13#include <QVBoxLayout> 14#include <QVBoxLayout>
14#include "common/fs/path_util.h" 15#include "common/fs/path_util.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h
index 56496ed31..0a9d4f982 100644
--- a/src/yuzu/applets/qt_profile_select.h
+++ b/src/yuzu/applets/qt_profile_select.h
@@ -7,7 +7,6 @@
7#include <vector> 7#include <vector>
8#include <QDialog> 8#include <QDialog>
9#include <QList> 9#include <QList>
10#include <QTreeView>
11#include "core/frontend/applets/profile_select.h" 10#include "core/frontend/applets/profile_select.h"
12#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
13 12
@@ -19,6 +18,7 @@ class QLabel;
19class QScrollArea; 18class QScrollArea;
20class QStandardItem; 19class QStandardItem;
21class QStandardItemModel; 20class QStandardItemModel;
21class QTreeView;
22class QVBoxLayout; 22class QVBoxLayout;
23 23
24namespace Core::HID { 24namespace Core::HID {
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index cb3c5d826..116ef54db 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -11,17 +11,15 @@
11#include <QWebEngineScriptCollection> 11#include <QWebEngineScriptCollection>
12#include <QWebEngineSettings> 12#include <QWebEngineSettings>
13#include <QWebEngineUrlScheme> 13#include <QWebEngineUrlScheme>
14
15#include "core/hid/input_interpreter.h"
16#include "yuzu/applets/qt_web_browser_scripts.h"
14#endif 17#endif
15 18
16#include "common/fs/path_util.h" 19#include "common/fs/path_util.h"
17#include "common/param_package.h"
18#include "core/core.h" 20#include "core/core.h"
19#include "core/hid/hid_types.h"
20#include "core/hid/input_interpreter.h"
21#include "input_common/drivers/keyboard.h" 21#include "input_common/drivers/keyboard.h"
22#include "input_common/main.h"
23#include "yuzu/applets/qt_web_browser.h" 22#include "yuzu/applets/qt_web_browser.h"
24#include "yuzu/applets/qt_web_browser_scripts.h"
25#include "yuzu/main.h" 23#include "yuzu/main.h"
26#include "yuzu/util/url_request_interceptor.h" 24#include "yuzu/util/url_request_interceptor.h"
27 25
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index fa18aecac..a47059412 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <memory>
9#include <thread> 8#include <thread>
10 9
11#include <QObject> 10#include <QObject>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 114f17c06..a1b819ae0 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -6,7 +6,6 @@
6 6
7#include <QApplication> 7#include <QApplication>
8#include <QHBoxLayout> 8#include <QHBoxLayout>
9#include <QKeyEvent>
10#include <QMessageBox> 9#include <QMessageBox>
11#include <QPainter> 10#include <QPainter>
12#include <QScreen> 11#include <QScreen>
@@ -28,7 +27,6 @@
28#include "common/assert.h" 27#include "common/assert.h"
29#include "common/microprofile.h" 28#include "common/microprofile.h"
30#include "common/scm_rev.h" 29#include "common/scm_rev.h"
31#include "common/scope_exit.h"
32#include "common/settings.h" 30#include "common/settings.h"
33#include "core/core.h" 31#include "core/core.h"
34#include "core/frontend/framebuffer_layout.h" 32#include "core/frontend/framebuffer_layout.h"
@@ -38,7 +36,6 @@
38#include "input_common/drivers/touch_screen.h" 36#include "input_common/drivers/touch_screen.h"
39#include "input_common/main.h" 37#include "input_common/main.h"
40#include "video_core/renderer_base.h" 38#include "video_core/renderer_base.h"
41#include "video_core/video_core.h"
42#include "yuzu/bootmanager.h" 39#include "yuzu/bootmanager.h"
43#include "yuzu/main.h" 40#include "yuzu/main.h"
44 41
@@ -936,6 +933,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
936 auto& renderer = system.Renderer(); 933 auto& renderer = system.Renderer();
937 const f32 res_scale = Settings::values.resolution_info.up_factor; 934 const f32 res_scale = Settings::values.resolution_info.up_factor;
938 935
936 if (renderer.IsScreenshotPending()) {
937 LOG_WARNING(Render,
938 "A screenshot is already requested or in progress, ignoring the request");
939 return;
940 }
941
939 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; 942 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
940 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 943 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
941 renderer.RequestScreenshot( 944 renderer.RequestScreenshot(
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 92297a43b..4b0ce0293 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -13,7 +13,6 @@
13#include <QThread> 13#include <QThread>
14#include <QTouchEvent> 14#include <QTouchEvent>
15#include <QWidget> 15#include <QWidget>
16#include <QWindow>
17 16
18#include "common/thread.h" 17#include "common/thread.h"
19#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui
index fed402176..3ca55eda6 100644
--- a/src/yuzu/compatdb.ui
+++ b/src/yuzu/compatdb.ui
@@ -86,7 +86,7 @@
86 <item row="4" column="0"> 86 <item row="4" column="0">
87 <widget class="QRadioButton" name="radioButton_Great"> 87 <widget class="QRadioButton" name="radioButton_Great">
88 <property name="text"> 88 <property name="text">
89 <string>Great </string> 89 <string>Great</string>
90 </property> 90 </property>
91 </widget> 91 </widget>
92 </item> 92 </item>
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index c2b66ff14..ac26b885b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -60,6 +60,11 @@ const std::array<int, 2> Config::default_stick_mod = {
60 0, 60 0,
61}; 61};
62 62
63const std::array<int, 2> Config::default_ringcon_analogs{{
64 Qt::Key_A,
65 Qt::Key_D,
66}};
67
63// This shouldn't have anything except static initializers (no functions). So 68// This shouldn't have anything except static initializers (no functions). So
64// QKeySequence(...).toString() is NOT ALLOWED HERE. 69// QKeySequence(...).toString() is NOT ALLOWED HERE.
65// This must be in alphabetical order according to action name as it must have the same order as 70// This must be in alphabetical order according to action name as it must have the same order as
@@ -346,6 +351,23 @@ void Config::ReadTouchscreenValues() {
346 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 351 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
347} 352}
348 353
354void Config::ReadHidbusValues() {
355 Settings::values.enable_ring_controller =
356 ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool();
357
358 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
359 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
360 auto& ringcon_analogs = Settings::values.ringcon_analogs;
361
362 ringcon_analogs =
363 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
364 .toString()
365 .toStdString();
366 if (ringcon_analogs.empty()) {
367 ringcon_analogs = default_param;
368 }
369}
370
349void Config::ReadAudioValues() { 371void Config::ReadAudioValues() {
350 qt_config->beginGroup(QStringLiteral("Audio")); 372 qt_config->beginGroup(QStringLiteral("Audio"));
351 373
@@ -369,6 +391,7 @@ void Config::ReadControlValues() {
369 ReadMouseValues(); 391 ReadMouseValues();
370 ReadTouchscreenValues(); 392 ReadTouchscreenValues();
371 ReadMotionTouchValues(); 393 ReadMotionTouchValues();
394 ReadHidbusValues();
372 395
373#ifdef _WIN32 396#ifdef _WIN32
374 ReadBasicSetting(Settings::values.enable_raw_input); 397 ReadBasicSetting(Settings::values.enable_raw_input);
@@ -775,6 +798,7 @@ void Config::ReadUIValues() {
775 ReadBasicSetting(UISettings::values.pause_when_in_background); 798 ReadBasicSetting(UISettings::values.pause_when_in_background);
776 ReadBasicSetting(UISettings::values.mute_when_in_background); 799 ReadBasicSetting(UISettings::values.mute_when_in_background);
777 ReadBasicSetting(UISettings::values.hide_mouse); 800 ReadBasicSetting(UISettings::values.hide_mouse);
801 ReadBasicSetting(UISettings::values.disable_web_applet);
778 802
779 qt_config->endGroup(); 803 qt_config->endGroup();
780} 804}
@@ -961,6 +985,16 @@ void Config::SaveMotionTouchValues() {
961 qt_config->endArray(); 985 qt_config->endArray();
962} 986}
963 987
988void Config::SaveHidbusValues() {
989 WriteBasicSetting(Settings::values.enable_ring_controller);
990
991 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
992 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
993 WriteSetting(QStringLiteral("ring_controller"),
994 QString::fromStdString(Settings::values.ringcon_analogs),
995 QString::fromStdString(default_param));
996}
997
964void Config::SaveValues() { 998void Config::SaveValues() {
965 if (global) { 999 if (global) {
966 SaveControlValues(); 1000 SaveControlValues();
@@ -1001,6 +1035,7 @@ void Config::SaveControlValues() {
1001 SaveMouseValues(); 1035 SaveMouseValues();
1002 SaveTouchscreenValues(); 1036 SaveTouchscreenValues();
1003 SaveMotionTouchValues(); 1037 SaveMotionTouchValues();
1038 SaveHidbusValues();
1004 1039
1005 WriteGlobalSetting(Settings::values.use_docked_mode); 1040 WriteGlobalSetting(Settings::values.use_docked_mode);
1006 WriteGlobalSetting(Settings::values.vibration_enabled); 1041 WriteGlobalSetting(Settings::values.vibration_enabled);
@@ -1155,6 +1190,8 @@ void Config::SaveCpuValues() {
1155 WriteBasicSetting(Settings::values.cpuopt_misc_ir); 1190 WriteBasicSetting(Settings::values.cpuopt_misc_ir);
1156 WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); 1191 WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
1157 WriteBasicSetting(Settings::values.cpuopt_fastmem); 1192 WriteBasicSetting(Settings::values.cpuopt_fastmem);
1193 WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
1194 WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
1158 } 1195 }
1159 1196
1160 qt_config->endGroup(); 1197 qt_config->endGroup();
@@ -1306,6 +1343,7 @@ void Config::SaveUIValues() {
1306 WriteBasicSetting(UISettings::values.pause_when_in_background); 1343 WriteBasicSetting(UISettings::values.pause_when_in_background);
1307 WriteBasicSetting(UISettings::values.mute_when_in_background); 1344 WriteBasicSetting(UISettings::values.mute_when_in_background);
1308 WriteBasicSetting(UISettings::values.hide_mouse); 1345 WriteBasicSetting(UISettings::values.hide_mouse);
1346 WriteBasicSetting(UISettings::values.disable_web_applet);
1309 1347
1310 qt_config->endGroup(); 1348 qt_config->endGroup();
1311} 1349}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ae3e36a11..f0ab6bdaa 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,7 @@ public:
42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; 42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
44 static const std::array<int, 2> default_stick_mod; 44 static const std::array<int, 2> default_stick_mod;
45 static const std::array<int, 2> default_ringcon_analogs;
45 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 46 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
46 default_mouse_buttons; 47 default_mouse_buttons;
47 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 48 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -66,6 +67,7 @@ private:
66 void ReadMouseValues(); 67 void ReadMouseValues();
67 void ReadTouchscreenValues(); 68 void ReadTouchscreenValues();
68 void ReadMotionTouchValues(); 69 void ReadMotionTouchValues();
70 void ReadHidbusValues();
69 71
70 // Read functions bases off the respective config section names. 72 // Read functions bases off the respective config section names.
71 void ReadAudioValues(); 73 void ReadAudioValues();
@@ -93,6 +95,7 @@ private:
93 void SaveMouseValues(); 95 void SaveMouseValues();
94 void SaveTouchscreenValues(); 96 void SaveTouchscreenValues();
95 void SaveMotionTouchValues(); 97 void SaveMotionTouchValues();
98 void SaveHidbusValues();
96 99
97 // Save functions based off the respective config section names. 100 // Save functions based off the respective config section names.
98 void SaveAudioValues(); 101 void SaveAudioValues();
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 251aab912..5190bd18b 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QCheckBox> 5#include <QCheckBox>
6#include <QComboBox>
7#include <QObject> 6#include <QObject>
8#include <QString> 7#include <QString>
9#include "common/settings.h" 8#include "common/settings.h"
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 5423dbc92..903a9baae 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -6,7 +6,6 @@
6 6
7#include <QCheckBox> 7#include <QCheckBox>
8#include <QComboBox> 8#include <QComboBox>
9#include <QString>
10#include "common/settings.h" 9#include "common/settings.h"
11 10
12namespace ConfigurationShared { 11namespace ConfigurationShared {
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index c33488718..2f9285e77 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -4,8 +4,6 @@
4 4
5#include <memory> 5#include <memory>
6 6
7#include <QSignalBlocker>
8
9#include "audio_core/sink.h" 7#include "audio_core/sink.h"
10#include "audio_core/sink_details.h" 8#include "audio_core/sink_details.h"
11#include "common/settings.h" 9#include "common/settings.h"
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index bf74ccc7c..0de7fbfed 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -2,11 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QComboBox>
6#include <QMessageBox>
7
8#include "common/common_types.h" 5#include "common/common_types.h"
9#include "common/logging/log.h"
10#include "common/settings.h" 6#include "common/settings.h"
11#include "core/core.h" 7#include "core/core.h"
12#include "ui_configure_cpu.h" 8#include "ui_configure_cpu.h"
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index 733e38be4..3209c11d5 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9#include "common/settings.h"
10 9
11namespace Core { 10namespace Core {
12class System; 11class System;
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 5d80a8c91..8ae569ee6 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -52,6 +52,11 @@
52 <string>Unsafe</string> 52 <string>Unsafe</string>
53 </property> 53 </property>
54 </item> 54 </item>
55 <item>
56 <property name="text">
57 <string>Paranoid (disables most optimizations)</string>
58 </property>
59 </item>
55 </widget> 60 </widget>
56 </item> 61 </item>
57 </layout> 62 </layout>
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp
index 616a0be75..087d9496c 100644
--- a/src/yuzu/configuration/configure_cpu_debug.cpp
+++ b/src/yuzu/configuration/configure_cpu_debug.cpp
@@ -2,10 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QComboBox>
6
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "common/settings.h" 5#include "common/settings.h"
10#include "core/core.h" 6#include "core/core.h"
11#include "ui_configure_cpu_debug.h" 7#include "ui_configure_cpu_debug.h"
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index c1cf4050c..bd50f7a68 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -59,6 +59,13 @@ void ConfigureDebug::SetConfiguration() {
59 ui->disable_loop_safety_checks->setChecked( 59 ui->disable_loop_safety_checks->setChecked(
60 Settings::values.disable_shader_loop_safety_checks.GetValue()); 60 Settings::values.disable_shader_loop_safety_checks.GetValue());
61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); 61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
62
63#ifdef YUZU_USE_QT_WEB_ENGINE
64 ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
65#else
66 ui->disable_web_applet->setEnabled(false);
67 ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
68#endif
62} 69}
63 70
64void ConfigureDebug::ApplyConfiguration() { 71void ConfigureDebug::ApplyConfiguration() {
@@ -80,6 +87,7 @@ void ConfigureDebug::ApplyConfiguration() {
80 ui->disable_loop_safety_checks->isChecked(); 87 ui->disable_loop_safety_checks->isChecked();
81 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 88 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
82 Settings::values.extended_logging = ui->extended_logging->isChecked(); 89 Settings::values.extended_logging = ui->extended_logging->isChecked();
90 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
83 Debugger::ToggleConsole(); 91 Debugger::ToggleConsole();
84 Common::Log::Filter filter; 92 Common::Log::Filter filter;
85 filter.ParseFilterString(Settings::values.log_filter.GetValue()); 93 filter.ParseFilterString(Settings::values.log_filter.GetValue());
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 4dd870855..c1d90d588 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -8,49 +8,49 @@
8 <property name="title"> 8 <property name="title">
9 <string>Logging</string> 9 <string>Logging</string>
10 </property> 10 </property>
11 <layout class="QGridLayout" name="gridLayout_1"> 11 <layout class="QGridLayout" name="gridLayout_1">
12 <item row="0" column="0" colspan="2"> 12 <item row="0" column="0" colspan="2">
13 <layout class="QHBoxLayout" name="horizontalLayout_1"> 13 <layout class="QHBoxLayout" name="horizontalLayout_1">
14 <item> 14 <item>
15 <widget class="QLabel" name="label_1"> 15 <widget class="QLabel" name="label_1">
16 <property name="text">
17 <string>Global Log Filter</string>
18 </property>
19 </widget>
20 </item>
21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text"> 16 <property name="text">
49 <string>Enable Extended Logging**</string> 17 <string>Global Log Filter</string>
50 </property> 18 </property>
51 </widget> 19 </widget>
52 </item> 20 </item>
53 </layout> 21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text">
49 <string>Enable Extended Logging**</string>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </widget> 54 </widget>
55 </item> 55 </item>
56 <item> 56 <item>
@@ -183,7 +183,7 @@
183 <string>Advanced</string> 183 <string>Advanced</string>
184 </property> 184 </property>
185 <layout class="QGridLayout" name="gridLayout_4"> 185 <layout class="QGridLayout" name="gridLayout_4">
186 <item> row="0" column="0"> 186 <item row="0" column="0">
187 <widget class="QCheckBox" name="quest_flag"> 187 <widget class="QCheckBox" name="quest_flag">
188 <property name="text"> 188 <property name="text">
189 <string>Kiosk (Quest) Mode</string> 189 <string>Kiosk (Quest) Mode</string>
@@ -214,7 +214,14 @@
214 <item row="1" column="1"> 214 <item row="1" column="1">
215 <widget class="QCheckBox" name="enable_all_controllers"> 215 <widget class="QCheckBox" name="enable_all_controllers">
216 <property name="text"> 216 <property name="text">
217 <string>Enable all Controller Types</string> 217 <string>Enable All Controller Types</string>
218 </property>
219 </widget>
220 </item>
221 <item row="2" column="1">
222 <widget class="QCheckBox" name="disable_web_applet">
223 <property name="text">
224 <string>Disable Web Applet</string>
218 </property> 225 </property>
219 </widget> 226 </widget>
220 </item> 227 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 19133ccf5..b415a1cc4 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -3,13 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <QAbstractButton>
7#include <QDialogButtonBox>
8#include <QHash>
9#include <QListWidgetItem>
10#include <QPushButton>
11#include <QSignalBlocker>
12#include <QTabWidget>
13#include "common/logging/log.h" 6#include "common/logging/log.h"
14#include "common/settings.h" 7#include "common/settings.h"
15#include "core/core.h" 8#include "core/core.h"
@@ -32,6 +25,7 @@
32#include "yuzu/configuration/configure_ui.h" 25#include "yuzu/configuration/configure_ui.h"
33#include "yuzu/configuration/configure_web.h" 26#include "yuzu/configuration/configure_web.h"
34#include "yuzu/hotkeys.h" 27#include "yuzu/hotkeys.h"
28#include "yuzu/uisettings.h"
35 29
36ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
37 InputCommon::InputSubsystem* input_subsystem, 31 InputCommon::InputSubsystem* input_subsystem,
@@ -176,6 +170,8 @@ void ConfigureDialog::PopulateSelectionList() {
176 170
177void ConfigureDialog::OnLanguageChanged(const QString& locale) { 171void ConfigureDialog::OnLanguageChanged(const QString& locale) {
178 emit LanguageChanged(locale); 172 emit LanguageChanged(locale);
173 // Reloading the game list is needed to force retranslation.
174 UISettings::values.is_game_list_reload_pending = true;
179 // first apply the configuration, and then restore the display 175 // first apply the configuration, and then restore the display
180 ApplyConfiguration(); 176 ApplyConfiguration();
181 RetranslateUI(); 177 RetranslateUI();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 08d5444ec..a31fabd3f 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -4,13 +4,10 @@
4 4
5#include <functional> 5#include <functional>
6#include <utility> 6#include <utility>
7#include <QCheckBox>
8#include <QMessageBox> 7#include <QMessageBox>
9#include <QSpinBox>
10#include "common/settings.h" 8#include "common/settings.h"
11#include "core/core.h" 9#include "core/core.h"
12#include "ui_configure_general.h" 10#include "ui_configure_general.h"
13#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configuration_shared.h" 11#include "yuzu/configuration/configuration_shared.h"
15#include "yuzu/configuration/configure_general.h" 12#include "yuzu/configuration/configure_general.h"
16#include "yuzu/uisettings.h" 13#include "yuzu/uisettings.h"
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 59f975a6e..2f1435b10 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -6,7 +6,6 @@
6#include "video_core/vulkan_common/vulkan_wrapper.h" 6#include "video_core/vulkan_common/vulkan_wrapper.h"
7 7
8#include <QColorDialog> 8#include <QColorDialog>
9#include <QComboBox>
10#include <QVulkanInstance> 9#include <QVulkanInstance>
11 10
12#include "common/common_types.h" 11#include "common/common_types.h"
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 53e629a5e..6679e9c53 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -35,8 +35,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); 35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
36 ui->hotkey_list->setModel(model); 36 ui->hotkey_list->setModel(model);
37 37
38 ui->hotkey_list->setColumnWidth(name_column, 200); 38 ui->hotkey_list->header()->setStretchLastSection(false);
39 ui->hotkey_list->resizeColumnToContents(hotkey_column); 39 ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch);
40 ui->hotkey_list->header()->setMinimumSectionSize(150);
40 41
41 connect(ui->button_restore_defaults, &QPushButton::clicked, this, 42 connect(ui->button_restore_defaults, &QPushButton::clicked, this,
42 &ConfigureHotkeys::RestoreDefaults); 43 &ConfigureHotkeys::RestoreDefaults);
@@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
76 } 77 }
77 78
78 ui->hotkey_list->expandAll(); 79 ui->hotkey_list->expandAll();
79 ui->hotkey_list->resizeColumnToContents(name_column);
80 ui->hotkey_list->resizeColumnToContents(hotkey_column); 80 ui->hotkey_list->resizeColumnToContents(hotkey_column);
81 ui->hotkey_list->resizeColumnToContents(controller_column);
81} 82}
82 83
83void ConfigureHotkeys::changeEvent(QEvent* event) { 84void ConfigureHotkeys::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7c5776189..73d7ba24b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -2,13 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <memory> 5#include <memory>
7#include <thread> 6#include <thread>
8 7
9#include <QSignalBlocker>
10#include <QTimer>
11
12#include "core/core.h" 8#include "core/core.h"
13#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
@@ -24,6 +20,7 @@
24#include "yuzu/configuration/configure_input_advanced.h" 20#include "yuzu/configuration/configure_input_advanced.h"
25#include "yuzu/configuration/configure_input_player.h" 21#include "yuzu/configuration/configure_input_player.h"
26#include "yuzu/configuration/configure_motion_touch.h" 22#include "yuzu/configuration/configure_motion_touch.h"
23#include "yuzu/configuration/configure_ringcon.h"
27#include "yuzu/configuration/configure_touchscreen_advanced.h" 24#include "yuzu/configuration/configure_touchscreen_advanced.h"
28#include "yuzu/configuration/configure_vibration.h" 25#include "yuzu/configuration/configure_vibration.h"
29#include "yuzu/configuration/input_profiles.h" 26#include "yuzu/configuration/input_profiles.h"
@@ -162,6 +159,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
162 [this, input_subsystem] { 159 [this, input_subsystem] {
163 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 160 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
164 }); 161 });
162 connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog,
163 [this, input_subsystem, &hid_core] {
164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
165 });
165 166
166 connect(ui->vibrationButton, &QPushButton::clicked, 167 connect(ui->vibrationButton, &QPushButton::clicked,
167 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); 168 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 20fc2599d..8fd1f4a38 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -79,13 +79,17 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
79 &ConfigureInputAdvanced::UpdateUIEnabled); 79 &ConfigureInputAdvanced::UpdateUIEnabled);
80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
81 &ConfigureInputAdvanced::UpdateUIEnabled); 81 &ConfigureInputAdvanced::UpdateUIEnabled);
82 connect(ui->enable_ring_controller, &QCheckBox::stateChanged, this,
83 &ConfigureInputAdvanced::UpdateUIEnabled);
82 84
83 connect(ui->debug_configure, &QPushButton::clicked, this, 85 connect(ui->debug_configure, &QPushButton::clicked, this,
84 [this] { CallDebugControllerDialog(); }); 86 [this] { CallDebugControllerDialog(); });
85 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
86 [this] { CallTouchscreenConfigDialog(); }); 88 [this] { CallTouchscreenConfigDialog(); });
87 connect(ui->buttonMotionTouch, &QPushButton::clicked, this, 89 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
88 &ConfigureInputAdvanced::CallMotionTouchConfigDialog); 90 [this] { CallMotionTouchConfigDialog(); });
91 connect(ui->ring_controller_configure, &QPushButton::clicked, this,
92 [this] { CallRingControllerDialog(); });
89 93
90#ifndef _WIN32 94#ifndef _WIN32
91 ui->enable_raw_input->setVisible(false); 95 ui->enable_raw_input->setVisible(false);
@@ -132,6 +136,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
132 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 136 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
133 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); 137 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
134 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
135} 140}
136 141
137void ConfigureInputAdvanced::LoadConfiguration() { 142void ConfigureInputAdvanced::LoadConfiguration() {
@@ -164,6 +169,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
164 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 169 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
165 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); 170 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
166 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 171 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
172 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
167 173
168 UpdateUIEnabled(); 174 UpdateUIEnabled();
169} 175}
@@ -185,4 +191,5 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
185 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 191 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
186 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); 192 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
187 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); 193 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
194 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
188} 195}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 3083d55c1..4472cb846 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -29,6 +29,7 @@ signals:
29 void CallMouseConfigDialog(); 29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog(); 30 void CallTouchscreenConfigDialog();
31 void CallMotionTouchConfigDialog(); 31 void CallMotionTouchConfigDialog();
32 void CallRingControllerDialog();
32 33
33private: 34private:
34 void changeEvent(QEvent* event) override; 35 void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 66f2075f2..14403cb10 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2603,6 +2603,20 @@
2603 </property> 2603 </property>
2604 </widget> 2604 </widget>
2605 </item> 2605 </item>
2606 <item row="4" column="0">
2607 <widget class="QCheckBox" name="enable_ring_controller">
2608 <property name="text">
2609 <string>Ring Controller</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="4" column="2">
2614 <widget class="QPushButton" name="ring_controller_configure">
2615 <property name="text">
2616 <string>Configure</string>
2617 </property>
2618 </widget>
2619 </item>
2606 </layout> 2620 </layout>
2607 </widget> 2621 </widget>
2608 </item> 2622 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 0aa4ac3e4..b4f5d172a 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -7,10 +7,10 @@
7#include <utility> 7#include <utility>
8#include <QGridLayout> 8#include <QGridLayout>
9#include <QInputDialog> 9#include <QInputDialog>
10#include <QKeyEvent>
11#include <QMenu> 10#include <QMenu>
12#include <QMessageBox> 11#include <QMessageBox>
13#include <QTimer> 12#include <QTimer>
13#include "common/assert.h"
14#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/hid/emulated_controller.h" 15#include "core/hid/emulated_controller.h"
16#include "core/hid/hid_core.h" 16#include "core/hid/hid_core.h"
@@ -23,7 +23,6 @@
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/configure_input_player_widget.h" 25#include "yuzu/configuration/configure_input_player_widget.h"
26#include "yuzu/configuration/configure_vibration.h"
27#include "yuzu/configuration/input_profiles.h" 26#include "yuzu/configuration/input_profiles.h"
28#include "yuzu/util/limitable_input_dialog.h" 27#include "yuzu/util/limitable_input_dialog.h"
29 28
@@ -121,6 +120,23 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
121 } 120 }
122} 121}
123 122
123QString GetDirectionName(const std::string& direction) {
124 if (direction == "left") {
125 return QObject::tr("Left");
126 }
127 if (direction == "right") {
128 return QObject::tr("Right");
129 }
130 if (direction == "up") {
131 return QObject::tr("Up");
132 }
133 if (direction == "down") {
134 return QObject::tr("Down");
135 }
136 UNIMPLEMENTED_MSG("Unimplemented direction name={}", direction);
137 return QString::fromStdString(direction);
138}
139
124void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, 140void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
125 const std::string& button_name) { 141 const std::string& button_name) {
126 // The poller returned a complete axis, so set all the buttons 142 // The poller returned a complete axis, so set all the buttons
@@ -164,7 +180,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
164 180
165 if (common_button_name == Common::Input::ButtonNames::Value) { 181 if (common_button_name == Common::Input::ButtonNames::Value) {
166 if (param.Has("hat")) { 182 if (param.Has("hat")) {
167 const QString hat = QString::fromStdString(param.Get("direction", "")); 183 const QString hat = GetDirectionName(param.Get("direction", ""));
168 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); 184 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
169 } 185 }
170 if (param.Has("axis")) { 186 if (param.Has("axis")) {
@@ -473,6 +489,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
473 [=, this](const Common::ParamPackage& params) { 489 [=, this](const Common::ParamPackage& params) {
474 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); 490 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
475 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); 491 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
492 // Correct axis direction for inverted sticks
493 if (input_subsystem->IsStickInverted(param)) {
494 switch (analog_id) {
495 case Settings::NativeAnalog::LStick: {
496 const bool invert_value = param.Get("invert_x", "+") == "-";
497 const std::string invert_str = invert_value ? "+" : "-";
498 param.Set("invert_x", invert_str);
499 break;
500 }
501 case Settings::NativeAnalog::RStick: {
502 const bool invert_value = param.Get("invert_y", "+") == "-";
503 const std::string invert_str = invert_value ? "+" : "-";
504 param.Set("invert_y", invert_str);
505 break;
506 }
507 default:
508 break;
509 }
510 }
476 emulated_controller->SetStickParam(analog_id, param); 511 emulated_controller->SetStickParam(analog_id, param);
477 }, 512 },
478 InputCommon::Polling::InputType::Stick); 513 InputCommon::Polling::InputType::Stick);
@@ -1409,10 +1444,10 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1409} 1444}
1410 1445
1411void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1446void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1412 event->ignore();
1413 if (!input_setter || !event) { 1447 if (!input_setter || !event) {
1414 return; 1448 return;
1415 } 1449 }
1450 event->ignore();
1416 if (event->key() != Qt::Key_Escape) { 1451 if (event->key() != Qt::Key_Escape) {
1417 input_subsystem->GetKeyboard()->PressKey(event->key()); 1452 input_subsystem->GetKeyboard()->PressKey(event->key());
1418 } 1453 }
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 4340de304..27559c37b 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -2,16 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <sstream> 5#include <sstream>
7 6
8#include <QCloseEvent> 7#include <QCloseEvent>
9#include <QLabel>
10#include <QMessageBox> 8#include <QMessageBox>
11#include <QPushButton>
12#include <QRegularExpression>
13#include <QStringListModel> 9#include <QStringListModel>
14#include <QVBoxLayout>
15 10
16#include "common/logging/log.h" 11#include "common/logging/log.h"
17#include "common/settings.h" 12#include "common/settings.h"
diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h
index 8b707d2ff..91d1ae671 100644
--- a/src/yuzu/configuration/configure_motion_touch.h
+++ b/src/yuzu/configuration/configure_motion_touch.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <QDialog> 8#include <QDialog>
9#include "common/param_package.h"
10 9
11class QLabel; 10class QLabel;
12class QPushButton; 11class QPushButton;
diff --git a/src/yuzu/configuration/configure_network.cpp b/src/yuzu/configuration/configure_network.cpp
index 7020d2964..1f5799546 100644
--- a/src/yuzu/configuration/configure_network.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QGraphicsItem>
6#include <QtConcurrent/QtConcurrent> 5#include <QtConcurrent/QtConcurrent>
7#include "common/settings.h" 6#include "common/settings.h"
8#include "core/core.h" 7#include "core/core.h"
diff --git a/src/yuzu/configuration/configure_network.h b/src/yuzu/configuration/configure_network.h
index 8507c62eb..1d07d0b53 100644
--- a/src/yuzu/configuration/configure_network.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <QFutureWatcher>
9#include <QWidget> 8#include <QWidget>
10 9
11namespace Ui { 10namespace Ui {
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index f4cf25f05..55b2aa74f 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -12,17 +12,11 @@
12 12
13#include <QAbstractButton> 13#include <QAbstractButton>
14#include <QCheckBox> 14#include <QCheckBox>
15#include <QDialogButtonBox>
16#include <QHeaderView>
17#include <QMenu>
18#include <QPushButton> 15#include <QPushButton>
19#include <QStandardItemModel>
20#include <QString> 16#include <QString>
21#include <QTimer> 17#include <QTimer>
22#include <QTreeView>
23 18
24#include "common/fs/fs_util.h" 19#include "common/fs/fs_util.h"
25#include "common/fs/path_util.h"
26#include "core/core.h" 20#include "core/core.h"
27#include "core/file_sys/control_metadata.h" 21#include "core/file_sys/control_metadata.h"
28#include "core/file_sys/patch_manager.h" 22#include "core/file_sys/patch_manager.h"
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index c1a57d87b..b34b28577 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <vector>
10 9
11#include <QDialog> 10#include <QDialog>
12#include <QList> 11#include <QList>
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 65e615963..7893a85bb 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -24,7 +24,6 @@
24#include "yuzu/configuration/configure_input.h" 24#include "yuzu/configuration/configure_input.h"
25#include "yuzu/configuration/configure_per_game_addons.h" 25#include "yuzu/configuration/configure_per_game_addons.h"
26#include "yuzu/uisettings.h" 26#include "yuzu/uisettings.h"
27#include "yuzu/util/util.h"
28 27
29ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* parent) 28ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* parent)
30 : QWidget(parent), ui{std::make_unique<Ui::ConfigurePerGameAddons>()}, system{system_} { 29 : QWidget(parent), ui{std::make_unique<Ui::ConfigurePerGameAddons>()}, system{system_} {
@@ -48,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p
48 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); 47 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
49 item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); 48 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
50 49
50 tree_view->header()->setStretchLastSection(false);
51 tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
52 tree_view->header()->setMinimumSectionSize(150);
53
51 // We must register all custom types with the Qt Automoc system so that we are able to use it 54 // We must register all custom types with the Qt Automoc system so that we are able to use it
52 // with signals/slots. In this case, QList falls under the umbrella of custom types. 55 // with signals/slots. In this case, QList falls under the umbrella of custom types.
53 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 56 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@@ -139,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() {
139 item_model->appendRow(list_items.back()); 142 item_model->appendRow(list_items.back());
140 } 143 }
141 144
142 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); 145 tree_view->resizeColumnToContents(1);
143} 146}
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index d9f6dee4e..5442fe328 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -5,12 +5,10 @@
5#include <algorithm> 5#include <algorithm>
6#include <QFileDialog> 6#include <QFileDialog>
7#include <QGraphicsItem> 7#include <QGraphicsItem>
8#include <QGraphicsScene>
9#include <QHeaderView> 8#include <QHeaderView>
10#include <QMessageBox> 9#include <QMessageBox>
11#include <QStandardItemModel> 10#include <QStandardItemModel>
12#include <QTreeView> 11#include <QTreeView>
13#include <QVBoxLayout>
14#include "common/assert.h" 12#include "common/assert.h"
15#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
16#include "common/settings.h" 14#include "common/settings.h"
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
new file mode 100644
index 000000000..f1cdd4fed
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <QKeyEvent>
7#include <QMenu>
8#include <QTimer>
9
10#include "core/hid/emulated_devices.h"
11#include "core/hid/hid_core.h"
12#include "input_common/drivers/keyboard.h"
13#include "input_common/drivers/mouse.h"
14#include "input_common/main.h"
15#include "ui_configure_ringcon.h"
16#include "yuzu/bootmanager.h"
17#include "yuzu/configuration/config.h"
18#include "yuzu/configuration/configure_ringcon.h"
19
20const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
21 ConfigureRingController::analog_sub_buttons{{
22 "left",
23 "right",
24 }};
25
26namespace {
27
28QString GetKeyName(int key_code) {
29 switch (key_code) {
30 case Qt::Key_Shift:
31 return QObject::tr("Shift");
32 case Qt::Key_Control:
33 return QObject::tr("Ctrl");
34 case Qt::Key_Alt:
35 return QObject::tr("Alt");
36 case Qt::Key_Meta:
37 return {};
38 default:
39 return QKeySequence(key_code).toString();
40 }
41}
42
43QString GetButtonName(Common::Input::ButtonNames button_name) {
44 switch (button_name) {
45 case Common::Input::ButtonNames::ButtonLeft:
46 return QObject::tr("Left");
47 case Common::Input::ButtonNames::ButtonRight:
48 return QObject::tr("Right");
49 case Common::Input::ButtonNames::ButtonDown:
50 return QObject::tr("Down");
51 case Common::Input::ButtonNames::ButtonUp:
52 return QObject::tr("Up");
53 case Common::Input::ButtonNames::TriggerZ:
54 return QObject::tr("Z");
55 case Common::Input::ButtonNames::TriggerR:
56 return QObject::tr("R");
57 case Common::Input::ButtonNames::TriggerL:
58 return QObject::tr("L");
59 case Common::Input::ButtonNames::ButtonA:
60 return QObject::tr("A");
61 case Common::Input::ButtonNames::ButtonB:
62 return QObject::tr("B");
63 case Common::Input::ButtonNames::ButtonX:
64 return QObject::tr("X");
65 case Common::Input::ButtonNames::ButtonY:
66 return QObject::tr("Y");
67 case Common::Input::ButtonNames::ButtonStart:
68 return QObject::tr("Start");
69 case Common::Input::ButtonNames::L1:
70 return QObject::tr("L1");
71 case Common::Input::ButtonNames::L2:
72 return QObject::tr("L2");
73 case Common::Input::ButtonNames::L3:
74 return QObject::tr("L3");
75 case Common::Input::ButtonNames::R1:
76 return QObject::tr("R1");
77 case Common::Input::ButtonNames::R2:
78 return QObject::tr("R2");
79 case Common::Input::ButtonNames::R3:
80 return QObject::tr("R3");
81 case Common::Input::ButtonNames::Circle:
82 return QObject::tr("Circle");
83 case Common::Input::ButtonNames::Cross:
84 return QObject::tr("Cross");
85 case Common::Input::ButtonNames::Square:
86 return QObject::tr("Square");
87 case Common::Input::ButtonNames::Triangle:
88 return QObject::tr("Triangle");
89 case Common::Input::ButtonNames::Share:
90 return QObject::tr("Share");
91 case Common::Input::ButtonNames::Options:
92 return QObject::tr("Options");
93 default:
94 return QObject::tr("[undefined]");
95 }
96}
97
98void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
99 const std::string& button_name) {
100 // The poller returned a complete axis, so set all the buttons
101 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
102 analog_param = input_param;
103 return;
104 }
105 // Check if the current configuration has either no engine or an axis binding.
106 // Clears out the old binding and adds one with analog_from_button.
107 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
108 analog_param = {
109 {"engine", "analog_from_button"},
110 };
111 }
112 analog_param.Set(button_name, input_param.Serialize());
113}
114} // namespace
115
116ConfigureRingController::ConfigureRingController(QWidget* parent,
117 InputCommon::InputSubsystem* input_subsystem_,
118 Core::HID::HIDCore& hid_core_)
119 : QDialog(parent), timeout_timer(std::make_unique<QTimer>()),
120 poll_timer(std::make_unique<QTimer>()), input_subsystem{input_subsystem_},
121
122 ui(std::make_unique<Ui::ConfigureRingController>()) {
123 ui->setupUi(this);
124
125 analog_map_buttons = {
126 ui->buttonRingAnalogPull,
127 ui->buttonRingAnalogPush,
128 };
129
130 emulated_device = hid_core_.GetEmulatedDevices();
131 emulated_device->SaveCurrentConfig();
132 emulated_device->EnableConfiguration();
133
134 LoadConfiguration();
135
136 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
137 auto* const analog_button = analog_map_buttons[sub_button_id];
138
139 if (analog_button == nullptr) {
140 continue;
141 }
142
143 connect(analog_button, &QPushButton::clicked, [=, this] {
144 HandleClick(
145 analog_map_buttons[sub_button_id],
146 [=, this](const Common::ParamPackage& params) {
147 Common::ParamPackage param = emulated_device->GetRingParam();
148 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
149 emulated_device->SetRingParam(param);
150 },
151 InputCommon::Polling::InputType::Stick);
152 });
153
154 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
155
156 connect(analog_button, &QPushButton::customContextMenuRequested,
157 [=, this](const QPoint& menu_location) {
158 QMenu context_menu;
159 Common::ParamPackage param = emulated_device->GetRingParam();
160 context_menu.addAction(tr("Clear"), [&] {
161 emulated_device->SetRingParam({});
162 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
163 });
164 context_menu.addAction(tr("Invert axis"), [&] {
165 const bool invert_value = param.Get("invert_x", "+") == "-";
166 const std::string invert_str = invert_value ? "+" : "-";
167 param.Set("invert_x", invert_str);
168 emulated_device->SetRingParam(param);
169 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
170 ++sub_button_id) {
171 analog_map_buttons[sub_button_id]->setText(
172 AnalogToText(param, analog_sub_buttons[sub_button_id]));
173 }
174 });
175 context_menu.exec(
176 analog_map_buttons[sub_button_id]->mapToGlobal(menu_location));
177 });
178 }
179
180 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
181 Common::ParamPackage param = emulated_device->GetRingParam();
182 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
183 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
184 param.Set("deadzone", slider_value / 100.0f);
185 emulated_device->SetRingParam(param);
186 });
187
188 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
189 &ConfigureRingController::RestoreDefaults);
190
191 timeout_timer->setSingleShot(true);
192 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
193
194 connect(poll_timer.get(), &QTimer::timeout, [this] {
195 const auto& params = input_subsystem->GetNextInput();
196 if (params.Has("engine") && IsInputAcceptable(params)) {
197 SetPollingResult(params, false);
198 return;
199 }
200 });
201
202 resize(0, 0);
203}
204
205ConfigureRingController::~ConfigureRingController() {
206 emulated_device->DisableConfiguration();
207};
208
209void ConfigureRingController::changeEvent(QEvent* event) {
210 if (event->type() == QEvent::LanguageChange) {
211 RetranslateUI();
212 }
213
214 QDialog::changeEvent(event);
215}
216
217void ConfigureRingController::RetranslateUI() {
218 ui->retranslateUi(this);
219}
220
221void ConfigureRingController::UpdateUI() {
222 RetranslateUI();
223 const Common::ParamPackage param = emulated_device->GetRingParam();
224
225 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
226 auto* const analog_button = analog_map_buttons[sub_button_id];
227
228 if (analog_button == nullptr) {
229 continue;
230 }
231
232 analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
233 }
234
235 const auto deadzone_label = ui->labelRingAnalogDeadzone;
236 const auto deadzone_slider = ui->sliderRingAnalogDeadzone;
237
238 int slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
239 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
240 deadzone_slider->setValue(slider_value);
241}
242
243void ConfigureRingController::ApplyConfiguration() {
244 emulated_device->DisableConfiguration();
245 emulated_device->SaveCurrentConfig();
246 emulated_device->EnableConfiguration();
247}
248
249void ConfigureRingController::LoadConfiguration() {
250 UpdateUI();
251}
252
253void ConfigureRingController::RestoreDefaults() {
254 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
255 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
256 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string));
257 UpdateUI();
258}
259
260void ConfigureRingController::HandleClick(
261 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
262 InputCommon::Polling::InputType type) {
263 button->setText(tr("[waiting]"));
264 button->setFocus();
265
266 input_setter = new_input_setter;
267
268 input_subsystem->BeginMapping(type);
269
270 QWidget::grabMouse();
271 QWidget::grabKeyboard();
272
273 timeout_timer->start(2500); // Cancel after 2.5 seconds
274 poll_timer->start(25); // Check for new inputs every 25ms
275}
276
277void ConfigureRingController::SetPollingResult(const Common::ParamPackage& params, bool abort) {
278 timeout_timer->stop();
279 poll_timer->stop();
280 input_subsystem->StopMapping();
281
282 QWidget::releaseMouse();
283 QWidget::releaseKeyboard();
284
285 if (!abort) {
286 (*input_setter)(params);
287 }
288
289 UpdateUI();
290
291 input_setter = std::nullopt;
292}
293
294bool ConfigureRingController::IsInputAcceptable(const Common::ParamPackage& params) const {
295 return true;
296}
297
298void ConfigureRingController::mousePressEvent(QMouseEvent* event) {
299 if (!input_setter || !event) {
300 return;
301 }
302
303 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
304 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
305}
306
307void ConfigureRingController::keyPressEvent(QKeyEvent* event) {
308 if (!input_setter || !event) {
309 return;
310 }
311 event->ignore();
312 if (event->key() != Qt::Key_Escape) {
313 input_subsystem->GetKeyboard()->PressKey(event->key());
314 }
315}
316
317QString ConfigureRingController::ButtonToText(const Common::ParamPackage& param) {
318 if (!param.Has("engine")) {
319 return QObject::tr("[not set]");
320 }
321
322 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
323 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
324 const auto common_button_name = input_subsystem->GetButtonName(param);
325
326 // Retrieve the names from Qt
327 if (param.Get("engine", "") == "keyboard") {
328 const QString button_str = GetKeyName(param.Get("code", 0));
329 return QObject::tr("%1%2").arg(toggle, button_str);
330 }
331
332 if (common_button_name == Common::Input::ButtonNames::Invalid) {
333 return QObject::tr("[invalid]");
334 }
335
336 if (common_button_name == Common::Input::ButtonNames::Engine) {
337 return QString::fromStdString(param.Get("engine", ""));
338 }
339
340 if (common_button_name == Common::Input::ButtonNames::Value) {
341 if (param.Has("hat")) {
342 const QString hat = QString::fromStdString(param.Get("direction", ""));
343 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
344 }
345 if (param.Has("axis")) {
346 const QString axis = QString::fromStdString(param.Get("axis", ""));
347 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
348 }
349 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
350 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
351 const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
352 const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
353 return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
354 }
355 if (param.Has("motion")) {
356 const QString motion = QString::fromStdString(param.Get("motion", ""));
357 return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
358 }
359 if (param.Has("button")) {
360 const QString button = QString::fromStdString(param.Get("button", ""));
361 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
362 }
363 }
364
365 QString button_name = GetButtonName(common_button_name);
366 if (param.Has("hat")) {
367 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
368 }
369 if (param.Has("axis")) {
370 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
371 }
372 if (param.Has("motion")) {
373 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
374 }
375 if (param.Has("button")) {
376 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
377 }
378
379 return QObject::tr("[unknown]");
380}
381
382QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param,
383 const std::string& dir) {
384 if (!param.Has("engine")) {
385 return QObject::tr("[not set]");
386 }
387
388 if (param.Get("engine", "") == "analog_from_button") {
389 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
390 }
391
392 if (!param.Has("axis_x") || !param.Has("axis_y")) {
393 return QObject::tr("[unknown]");
394 }
395
396 const auto engine_str = param.Get("engine", "");
397 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
398 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
399 const bool invert_x = param.Get("invert_x", "+") == "-";
400 const bool invert_y = param.Get("invert_y", "+") == "-";
401
402 if (dir == "modifier") {
403 return QObject::tr("[unused]");
404 }
405
406 if (dir == "left") {
407 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
408 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
409 }
410 if (dir == "right") {
411 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
412 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
413 }
414 if (dir == "up") {
415 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
416 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
417 }
418 if (dir == "down") {
419 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
420 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
421 }
422
423 return QObject::tr("[unknown]");
424} \ No newline at end of file
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
new file mode 100644
index 000000000..cf9e54f09
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -0,0 +1,85 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <QDialog>
10
11namespace InputCommon {
12class InputSubsystem;
13} // namespace InputCommon
14
15namespace Core::HID {
16class HIDCore;
17class EmulatedDevices;
18} // namespace Core::HID
19
20namespace Ui {
21class ConfigureRingController;
22} // namespace Ui
23
24class ConfigureRingController : public QDialog {
25 Q_OBJECT
26
27public:
28 explicit ConfigureRingController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_,
29 Core::HID::HIDCore& hid_core_);
30 ~ConfigureRingController() override;
31
32 void ApplyConfiguration();
33
34private:
35 void changeEvent(QEvent* event) override;
36 void RetranslateUI();
37
38 void UpdateUI();
39
40 /// Load configuration settings.
41 void LoadConfiguration();
42
43 /// Restore all buttons to their default values.
44 void RestoreDefaults();
45
46 /// Called when the button was pressed.
47 void HandleClick(QPushButton* button,
48 std::function<void(const Common::ParamPackage&)> new_input_setter,
49 InputCommon::Polling::InputType type);
50
51 /// Finish polling and configure input using the input_setter.
52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
53
54 /// Checks whether a given input can be accepted.
55 bool IsInputAcceptable(const Common::ParamPackage& params) const;
56
57 /// Handle mouse button press events.
58 void mousePressEvent(QMouseEvent* event) override;
59
60 /// Handle key press events.
61 void keyPressEvent(QKeyEvent* event) override;
62
63 QString ButtonToText(const Common::ParamPackage& param);
64
65 QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
66
67 static constexpr int ANALOG_SUB_BUTTONS_NUM = 2;
68
69 // A group of four QPushButtons represent one analog input. The buttons each represent left,
70 // right, respectively.
71 std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM> analog_map_buttons;
72
73 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
74
75 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer;
77
78 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80
81 InputCommon::InputSubsystem* input_subsystem;
82 Core::HID::EmulatedDevices* emulated_device;
83
84 std::unique_ptr<Ui::ConfigureRingController> ui;
85};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
new file mode 100644
index 000000000..9ec634dd4
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -0,0 +1,278 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureRingController</class>
4 <widget class="QDialog" name="ConfigureRingController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Ring Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>10</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title">
52 <string>Ring Sensor Parameters</string>
53 </property>
54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
63 </property>
64 <property name="leftMargin">
65 <number>3</number>
66 </property>
67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing">
79 <number>3</number>
80 </property>
81 <item alignment="Qt::AlignHCenter">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
83 <property name="title">
84 <string>Pull</string>
85 </property>
86 <property name="alignment">
87 <set>Qt::AlignCenter</set>
88 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
90 <property name="spacing">
91 <number>3</number>
92 </property>
93 <property name="leftMargin">
94 <number>3</number>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property>
99 <property name="rightMargin">
100 <number>3</number>
101 </property>
102 <property name="bottomMargin">
103 <number>3</number>
104 </property>
105 <item>
106 <widget class="QPushButton" name="buttonRingAnalogPull">
107 <property name="minimumSize">
108 <size>
109 <width>68</width>
110 <height>0</height>
111 </size>
112 </property>
113 <property name="maximumSize">
114 <size>
115 <width>68</width>
116 <height>16777215</height>
117 </size>
118 </property>
119 <property name="styleSheet">
120 <string notr="true">min-width: 68px;</string>
121 </property>
122 <property name="text">
123 <string>Pull</string>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </widget>
129 </item>
130 <item alignment="Qt::AlignHCenter">
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
132 <property name="title">
133 <string>Push</string>
134 </property>
135 <property name="alignment">
136 <set>Qt::AlignCenter</set>
137 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
139 <property name="spacing">
140 <number>3</number>
141 </property>
142 <property name="leftMargin">
143 <number>3</number>
144 </property>
145 <property name="topMargin">
146 <number>3</number>
147 </property>
148 <property name="rightMargin">
149 <number>3</number>
150 </property>
151 <property name="bottomMargin">
152 <number>3</number>
153 </property>
154 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush">
156 <property name="minimumSize">
157 <size>
158 <width>68</width>
159 <height>0</height>
160 </size>
161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text">
172 <string>Push</string>
173 </property>
174 </widget>
175 </item>
176 </layout>
177 </widget>
178 </item>
179 </layout>
180 </item>
181 <item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
183 <property name="spacing">
184 <number>3</number>
185 </property>
186 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum>
188 </property>
189 <property name="leftMargin">
190 <number>0</number>
191 </property>
192 <property name="topMargin">
193 <number>10</number>
194 </property>
195 <property name="rightMargin">
196 <number>0</number>
197 </property>
198 <property name="bottomMargin">
199 <number>3</number>
200 </property>
201 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
203 <item>
204 <widget class="QLabel" name="labelRingAnalogDeadzone">
205 <property name="text">
206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item>
225 </layout>
226 </item>
227 </layout>
228 </widget>
229 </item>
230 <item>
231 <spacer name="verticalSpacer">
232 <property name="orientation">
233 <enum>Qt::Vertical</enum>
234 </property>
235 <property name="sizeHint" stdset="0">
236 <size>
237 <width>20</width>
238 <height>40</height>
239 </size>
240 </property>
241 </spacer>
242 </item>
243 <item>
244 <layout class="QHBoxLayout" name="horizontalLayout">
245 <item>
246 <widget class="QPushButton" name="restore_defaults_button">
247 <property name="text">
248 <string>Restore Defaults</string>
249 </property>
250 </widget>
251 </item>
252 <item>
253 <widget class="QDialogButtonBox" name="buttonBox">
254 <property name="standardButtons">
255 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 </layout>
262 </widget>
263 <resources/>
264 <connections>
265 <connection>
266 <sender>buttonBox</sender>
267 <signal>accepted()</signal>
268 <receiver>ConfigureRingController</receiver>
269 <slot>accept()</slot>
270 </connection>
271 <connection>
272 <sender>buttonBox</sender>
273 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot>
276 </connection>
277 </connections>
278</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 56c762d64..19aa589f9 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,14 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <chrono> 5#include <chrono>
7#include <optional> 6#include <optional>
8 7
9#include <QFileDialog> 8#include <QFileDialog>
10#include <QGraphicsItem> 9#include <QGraphicsItem>
11#include <QMessageBox> 10#include <QMessageBox>
12#include "common/assert.h"
13#include "common/settings.h" 11#include "common/settings.h"
14#include "core/core.h" 12#include "core/core.h"
15#include "core/hle/service/time/time_manager.h" 13#include "core/hle/service/time/time_manager.h"
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index bb24c9ae7..5a1633192 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8 8
9#include <QList>
10#include <QWidget> 9#include <QWidget>
11 10
12namespace Core { 11namespace Core {
diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h
index 1546bf16f..23a3673a7 100644
--- a/src/yuzu/configuration/configure_tas.h
+++ b/src/yuzu/configuration/configure_tas.h
@@ -6,6 +6,8 @@
6 6
7#include <QDialog> 7#include <QDialog>
8 8
9class QLineEdit;
10
9namespace Ui { 11namespace Ui {
10class ConfigureTas; 12class ConfigureTas;
11} 13}
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index 211a00217..c17da6fd1 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -6,7 +6,6 @@
6#include <QKeyEvent> 6#include <QKeyEvent>
7#include <QMessageBox> 7#include <QMessageBox>
8#include <QMouseEvent> 8#include <QMouseEvent>
9#include <QResizeEvent>
10#include <QStandardItemModel> 9#include <QStandardItemModel>
11#include <QTimer> 10#include <QTimer>
12#include "common/param_package.h" 11#include "common/param_package.h"
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index 779b6401c..d134ed02f 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -2,12 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <unordered_map>
7
8#include <fmt/format.h>
9
10#include "common/param_package.h"
11#include "common/settings.h" 5#include "common/settings.h"
12#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
index a567bd5a9..7353aa77b 100644
--- a/src/yuzu/configuration/input_profiles.h
+++ b/src/yuzu/configuration/input_profiles.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <string_view>
9#include <unordered_map> 8#include <unordered_map>
10 9
11namespace Core { 10namespace Core {
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 2d1a2d9cb..8f486a131 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -7,9 +7,7 @@
7 7
8#include "yuzu/debugger/wait_tree.h" 8#include "yuzu/debugger/wait_tree.h"
9#include "yuzu/uisettings.h" 9#include "yuzu/uisettings.h"
10#include "yuzu/util/util.h"
11 10
12#include "common/assert.h"
13#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
14#include "core/core.h" 12#include "core/core.h"
15#include "core/hle/kernel/k_class_token.h" 13#include "core/hle/kernel/k_class_token.h"
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index ea4d2e299..4a36dfc48 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <vector> 9#include <vector>
10 10
11#include <QAbstractItemModel>
12#include <QDockWidget> 11#include <QDockWidget>
13#include <QTreeView> 12#include <QTreeView>
14 13
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index e3661b390..4a6d74a7e 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -10,10 +10,10 @@
10#include <QJsonArray> 10#include <QJsonArray>
11#include <QJsonDocument> 11#include <QJsonDocument>
12#include <QJsonObject> 12#include <QJsonObject>
13#include <QKeyEvent>
14#include <QList> 13#include <QList>
15#include <QMenu> 14#include <QMenu>
16#include <QThreadPool> 15#include <QThreadPool>
16#include <QToolButton>
17#include <fmt/format.h> 17#include <fmt/format.h>
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/logging/log.h" 19#include "common/logging/log.h"
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index a94ea1477..d19dbe4b0 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -5,16 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <QFileSystemWatcher> 7#include <QFileSystemWatcher>
8#include <QHBoxLayout>
9#include <QLabel> 8#include <QLabel>
10#include <QLineEdit> 9#include <QLineEdit>
11#include <QList> 10#include <QList>
12#include <QModelIndex>
13#include <QSettings>
14#include <QStandardItem>
15#include <QStandardItemModel> 11#include <QStandardItemModel>
16#include <QString> 12#include <QString>
17#include <QToolButton>
18#include <QTreeView> 13#include <QTreeView>
19#include <QVBoxLayout> 14#include <QVBoxLayout>
20#include <QVector> 15#include <QVector>
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 9dc3cc7c3..f2a986ed8 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -11,7 +11,6 @@
11 11
12#include <QCoreApplication> 12#include <QCoreApplication>
13#include <QFileInfo> 13#include <QFileInfo>
14#include <QImage>
15#include <QObject> 14#include <QObject>
16#include <QStandardItem> 15#include <QStandardItem>
17#include <QString> 16#include <QString>
@@ -165,8 +164,8 @@ public:
165 } 164 }
166 const CompatStatus& status = iterator->second; 165 const CompatStatus& status = iterator->second;
167 setData(compatibility, CompatNumberRole); 166 setData(compatibility, CompatNumberRole);
168 setText(QObject::tr(status.text)); 167 setText(tr(status.text));
169 setToolTip(QObject::tr(status.tooltip)); 168 setToolTip(tr(status.tooltip));
170 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); 169 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
171 } 170 }
172 171
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index fd92b36df..5e9b3eee8 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -23,7 +23,6 @@
23#include "core/file_sys/patch_manager.h" 23#include "core/file_sys/patch_manager.h"
24#include "core/file_sys/registered_cache.h" 24#include "core/file_sys/registered_cache.h"
25#include "core/file_sys/submission_package.h" 25#include "core/file_sys/submission_package.h"
26#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h" 26#include "core/loader/loader.h"
28#include "yuzu/compatibility_list.h" 27#include "yuzu/compatibility_list.h"
29#include "yuzu/game_list.h" 28#include "yuzu/game_list.h"
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 1383e9fbc..2e0f89cbd 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -5,18 +5,14 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <map>
9#include <memory> 8#include <memory>
10#include <string> 9#include <string>
11#include <unordered_map>
12 10
13#include <QList> 11#include <QList>
14#include <QObject> 12#include <QObject>
15#include <QRunnable> 13#include <QRunnable>
16#include <QString> 14#include <QString>
17#include <QVector>
18 15
19#include "common/common_types.h"
20#include "yuzu/compatibility_list.h" 16#include "yuzu/compatibility_list.h"
21 17
22namespace Core { 18namespace Core {
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 6ed9611c7..d59aa5d18 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <sstream> 5#include <sstream>
6#include <QKeySequence>
7#include <QShortcut> 6#include <QShortcut>
8#include <QTreeWidgetItem> 7#include <QTreeWidgetItem>
9#include <QtGlobal> 8#include <QtGlobal>
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 06b0b1874..55088bd87 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -5,7 +5,6 @@
5#include <QCheckBox> 5#include <QCheckBox>
6#include <QDialogButtonBox> 6#include <QDialogButtonBox>
7#include <QFileInfo> 7#include <QFileInfo>
8#include <QHBoxLayout>
9#include <QLabel> 8#include <QLabel>
10#include <QListWidget> 9#include <QListWidget>
11#include <QVBoxLayout> 10#include <QVBoxLayout>
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index b001b8c23..cd2148deb 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -6,19 +6,12 @@
6#include <QBuffer> 6#include <QBuffer>
7#include <QByteArray> 7#include <QByteArray>
8#include <QGraphicsOpacityEffect> 8#include <QGraphicsOpacityEffect>
9#include <QHBoxLayout>
10#include <QIODevice> 9#include <QIODevice>
11#include <QImage> 10#include <QImage>
12#include <QLabel>
13#include <QPainter> 11#include <QPainter>
14#include <QPalette>
15#include <QPixmap> 12#include <QPixmap>
16#include <QProgressBar>
17#include <QPropertyAnimation> 13#include <QPropertyAnimation>
18#include <QStyleOption> 14#include <QStyleOption>
19#include <QTime>
20#include <QtConcurrent/QtConcurrentRun>
21#include "common/logging/log.h"
22#include "core/frontend/framebuffer_layout.h" 15#include "core/frontend/framebuffer_layout.h"
23#include "core/loader/loader.h" 16#include "core/loader/loader.h"
24#include "ui_loading_screen.h" 17#include "ui_loading_screen.h"
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b3a8da0ea..5e26aad29 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -20,11 +20,11 @@
20#include "configuration/configure_input.h" 20#include "configuration/configure_input.h"
21#include "configuration/configure_per_game.h" 21#include "configuration/configure_per_game.h"
22#include "configuration/configure_tas.h" 22#include "configuration/configure_tas.h"
23#include "configuration/configure_vibration.h"
24#include "core/file_sys/vfs.h" 23#include "core/file_sys/vfs.h"
25#include "core/file_sys/vfs_real.h" 24#include "core/file_sys/vfs_real.h"
26#include "core/frontend/applets/controller.h" 25#include "core/frontend/applets/controller.h"
27#include "core/frontend/applets/general_frontend.h" 26#include "core/frontend/applets/general_frontend.h"
27#include "core/frontend/applets/mii_edit.h"
28#include "core/frontend/applets/software_keyboard.h" 28#include "core/frontend/applets/software_keyboard.h"
29#include "core/hid/emulated_controller.h" 29#include "core/hid/emulated_controller.h"
30#include "core/hid/hid_core.h" 30#include "core/hid/hid_core.h"
@@ -53,8 +53,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
53#include <QClipboard> 53#include <QClipboard>
54#include <QDesktopServices> 54#include <QDesktopServices>
55#include <QDesktopWidget> 55#include <QDesktopWidget>
56#include <QDialogButtonBox>
57#include <QDir>
58#include <QFile> 56#include <QFile>
59#include <QFileDialog> 57#include <QFileDialog>
60#include <QInputDialog> 58#include <QInputDialog>
@@ -76,11 +74,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
76#include <fmt/format.h> 74#include <fmt/format.h>
77#include "common/detached_tasks.h" 75#include "common/detached_tasks.h"
78#include "common/fs/fs.h" 76#include "common/fs/fs.h"
79#include "common/fs/fs_paths.h"
80#include "common/fs/path_util.h" 77#include "common/fs/path_util.h"
81#include "common/literals.h" 78#include "common/literals.h"
82#include "common/logging/backend.h" 79#include "common/logging/backend.h"
83#include "common/logging/filter.h"
84#include "common/logging/log.h" 80#include "common/logging/log.h"
85#include "common/memory_detect.h" 81#include "common/memory_detect.h"
86#include "common/microprofile.h" 82#include "common/microprofile.h"
@@ -156,7 +152,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
156} 152}
157#endif 153#endif
158 154
159constexpr int default_mouse_timeout = 2500; 155constexpr int default_mouse_hide_timeout = 2500;
156constexpr int default_mouse_center_timeout = 10;
160 157
161/** 158/**
162 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 159 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
@@ -249,9 +246,9 @@ GMainWindow::GMainWindow()
249#ifdef ARCHITECTURE_x86_64 246#ifdef ARCHITECTURE_x86_64
250 const auto& caps = Common::GetCPUCaps(); 247 const auto& caps = Common::GetCPUCaps();
251 std::string cpu_string = caps.cpu_string; 248 std::string cpu_string = caps.cpu_string;
252 if (caps.avx || caps.avx2 || caps.avx512) { 249 if (caps.avx || caps.avx2 || caps.avx512f) {
253 cpu_string += " | AVX"; 250 cpu_string += " | AVX";
254 if (caps.avx512) { 251 if (caps.avx512f) {
255 cpu_string += "512"; 252 cpu_string += "512";
256 } else if (caps.avx2) { 253 } else if (caps.avx2) {
257 cpu_string += '2'; 254 cpu_string += '2';
@@ -291,13 +288,14 @@ GMainWindow::GMainWindow()
291 ui->menubar->setCursor(QCursor()); 288 ui->menubar->setCursor(QCursor());
292 statusBar()->setCursor(QCursor()); 289 statusBar()->setCursor(QCursor());
293 290
294 mouse_hide_timer.setInterval(default_mouse_timeout); 291 mouse_hide_timer.setInterval(default_mouse_hide_timeout);
295 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); 292 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
296 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); 293 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
297 294
298 MigrateConfigFiles(); 295 mouse_center_timer.setInterval(default_mouse_center_timeout);
296 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
299 297
300 ui->action_Fullscreen->setChecked(false); 298 MigrateConfigFiles();
301 299
302#if defined(HAVE_SDL2) && !defined(_WIN32) 300#if defined(HAVE_SDL2) && !defined(_WIN32)
303 SDL_InitSubSystem(SDL_INIT_VIDEO); 301 SDL_InitSubSystem(SDL_INIT_VIDEO);
@@ -316,17 +314,20 @@ GMainWindow::GMainWindow()
316 } 314 }
317 315
318 QString game_path; 316 QString game_path;
317 bool has_gamepath = false;
318 bool is_fullscreen = false;
319 319
320 for (int i = 1; i < args.size(); ++i) { 320 for (int i = 1; i < args.size(); ++i) {
321 // Preserves drag/drop functionality 321 // Preserves drag/drop functionality
322 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { 322 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
323 game_path = args[1]; 323 game_path = args[1];
324 has_gamepath = true;
324 break; 325 break;
325 } 326 }
326 327
327 // Launch game in fullscreen mode 328 // Launch game in fullscreen mode
328 if (args[i] == QStringLiteral("-f")) { 329 if (args[i] == QStringLiteral("-f")) {
329 ui->action_Fullscreen->setChecked(true); 330 is_fullscreen = true;
330 continue; 331 continue;
331 } 332 }
332 333
@@ -369,9 +370,15 @@ GMainWindow::GMainWindow()
369 } 370 }
370 371
371 game_path = args[++i]; 372 game_path = args[++i];
373 has_gamepath = true;
372 } 374 }
373 } 375 }
374 376
377 // Override fullscreen setting if gamepath or argument is provided
378 if (has_gamepath || is_fullscreen) {
379 ui->action_Fullscreen->setChecked(is_fullscreen);
380 }
381
375 if (!game_path.isEmpty()) { 382 if (!game_path.isEmpty()) {
376 BootGame(game_path); 383 BootGame(game_path);
377 } 384 }
@@ -586,7 +593,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
586#ifdef YUZU_USE_QT_WEB_ENGINE 593#ifdef YUZU_USE_QT_WEB_ENGINE
587 594
588 // Raw input breaks with the web applet, Disable web applets if enabled 595 // Raw input breaks with the web applet, Disable web applets if enabled
589 if (disable_web_applet || Settings::values.enable_raw_input) { 596 if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
590 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, 597 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed,
591 "http://localhost/"); 598 "http://localhost/");
592 return; 599 return;
@@ -651,12 +658,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
651 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] { 658 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] {
652 const auto result = QMessageBox::warning( 659 const auto result = QMessageBox::warning(
653 this, tr("Disable Web Applet"), 660 this, tr("Disable Web Applet"),
654 tr("Disabling the web applet will cause it to not be shown again for the rest of the " 661 tr("Disabling the web applet can lead to undefined behavior and should only be used "
655 "emulated session. This can lead to undefined behavior and should only be used with " 662 "with Super Mario 3D All-Stars. Are you sure you want to disable the web "
656 "Super Mario 3D All-Stars. Are you sure you want to disable the web applet?"), 663 "applet?\n(This can be re-enabled in the Debug settings.)"),
657 QMessageBox::Yes | QMessageBox::No); 664 QMessageBox::Yes | QMessageBox::No);
658 if (result == QMessageBox::Yes) { 665 if (result == QMessageBox::Yes) {
659 disable_web_applet = true; 666 UISettings::values.disable_web_applet = true;
660 web_browser_view.SetFinished(true); 667 web_browser_view.SetFinished(true);
661 } 668 }
662 }); 669 });
@@ -1284,6 +1291,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1284 system->SetAppletFrontendSet({ 1291 system->SetAppletFrontendSet({
1285 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1292 std::make_unique<QtControllerSelector>(*this), // Controller Selector
1286 std::make_unique<QtErrorDisplay>(*this), // Error Display 1293 std::make_unique<QtErrorDisplay>(*this), // Error Display
1294 nullptr, // Mii Editor
1287 nullptr, // Parental Controls 1295 nullptr, // Parental Controls
1288 nullptr, // Photo Viewer 1296 nullptr, // Photo Viewer
1289 std::make_unique<QtProfileSelector>(*this), // Profile Selector 1297 std::make_unique<QtProfileSelector>(*this), // Profile Selector
@@ -3297,10 +3305,26 @@ void GMainWindow::ShowMouseCursor() {
3297 } 3305 }
3298} 3306}
3299 3307
3308void GMainWindow::CenterMouseCursor() {
3309 if (emu_thread == nullptr || !Settings::values.mouse_panning) {
3310 mouse_center_timer.stop();
3311 return;
3312 }
3313 if (!this->isActiveWindow()) {
3314 mouse_center_timer.stop();
3315 return;
3316 }
3317 const int center_x = render_window->width() / 2;
3318 const int center_y = render_window->height() / 2;
3319
3320 QCursor::setPos(mapToGlobal({center_x, center_y}));
3321}
3322
3300void GMainWindow::OnMouseActivity() { 3323void GMainWindow::OnMouseActivity() {
3301 if (!Settings::values.mouse_panning) { 3324 if (!Settings::values.mouse_panning) {
3302 ShowMouseCursor(); 3325 ShowMouseCursor();
3303 } 3326 }
3327 mouse_center_timer.stop();
3304} 3328}
3305 3329
3306void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) { 3330void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
@@ -3573,6 +3597,22 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
3573 AcceptDropEvent(event); 3597 AcceptDropEvent(event);
3574} 3598}
3575 3599
3600void GMainWindow::leaveEvent(QEvent* event) {
3601 if (Settings::values.mouse_panning) {
3602 const QRect& rect = geometry();
3603 QPoint position = QCursor::pos();
3604
3605 qint32 x = qBound(rect.left(), position.x(), rect.right());
3606 qint32 y = qBound(rect.top(), position.y(), rect.bottom());
3607 // Only start the timer if the mouse has left the window bound.
3608 // The leave event is also triggered when the window looses focus.
3609 if (x != position.x() || y != position.y()) {
3610 mouse_center_timer.start();
3611 }
3612 event->accept();
3613 }
3614}
3615
3576bool GMainWindow::ConfirmChangeGame() { 3616bool GMainWindow::ConfirmChangeGame() {
3577 if (emu_thread == nullptr) 3617 if (emu_thread == nullptr)
3578 return true; 3618 return true;
@@ -3648,6 +3688,14 @@ void GMainWindow::UpdateUITheme() {
3648 setStyleSheet({}); 3688 setStyleSheet({});
3649 } 3689 }
3650 3690
3691 QPalette new_pal(qApp->palette());
3692 if (UISettings::IsDarkTheme()) {
3693 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3694 } else {
3695 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3696 }
3697 qApp->setPalette(new_pal);
3698
3651 QIcon::setThemeName(current_theme); 3699 QIcon::setThemeName(current_theme);
3652 QIcon::setThemeSearchPaths(theme_paths); 3700 QIcon::setThemeSearchPaths(theme_paths);
3653} 3701}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6a35b9e3d..b399e9b01 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -6,14 +6,12 @@
6 6
7#include <memory> 7#include <memory>
8#include <optional> 8#include <optional>
9#include <unordered_map>
10 9
11#include <QMainWindow> 10#include <QMainWindow>
12#include <QTimer> 11#include <QTimer>
13#include <QTranslator> 12#include <QTranslator>
14 13
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "core/hle/service/acc/profile_manager.h"
17#include "yuzu/compatibility_list.h" 15#include "yuzu/compatibility_list.h"
18#include "yuzu/hotkeys.h" 16#include "yuzu/hotkeys.h"
19 17
@@ -330,6 +328,7 @@ private:
330 void UpdateUISettings(); 328 void UpdateUISettings();
331 void HideMouseCursor(); 329 void HideMouseCursor();
332 void ShowMouseCursor(); 330 void ShowMouseCursor();
331 void CenterMouseCursor();
333 void OpenURL(const QUrl& url); 332 void OpenURL(const QUrl& url);
334 void LoadTranslation(); 333 void LoadTranslation();
335 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 334 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
@@ -374,6 +373,7 @@ private:
374 bool auto_paused = false; 373 bool auto_paused = false;
375 bool auto_muted = false; 374 bool auto_muted = false;
376 QTimer mouse_hide_timer; 375 QTimer mouse_hide_timer;
376 QTimer mouse_center_timer;
377 377
378 // FS 378 // FS
379 std::shared_ptr<FileSys::VfsFilesystem> vfs; 379 std::shared_ptr<FileSys::VfsFilesystem> vfs;
@@ -400,9 +400,6 @@ private:
400 // Last game booted, used for multi-process apps 400 // Last game booted, used for multi-process apps
401 QString last_filename_booted; 401 QString last_filename_booted;
402 402
403 // Disables the web applet for the rest of the emulated session
404 bool disable_web_applet{};
405
406 // Applets 403 // Applets
407 QtSoftwareKeyboardDialog* software_keyboard = nullptr; 404 QtSoftwareKeyboardDialog* software_keyboard = nullptr;
408 405
@@ -423,4 +420,5 @@ protected:
423 void dropEvent(QDropEvent* event) override; 420 void dropEvent(QDropEvent* event) override;
424 void dragEnterEvent(QDragEnterEvent* event) override; 421 void dragEnterEvent(QDragEnterEvent* event) override;
425 void dragMoveEvent(QDragMoveEvent* event) override; 422 void dragMoveEvent(QDragMoveEvent* event) override;
423 void leaveEvent(QEvent* event) override;
426}; 424};
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 21683576c..f683b80f7 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -15,6 +15,14 @@ const Themes themes{{
15 {"Midnight Blue Colorful", "colorful_midnight_blue"}, 15 {"Midnight Blue Colorful", "colorful_midnight_blue"},
16}}; 16}};
17 17
18bool IsDarkTheme() {
19 const auto& theme = UISettings::values.theme;
20 return theme == QStringLiteral("qdarkstyle") ||
21 theme == QStringLiteral("qdarkstyle_midnight_blue") ||
22 theme == QStringLiteral("colorful_dark") ||
23 theme == QStringLiteral("colorful_midnight_blue");
24}
25
18Values values = {}; 26Values values = {};
19 27
20} // namespace UISettings 28} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 06e8b46da..15ba9ea17 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -17,6 +17,8 @@
17 17
18namespace UISettings { 18namespace UISettings {
19 19
20bool IsDarkTheme();
21
20struct ContextualShortcut { 22struct ContextualShortcut {
21 QString keyseq; 23 QString keyseq;
22 QString controller_keyseq; 24 QString controller_keyseq;
@@ -114,6 +116,7 @@ struct Values {
114 116
115 bool configuration_applied; 117 bool configuration_applied;
116 bool reset_to_defaults; 118 bool reset_to_defaults;
119 Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"};
117}; 120};
118 121
119extern Values values; 122extern Values values;
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
index c2b13123d..9a1868cae 100644
--- a/src/yuzu/util/controller_navigation.cpp
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -39,7 +39,7 @@ void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_b
39} 39}
40 40
41void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { 41void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
42 std::lock_guard lock{mutex}; 42 std::scoped_lock lock{mutex};
43 if (!Settings::values.controller_navigation) { 43 if (!Settings::values.controller_navigation) {
44 return; 44 return;
45 } 45 }
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index d8a140ff3..014c943c7 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <atomic> 7#include <atomic>
9#include <memory> 8#include <memory>
10#include <thread> 9#include <thread>
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 74fc24972..c8901f2df 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -45,7 +45,7 @@ if (YUZU_USE_EXTERNAL_SDL2)
45endif() 45endif()
46 46
47if(UNIX AND NOT APPLE) 47if(UNIX AND NOT APPLE)
48 install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 48 install(TARGETS yuzu-cmd)
49endif() 49endif()
50 50
51if (MSVC) 51if (MSVC)
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index b74411c84..fc16f0f0c 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <optional>
6#include <sstream> 7#include <sstream>
7 8
8// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 9// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
@@ -20,7 +21,6 @@
20#include "common/fs/fs.h" 21#include "common/fs/fs.h"
21#include "common/fs/path_util.h" 22#include "common/fs/path_util.h"
22#include "common/logging/log.h" 23#include "common/logging/log.h"
23#include "common/param_package.h"
24#include "common/settings.h" 24#include "common/settings.h"
25#include "core/hle/service/acc/profile_manager.h" 25#include "core/hle/service/acc/profile_manager.h"
26#include "input_common/main.h" 26#include "input_common/main.h"
@@ -29,11 +29,12 @@
29 29
30namespace FS = Common::FS; 30namespace FS = Common::FS;
31 31
32Config::Config() { 32const std::filesystem::path default_config_path =
33 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 33 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
34 sdl2_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
35 sdl2_config = std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc));
36 34
35Config::Config(std::optional<std::filesystem::path> config_path)
36 : sdl2_config_loc{config_path.value_or(default_config_path)},
37 sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} {
37 Reload(); 38 Reload();
38} 39}
39 40
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
index 1ee932be2..f61ba23ec 100644
--- a/src/yuzu_cmd/config.h
+++ b/src/yuzu_cmd/config.h
@@ -6,6 +6,7 @@
6 6
7#include <filesystem> 7#include <filesystem>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10 11
11#include "common/settings.h" 12#include "common/settings.h"
@@ -13,14 +14,14 @@
13class INIReader; 14class INIReader;
14 15
15class Config { 16class Config {
16 std::unique_ptr<INIReader> sdl2_config;
17 std::filesystem::path sdl2_config_loc; 17 std::filesystem::path sdl2_config_loc;
18 std::unique_ptr<INIReader> sdl2_config;
18 19
19 bool LoadINI(const std::string& default_contents = "", bool retry = true); 20 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues(); 21 void ReadValues();
21 22
22public: 23public:
23 Config(); 24 explicit Config(std::optional<std::filesystem::path> config_path);
24 ~Config(); 25 ~Config();
25 26
26 void Reload(); 27 void Reload();
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 3ac1440c9..f34d6b728 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -124,7 +124,11 @@ keyboard_enabled =
124[Core] 124[Core]
125# Whether to use multi-core for CPU emulation 125# Whether to use multi-core for CPU emulation
126# 0: Disabled, 1 (default): Enabled 126# 0: Disabled, 1 (default): Enabled
127use_multi_core= 127use_multi_core =
128
129# Enable extended guest system memory layout (6GB DRAM)
130# 0 (default): Disabled, 1: Enabled
131use_extended_memory_layout =
128 132
129[Cpu] 133[Cpu]
130# Adjusts various optimizations. 134# Adjusts various optimizations.
@@ -338,12 +342,6 @@ fps_cap =
338# null: No audio output 342# null: No audio output
339output_engine = 343output_engine =
340 344
341# Whether or not to enable the audio-stretching post-processing effect.
342# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
343# at the cost of increasing audio latency.
344# 0: No, 1 (default): Yes
345enable_audio_stretching =
346
347# Which audio device to use. 345# Which audio device to use.
348# auto (default): Auto-select 346# auto (default): Auto-select
349output_device = 347output_device =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 57f807826..ae2e62dc5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -123,14 +123,15 @@ void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
123} 123}
124 124
125void EmuWindow_SDL2::Fullscreen() { 125void EmuWindow_SDL2::Fullscreen() {
126 SDL_DisplayMode display_mode;
126 switch (Settings::values.fullscreen_mode.GetValue()) { 127 switch (Settings::values.fullscreen_mode.GetValue()) {
127 case Settings::FullscreenMode::Exclusive: 128 case Settings::FullscreenMode::Exclusive:
128 // Set window size to render size before entering fullscreen -- SDL does not resize to 129 // Set window size to render size before entering fullscreen -- SDL2 does not resize window
129 // display dimensions in this mode. 130 // to display dimensions automatically in this mode.
130 // TODO: Multiply the window size by resolution_factor (for both docked modes) 131 if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
131 if (Settings::values.use_docked_mode) { 132 SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
132 SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, 133 } else {
133 Layout::ScreenDocked::Height); 134 LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
134 } 135 }
135 136
136 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { 137 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 0af002693..9746585f5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <utility> 7#include <utility>
9#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
10 9
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 70db865ec..bbe90eb77 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -11,7 +11,6 @@
11 11
12#include <fmt/format.h> 12#include <fmt/format.h>
13#include <glad/glad.h> 13#include <glad/glad.h>
14#include "common/assert.h"
15#include "common/logging/log.h" 14#include "common/logging/log.h"
16#include "common/scm_rev.h" 15#include "common/scm_rev.h"
17#include "common/settings.h" 16#include "common/settings.h"
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index de40b76bf..49cd9213c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -8,10 +8,8 @@
8 8
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13#include "common/scm_rev.h" 12#include "common/scm_rev.h"
14#include "common/settings.h"
15#include "video_core/renderer_vulkan/renderer_vulkan.h" 13#include "video_core/renderer_vulkan/renderer_vulkan.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
17 15
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b44ea0cc4..ab12dd15d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -11,11 +11,7 @@
11#include <fmt/ostream.h> 11#include <fmt/ostream.h>
12 12
13#include "common/detached_tasks.h" 13#include "common/detached_tasks.h"
14#include "common/fs/fs.h"
15#include "common/fs/fs_paths.h"
16#include "common/fs/path_util.h"
17#include "common/logging/backend.h" 14#include "common/logging/backend.h"
18#include "common/logging/filter.h"
19#include "common/logging/log.h" 15#include "common/logging/log.h"
20#include "common/microprofile.h" 16#include "common/microprofile.h"
21#include "common/nvidia_flags.h" 17#include "common/nvidia_flags.h"
@@ -66,7 +62,8 @@ static void PrintHelp(const char* argv0) {
66 "-f, --fullscreen Start in fullscreen mode\n" 62 "-f, --fullscreen Start in fullscreen mode\n"
67 "-h, --help Display this help and exit\n" 63 "-h, --help Display this help and exit\n"
68 "-v, --version Output version information and exit\n" 64 "-v, --version Output version information and exit\n"
69 "-p, --program Pass following string as arguments to executable\n"; 65 "-p, --program Pass following string as arguments to executable\n"
66 "-c, --config Load the specified configuration file\n";
70} 67}
71 68
72static void PrintVersion() { 69static void PrintVersion() {
@@ -77,8 +74,8 @@ static void PrintVersion() {
77int main(int argc, char** argv) { 74int main(int argc, char** argv) {
78 Common::Log::Initialize(); 75 Common::Log::Initialize();
79 Common::Log::SetColorConsoleBackendEnabled(true); 76 Common::Log::SetColorConsoleBackendEnabled(true);
77 Common::Log::Start();
80 Common::DetachedTasks detached_tasks; 78 Common::DetachedTasks detached_tasks;
81 Config config;
82 79
83 int option_index = 0; 80 int option_index = 0;
84#ifdef _WIN32 81#ifdef _WIN32
@@ -91,19 +88,24 @@ int main(int argc, char** argv) {
91 } 88 }
92#endif 89#endif
93 std::string filepath; 90 std::string filepath;
91 std::optional<std::string> config_path;
92 std::string program_args;
94 93
95 bool fullscreen = false; 94 bool fullscreen = false;
96 95
97 static struct option long_options[] = { 96 static struct option long_options[] = {
97 // clang-format off
98 {"fullscreen", no_argument, 0, 'f'}, 98 {"fullscreen", no_argument, 0, 'f'},
99 {"help", no_argument, 0, 'h'}, 99 {"help", no_argument, 0, 'h'},
100 {"version", no_argument, 0, 'v'}, 100 {"version", no_argument, 0, 'v'},
101 {"program", optional_argument, 0, 'p'}, 101 {"program", optional_argument, 0, 'p'},
102 {"config", required_argument, 0, 'c'},
102 {0, 0, 0, 0}, 103 {0, 0, 0, 0},
104 // clang-format on
103 }; 105 };
104 106
105 while (optind < argc) { 107 while (optind < argc) {
106 int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); 108 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
107 if (arg != -1) { 109 if (arg != -1) {
108 switch (static_cast<char>(arg)) { 110 switch (static_cast<char>(arg)) {
109 case 'f': 111 case 'f':
@@ -117,9 +119,12 @@ int main(int argc, char** argv) {
117 PrintVersion(); 119 PrintVersion();
118 return 0; 120 return 0;
119 case 'p': 121 case 'p':
120 Settings::values.program_args = argv[optind]; 122 program_args = argv[optind];
121 ++optind; 123 ++optind;
122 break; 124 break;
125 case 'c':
126 config_path = optarg;
127 break;
123 } 128 }
124 } else { 129 } else {
125#ifdef _WIN32 130#ifdef _WIN32
@@ -131,6 +136,12 @@ int main(int argc, char** argv) {
131 } 136 }
132 } 137 }
133 138
139 Config config{config_path};
140
141 if (!program_args.empty()) {
142 Settings::values.program_args = program_args;
143 }
144
134#ifdef _WIN32 145#ifdef _WIN32
135 LocalFree(argv_w); 146 LocalFree(argv_w);
136#endif 147#endif