summaryrefslogtreecommitdiffstats
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)
/we4305 # 'context': truncation from 'type1' to 'type2'
/we4388 # 'expression': signed/unsigned mismatch
/we4389 # 'operator': signed/unsigned mismatch
+ /we4505 # 'function': unreferenced local function has been removed
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4715 # 'function': not all control paths return a value
/we4834 # Discarding return value of function with 'nodiscard' attribute
/we5038 # data member 'member1' will be initialized after data member 'member2'
+ /we5245 # 'function': unreferenced function with internal linkage has been removed
)
if (ARCHITECTURE_x86_64)
@@ -103,12 +105,6 @@ else()
-Wno-unused-parameter
)
- # TODO: Remove when we update to a GCC compiler that enables this
- # by default (i.e. GCC 10 or newer).
- if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
- add_compile_options(-fconcepts)
- endif()
-
if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16")
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
splitter_context.h
stream.cpp
stream.h
- time_stretch.cpp
- time_stretch.h
voice_context.cpp
voice_context.h
@@ -63,7 +61,6 @@ if (NOT MSVC)
endif()
target_link_libraries(audio_core PUBLIC common core)
-target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB)
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 @@
#include <cstring>
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
-#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
public:
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
- : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
- num_channels} {
+ : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
cubeb_stream_params params{};
params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
Common::RingBuffer<s16, 0x10000> queue;
std::array<s16, 2> last_frame{};
std::atomic<bool> should_flush{};
- TimeStretcher time_stretch;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
const std::size_t num_channels = impl->GetNumChannels();
const std::size_t samples_to_write = num_channels * num_frames;
- std::size_t samples_written;
-
- /*
- if (Settings::values.enable_audio_stretching.GetValue()) {
- const std::vector<s16> in{impl->queue.Pop()};
- const std::size_t num_in{in.size() / num_channels};
- s16* const out{reinterpret_cast<s16*>(buffer)};
- const std::size_t out_frames =
- impl->time_stretch.Process(in.data(), num_in, out, num_frames);
- samples_written = out_frames * num_channels;
-
- if (impl->should_flush) {
- impl->time_stretch.Flush();
- impl->should_flush = false;
- }
- } else {
- samples_written = impl->queue.Pop(buffer, samples_to_write);
- }*/
- samples_written = impl->queue.Pop(buffer, samples_to_write);
+ const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
if (samples_written >= num_channels) {
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 @@
#include <cstring>
#include "audio_core/sdl2_sink.h"
#include "audio_core/stream.h"
-#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
class SDLSinkStream final : public SinkStream {
public:
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
- : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
+ : num_channels{std::min(num_channels_, 6u)} {
SDL_AudioSpec spec;
spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
SDL_AudioDeviceID dev = 0;
u32 num_channels{};
std::atomic<bool> should_flush{};
- TimeStretcher time_stretch;
};
SDLSink::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 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cmath>
-#include <cstddef>
-#include "audio_core/time_stretch.h"
-#include "common/logging/log.h"
-
-namespace AudioCore {
-
-TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
- m_sound_touch.setChannels(channel_count);
- m_sound_touch.setSampleRate(sample_rate);
- m_sound_touch.setPitch(1.0);
- m_sound_touch.setTempo(1.0);
-}
-
-void TimeStretcher::Clear() {
- m_sound_touch.clear();
-}
-
-void TimeStretcher::Flush() {
- m_sound_touch.flush();
-}
-
-std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
- std::size_t num_out) {
- const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
-
- // We were given actual_samples number of samples, and num_samples were requested from us.
- double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
-
- const double max_latency = 0.25; // seconds
- const double max_backlog = m_sample_rate * max_latency;
- const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
- if (backlog_fullness > 4.0) {
- // Too many samples in backlog: Don't push anymore on
- num_in = 0;
- }
-
- // We ideally want the backlog to be about 50% full.
- // This gives some headroom both ways to prevent underflow and overflow.
- // We tweak current_ratio to encourage this.
- constexpr double tweak_time_scale = 0.05; // seconds
- const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
- current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
-
- // This low-pass filter smoothes out variance in the calculated stretch ratio.
- // The time-scale determines how responsive this filter is.
- constexpr double lpf_time_scale = 0.712; // seconds
- const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
- m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
-
- // Place a lower limit of 5% speed. When a game boots up, there will be
- // many silence samples. These do not need to be timestretched.
- m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
- m_sound_touch.setTempo(m_stretch_ratio);
-
- LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
- backlog_fullness);
-
- m_sound_touch.putSamples(in, static_cast<u32>(num_in));
- return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
-}
-
-} // 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 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <cstddef>
-#include <SoundTouch.h>
-#include "common/common_types.h"
-
-namespace AudioCore {
-
-class TimeStretcher {
-public:
- TimeStretcher(u32 sample_rate, u32 channel_count);
-
- /// @param in Input sample buffer
- /// @param num_in Number of input frames in `in`
- /// @param out Output sample buffer
- /// @param num_out Desired number of output frames in `out`
- /// @returns Actual number of frames written to `out`
- std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
-
- void Clear();
-
- void Flush();
-
-private:
- u32 m_sample_rate;
- soundtouch::SoundTouch m_sound_touch;
- double m_stretch_ratio = 1.0;
-};
-
-} // 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 @@
#pragma once
-#include <cstring>
-#include <memory>
-
#include "common/common_types.h"
#if _MSC_VER
#include <intrin.h>
+#else
+#include <cstring>
#endif
namespace Common {
@@ -47,6 +46,50 @@ namespace Common {
reinterpret_cast<__int64*>(expected.data())) != 0;
}
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
+ u8& actual) {
+ actual =
+ _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
+ u16& actual) {
+ actual =
+ _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
+ u32& actual) {
+ actual =
+ _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
+ u64& actual) {
+ actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
+ expected);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
+ u128& actual) {
+ const bool result =
+ _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
+ value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
+ actual = expected;
+ return result;
+}
+
+[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
+ u128 result{};
+ _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
+ result[0], reinterpret_cast<__int64*>(result.data()));
+ return result;
+}
+
#else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -73,6 +116,52 @@ namespace Common {
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
+ u8& actual) {
+ actual = __sync_val_compare_and_swap(pointer, expected, value);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
+ u16& actual) {
+ actual = __sync_val_compare_and_swap(pointer, expected, value);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
+ u32& actual) {
+ actual = __sync_val_compare_and_swap(pointer, expected, value);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
+ u64& actual) {
+ actual = __sync_val_compare_and_swap(pointer, expected, value);
+ return actual == expected;
+}
+
+[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
+ u128& actual) {
+ unsigned __int128 value_a;
+ unsigned __int128 expected_a;
+ unsigned __int128 actual_a;
+ std::memcpy(&value_a, value.data(), sizeof(u128));
+ std::memcpy(&expected_a, expected.data(), sizeof(u128));
+ actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
+ std::memcpy(actual.data(), &actual_a, sizeof(u128));
+ return actual_a == expected_a;
+}
+
+[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
+ unsigned __int128 zeros_a = 0;
+ unsigned __int128 result_a =
+ __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
+
+ u128 result;
+ std::memcpy(result.data(), &result_a, sizeof(u128));
+ return result;
+}
+
#endif
} // 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 @@
#include <cstddef>
#include <limits>
#include <type_traits>
-#include "common/common_funcs.h"
#include "common/swap.h"
/*
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>
return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
}
+template <size_t bit_index, typename T>
+requires std::is_integral_v<T>
+[[nodiscard]] constexpr bool Bit(const T value) {
+ static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
+ return ((value >> bit_index) & T(1)) == T(1);
+}
+
} // 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 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include <cstring>
#include <string>
#include <utility>
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <mutex>
+
#include "common/assert.h"
#include "common/fiber.h"
-#include "common/spin_lock.h"
#include "common/virtual_buffer.h"
#include <boost/context/detail/fcontext.hpp>
@@ -19,7 +20,7 @@ struct Fiber::FiberImpl {
VirtualBuffer<u8> stack;
VirtualBuffer<u8> rewind_stack;
- SpinLock guard{};
+ std::mutex guard;
std::function<void(void*)> entry_point;
std::function<void(void*)> rewind_point;
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 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
-#include "common/fs/path_util.h"
#include "common/logging/log.h"
#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 @@
#include <cstdio>
#include <filesystem>
-#include <fstream>
#include <span>
#include <type_traits>
-#include <vector>
#include "common/concepts.h"
#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 @@
#include <functional>
#include "common/common_funcs.h"
-#include "common/common_types.h"
namespace Common::FS {
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 @@
#include <filesystem>
#include <span>
#include <string>
-#include <string_view>
#include "common/common_types.h"
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 @@
#include <array>
#include <cstddef>
#include <string>
-#include <type_traits>
#include <vector>
#include <fmt/format.h>
#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 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
+#include "common/scope_exit.h"
#endif // ^^^ Linux ^^^
@@ -27,7 +28,6 @@
#include "common/assert.h"
#include "common/host_memory.h"
#include "common/logging/log.h"
-#include "common/scope_exit.h"
namespace Common {
@@ -149,7 +149,7 @@ public:
}
void Unmap(size_t virtual_offset, size_t length) {
- std::lock_guard lock{placeholder_mutex};
+ std::scoped_lock lock{placeholder_mutex};
// Unmap until there are no more placeholders
while (UnmapOnePlaceholder(virtual_offset, length)) {
@@ -169,7 +169,7 @@ public:
}
const size_t virtual_end = virtual_offset + length;
- std::lock_guard lock{placeholder_mutex};
+ std::scoped_lock lock{placeholder_mutex};
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
while (it != end) {
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 @@
#pragma once
+#include "common/common_funcs.h"
#include "common/parent_of_member.h"
#include "common/tree.h"
@@ -15,32 +16,33 @@ class IntrusiveRedBlackTreeImpl;
}
+#pragma pack(push, 4)
struct IntrusiveRedBlackTreeNode {
+ YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode);
+
public:
- using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
+ using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
- constexpr IntrusiveRedBlackTreeNode() = default;
+private:
+ RBEntry m_entry;
- void SetEntry(const EntryType& new_entry) {
- entry = new_entry;
- }
+public:
+ explicit IntrusiveRedBlackTreeNode() = default;
- [[nodiscard]] EntryType& GetEntry() {
- return entry;
+ [[nodiscard]] constexpr RBEntry& GetRBEntry() {
+ return m_entry;
}
-
- [[nodiscard]] const EntryType& GetEntry() const {
- return entry;
+ [[nodiscard]] constexpr const RBEntry& GetRBEntry() const {
+ return m_entry;
}
-private:
- EntryType entry{};
-
- friend class impl::IntrusiveRedBlackTreeImpl;
-
- template <class, class, class>
- friend class IntrusiveRedBlackTree;
+ constexpr void SetRBEntry(const RBEntry& entry) {
+ m_entry = entry;
+ }
};
+static_assert(sizeof(IntrusiveRedBlackTreeNode) ==
+ 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
+#pragma pack(pop)
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree;
@@ -48,12 +50,17 @@ class IntrusiveRedBlackTree;
namespace impl {
class IntrusiveRedBlackTreeImpl {
+ YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl);
+
private:
template <class, class, class>
friend class ::Common::IntrusiveRedBlackTree;
- using RootType = RBHead<IntrusiveRedBlackTreeNode>;
- RootType root;
+private:
+ using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
+
+private:
+ RootType m_root;
public:
template <bool Const>
@@ -81,149 +88,150 @@ public:
IntrusiveRedBlackTreeImpl::reference>;
private:
- pointer node;
+ pointer m_node;
public:
- explicit Iterator(pointer n) : node(n) {}
+ constexpr explicit Iterator(pointer n) : m_node(n) {}
- bool operator==(const Iterator& rhs) const {
- return this->node == rhs.node;
+ constexpr bool operator==(const Iterator& rhs) const {
+ return m_node == rhs.m_node;
}
- bool operator!=(const Iterator& rhs) const {
+ constexpr bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
- pointer operator->() const {
- return this->node;
+ constexpr pointer operator->() const {
+ return m_node;
}
- reference operator*() const {
- return *this->node;
+ constexpr reference operator*() const {
+ return *m_node;
}
- Iterator& operator++() {
- this->node = GetNext(this->node);
+ constexpr Iterator& operator++() {
+ m_node = GetNext(m_node);
return *this;
}
- Iterator& operator--() {
- this->node = GetPrev(this->node);
+ constexpr Iterator& operator--() {
+ m_node = GetPrev(m_node);
return *this;
}
- Iterator operator++(int) {
+ constexpr Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
- Iterator operator--(int) {
+ constexpr Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
- operator Iterator<true>() const {
- return Iterator<true>(this->node);
+ constexpr operator Iterator<true>() const {
+ return Iterator<true>(m_node);
}
};
private:
- // Define accessors using RB_* functions.
- bool EmptyImpl() const {
- return root.IsEmpty();
+ constexpr bool EmptyImpl() const {
+ return m_root.IsEmpty();
}
- IntrusiveRedBlackTreeNode* GetMinImpl() const {
- return RB_MIN(const_cast<RootType*>(&root));
+ constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const {
+ return freebsd::RB_MIN(const_cast<RootType&>(m_root));
}
- IntrusiveRedBlackTreeNode* GetMaxImpl() const {
- return RB_MAX(const_cast<RootType*>(&root));
+ constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const {
+ return freebsd::RB_MAX(const_cast<RootType&>(m_root));
}
- IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
- return RB_REMOVE(&root, node);
+ constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
+ return freebsd::RB_REMOVE(m_root, node);
}
public:
- static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
- return RB_NEXT(node);
+ static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
+ return freebsd::RB_NEXT(node);
}
- static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
- return RB_PREV(node);
+ static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
+ return freebsd::RB_PREV(node);
}
- static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
+ static constexpr IntrusiveRedBlackTreeNode const* GetNext(
+ IntrusiveRedBlackTreeNode const* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
- static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
+ static constexpr IntrusiveRedBlackTreeNode const* GetPrev(
+ IntrusiveRedBlackTreeNode const* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
public:
- constexpr IntrusiveRedBlackTreeImpl() {}
+ constexpr IntrusiveRedBlackTreeImpl() = default;
// Iterator accessors.
- iterator begin() {
+ constexpr iterator begin() {
return iterator(this->GetMinImpl());
}
- const_iterator begin() const {
+ constexpr const_iterator begin() const {
return const_iterator(this->GetMinImpl());
}
- iterator end() {
+ constexpr iterator end() {
return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
}
- const_iterator end() const {
+ constexpr const_iterator end() const {
return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
}
- const_iterator cbegin() const {
+ constexpr const_iterator cbegin() const {
return this->begin();
}
- const_iterator cend() const {
+ constexpr const_iterator cend() const {
return this->end();
}
- iterator iterator_to(reference ref) {
- return iterator(&ref);
+ constexpr iterator iterator_to(reference ref) {
+ return iterator(std::addressof(ref));
}
- const_iterator iterator_to(const_reference ref) const {
- return const_iterator(&ref);
+ constexpr const_iterator iterator_to(const_reference ref) const {
+ return const_iterator(std::addressof(ref));
}
// Content management.
- bool empty() const {
+ constexpr bool empty() const {
return this->EmptyImpl();
}
- reference back() {
+ constexpr reference back() {
return *this->GetMaxImpl();
}
- const_reference back() const {
+ constexpr const_reference back() const {
return *this->GetMaxImpl();
}
- reference front() {
+ constexpr reference front() {
return *this->GetMinImpl();
}
- const_reference front() const {
+ constexpr const_reference front() const {
return *this->GetMinImpl();
}
- iterator erase(iterator it) {
+ constexpr iterator erase(iterator it) {
auto cur = std::addressof(*it);
auto next = GetNext(cur);
this->RemoveImpl(cur);
@@ -234,16 +242,16 @@ public:
} // namespace impl
template <typename T>
-concept HasLightCompareType = requires {
- { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>;
+concept HasRedBlackKeyType = requires {
+ { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
};
namespace impl {
template <typename T, typename Default>
- consteval auto* GetLightCompareType() {
- if constexpr (HasLightCompareType<T>) {
- return static_cast<typename T::LightCompareType*>(nullptr);
+ consteval auto* GetRedBlackKeyType() {
+ if constexpr (HasRedBlackKeyType<T>) {
+ return static_cast<typename T::RedBlackKeyType*>(nullptr);
} else {
return static_cast<Default*>(nullptr);
}
@@ -252,16 +260,17 @@ namespace impl {
} // namespace impl
template <typename T, typename Default>
-using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>;
+using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>;
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree {
+ YUZU_NON_COPYABLE(IntrusiveRedBlackTree);
public:
using ImplType = impl::IntrusiveRedBlackTreeImpl;
private:
- ImplType impl{};
+ ImplType m_impl;
public:
template <bool Const>
@@ -277,9 +286,9 @@ public:
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
- using light_value_type = LightCompareType<Comparator, value_type>;
- using const_light_pointer = const light_value_type*;
- using const_light_reference = const light_value_type&;
+ using key_type = RedBlackKeyType<Comparator, value_type>;
+ using const_key_pointer = const key_type*;
+ using const_key_reference = const key_type&;
template <bool Const>
class Iterator {
@@ -298,183 +307,201 @@ public:
IntrusiveRedBlackTree::reference>;
private:
- ImplIterator iterator;
+ ImplIterator m_impl;
private:
- explicit Iterator(ImplIterator it) : iterator(it) {}
+ constexpr explicit Iterator(ImplIterator it) : m_impl(it) {}
- explicit Iterator(typename std::conditional<Const, ImplType::const_iterator,
- ImplType::iterator>::type::pointer ptr)
- : iterator(ptr) {}
+ constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {}
- ImplIterator GetImplIterator() const {
- return this->iterator;
+ constexpr ImplIterator GetImplIterator() const {
+ return m_impl;
}
public:
- bool operator==(const Iterator& rhs) const {
- return this->iterator == rhs.iterator;
+ constexpr bool operator==(const Iterator& rhs) const {
+ return m_impl == rhs.m_impl;
}
- bool operator!=(const Iterator& rhs) const {
+ constexpr bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
- pointer operator->() const {
- return Traits::GetParent(std::addressof(*this->iterator));
+ constexpr pointer operator->() const {
+ return Traits::GetParent(std::addressof(*m_impl));
}
- reference operator*() const {
- return *Traits::GetParent(std::addressof(*this->iterator));
+ constexpr reference operator*() const {
+ return *Traits::GetParent(std::addressof(*m_impl));
}
- Iterator& operator++() {
- ++this->iterator;
+ constexpr Iterator& operator++() {
+ ++m_impl;
return *this;
}
- Iterator& operator--() {
- --this->iterator;
+ constexpr Iterator& operator--() {
+ --m_impl;
return *this;
}
- Iterator operator++(int) {
+ constexpr Iterator operator++(int) {
const Iterator it{*this};
- ++this->iterator;
+ ++m_impl;
return it;
}
- Iterator operator--(int) {
+ constexpr Iterator operator--(int) {
const Iterator it{*this};
- --this->iterator;
+ --m_impl;
return it;
}
- operator Iterator<true>() const {
- return Iterator<true>(this->iterator);
+ constexpr operator Iterator<true>() const {
+ return Iterator<true>(m_impl);
}
};
private:
- static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
- const IntrusiveRedBlackTreeNode* rhs) {
+ static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
+ const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
}
- static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) {
- return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs));
+ static constexpr int CompareKeyImpl(const_key_reference key,
+ const IntrusiveRedBlackTreeNode* rhs) {
+ return Comparator::Compare(key, *Traits::GetParent(rhs));
}
// Define accessors using RB_* functions.
- IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
- return RB_INSERT(&impl.root, node, CompareImpl);
+ constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
+ return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl);
}
- IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
- return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
- const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
+ constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const {
+ return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root),
+ const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
- IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
- return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
- const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
+ constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const {
+ return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root),
+ const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
- IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
- return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
- static_cast<const void*>(lelm), LightCompareImpl);
+ constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const {
+ return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
+ CompareKeyImpl);
}
- IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
- return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
- static_cast<const void*>(lelm), LightCompareImpl);
+ constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const {
+ return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
+ CompareKeyImpl);
+ }
+
+ constexpr IntrusiveRedBlackTreeNode* FindExistingImpl(
+ IntrusiveRedBlackTreeNode const* node) const {
+ return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root),
+ const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
+ }
+
+ constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const {
+ return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
+ CompareKeyImpl);
}
public:
constexpr IntrusiveRedBlackTree() = default;
// Iterator accessors.
- iterator begin() {
- return iterator(this->impl.begin());
+ constexpr iterator begin() {
+ return iterator(m_impl.begin());
}
- const_iterator begin() const {
- return const_iterator(this->impl.begin());
+ constexpr const_iterator begin() const {
+ return const_iterator(m_impl.begin());
}
- iterator end() {
- return iterator(this->impl.end());
+ constexpr iterator end() {
+ return iterator(m_impl.end());
}
- const_iterator end() const {
- return const_iterator(this->impl.end());
+ constexpr const_iterator end() const {
+ return const_iterator(m_impl.end());
}
- const_iterator cbegin() const {
+ constexpr const_iterator cbegin() const {
return this->begin();
}
- const_iterator cend() const {
+ constexpr const_iterator cend() const {
return this->end();
}
- iterator iterator_to(reference ref) {
- return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
+ constexpr iterator iterator_to(reference ref) {
+ return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
- const_iterator iterator_to(const_reference ref) const {
- return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
+ constexpr const_iterator iterator_to(const_reference ref) const {
+ return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
// Content management.
- bool empty() const {
- return this->impl.empty();
+ constexpr bool empty() const {
+ return m_impl.empty();
}
- reference back() {
- return *Traits::GetParent(std::addressof(this->impl.back()));
+ constexpr reference back() {
+ return *Traits::GetParent(std::addressof(m_impl.back()));
}
- const_reference back() const {
- return *Traits::GetParent(std::addressof(this->impl.back()));
+ constexpr const_reference back() const {
+ return *Traits::GetParent(std::addressof(m_impl.back()));
}
- reference front() {
- return *Traits::GetParent(std::addressof(this->impl.front()));
+ constexpr reference front() {
+ return *Traits::GetParent(std::addressof(m_impl.front()));
}
- const_reference front() const {
- return *Traits::GetParent(std::addressof(this->impl.front()));
+ constexpr const_reference front() const {
+ return *Traits::GetParent(std::addressof(m_impl.front()));
}
- iterator erase(iterator it) {
- return iterator(this->impl.erase(it.GetImplIterator()));
+ constexpr iterator erase(iterator it) {
+ return iterator(m_impl.erase(it.GetImplIterator()));
}
- iterator insert(reference ref) {
+ constexpr iterator insert(reference ref) {
ImplType::pointer node = Traits::GetNode(std::addressof(ref));
this->InsertImpl(node);
return iterator(node);
}
- iterator find(const_reference ref) const {
+ constexpr iterator find(const_reference ref) const {
return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
}
- iterator nfind(const_reference ref) const {
+ constexpr iterator nfind(const_reference ref) const {
return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
}
- iterator find_light(const_light_reference ref) const {
- return iterator(this->FindLightImpl(std::addressof(ref)));
+ constexpr iterator find_key(const_key_reference ref) const {
+ return iterator(this->FindKeyImpl(ref));
+ }
+
+ constexpr iterator nfind_key(const_key_reference ref) const {
+ return iterator(this->NFindKeyImpl(ref));
+ }
+
+ constexpr iterator find_existing(const_reference ref) const {
+ return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref))));
}
- iterator nfind_light(const_light_reference ref) const {
- return iterator(this->NFindLightImpl(std::addressof(ref)));
+ constexpr iterator find_existing_key(const_key_reference ref) const {
+ return iterator(this->FindExistingKeyImpl(ref));
}
};
-template <auto T, class Derived = impl::GetParentType<T>>
+template <auto T, class Derived = Common::impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraits;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -498,19 +525,16 @@ private:
return std::addressof(parent->*Member);
}
- static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
- return GetParentPointer<Member, Derived>(node);
+ static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
+ return Common::GetParentPointer<Member, Derived>(node);
}
- static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
- return GetParentPointer<Member, Derived>(node);
+ static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
+ return Common::GetParentPointer<Member, Derived>(node);
}
-
-private:
- static constexpr TypedStorage<Derived> DerivedStorage = {};
};
-template <auto T, class Derived = impl::GetParentType<T>>
+template <auto T, class Derived = Common::impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -521,11 +545,6 @@ public:
IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
- static constexpr bool IsValid() {
- TypedStorage<Derived> DerivedStorage = {};
- return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
- }
-
private:
template <class, class, class>
friend class IntrusiveRedBlackTree;
@@ -540,30 +559,36 @@ private:
return std::addressof(parent->*Member);
}
- static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
- return GetParentPointer<Member, Derived>(node);
+ static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
+ return Common::GetParentPointer<Member, Derived>(node);
}
- static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
- return GetParentPointer<Member, Derived>(node);
+ static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
+ return Common::GetParentPointer<Member, Derived>(node);
}
};
template <class Derived>
-class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
+class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
public:
+ using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode;
+
constexpr Derived* GetPrev() {
- return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
+ return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
+ impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
}
constexpr const Derived* GetPrev() const {
- return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
+ return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
+ impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
}
constexpr Derived* GetNext() {
- return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
+ return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
+ impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
}
constexpr const Derived* GetNext() const {
- return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
+ return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
+ impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
}
};
@@ -581,19 +606,22 @@ private:
friend class impl::IntrusiveRedBlackTreeImpl;
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
- return static_cast<IntrusiveRedBlackTreeNode*>(parent);
+ return static_cast<IntrusiveRedBlackTreeNode*>(
+ static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
}
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
- return static_cast<const IntrusiveRedBlackTreeNode*>(parent);
+ return static_cast<const IntrusiveRedBlackTreeNode*>(
+ static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
- return static_cast<Derived*>(node);
+ return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
}
- static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
- return static_cast<const Derived*>(node);
+ static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
+ return static_cast<const Derived*>(
+ static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
}
};
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 @@
#include <atomic>
#include <chrono>
#include <climits>
-#include <exception>
#include <stop_token>
#include <thread>
-#include <vector>
#include <fmt/format.h>
@@ -276,9 +274,9 @@ private:
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
- std::jthread backend_thread;
MPSCQueue<Entry, true> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
+ std::jthread backend_thread;
};
} // namespace
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 @@
#pragma once
-#include <filesystem>
#include "common/logging/filter.h"
namespace 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) {
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
+ SUB(Service, JIT) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
@@ -119,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
+ SUB(Service, NVFlinger) \
SUB(Service, OLSC) \
SUB(Service, PCIE) \
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 @@
#include <array>
#include <chrono>
#include <cstddef>
-#include <string_view>
#include "common/logging/log.h"
namespace 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 @@
#endif
#include "common/assert.h"
-#include "common/common_funcs.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/log_entry.h"
#include "common/logging/text_formatter.h"
-#include "common/string_util.h"
namespace Common::Log {
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 @@
#pragma once
-#include <cstddef>
#include <string>
namespace 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 {
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
+ Service_JIT, ///< The JIT service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
@@ -87,6 +88,7 @@ enum class Class : u8 {
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
+ Service_NVFlinger, ///< The NVFlinger service
Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
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 @@
#pragma once
+#include <algorithm>
#include <cstdlib>
#include <type_traits>
@@ -20,10 +21,32 @@ struct Rectangle {
constexpr Rectangle() = default;
+ constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
+
constexpr Rectangle(T left_, T top_, T right_, T bottom_)
: left(left_), top(top_), right(right_), bottom(bottom_) {}
- [[nodiscard]] T GetWidth() const {
+ [[nodiscard]] constexpr T Left() const {
+ return left;
+ }
+
+ [[nodiscard]] constexpr T Top() const {
+ return top;
+ }
+
+ [[nodiscard]] constexpr T Right() const {
+ return right;
+ }
+
+ [[nodiscard]] constexpr T Bottom() const {
+ return bottom;
+ }
+
+ [[nodiscard]] constexpr bool IsEmpty() const {
+ return (GetWidth() <= 0) || (GetHeight() <= 0);
+ }
+
+ [[nodiscard]] constexpr T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
@@ -31,7 +54,7 @@ struct Rectangle {
}
}
- [[nodiscard]] T GetHeight() const {
+ [[nodiscard]] constexpr T GetHeight() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
@@ -39,18 +62,35 @@ struct Rectangle {
}
}
- [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
+ [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};
}
- [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
+ [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
return Rectangle{left, top + y, right, bottom + y};
}
- [[nodiscard]] Rectangle<T> Scale(const float s) const {
+ [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
}
+
+ [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
+ return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
+ (bottom == rhs.bottom);
+ }
+
+ [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
+ return !operator==(rhs);
+ }
+
+ [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
+ result->left = std::max(left, with.left);
+ result->top = std::max(top, with.top);
+ result->right = std::min(right, with.right);
+ result->bottom = std::min(bottom, with.bottom);
+ return !result->IsEmpty();
+ }
};
template <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() {
return mem_info;
}
-} // namespace Common \ No newline at end of file
+} // 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 @@
#include <fmt/format.h>
-#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#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 @@
#pragma once
#include <atomic>
-#include <tuple>
#include "common/common_types.h"
#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 @@
#include <type_traits>
#include "common/assert.h"
-#include "common/common_types.h"
namespace Common {
namespace 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 @@
#include <new>
#include <type_traits>
#include <vector>
-#include "common/common_types.h"
namespace Common {
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 {
Auto = 0,
Accurate = 1,
Unsafe = 2,
+ Paranoid = 3,
};
enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
// Cpu
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
- CPUAccuracy::Unsafe, "cpu_accuracy"};
+ CPUAccuracy::Paranoid, "cpu_accuracy"};
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -589,6 +590,9 @@ struct Values {
BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
std::vector<TouchFromButtonMap> touch_from_button_maps;
+ BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"};
+ RingconRaw ringcon_analogs;
+
// Data Storage
BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
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;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
+using RingconRaw = std::string;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr 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 @@
#include <algorithm>
#include <cctype>
#include <codecvt>
-#include <cstdlib>
#include <locale>
#include <sstream>
-#include "common/logging/log.h"
#include "common/string_util.h"
#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 @@
#include <algorithm>
#include <cstring>
-#include "common/assert.h"
#include "common/scm_rev.h"
#include "common/telemetry.h"
@@ -55,22 +54,50 @@ void AppendBuildInfo(FieldCollection& fc) {
void AppendCPUInfo(FieldCollection& fc) {
#ifdef ARCHITECTURE_x86_64
- fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
- fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1);
- fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2);
+
+ const auto& caps = Common::GetCPUCaps();
+ const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
+ fc.AddField(FieldType::UserSystem, field_name, field_value);
+ };
+ add_field("CPU_Model", caps.cpu_string);
+ add_field("CPU_BrandString", caps.brand_string);
+
+ add_field("CPU_Extension_x64_SSE", caps.sse);
+ add_field("CPU_Extension_x64_SSE2", caps.sse2);
+ add_field("CPU_Extension_x64_SSE3", caps.sse3);
+ add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
+ add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
+ add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
+
+ add_field("CPU_Extension_x64_AVX", caps.avx);
+ add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
+ add_field("CPU_Extension_x64_AVX2", caps.avx2);
+
+ // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
+ add_field("CPU_Extension_x64_AVX512",
+ caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
+
+ add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
+ add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
+ add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
+ add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
+ add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
+ add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
+ add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
+
+ add_field("CPU_Extension_x64_AES", caps.aes);
+ add_field("CPU_Extension_x64_BMI1", caps.bmi1);
+ add_field("CPU_Extension_x64_BMI2", caps.bmi2);
+ add_field("CPU_Extension_x64_F16C", caps.f16c);
+ add_field("CPU_Extension_x64_FMA", caps.fma);
+ add_field("CPU_Extension_x64_FMA4", caps.fma4);
+ add_field("CPU_Extension_x64_GFNI", caps.gfni);
+ add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
+ add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
+ add_field("CPU_Extension_x64_MOVBE", caps.movbe);
+ add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
+ add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
+ add_field("CPU_Extension_x64_SHA", caps.sha);
#else
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
#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 {
public:
YUZU_NON_COPYABLE(Field);
- Field(FieldType type_, std::string name_, T value_)
- : name(std::move(name_)), type(type_), value(std::move(value_)) {}
+ Field(FieldType type_, std::string_view name_, T value_)
+ : name(name_), type(type_), value(std::move(value_)) {}
~Field() override = default;
@@ -123,7 +123,7 @@ public:
* @param value Value for the field to add.
*/
template <typename T>
- void AddField(FieldType type, const char* name, T value) {
+ void AddField(FieldType type, std::string_view name, T value) {
return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
}
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 {
class Event {
public:
void Set() {
- std::lock_guard lk{mutex};
+ std::scoped_lock lk{mutex};
if (!is_set) {
is_set = true;
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:
// line before cv.wait
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
- std::lock_guard lock{cv_mutex};
+ std::scoped_lock lock{cv_mutex};
cv.notify_one();
}
@@ -159,7 +159,7 @@ public:
template <typename Arg>
void Push(Arg&& t) {
- std::lock_guard lock{write_lock};
+ std::scoped_lock lock{write_lock};
spsc_queue.Push(t);
}
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 @@
* The maximum height of a red-black tree is 2lg (n+1).
*/
-#include "common/assert.h"
+namespace Common::freebsd {
-namespace Common {
+enum class RBColor {
+ RB_BLACK = 0,
+ RB_RED = 1,
+};
+
+#pragma pack(push, 4)
template <typename T>
-class RBHead {
+class RBEntry {
public:
- [[nodiscard]] T* Root() {
- return rbh_root;
- }
+ constexpr RBEntry() = default;
- [[nodiscard]] const T* Root() const {
- return rbh_root;
+ [[nodiscard]] constexpr T* Left() {
+ return m_rbe_left;
}
-
- void SetRoot(T* root) {
- rbh_root = root;
+ [[nodiscard]] constexpr const T* Left() const {
+ return m_rbe_left;
}
- [[nodiscard]] bool IsEmpty() const {
- return Root() == nullptr;
+ constexpr void SetLeft(T* e) {
+ m_rbe_left = e;
}
-private:
- T* rbh_root = nullptr;
-};
-
-enum class EntryColor {
- Black,
- Red,
-};
-
-template <typename T>
-class RBEntry {
-public:
- [[nodiscard]] T* Left() {
- return rbe_left;
+ [[nodiscard]] constexpr T* Right() {
+ return m_rbe_right;
}
-
- [[nodiscard]] const T* Left() const {
- return rbe_left;
+ [[nodiscard]] constexpr const T* Right() const {
+ return m_rbe_right;
}
- void SetLeft(T* left) {
- rbe_left = left;
+ constexpr void SetRight(T* e) {
+ m_rbe_right = e;
}
- [[nodiscard]] T* Right() {
- return rbe_right;
+ [[nodiscard]] constexpr T* Parent() {
+ return m_rbe_parent;
}
-
- [[nodiscard]] const T* Right() const {
- return rbe_right;
+ [[nodiscard]] constexpr const T* Parent() const {
+ return m_rbe_parent;
}
- void SetRight(T* right) {
- rbe_right = right;
+ constexpr void SetParent(T* e) {
+ m_rbe_parent = e;
}
- [[nodiscard]] T* Parent() {
- return rbe_parent;
+ [[nodiscard]] constexpr bool IsBlack() const {
+ return m_rbe_color == RBColor::RB_BLACK;
}
-
- [[nodiscard]] const T* Parent() const {
- return rbe_parent;
+ [[nodiscard]] constexpr bool IsRed() const {
+ return m_rbe_color == RBColor::RB_RED;
}
-
- void SetParent(T* parent) {
- rbe_parent = parent;
+ [[nodiscard]] constexpr RBColor Color() const {
+ return m_rbe_color;
}
- [[nodiscard]] bool IsBlack() const {
- return rbe_color == EntryColor::Black;
+ constexpr void SetColor(RBColor c) {
+ m_rbe_color = c;
}
- [[nodiscard]] bool IsRed() const {
- return rbe_color == EntryColor::Red;
- }
+private:
+ T* m_rbe_left{};
+ T* m_rbe_right{};
+ T* m_rbe_parent{};
+ RBColor m_rbe_color{RBColor::RB_BLACK};
+};
+#pragma pack(pop)
- [[nodiscard]] EntryColor Color() const {
- return rbe_color;
- }
+template <typename T>
+struct CheckRBEntry {
+ static constexpr bool value = false;
+};
+template <typename T>
+struct CheckRBEntry<RBEntry<T>> {
+ static constexpr bool value = true;
+};
- void SetColor(EntryColor color) {
- rbe_color = color;
- }
+template <typename T>
+concept IsRBEntry = CheckRBEntry<T>::value;
+template <typename T>
+concept HasRBEntry = requires(T& t, const T& ct) {
+ { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
+ { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
+};
+
+template <typename T>
+requires HasRBEntry<T>
+class RBHead {
private:
- T* rbe_left = nullptr;
- T* rbe_right = nullptr;
- T* rbe_parent = nullptr;
- EntryColor rbe_color{};
+ T* m_rbh_root = nullptr;
+
+public:
+ [[nodiscard]] constexpr T* Root() {
+ return m_rbh_root;
+ }
+ [[nodiscard]] constexpr const T* Root() const {
+ return m_rbh_root;
+ }
+ constexpr void SetRoot(T* root) {
+ m_rbh_root = root;
+ }
+
+ [[nodiscard]] constexpr bool IsEmpty() const {
+ return this->Root() == nullptr;
+ }
};
-template <typename Node>
-[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
- return node->GetEntry();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
+ return t->GetRBEntry();
}
-
-template <typename Node>
-[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
- return node->GetEntry();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
+ return t->GetRBEntry();
}
-template <typename Node>
-[[nodiscard]] Node* RB_PARENT(Node* node) {
- return RB_ENTRY(node).Parent();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr T* RB_LEFT(T* t) {
+ return RB_ENTRY(t).Left();
}
-
-template <typename Node>
-[[nodiscard]] const Node* RB_PARENT(const Node* node) {
- return RB_ENTRY(node).Parent();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
+ return RB_ENTRY(t).Left();
}
-template <typename Node>
-void RB_SET_PARENT(Node* node, Node* parent) {
- return RB_ENTRY(node).SetParent(parent);
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
+ return RB_ENTRY(t).Right();
}
-
-template <typename Node>
-[[nodiscard]] Node* RB_LEFT(Node* node) {
- return RB_ENTRY(node).Left();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
+ return RB_ENTRY(t).Right();
}
-template <typename Node>
-[[nodiscard]] const Node* RB_LEFT(const Node* node) {
- return RB_ENTRY(node).Left();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr T* RB_PARENT(T* t) {
+ return RB_ENTRY(t).Parent();
}
-
-template <typename Node>
-void RB_SET_LEFT(Node* node, Node* left) {
- return RB_ENTRY(node).SetLeft(left);
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
+ return RB_ENTRY(t).Parent();
}
-template <typename Node>
-[[nodiscard]] Node* RB_RIGHT(Node* node) {
- return RB_ENTRY(node).Right();
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET_LEFT(T* t, T* e) {
+ RB_ENTRY(t).SetLeft(e);
}
-
-template <typename Node>
-[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
- return RB_ENTRY(node).Right();
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET_RIGHT(T* t, T* e) {
+ RB_ENTRY(t).SetRight(e);
}
-
-template <typename Node>
-void RB_SET_RIGHT(Node* node, Node* right) {
- return RB_ENTRY(node).SetRight(right);
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET_PARENT(T* t, T* e) {
+ RB_ENTRY(t).SetParent(e);
}
-template <typename Node>
-[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
- return RB_ENTRY(node).IsBlack();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
+ return RB_ENTRY(t).IsBlack();
}
-
-template <typename Node>
-[[nodiscard]] bool RB_IS_RED(const Node* node) {
- return RB_ENTRY(node).IsRed();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
+ return RB_ENTRY(t).IsRed();
}
-template <typename Node>
-[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
- return RB_ENTRY(node).Color();
+template <typename T>
+requires HasRBEntry<T>
+[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
+ return RB_ENTRY(t).Color();
}
-template <typename Node>
-void RB_SET_COLOR(Node* node, EntryColor color) {
- return RB_ENTRY(node).SetColor(color);
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET_COLOR(T* t, RBColor c) {
+ RB_ENTRY(t).SetColor(c);
}
-template <typename Node>
-void RB_SET(Node* node, Node* parent) {
- auto& entry = RB_ENTRY(node);
- entry.SetParent(parent);
- entry.SetLeft(nullptr);
- entry.SetRight(nullptr);
- entry.SetColor(EntryColor::Red);
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET(T* elm, T* parent) {
+ auto& rb_entry = RB_ENTRY(elm);
+ rb_entry.SetParent(parent);
+ rb_entry.SetLeft(nullptr);
+ rb_entry.SetRight(nullptr);
+ rb_entry.SetColor(RBColor::RB_RED);
}
-template <typename Node>
-void RB_SET_BLACKRED(Node* black, Node* red) {
- RB_SET_COLOR(black, EntryColor::Black);
- RB_SET_COLOR(red, EntryColor::Red);
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_SET_BLACKRED(T* black, T* red) {
+ RB_SET_COLOR(black, RBColor::RB_BLACK);
+ RB_SET_COLOR(red, RBColor::RB_RED);
}
-template <typename Node>
-void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_RIGHT(elm);
- RB_SET_RIGHT(elm, RB_LEFT(tmp));
- if (RB_RIGHT(elm) != nullptr) {
+ if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
RB_SET_PARENT(RB_LEFT(tmp), elm);
}
- RB_SET_PARENT(tmp, RB_PARENT(elm));
- if (RB_PARENT(tmp) != nullptr) {
+ if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
- head->SetRoot(tmp);
+ head.SetRoot(tmp);
}
RB_SET_LEFT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
-template <typename Node>
-void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_LEFT(elm);
- RB_SET_LEFT(elm, RB_RIGHT(tmp));
- if (RB_LEFT(elm) != nullptr) {
+ if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
RB_SET_PARENT(RB_RIGHT(tmp), elm);
}
- RB_SET_PARENT(tmp, RB_PARENT(elm));
- if (RB_PARENT(tmp) != nullptr) {
+ if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
- head->SetRoot(tmp);
+ head.SetRoot(tmp);
}
RB_SET_RIGHT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
-template <typename Node>
-void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
- Node* parent = nullptr;
- Node* tmp = nullptr;
-
- while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
- Node* gparent = RB_PARENT(parent);
- if (parent == RB_LEFT(gparent)) {
- tmp = RB_RIGHT(gparent);
- if (tmp && RB_IS_RED(tmp)) {
- RB_SET_COLOR(tmp, EntryColor::Black);
- RB_SET_BLACKRED(parent, gparent);
- elm = gparent;
- continue;
- }
-
- if (RB_RIGHT(parent) == elm) {
- RB_ROTATE_LEFT(head, parent, tmp);
- tmp = parent;
- parent = elm;
- elm = tmp;
- }
-
- RB_SET_BLACKRED(parent, gparent);
- RB_ROTATE_RIGHT(head, gparent, tmp);
- } else {
- tmp = RB_LEFT(gparent);
- if (tmp && RB_IS_RED(tmp)) {
- RB_SET_COLOR(tmp, EntryColor::Black);
- RB_SET_BLACKRED(parent, gparent);
- elm = gparent;
- continue;
- }
-
- if (RB_LEFT(parent) == elm) {
- RB_ROTATE_RIGHT(head, parent, tmp);
- tmp = parent;
- parent = elm;
- elm = tmp;
- }
-
- RB_SET_BLACKRED(parent, gparent);
- RB_ROTATE_LEFT(head, gparent, tmp);
- }
- }
-
- RB_SET_COLOR(head->Root(), EntryColor::Black);
-}
-
-template <typename Node>
-void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
- Node* tmp;
- while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) {
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
+ T* tmp;
+ while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
if (RB_LEFT(parent) == elm) {
tmp = RB_RIGHT(parent);
- if (!tmp) {
- ASSERT_MSG(false, "tmp is invalid!");
- break;
- }
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_LEFT(head, parent, tmp);
@@ -339,29 +310,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
- RB_SET_COLOR(tmp, EntryColor::Red);
+ RB_SET_COLOR(tmp, RBColor::RB_RED);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
- Node* oleft;
+ T* oleft;
if ((oleft = RB_LEFT(tmp)) != nullptr) {
- RB_SET_COLOR(oleft, EntryColor::Black);
+ RB_SET_COLOR(oleft, RBColor::RB_BLACK);
}
- RB_SET_COLOR(tmp, EntryColor::Red);
+ RB_SET_COLOR(tmp, RBColor::RB_RED);
RB_ROTATE_RIGHT(head, tmp, oleft);
tmp = RB_RIGHT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
- RB_SET_COLOR(parent, EntryColor::Black);
+ RB_SET_COLOR(parent, RBColor::RB_BLACK);
if (RB_RIGHT(tmp)) {
- RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
+ RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK);
}
RB_ROTATE_LEFT(head, parent, tmp);
- elm = head->Root();
+ elm = head.Root();
break;
}
} else {
@@ -372,68 +343,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
tmp = RB_LEFT(parent);
}
- if (!tmp) {
- ASSERT_MSG(false, "tmp is invalid!");
- break;
- }
-
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
- RB_SET_COLOR(tmp, EntryColor::Red);
+ RB_SET_COLOR(tmp, RBColor::RB_RED);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
- Node* oright;
+ T* oright;
if ((oright = RB_RIGHT(tmp)) != nullptr) {
- RB_SET_COLOR(oright, EntryColor::Black);
+ RB_SET_COLOR(oright, RBColor::RB_BLACK);
}
- RB_SET_COLOR(tmp, EntryColor::Red);
+ RB_SET_COLOR(tmp, RBColor::RB_RED);
RB_ROTATE_LEFT(head, tmp, oright);
tmp = RB_LEFT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
- RB_SET_COLOR(parent, EntryColor::Black);
+ RB_SET_COLOR(parent, RBColor::RB_BLACK);
if (RB_LEFT(tmp)) {
- RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
+ RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK);
}
RB_ROTATE_RIGHT(head, parent, tmp);
- elm = head->Root();
+ elm = head.Root();
break;
}
}
}
if (elm) {
- RB_SET_COLOR(elm, EntryColor::Black);
+ RB_SET_COLOR(elm, RBColor::RB_BLACK);
}
}
-template <typename Node>
-Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
- Node* child = nullptr;
- Node* parent = nullptr;
- Node* old = elm;
- EntryColor color{};
-
- const auto finalize = [&] {
- if (color == EntryColor::Black) {
- RB_REMOVE_COLOR(head, parent, child);
- }
-
- return old;
- };
+template <typename T>
+requires HasRBEntry<T>
+constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
+ T* child = nullptr;
+ T* parent = nullptr;
+ T* old = elm;
+ RBColor color = RBColor::RB_BLACK;
if (RB_LEFT(elm) == nullptr) {
child = RB_RIGHT(elm);
} else if (RB_RIGHT(elm) == nullptr) {
child = RB_LEFT(elm);
} else {
- Node* left;
+ T* left;
elm = RB_RIGHT(elm);
while ((left = RB_LEFT(elm)) != nullptr) {
elm = left;
@@ -446,6 +405,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
if (child) {
RB_SET_PARENT(child, parent);
}
+
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
@@ -453,14 +413,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
RB_SET_RIGHT(parent, child);
}
} else {
- head->SetRoot(child);
+ head.SetRoot(child);
}
if (RB_PARENT(elm) == old) {
parent = elm;
}
- elm->SetEntry(old->GetEntry());
+ elm->SetRBEntry(old->GetRBEntry());
if (RB_PARENT(old)) {
if (RB_LEFT(RB_PARENT(old)) == old) {
@@ -469,17 +429,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
RB_SET_RIGHT(RB_PARENT(old), elm);
}
} else {
- head->SetRoot(elm);
+ head.SetRoot(elm);
}
+
RB_SET_PARENT(RB_LEFT(old), elm);
+
if (RB_RIGHT(old)) {
RB_SET_PARENT(RB_RIGHT(old), elm);
}
+
if (parent) {
left = parent;
}
- return finalize();
+ if (color == RBColor::RB_BLACK) {
+ RB_REMOVE_COLOR(head, parent, child);
+ }
+
+ return old;
}
parent = RB_PARENT(elm);
@@ -495,17 +462,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
RB_SET_RIGHT(parent, child);
}
} else {
- head->SetRoot(child);
+ head.SetRoot(child);
+ }
+
+ if (color == RBColor::RB_BLACK) {
+ RB_REMOVE_COLOR(head, parent, child);
+ }
+
+ return old;
+}
+
+template <typename T>
+requires HasRBEntry<T>
+constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
+ T *parent = nullptr, *tmp = nullptr;
+ while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
+ T* gparent = RB_PARENT(parent);
+ if (parent == RB_LEFT(gparent)) {
+ tmp = RB_RIGHT(gparent);
+ if (tmp && RB_IS_RED(tmp)) {
+ RB_SET_COLOR(tmp, RBColor::RB_BLACK);
+ RB_SET_BLACKRED(parent, gparent);
+ elm = gparent;
+ continue;
+ }
+
+ if (RB_RIGHT(parent) == elm) {
+ RB_ROTATE_LEFT(head, parent, tmp);
+ tmp = parent;
+ parent = elm;
+ elm = tmp;
+ }
+
+ RB_SET_BLACKRED(parent, gparent);
+ RB_ROTATE_RIGHT(head, gparent, tmp);
+ } else {
+ tmp = RB_LEFT(gparent);
+ if (tmp && RB_IS_RED(tmp)) {
+ RB_SET_COLOR(tmp, RBColor::RB_BLACK);
+ RB_SET_BLACKRED(parent, gparent);
+ elm = gparent;
+ continue;
+ }
+
+ if (RB_LEFT(parent) == elm) {
+ RB_ROTATE_RIGHT(head, parent, tmp);
+ tmp = parent;
+ parent = elm;
+ elm = tmp;
+ }
+
+ RB_SET_BLACKRED(parent, gparent);
+ RB_ROTATE_LEFT(head, gparent, tmp);
+ }
}
- return finalize();
+ RB_SET_COLOR(head.Root(), RBColor::RB_BLACK);
}
-// Inserts a node into the RB tree
-template <typename Node, typename CompareFunction>
-Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
- Node* parent = nullptr;
- Node* tmp = head->Root();
+template <typename T, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
+ T* parent = nullptr;
+ T* tmp = head.Root();
int comp = 0;
while (tmp) {
@@ -529,17 +548,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
RB_SET_RIGHT(parent, elm);
}
} else {
- head->SetRoot(elm);
+ head.SetRoot(elm);
}
RB_INSERT_COLOR(head, elm);
return nullptr;
}
-// Finds the node with the same key as elm
-template <typename Node, typename CompareFunction>
-Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
- Node* tmp = head->Root();
+template <typename T, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
+ T* tmp = head.Root();
while (tmp) {
const int comp = cmp(elm, tmp);
@@ -555,11 +574,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
return nullptr;
}
-// Finds the first node greater than or equal to the search key
-template <typename Node, typename CompareFunction>
-Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
- Node* tmp = head->Root();
- Node* res = nullptr;
+template <typename T, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
+ T* tmp = head.Root();
+ T* res = nullptr;
while (tmp) {
const int comp = cmp(elm, tmp);
@@ -576,13 +595,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
return res;
}
-// Finds the node with the same key as lelm
-template <typename Node, typename CompareFunction>
-Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
- Node* tmp = head->Root();
+template <typename T, typename U, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
+ T* tmp = head.Root();
while (tmp) {
- const int comp = lcmp(lelm, tmp);
+ const int comp = cmp(key, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
@@ -595,14 +614,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
return nullptr;
}
-// Finds the first node greater than or equal to the search key
-template <typename Node, typename CompareFunction>
-Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
- Node* tmp = head->Root();
- Node* res = nullptr;
+template <typename T, typename U, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
+ T* tmp = head.Root();
+ T* res = nullptr;
while (tmp) {
- const int comp = lcmp(lelm, tmp);
+ const int comp = cmp(key, tmp);
if (comp < 0) {
res = tmp;
tmp = RB_LEFT(tmp);
@@ -616,8 +635,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
return res;
}
-template <typename Node>
-Node* RB_NEXT(Node* elm) {
+template <typename T, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
+ T* tmp = head.Root();
+
+ while (true) {
+ const int comp = cmp(elm, tmp);
+ if (comp < 0) {
+ tmp = RB_LEFT(tmp);
+ } else if (comp > 0) {
+ tmp = RB_RIGHT(tmp);
+ } else {
+ return tmp;
+ }
+ }
+}
+
+template <typename T, typename U, typename Compare>
+requires HasRBEntry<T>
+constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
+ T* tmp = head.Root();
+
+ while (true) {
+ const int comp = cmp(key, tmp);
+ if (comp < 0) {
+ tmp = RB_LEFT(tmp);
+ } else if (comp > 0) {
+ tmp = RB_RIGHT(tmp);
+ } else {
+ return tmp;
+ }
+ }
+}
+
+template <typename T>
+requires HasRBEntry<T>
+constexpr T* RB_NEXT(T* elm) {
if (RB_RIGHT(elm)) {
elm = RB_RIGHT(elm);
while (RB_LEFT(elm)) {
@@ -636,8 +690,9 @@ Node* RB_NEXT(Node* elm) {
return elm;
}
-template <typename Node>
-Node* RB_PREV(Node* elm) {
+template <typename T>
+requires HasRBEntry<T>
+constexpr T* RB_PREV(T* elm) {
if (RB_LEFT(elm)) {
elm = RB_LEFT(elm);
while (RB_RIGHT(elm)) {
@@ -656,30 +711,32 @@ Node* RB_PREV(Node* elm) {
return elm;
}
-template <typename Node>
-Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
- Node* tmp = head->Root();
- Node* parent = nullptr;
+template <typename T>
+requires HasRBEntry<T>
+constexpr T* RB_MIN(RBHead<T>& head) {
+ T* tmp = head.Root();
+ T* parent = nullptr;
while (tmp) {
parent = tmp;
- if (is_min) {
- tmp = RB_LEFT(tmp);
- } else {
- tmp = RB_RIGHT(tmp);
- }
+ tmp = RB_LEFT(tmp);
}
return parent;
}
-template <typename Node>
-Node* RB_MIN(RBHead<Node>* head) {
- return RB_MINMAX(head, true);
-}
+template <typename T>
+requires HasRBEntry<T>
+constexpr T* RB_MAX(RBHead<T>& head) {
+ T* tmp = head.Root();
+ T* parent = nullptr;
-template <typename Node>
-Node* RB_MAX(RBHead<Node>* head) {
- return RB_MINMAX(head, false);
+ while (tmp) {
+ parent = tmp;
+ tmp = RB_RIGHT(tmp);
+ }
+
+ return parent;
}
-} // namespace Common
+
+} // 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 @@
#pragma once
-#include <cstring>
#include <utility>
#ifdef _MSC_VER
@@ -13,6 +12,7 @@
#pragma intrinsic(_umul128)
#pragma intrinsic(_udiv128)
#else
+#include <cstring>
#include <x86intrin.h>
#endif
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 @@
#include <array>
#include <functional>
#include <string>
-#include <string_view>
#include "common/common_types.h"
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 @@
#pragma once
-#include <type_traits>
#include <utility>
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstdint>
-
#include "common/uint128.h"
#include "common/wall_clock.h"
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 @@
-// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
+// Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
+#include <array>
#include <cstring>
+#include <iterator>
+#include <string_view>
+#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/x64/cpu_detect.h"
@@ -17,7 +20,7 @@
// clang-format on
#endif
-static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
+static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
#if defined(__DragonFly__) || defined(__FreeBSD__)
// Despite the name, this is just do_cpuid() with ECX as second input.
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) {
#endif
}
-static inline void __cpuid(int info[4], int function_id) {
+static inline void __cpuid(int info[4], u32 function_id) {
return __cpuidex(info, function_id, 0);
}
@@ -45,6 +48,17 @@ static inline u64 _xgetbv(u32 index) {
namespace Common {
+CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
+ if (brand_string == "GenuineIntel") {
+ return Manufacturer::Intel;
+ } else if (brand_string == "AuthenticAMD") {
+ return Manufacturer::AMD;
+ } else if (brand_string == "HygonGenuine") {
+ return Manufacturer::Hygon;
+ }
+ return Manufacturer::Unknown;
+}
+
// Detects the various CPU features
static CPUCaps Detect() {
CPUCaps caps = {};
@@ -53,75 +67,74 @@ static CPUCaps Detect() {
// yuzu at all anyway
int cpu_id[4];
- memset(caps.brand_string, 0, sizeof(caps.brand_string));
- // Detect CPU's CPUID capabilities and grab CPU string
+ // Detect CPU's CPUID capabilities and grab manufacturer string
__cpuid(cpu_id, 0x00000000);
- u32 max_std_fn = cpu_id[0]; // EAX
-
- std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
- std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
- std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
- if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
- caps.manufacturer = Manufacturer::Intel;
- else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
- caps.manufacturer = Manufacturer::AMD;
- else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
- caps.manufacturer = Manufacturer::Hygon;
- else
- caps.manufacturer = Manufacturer::Unknown;
+ const u32 max_std_fn = cpu_id[0]; // EAX
- __cpuid(cpu_id, 0x80000000);
+ std::memset(caps.brand_string, 0, std::size(caps.brand_string));
+ std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
+ std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
+ std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
+
+ caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
- u32 max_ex_fn = cpu_id[0];
+ // Set reasonable default cpu string even if brand string not available
+ std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
- // Set reasonable default brand string even if brand string not available
- strcpy(caps.cpu_string, caps.brand_string);
+ __cpuid(cpu_id, 0x80000000);
+
+ const u32 max_ex_fn = cpu_id[0];
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
- if ((cpu_id[3] >> 25) & 1)
- caps.sse = true;
- if ((cpu_id[3] >> 26) & 1)
- caps.sse2 = true;
- if ((cpu_id[2]) & 1)
- caps.sse3 = true;
- if ((cpu_id[2] >> 9) & 1)
- caps.ssse3 = true;
- if ((cpu_id[2] >> 19) & 1)
- caps.sse4_1 = true;
- if ((cpu_id[2] >> 20) & 1)
- caps.sse4_2 = true;
- if ((cpu_id[2] >> 25) & 1)
- caps.aes = true;
+ caps.sse = Common::Bit<25>(cpu_id[3]);
+ caps.sse2 = Common::Bit<26>(cpu_id[3]);
+ caps.sse3 = Common::Bit<0>(cpu_id[2]);
+ caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
+ caps.ssse3 = Common::Bit<9>(cpu_id[2]);
+ caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
+ caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
+ caps.movbe = Common::Bit<22>(cpu_id[2]);
+ caps.popcnt = Common::Bit<23>(cpu_id[2]);
+ caps.aes = Common::Bit<25>(cpu_id[2]);
+ caps.f16c = Common::Bit<29>(cpu_id[2]);
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
- if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
+ if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
- if ((cpu_id[2] >> 12) & 1)
+ if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
- // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed
- if ((cpu_id[1] >> 5) & 1)
- caps.avx2 = caps.avx;
- if ((cpu_id[1] >> 3) & 1)
- caps.bmi1 = true;
- if ((cpu_id[1] >> 8) & 1)
- caps.bmi2 = true;
- // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP)
- if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 &&
- (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) {
- caps.avx512 = caps.avx2;
+ // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
+ if (caps.avx) {
+ caps.avx2 = Common::Bit<5>(cpu_id[1]);
+ caps.avx512f = Common::Bit<16>(cpu_id[1]);
+ caps.avx512dq = Common::Bit<17>(cpu_id[1]);
+ caps.avx512cd = Common::Bit<28>(cpu_id[1]);
+ caps.avx512bw = Common::Bit<30>(cpu_id[1]);
+ caps.avx512vl = Common::Bit<31>(cpu_id[1]);
+ caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
+ caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
}
+
+ caps.bmi1 = Common::Bit<3>(cpu_id[1]);
+ caps.bmi2 = Common::Bit<8>(cpu_id[1]);
+ caps.sha = Common::Bit<29>(cpu_id[1]);
+
+ caps.gfni = Common::Bit<8>(cpu_id[2]);
+
+ __cpuidex(cpu_id, 0x00000007, 0x00000001);
+ caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
}
}
@@ -138,15 +151,13 @@ static CPUCaps Detect() {
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
- if ((cpu_id[2] >> 16) & 1)
- caps.fma4 = true;
+ caps.lzcnt = Common::Bit<5>(cpu_id[2]);
+ caps.fma4 = Common::Bit<16>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
__cpuid(cpu_id, 0x80000007);
- if (cpu_id[3] & (1 << 8)) {
- caps.invariant_tsc = true;
- }
+ caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
}
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 @@
-// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
+// Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
#pragma once
-namespace Common {
+#include <string_view>
+#include "common/common_types.h"
-enum class Manufacturer : u32 {
- Intel = 0,
- AMD = 1,
- Hygon = 2,
- Unknown = 3,
-};
+namespace Common {
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
+
+ enum class Manufacturer : u8 {
+ Unknown = 0,
+ Intel = 1,
+ AMD = 2,
+ Hygon = 3,
+ };
+
+ static Manufacturer ParseManufacturer(std::string_view brand_string);
+
Manufacturer manufacturer;
- char cpu_string[0x21];
- char brand_string[0x41];
- bool sse;
- bool sse2;
- bool sse3;
- bool ssse3;
- bool sse4_1;
- bool sse4_2;
- bool lzcnt;
- bool avx;
- bool avx2;
- bool avx512;
- bool bmi1;
- bool bmi2;
- bool fma;
- bool fma4;
- bool aes;
- bool invariant_tsc;
+ char brand_string[13];
+
+ char cpu_string[48];
+
u32 base_frequency;
u32 max_frequency;
u32 bus_frequency;
+
+ bool sse : 1;
+ bool sse2 : 1;
+ bool sse3 : 1;
+ bool ssse3 : 1;
+ bool sse4_1 : 1;
+ bool sse4_2 : 1;
+
+ bool avx : 1;
+ bool avx_vnni : 1;
+ bool avx2 : 1;
+ bool avx512f : 1;
+ bool avx512dq : 1;
+ bool avx512cd : 1;
+ bool avx512bw : 1;
+ bool avx512vl : 1;
+ bool avx512vbmi : 1;
+ bool avx512bitalg : 1;
+
+ bool aes : 1;
+ bool bmi1 : 1;
+ bool bmi2 : 1;
+ bool f16c : 1;
+ bool fma : 1;
+ bool fma4 : 1;
+ bool gfni : 1;
+ bool invariant_tsc : 1;
+ bool lzcnt : 1;
+ bool movbe : 1;
+ bool pclmulqdq : 1;
+ bool popcnt : 1;
+ bool sha : 1;
};
/**
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 @@
#include <array>
#include <chrono>
-#include <limits>
-#include <mutex>
#include <thread>
#include "common/atomic_ops.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
namespace Common {
+#ifdef _MSC_VER
+__forceinline static u64 FencedRDTSC() {
+ _mm_lfence();
+ _ReadWriteBarrier();
+ const u64 result = __rdtsc();
+ _mm_lfence();
+ _ReadWriteBarrier();
+ return result;
+}
+#else
+static u64 FencedRDTSC() {
+ u64 result;
+ asm volatile("lfence\n\t"
+ "rdtsc\n\t"
+ "shl $32, %%rdx\n\t"
+ "or %%rdx, %0\n\t"
+ "lfence"
+ : "=a"(result)
+ :
+ : "rdx", "memory", "cc");
+ return result;
+}
+#endif
+
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
- _mm_mfence();
- __rdtsc();
+ FencedRDTSC();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
- _mm_mfence();
- __rdtsc();
+ FencedRDTSC();
// Get the current time.
const auto start_time = std::chrono::steady_clock::now();
- _mm_mfence();
- const u64 tsc_start = __rdtsc();
+ const u64 tsc_start = FencedRDTSC();
// Wait for 200 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{200});
const auto end_time = std::chrono::steady_clock::now();
- _mm_mfence();
- const u64 tsc_end = __rdtsc();
+ const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
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
u64 rtsc_frequency_)
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
rtsc_frequency_} {
- _mm_mfence();
- time_point.inner.last_measure = __rdtsc();
+ time_point.inner.last_measure = FencedRDTSC();
time_point.inner.accumulated_ticks = 0U;
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -57,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
u64 NativeClock::GetRTSC() {
TimePoint new_time_point{};
TimePoint current_time_point{};
+
+ current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
- current_time_point.pack = time_point.pack;
- _mm_mfence();
- const u64 current_measure = __rdtsc();
+ const u64 current_measure = FencedRDTSC();
u64 diff = current_measure - current_time_point.inner.last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -68,7 +89,7 @@ u64 NativeClock::GetRTSC() {
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
- current_time_point.pack));
+ current_time_point.pack, current_time_point.pack));
/// The clock cannot be more precise than the guest timer, remove the lower bits
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
}
@@ -77,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
TimePoint current_time_point{};
TimePoint new_time_point{};
+
+ current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
- current_time_point.pack = time_point.pack;
new_time_point.pack = current_time_point.pack;
- _mm_mfence();
- new_time_point.inner.last_measure = __rdtsc();
+ new_time_point.inner.last_measure = FencedRDTSC();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
- current_time_point.pack));
+ current_time_point.pack, current_time_point.pack));
}
}
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 @@
#pragma once
-#include <optional>
-
#include "common/wall_clock.h"
namespace 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
arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
+ arm/symbols.cpp
+ arm/symbols.h
constants.cpp
constants.h
core.cpp
@@ -122,6 +124,8 @@ add_library(core STATIC
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
+ frontend/applets/mii_edit.cpp
+ frontend/applets/mii_edit.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -207,6 +211,8 @@ add_library(core STATIC
hle/kernel/k_memory_region.h
hle/kernel/k_memory_region_type.h
hle/kernel/k_page_bitmap.h
+ hle/kernel/k_page_buffer.cpp
+ hle/kernel/k_page_buffer.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
hle/kernel/k_page_linked_list.h
@@ -244,6 +250,8 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
+ hle/kernel/k_thread_local_page.cpp
+ hle/kernel/k_thread_local_page.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
@@ -300,6 +308,9 @@ add_library(core STATIC
hle/service/am/applets/applet_error.h
hle/service/am/applets/applet_general_backend.cpp
hle/service/am/applets/applet_general_backend.h
+ hle/service/am/applets/applet_mii_edit.cpp
+ hle/service/am/applets/applet_mii_edit.h
+ hle/service/am/applets/applet_mii_edit_types.h
hle/service/am/applets/applet_profile_select.cpp
hle/service/am/applets/applet_profile_select.h
hle/service/am/applets/applet_software_keyboard.cpp
@@ -423,6 +434,8 @@ add_library(core STATIC
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
+ hle/service/hid/hidbus.cpp
+ hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/ring_lifo.h
@@ -449,6 +462,18 @@ add_library(core STATIC
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
+ hle/service/hid/hidbus/hidbus_base.cpp
+ hle/service/hid/hidbus/hidbus_base.h
+ hle/service/hid/hidbus/ringcon.cpp
+ hle/service/hid/hidbus/ringcon.h
+ hle/service/hid/hidbus/starlink.cpp
+ hle/service/hid/hidbus/starlink.h
+ hle/service/hid/hidbus/stubbed.cpp
+ hle/service/hid/hidbus/stubbed.h
+ hle/service/jit/jit_context.cpp
+ hle/service/jit/jit_context.h
+ hle/service/jit/jit.cpp
+ hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/errors.h
@@ -526,10 +551,35 @@ add_library(core STATIC
hle/service/nvdrv/nvmemp.h
hle/service/nvdrv/syncpoint_manager.cpp
hle/service/nvdrv/syncpoint_manager.h
- hle/service/nvflinger/buffer_queue.cpp
- hle/service/nvflinger/buffer_queue.h
+ hle/service/nvflinger/binder.h
+ hle/service/nvflinger/buffer_item.h
+ hle/service/nvflinger/buffer_item_consumer.cpp
+ hle/service/nvflinger/buffer_item_consumer.h
+ hle/service/nvflinger/buffer_queue_consumer.cpp
+ hle/service/nvflinger/buffer_queue_consumer.h
+ hle/service/nvflinger/buffer_queue_core.cpp
+ hle/service/nvflinger/buffer_queue_core.h
+ hle/service/nvflinger/buffer_queue_defs.h
+ hle/service/nvflinger/buffer_queue_producer.cpp
+ hle/service/nvflinger/buffer_queue_producer.h
+ hle/service/nvflinger/buffer_slot.h
+ hle/service/nvflinger/buffer_transform_flags.h
+ hle/service/nvflinger/consumer_base.cpp
+ hle/service/nvflinger/consumer_base.h
+ hle/service/nvflinger/consumer_listener.h
+ hle/service/nvflinger/graphic_buffer_producer.cpp
+ hle/service/nvflinger/graphic_buffer_producer.h
+ hle/service/nvflinger/hos_binder_driver_server.cpp
+ hle/service/nvflinger/hos_binder_driver_server.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
+ hle/service/nvflinger/parcel.h
+ hle/service/nvflinger/pixel_format.h
+ hle/service/nvflinger/producer_listener.h
+ hle/service/nvflinger/status.h
+ hle/service/nvflinger/ui/fence.h
+ hle/service/nvflinger/ui/graphic_buffer.h
+ hle/service/nvflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
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 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
+#include "core/arm/symbols.h"
#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
#include "core/loader/loader.h"
#include "core/memory.h"
-namespace Core {
-namespace {
-
-constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
-constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
-constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
-constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
-
-enum class ELFSymbolType : u8 {
- None = 0,
- Object = 1,
- Function = 2,
- Section = 3,
- File = 4,
- Common = 5,
- TLS = 6,
-};
-
-enum class ELFSymbolBinding : u8 {
- Local = 0,
- Global = 1,
- Weak = 2,
-};
-
-enum class ELFSymbolVisibility : u8 {
- Default = 0,
- Internal = 1,
- Hidden = 2,
- Protected = 3,
-};
-
-struct ELFSymbol {
- u32 name_index;
- union {
- u8 info;
-
- BitField<0, 4, ELFSymbolType> type;
- BitField<4, 4, ELFSymbolBinding> binding;
- };
- ELFSymbolVisibility visibility;
- u16 sh_index;
- u64 value;
- u64 size;
-};
-static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
-
-using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
-
-Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
- const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
-
- if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
- memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
- return {};
- }
-
- const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
-
- VAddr string_table_offset{};
- VAddr symbol_table_offset{};
- u64 symbol_entry_size{};
-
- VAddr dynamic_index = dynamic_offset;
- while (true) {
- const u64 tag = memory.Read64(dynamic_index);
- const u64 value = memory.Read64(dynamic_index + 0x8);
- dynamic_index += 0x10;
-
- if (tag == ELF_DYNAMIC_TAG_NULL) {
- break;
- }
-
- if (tag == ELF_DYNAMIC_TAG_STRTAB) {
- string_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
- symbol_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
- symbol_entry_size = value;
- }
- }
-
- if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
- return {};
- }
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
- const auto string_table_address = text_offset + string_table_offset;
- const auto symbol_table_address = text_offset + symbol_table_offset;
-
- Symbols out;
-
- VAddr symbol_index = symbol_table_address;
- while (symbol_index < string_table_address) {
- ELFSymbol symbol{};
- memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
-
- VAddr string_offset = string_table_address + symbol.name_index;
- std::string name;
- for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
- name += static_cast<char>(c);
- }
-
- symbol_index += symbol_entry_size;
- out.push_back({symbol, name});
- }
-
- return out;
-}
-
-std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
- const auto iter =
- std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
- const auto& symbol = pair.first;
- const auto end_address = symbol.value + symbol.size;
- return func_address >= symbol.value && func_address < end_address;
- });
-
- if (iter == symbols.end()) {
- return std::nullopt;
- }
-
- return iter->second;
-}
-
-} // Anonymous namespace
+namespace Core {
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
- System& system, const ThreadContext64& ctx) {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = ctx.cpu_registers[29];
- auto lr = ctx.cpu_registers[30];
- while (true) {
- out.push_back({
- .module = "",
- .address = 0,
- .original_address = lr,
- .offset = 0,
- .name = {},
- });
-
- if (fp == 0) {
- break;
- }
-
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
-
- std::map<VAddr, std::string> modules;
- auto& loader{system.GetAppLoader()};
- if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
- }
-
- std::map<std::string, Symbols> symbols;
- for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
- }
-
- for (auto& entry : out) {
- VAddr base = 0;
- for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
- const auto& module{*iter};
- if (entry.original_address >= module.first) {
- entry.module = module.second;
- base = module.first;
- break;
- }
- }
-
- entry.offset = entry.original_address - base;
- entry.address = SEGMENT_BASE + entry.offset;
-
- if (entry.module.empty())
- entry.module = "unknown";
-
- const auto symbol_set = symbols.find(entry.module);
- if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
- if (symbol.has_value()) {
- // TODO(DarkLordZach): Add demangling of symbol names.
- entry.name = *symbol;
- }
- }
- }
-
- return out;
+ Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
+ return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
}
-std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = GetReg(29);
- auto lr = GetReg(30);
- while (true) {
- out.push_back({"", 0, lr, 0, ""});
- if (!fp) {
- break;
- }
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
+ Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
+ return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
+}
+void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
std::map<VAddr, std::string> modules;
auto& loader{system.GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
+ return;
}
- std::map<std::string, Symbols> symbols;
+ std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
+ symbols.insert_or_assign(module.second,
+ Symbols::GetSymbols(module.first, system.Memory(),
+ system.CurrentProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -244,24 +59,23 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
entry.offset = entry.original_address - base;
entry.address = SEGMENT_BASE + entry.offset;
- if (entry.module.empty())
+ if (entry.module.empty()) {
entry.module = "unknown";
+ }
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+ const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
}
}
}
-
- return out;
}
void ARM_Interface::LogBacktrace() const {
- const VAddr sp = GetReg(13);
+ const VAddr sp = GetSP();
const VAddr pc = GetPC();
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
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:
virtual u64 GetPC() const = 0;
/**
+ * Get the current Stack Pointer
+ * @return Returns current SP
+ */
+ virtual u64 GetSP() const = 0;
+
+ /**
* Get an ARM register
* @param index Register index
* @return Returns the value in the register
@@ -171,6 +177,9 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
+ /// Signal an interrupt and ask the core to halt as soon as possible.
+ virtual void SignalInterrupt() = 0;
+
struct BacktraceEntry {
std::string module;
u64 address;
@@ -180,16 +189,12 @@ public:
};
static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
const ThreadContext64& ctx);
- std::vector<BacktraceEntry> GetBacktrace() const;
+ virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
- /// fp (= r29) points to the last frame record.
- /// Note that this is the frame record for the *previous* frame, not the current one.
- /// Note we need to subtract 4 from our last read to get the proper address
- /// Frame records are two words long:
- /// fp+0 : pointer to previous frame record
- /// fp+8 : value of lr for frame
void LogBacktrace() const;
protected:
@@ -197,6 +202,8 @@ protected:
System& system;
CPUInterrupts& interrupt_handlers;
bool uses_wall_clock;
+
+ static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
};
} // 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 {
using namespace Common::Literals;
+constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
+constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
+
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -70,11 +73,13 @@ public:
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
+ parent.LogBacktrace();
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
MemoryReadCode(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
+ parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -82,15 +87,13 @@ public:
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(svc_call);
}
void AddTicks(u64 ticks) override {
- if (parent.uses_wall_clock) {
- return;
- }
+ ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -106,12 +109,8 @@ public:
}
u64 GetTicksRemaining() override {
- if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
- return minimum_run_cycles;
- }
- return 0U;
- }
+ ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -146,11 +145,19 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ config.far_code_offset = 4_MiB;
+ }
+
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
if (!Settings::values.cpuopt_page_tables) {
@@ -186,35 +193,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
- }
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
+ }
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- }
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
- }
- if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
- }
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- }
- if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,20 +235,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
void ARM_Dynarmic_32::Run() {
while (true) {
- jit->Run();
- if (!svc_called) {
- break;
+ const auto hr = jit.load()->Run();
+ if (Has(hr, svc_call)) {
+ Kernel::Svc::Call(system, svc_swi);
}
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
+ if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
}
void ARM_Dynarmic_32::Step() {
- jit->Step();
+ jit.load()->Step();
}
ARM_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
cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr)) {}
+ null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
void ARM_Dynarmic_32::SetPC(u64 pc) {
- jit->Regs()[15] = static_cast<u32>(pc);
+ jit.load()->Regs()[15] = static_cast<u32>(pc);
}
u64 ARM_Dynarmic_32::GetPC() const {
- return jit->Regs()[15];
+ return jit.load()->Regs()[15];
+}
+
+u64 ARM_Dynarmic_32::GetSP() const {
+ return jit.load()->Regs()[13];
}
u64 ARM_Dynarmic_32::GetReg(int index) const {
- return jit->Regs()[index];
+ return jit.load()->Regs()[index];
}
void ARM_Dynarmic_32::SetReg(int index, u64 value) {
- jit->Regs()[index] = static_cast<u32>(value);
+ jit.load()->Regs()[index] = static_cast<u32>(value);
}
u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -272,11 +287,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
u32 ARM_Dynarmic_32::GetPSTATE() const {
- return jit->Cpsr();
+ return jit.load()->Cpsr();
}
void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
- jit->SetCpsr(cpsr);
+ jit.load()->SetCpsr(cpsr);
}
u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -297,7 +312,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
Dynarmic::A32::Context context;
- jit->SaveContext(context);
+ jit.load()->SaveContext(context);
ctx.cpu_registers = context.Regs();
ctx.extension_registers = context.ExtRegs();
ctx.cpsr = context.Cpsr();
@@ -310,24 +325,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
context.ExtRegs() = ctx.extension_registers;
context.SetCpsr(ctx.cpsr);
context.SetFpscr(ctx.fpscr);
- jit->LoadContext(context);
+ jit.load()->LoadContext(context);
}
void ARM_Dynarmic_32::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_32::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
}
void ARM_Dynarmic_32::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(static_cast<u32>(addr), size);
+ jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
}
void ARM_Dynarmic_32::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -338,13 +356,29 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
- jit = iter->second;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table);
+ std::shared_ptr new_jit = MakeJit(&page_table);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
+ u64 sp, u64 lr) {
+ // No way to get accurate stack traces in A32 yet
+ return {};
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
+ System& system, const ThreadContext32& ctx) {
+ return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(13), GetReg(14));
}
} // 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 @@
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -34,6 +35,7 @@ public:
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
@@ -57,6 +59,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override {}
void PrepareReschedule() override;
+ void SignalInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -64,9 +67,16 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +89,14 @@ private:
std::shared_ptr<DynarmicCP15> cp15;
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A32::Jit> jit;
+
+ std::shared_ptr<Dynarmic::A32::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A32::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
-
- bool shutdown{};
};
} // 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 {
using Vector = Dynarmic::A64::Vector;
using namespace Common::Literals;
+constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
+constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
+
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -81,6 +84,7 @@ public:
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
+ parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
num_instructions, MemoryReadCode(pc));
@@ -93,17 +97,19 @@ public:
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
- parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
+ parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
- parent.ClearInstructionCache();
+ parent.system.InvalidateCpuInstructionCaches();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
+
+ parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -116,21 +122,19 @@ public:
return;
case Dynarmic::A64::Exception::Breakpoint:
default:
+ parent.LogBacktrace();
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
}
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(svc_call);
}
void AddTicks(u64 ticks) override {
- if (parent.uses_wall_clock) {
- return;
- }
+ ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -145,12 +149,8 @@ public:
}
u64 GetTicksRemaining() override {
- if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
- return minimum_run_cycles;
- }
- return 0U;
- }
+ ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -206,11 +206,19 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ config.far_code_offset = 4_MiB;
+ }
+
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
if (!Settings::values.cpuopt_page_tables) {
@@ -246,35 +254,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
- }
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_fastmem_check) {
+ config.fastmem_address_space_bits = 64;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
+ }
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- }
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
- }
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- }
- if (Settings::values.cpuopt_unsafe_fastmem_check) {
config.fastmem_address_space_bits = 64;
- }
- if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- config.fastmem_address_space_bits = 64;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -282,20 +296,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
void ARM_Dynarmic_64::Run() {
while (true) {
- jit->Run();
- if (!svc_called) {
- break;
+ const auto hr = jit.load()->Run();
+ if (Has(hr, svc_call)) {
+ Kernel::Svc::Call(system, svc_swi);
}
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
+ if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
}
void ARM_Dynarmic_64::Step() {
- jit->Step();
+ jit.load()->Step();
}
ARM_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
: ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr, 48)) {}
+ null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
void ARM_Dynarmic_64::SetPC(u64 pc) {
- jit->SetPC(pc);
+ jit.load()->SetPC(pc);
}
u64 ARM_Dynarmic_64::GetPC() const {
- return jit->GetPC();
+ return jit.load()->GetPC();
+}
+
+u64 ARM_Dynarmic_64::GetSP() const {
+ return jit.load()->GetSP();
}
u64 ARM_Dynarmic_64::GetReg(int index) const {
- return jit->GetRegister(index);
+ return jit.load()->GetRegister(index);
}
void ARM_Dynarmic_64::SetReg(int index, u64 value) {
- jit->SetRegister(index, value);
+ jit.load()->SetRegister(index, value);
}
u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
- return jit->GetVector(index);
+ return jit.load()->GetVector(index);
}
void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
- jit->SetVector(index, value);
+ jit.load()->SetVector(index, value);
}
u32 ARM_Dynarmic_64::GetPSTATE() const {
- return jit->GetPstate();
+ return jit.load()->GetPstate();
}
void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
- jit->SetPstate(pstate);
+ jit.load()->SetPstate(pstate);
}
u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -357,42 +373,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
- ctx.cpu_registers = jit->GetRegisters();
- ctx.sp = jit->GetSP();
- ctx.pc = jit->GetPC();
- ctx.pstate = jit->GetPstate();
- ctx.vector_registers = jit->GetVectors();
- ctx.fpcr = jit->GetFpcr();
- ctx.fpsr = jit->GetFpsr();
+ Dynarmic::A64::Jit* j = jit.load();
+ ctx.cpu_registers = j->GetRegisters();
+ ctx.sp = j->GetSP();
+ ctx.pc = j->GetPC();
+ ctx.pstate = j->GetPstate();
+ ctx.vector_registers = j->GetVectors();
+ ctx.fpcr = j->GetFpcr();
+ ctx.fpsr = j->GetFpsr();
ctx.tpidr = cb->tpidr_el0;
}
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
- jit->SetRegisters(ctx.cpu_registers);
- jit->SetSP(ctx.sp);
- jit->SetPC(ctx.pc);
- jit->SetPstate(ctx.pstate);
- jit->SetVectors(ctx.vector_registers);
- jit->SetFpcr(ctx.fpcr);
- jit->SetFpsr(ctx.fpsr);
+ Dynarmic::A64::Jit* j = jit.load();
+ j->SetRegisters(ctx.cpu_registers);
+ j->SetSP(ctx.sp);
+ j->SetPC(ctx.pc);
+ j->SetPstate(ctx.pstate);
+ j->SetVectors(ctx.vector_registers);
+ j->SetFpcr(ctx.fpcr);
+ j->SetFpsr(ctx.fpsr);
SetTPIDR_EL0(ctx.tpidr);
}
void ARM_Dynarmic_64::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_64::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
}
void ARM_Dynarmic_64::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(addr, size);
+ jit.load()->InvalidateCacheRange(addr, size);
}
void ARM_Dynarmic_64::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -403,13 +424,48 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
- jit = iter->second;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ // fp (= r29) points to the last frame record.
+ // Note that this is the frame record for the *previous* frame, not the current one.
+ // Note we need to subtract 4 from our last read to get the proper address
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+8 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp) {
+ break;
+ }
+ lr = memory.Read64(fp + 8) - 4;
+ fp = memory.Read64(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
+ System& system, const ThreadContext64& ctx) {
+ return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(29), GetReg(30));
}
} // 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 @@
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -32,6 +33,7 @@ public:
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
@@ -51,6 +53,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override;
void PrepareReschedule() override;
+ void SignalInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -58,10 +61,17 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext64& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +83,13 @@ private:
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A64::Jit> jit;
+ std::shared_ptr<Dynarmic::A64::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A64::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
-
- bool shutdown{};
};
} // 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 @@
#pragma once
-#include <unordered_map>
-
#include <dynarmic/interface/exclusive_monitor.h>
#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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/memory.h"
+
+namespace Core {
+namespace {
+
+constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
+constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
+constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
+constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
+
+enum class ELFSymbolType : u8 {
+ None = 0,
+ Object = 1,
+ Function = 2,
+ Section = 3,
+ File = 4,
+ Common = 5,
+ TLS = 6,
+};
+
+enum class ELFSymbolBinding : u8 {
+ Local = 0,
+ Global = 1,
+ Weak = 2,
+};
+
+enum class ELFSymbolVisibility : u8 {
+ Default = 0,
+ Internal = 1,
+ Hidden = 2,
+ Protected = 3,
+};
+
+struct ELF64Symbol {
+ u32 name_index;
+ union {
+ u8 info;
+
+ BitField<0, 4, ELFSymbolType> type;
+ BitField<4, 4, ELFSymbolBinding> binding;
+ };
+ ELFSymbolVisibility visibility;
+ u16 sh_index;
+ u64 value;
+ u64 size;
+};
+static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
+
+struct ELF32Symbol {
+ u32 name_index;
+ u32 value;
+ u32 size;
+ union {
+ u8 info;
+
+ BitField<0, 4, ELFSymbolType> type;
+ BitField<4, 4, ELFSymbolBinding> binding;
+ };
+ ELFSymbolVisibility visibility;
+ u16 sh_index;
+};
+static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
+
+} // Anonymous namespace
+
+namespace Symbols {
+
+template <typename Word, typename ELFSymbol, typename ByteReader>
+static Symbols GetSymbols(ByteReader ReadBytes) {
+ const auto Read8{[&](u64 index) {
+ u8 ret;
+ ReadBytes(&ret, index, sizeof(u8));
+ return ret;
+ }};
+
+ const auto Read32{[&](u64 index) {
+ u32 ret;
+ ReadBytes(&ret, index, sizeof(u32));
+ return ret;
+ }};
+
+ const auto ReadWord{[&](u64 index) {
+ Word ret;
+ ReadBytes(&ret, index, sizeof(Word));
+ return ret;
+ }};
+
+ const u32 mod_offset = Read32(4);
+
+ if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return {};
+ }
+
+ VAddr string_table_offset{};
+ VAddr symbol_table_offset{};
+ u64 symbol_entry_size{};
+
+ const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
+
+ VAddr dynamic_index = dynamic_offset;
+ while (true) {
+ const Word tag = ReadWord(dynamic_index);
+ const Word value = ReadWord(dynamic_index + sizeof(Word));
+ dynamic_index += 2 * sizeof(Word);
+
+ if (tag == ELF_DYNAMIC_TAG_NULL) {
+ break;
+ }
+
+ if (tag == ELF_DYNAMIC_TAG_STRTAB) {
+ string_table_offset = value;
+ } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
+ symbol_table_offset = value;
+ } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
+ symbol_entry_size = value;
+ }
+ }
+
+ if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
+ return {};
+ }
+
+ Symbols out;
+
+ VAddr symbol_index = symbol_table_offset;
+ while (symbol_index < string_table_offset) {
+ ELFSymbol symbol{};
+ ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
+
+ VAddr string_offset = string_table_offset + symbol.name_index;
+ std::string name;
+ for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
+ name += static_cast<char>(c);
+ }
+
+ symbol_index += symbol_entry_size;
+ out[name] = std::make_pair(symbol.value, symbol.size);
+ }
+
+ return out;
+}
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
+ const auto ReadBytes{
+ [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
+
+ if (is_64) {
+ return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+ } else {
+ return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+ }
+}
+
+Symbols GetSymbols(std::span<const u8> data, bool is_64) {
+ const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
+ std::memcpy(ptr, data.data() + offset, size);
+ }};
+
+ if (is_64) {
+ return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+ } else {
+ return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+ }
+}
+
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
+ const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
+ const auto& [name, sym_info] = pair;
+ const auto& [start_address, size] = sym_info;
+ const auto end_address = start_address + size;
+ return addr >= start_address && addr < end_address;
+ });
+
+ if (iter == symbols.cend()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
+} // namespace Symbols
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <optional>
+#include <span>
+#include <string>
+#include <utility>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+} // namespace Core::Memory
+
+namespace Core::Symbols {
+
+using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
+Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
+
+} // 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 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
-#include "core/hle/service/hid/hid.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
@@ -326,7 +325,9 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
- gpu_core->NotifyShutdown();
+ if (gpu_core != nullptr) {
+ gpu_core->NotifyShutdown();
+ }
services.reset();
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 @@
#include <chrono>
#include <functional>
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "common/thread.h"
#include "common/wall_clock.h"
@@ -149,8 +149,8 @@ private:
std::shared_ptr<EventType> ev_lost;
Common::Event event{};
Common::Event pause_event{};
- Common::SpinLock basic_lock{};
- Common::SpinLock advance_lock{};
+ std::mutex basic_lock;
+ std::mutex advance_lock;
std::unique_ptr<std::thread> timer_thread;
std::atomic<bool> paused{};
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 @@
#pragma once
#include <cstddef>
-#include <iterator>
#include "common/common_funcs.h"
#include "common/common_types.h"
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 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
+#ifndef _WIN32
#include "common/string_util.h"
+#endif
+
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
@@ -145,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
// LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
+ const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
+
+ std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr && load_dir->GetSize() > 0) {
- auto patch_dirs = load_dir->GetSubdirectories();
- std::sort(
- patch_dirs.begin(), patch_dirs.end(),
- [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
-
- std::vector<VirtualDir> layers;
- layers.reserve(patch_dirs.size() + 1);
- for (const auto& subdir : patch_dirs) {
- if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
- continue;
+ const auto load_patch_dirs = load_dir->GetSubdirectories();
+ patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
+ }
- auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
- if (exefs_dir != nullptr)
- layers.push_back(std::move(exefs_dir));
- }
- layers.push_back(exefs);
+ std::sort(patch_dirs.begin(), patch_dirs.end(),
+ [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
- auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
- if (layered != nullptr) {
- LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
- exefs = std::move(layered);
- }
+ std::vector<VirtualDir> layers;
+ layers.reserve(patch_dirs.size() + 1);
+ for (const auto& subdir : patch_dirs) {
+ if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
+ continue;
+
+ auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
+ if (exefs_dir != nullptr)
+ layers.push_back(std::move(exefs_dir));
+ }
+ layers.push_back(exefs);
+
+ auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
+ if (layered != nullptr) {
+ LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
+ exefs = std::move(layered);
}
if (Settings::values.dump_exefs) {
@@ -533,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
// SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
- if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 &&
- IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
- const auto mod_disabled =
- std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
- out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS");
+ if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
+ std::string types;
+ if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
+ AppendCommaIfNotEmpty(types, "LayeredExeFS");
+ }
+ if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
+ AppendCommaIfNotEmpty(types, "LayeredFS");
+ }
+
+ if (!types.empty()) {
+ const auto mod_disabled =
+ std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
+ out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
+ }
}
// 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 @@
#include <array>
#include <vector>
+
#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#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 {
continue;
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
- if (!FollowsNcaIdFormat(nca_dir->GetName()))
+ if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
continue;
+ }
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
}
for (const auto& nca_file : d2_dir->GetFiles()) {
- if (!FollowsNcaIdFormat(nca_file->GetName()))
+ if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
continue;
+ }
ids.push_back(
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/frontend/applets/mii_edit.h"
+
+namespace Core::Frontend {
+
+MiiEditApplet::~MiiEditApplet() = default;
+
+void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ callback();
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+
+namespace Core::Frontend {
+
+class MiiEditApplet {
+public:
+ virtual ~MiiEditApplet();
+
+ virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
+};
+
+class DefaultMiiEditApplet final : public MiiEditApplet {
+public:
+ void ShowMiiEdit(const std::function<void()>& callback) const override;
+};
+
+} // 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:
context.MakeCurrent();
}
~Scoped() {
- context.DoneCurrent();
+ if (active) {
+ context.DoneCurrent();
+ }
+ }
+
+ /// In the event that context was destroyed before the Scoped is destroyed, this provides a
+ /// mechanism to prevent calling a destroyed object's method during the deconstructor
+ void Cancel() {
+ active = false;
}
private:
GraphicsContext& context;
+ bool active{true};
};
/// 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) {
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
// Find what is this value
motion.verticalization_error = 0.0f;
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
}
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
if (index >= console.touch_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
.pressed = console.touch_values[index].pressed.value,
};
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
+ std::scoped_lock lock{mutex};
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
+ std::scoped_lock lock{mutex};
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
+ std::scoped_lock lock{mutex};
return console.touch_state;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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:
TouchDevices touch_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;
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() {
}
void EmulatedController::EnableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = true;
}
void EmulatedController::DisableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = false;
}
void EmulatedController::ResetSystemButtons() {
+ std::scoped_lock lock{mutex};
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (index >= controller.button_values.size()) {
return;
}
- {
- std::lock_guard lock{mutex};
- bool value_changed = false;
- const auto new_status = TransformToButton(callback);
- auto& current_status = controller.button_values[index];
+ std::unique_lock lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = controller.button_values[index];
- // Only read button values that have the same uuid or are pressed once
- if (current_status.uuid != uuid) {
- if (!new_status.value) {
- return;
- }
+ // Only read button values that have the same uuid or are pressed once
+ if (current_status.uuid != uuid) {
+ if (!new_status.value) {
+ return;
}
+ }
- current_status.toggle = new_status.toggle;
- current_status.uuid = uuid;
-
- // Update button status with current
- if (!current_status.toggle) {
- current_status.locked = false;
- if (current_status.value != new_status.value) {
- current_status.value = new_status.value;
- value_changed = true;
- }
- } else {
- // Toggle button and lock status
- if (new_status.value && !current_status.locked) {
- current_status.locked = true;
- current_status.value = !current_status.value;
- value_changed = true;
- }
+ current_status.toggle = new_status.toggle;
+ current_status.uuid = uuid;
- // Unlock button ready for next press
- if (!new_status.value && current_status.locked) {
- current_status.locked = false;
- }
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
}
-
- if (!value_changed) {
- return;
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
}
- if (is_configuring) {
- controller.npad_button_state.raw = NpadButton::None;
- controller.debug_pad_button_state.raw = 0;
- TriggerOnChange(ControllerTriggerType::Button, false);
- return;
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
}
+ }
- switch (index) {
- case Settings::NativeButton::A:
- controller.npad_button_state.a.Assign(current_status.value);
- controller.debug_pad_button_state.a.Assign(current_status.value);
- break;
- case Settings::NativeButton::B:
- controller.npad_button_state.b.Assign(current_status.value);
- controller.debug_pad_button_state.b.Assign(current_status.value);
- break;
- case Settings::NativeButton::X:
- controller.npad_button_state.x.Assign(current_status.value);
- controller.debug_pad_button_state.x.Assign(current_status.value);
- break;
- case Settings::NativeButton::Y:
- controller.npad_button_state.y.Assign(current_status.value);
- controller.debug_pad_button_state.y.Assign(current_status.value);
- break;
- case Settings::NativeButton::LStick:
- controller.npad_button_state.stick_l.Assign(current_status.value);
- break;
- case Settings::NativeButton::RStick:
- controller.npad_button_state.stick_r.Assign(current_status.value);
- break;
- case Settings::NativeButton::L:
- controller.npad_button_state.l.Assign(current_status.value);
- controller.debug_pad_button_state.l.Assign(current_status.value);
- break;
- case Settings::NativeButton::R:
- controller.npad_button_state.r.Assign(current_status.value);
- controller.debug_pad_button_state.r.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZL:
- controller.npad_button_state.zl.Assign(current_status.value);
- controller.debug_pad_button_state.zl.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZR:
- controller.npad_button_state.zr.Assign(current_status.value);
- controller.debug_pad_button_state.zr.Assign(current_status.value);
- break;
- case Settings::NativeButton::Plus:
- controller.npad_button_state.plus.Assign(current_status.value);
- controller.debug_pad_button_state.plus.Assign(current_status.value);
- break;
- case Settings::NativeButton::Minus:
- controller.npad_button_state.minus.Assign(current_status.value);
- controller.debug_pad_button_state.minus.Assign(current_status.value);
- break;
- case Settings::NativeButton::DLeft:
- controller.npad_button_state.left.Assign(current_status.value);
- controller.debug_pad_button_state.d_left.Assign(current_status.value);
- break;
- case Settings::NativeButton::DUp:
- controller.npad_button_state.up.Assign(current_status.value);
- controller.debug_pad_button_state.d_up.Assign(current_status.value);
- break;
- case Settings::NativeButton::DRight:
- controller.npad_button_state.right.Assign(current_status.value);
- controller.debug_pad_button_state.d_right.Assign(current_status.value);
- break;
- case Settings::NativeButton::DDown:
- controller.npad_button_state.down.Assign(current_status.value);
- controller.debug_pad_button_state.d_down.Assign(current_status.value);
- break;
- case Settings::NativeButton::SL:
- controller.npad_button_state.left_sl.Assign(current_status.value);
- controller.npad_button_state.right_sl.Assign(current_status.value);
- break;
- case Settings::NativeButton::SR:
- controller.npad_button_state.left_sr.Assign(current_status.value);
- controller.npad_button_state.right_sr.Assign(current_status.value);
- break;
- case Settings::NativeButton::Home:
- if (!system_buttons_enabled) {
- break;
- }
- controller.home_button_state.home.Assign(current_status.value);
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ controller.npad_button_state.raw = NpadButton::None;
+ controller.debug_pad_button_state.raw = 0;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Button, false);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeButton::A:
+ controller.npad_button_state.a.Assign(current_status.value);
+ controller.debug_pad_button_state.a.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::B:
+ controller.npad_button_state.b.Assign(current_status.value);
+ controller.debug_pad_button_state.b.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::X:
+ controller.npad_button_state.x.Assign(current_status.value);
+ controller.debug_pad_button_state.x.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Y:
+ controller.npad_button_state.y.Assign(current_status.value);
+ controller.debug_pad_button_state.y.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::LStick:
+ controller.npad_button_state.stick_l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::RStick:
+ controller.npad_button_state.stick_r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::L:
+ controller.npad_button_state.l.Assign(current_status.value);
+ controller.debug_pad_button_state.l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::R:
+ controller.npad_button_state.r.Assign(current_status.value);
+ controller.debug_pad_button_state.r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZL:
+ controller.npad_button_state.zl.Assign(current_status.value);
+ controller.debug_pad_button_state.zl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZR:
+ controller.npad_button_state.zr.Assign(current_status.value);
+ controller.debug_pad_button_state.zr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Plus:
+ controller.npad_button_state.plus.Assign(current_status.value);
+ controller.debug_pad_button_state.plus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Minus:
+ controller.npad_button_state.minus.Assign(current_status.value);
+ controller.debug_pad_button_state.minus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DLeft:
+ controller.npad_button_state.left.Assign(current_status.value);
+ controller.debug_pad_button_state.d_left.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DUp:
+ controller.npad_button_state.up.Assign(current_status.value);
+ controller.debug_pad_button_state.d_up.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DRight:
+ controller.npad_button_state.right.Assign(current_status.value);
+ controller.debug_pad_button_state.d_right.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DDown:
+ controller.npad_button_state.down.Assign(current_status.value);
+ controller.debug_pad_button_state.d_down.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SL:
+ controller.npad_button_state.left_sl.Assign(current_status.value);
+ controller.npad_button_state.right_sl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SR:
+ controller.npad_button_state.left_sr.Assign(current_status.value);
+ controller.npad_button_state.right_sr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Home:
+ if (!system_buttons_enabled) {
break;
- case Settings::NativeButton::Screenshot:
- if (!system_buttons_enabled) {
- break;
- }
- controller.capture_button_state.capture.Assign(current_status.value);
+ }
+ controller.home_button_state.home.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Screenshot:
+ if (!system_buttons_enabled) {
break;
}
+ controller.capture_button_state.capture.Assign(current_status.value);
+ break;
}
+
+ lock.unlock();
+
if (!is_connected) {
if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
Connect();
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, true);
}
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// 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
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, true);
}
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
force_update_motion = raw_status.force_update;
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, true);
}
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, true);
}
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
}
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
return;
}
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = true;
- TriggerOnChange(ControllerTriggerType::Connected, false);
- return;
- }
- if (is_connected) {
- return;
- }
- is_connected = true;
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = true;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Connected, false);
+ return;
+ }
+
+ if (is_connected) {
+ return;
}
+ is_connected = true;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = false;
- TriggerOnChange(ControllerTriggerType::Disconnected, false);
- return;
- }
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = false;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Disconnected, false);
+ return;
+ }
- if (!is_connected) {
- return;
- }
- is_connected = false;
+ if (!is_connected) {
+ return;
}
+ is_connected = false;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
}
NpadIdType EmulatedController::GetNpadIdType() const {
+ std::scoped_lock lock{mutex};
return npad_id_type;
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
- if (is_configuring) {
- if (tmp_npad_type == npad_type_) {
- return;
- }
- tmp_npad_type = npad_type_;
- TriggerOnChange(ControllerTriggerType::Type, false);
+ if (is_configuring) {
+ if (tmp_npad_type == npad_type_) {
return;
}
+ tmp_npad_type = npad_type_;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Type, false);
+ return;
+ }
- if (npad_type == npad_type_) {
- return;
- }
- if (is_connected) {
- LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
- NpadIdTypeToIndex(npad_id_type));
- }
- npad_type = npad_type_;
+ if (npad_type == npad_type_) {
+ return;
+ }
+ if (is_connected) {
+ LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
+ NpadIdTypeToIndex(npad_id_type));
}
+ npad_type = npad_type_;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Type, true);
}
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
}
ButtonValues EmulatedController::GetButtonsValues() const {
+ std::scoped_lock lock{mutex};
return controller.button_values;
}
SticksValues EmulatedController::GetSticksValues() const {
+ std::scoped_lock lock{mutex};
return controller.stick_values;
}
TriggerValues EmulatedController::GetTriggersValues() const {
+ std::scoped_lock lock{mutex};
return controller.trigger_values;
}
ControllerMotionValues EmulatedController::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return controller.motion_values;
}
ColorValues EmulatedController::GetColorsValues() const {
+ std::scoped_lock lock{mutex};
return controller.color_values;
}
BatteryValues EmulatedController::GetBatteryValues() const {
+ std::scoped_lock lock{mutex};
return controller.battery_values;
}
HomeButtonState EmulatedController::GetHomeButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
}
CaptureButtonState EmulatedController::GetCaptureButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
}
NpadButtonState EmulatedController::GetNpadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
}
DebugPadButton EmulatedController::GetDebugPadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
+ std::unique_lock lock{mutex};
+
if (is_configuring) {
return {};
}
+
// Some drivers like stick from buttons need constant refreshing
for (auto& device : stick_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->SoftUpdate();
+ lock.lock();
}
+
return controller.analog_stick_state;
}
NpadGcTriggerState EmulatedController::GetTriggers() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
}
MotionState EmulatedController::GetMotions() const {
+ std::unique_lock lock{mutex};
+
+ // Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->ForceUpdate();
+ lock.lock();
}
}
+
return controller.motion_state;
}
ControllerColors EmulatedController::GetColors() const {
+ std::scoped_lock lock{mutex};
return controller.colors_state;
}
BatteryLevelState EmulatedController::GetBattery() const {
+ std::scoped_lock lock{mutex};
return controller.battery_state;
}
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ControllerUpdateCallback& poller = poller_pair.second;
if (!is_npad_service_update && poller.is_npad_service) {
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
}
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedController::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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:
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
- NpadIdType npad_id_type;
+ const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
@@ -434,6 +434,7 @@ private:
StickDevices tas_stick_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
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;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
+ ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
ReloadInput();
}
@@ -66,6 +67,8 @@ void EmulatedDevices::ReloadInput() {
key_index++;
}
+ ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
+
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
@@ -120,6 +123,13 @@ void EmulatedDevices::ReloadInput() {
},
});
}
+
+ if (ring_analog_device) {
+ ring_analog_device->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
+ });
+ }
}
void EmulatedDevices::UnloadInput() {
@@ -155,6 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
+ Settings::values.ringcon_analogs = ring_params.Serialize();
}
void EmulatedDevices::RestoreConfig() {
@@ -164,12 +175,21 @@ void EmulatedDevices::RestoreConfig() {
ReloadFromSettings();
}
+Common::ParamPackage EmulatedDevices::GetRingParam() const {
+ return ring_params;
+}
+
+void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
+ ring_params = std::move(param);
+ ReloadInput();
+}
+
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +221,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
@@ -208,6 +229,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
}
@@ -227,7 +249,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +281,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
@@ -289,6 +312,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
@@ -297,7 +321,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_button_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +353,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -351,6 +376,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
@@ -359,13 +385,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_analog_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_analog_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -379,17 +406,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -397,42 +426,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
+void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
+ std::lock_guard lock{mutex};
+ const auto force_value = TransformToStick(callback);
+
+ device_status.ring_analog_value = force_value.x;
+
+ if (is_configuring) {
+ device_status.ring_analog_value = {};
+ TriggerOnChange(DeviceTriggerType::RingController);
+ return;
+ }
+
+ device_status.ring_analog_state.force = force_value.x.value;
+
+ TriggerOnChange(DeviceTriggerType::RingController);
+}
+
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_values;
}
+RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
+ return device_status.ring_analog_value;
+}
+
KeyboardKey EmulatedDevices::GetKeyboard() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_wheel_state;
}
+RingSensorForce EmulatedDevices::GetRingSensorForce() const {
+ return device_status.ring_analog_state;
+}
+
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -442,13 +506,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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
using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
+using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
+using RingAnalogParams = Common::ParamPackage;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +41,17 @@ using MouseButtonValues =
using MouseAnalogValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
+using RingAnalogValue = Common::Input::AnalogStatus;
struct MousePosition {
f32 x;
f32 y;
};
+struct RingSensorForce {
+ f32 force;
+};
+
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
@@ -52,6 +59,7 @@ struct DeviceStatus {
MouseButtonValues mouse_button_values{};
MouseAnalogValues mouse_analog_values{};
MouseStickValue mouse_stick_value{};
+ RingAnalogValue ring_analog_value{};
// Data for HID serices
KeyboardKey keyboard_state{};
@@ -59,12 +67,14 @@ struct DeviceStatus {
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
+ RingSensorForce ring_analog_state{};
};
enum class DeviceTriggerType {
Keyboard,
KeyboardModdifier,
Mouse,
+ RingController,
};
struct InterfaceUpdateCallback {
@@ -110,6 +120,15 @@ public:
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
+ // Returns the current mapped ring device
+ Common::ParamPackage GetRingParam() const;
+
+ /**
+ * Updates the current mapped ring device
+ * @param param ParamPackage with ring sensor data to be mapped
+ */
+ void SetRingParam(Common::ParamPackage param);
+
/// Returns the latest status of button input from the keyboard with parameters
KeyboardValues GetKeyboardValues() const;
@@ -119,6 +138,9 @@ public:
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
+ /// Returns the latest status of analog input from the ring sensor with parameters
+ RingAnalogValue GetRingSensorValues() const;
+
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
@@ -134,6 +156,9 @@ public:
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
+ /// Returns the latest ringcon force sensor value
+ RingSensorForce GetRingSensorForce() const;
+
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +211,12 @@ private:
void SetMouseStick(const Common::Input::CallbackStatus& callback);
/**
+ * Updates the ring analog sensor status of the ring controller
+ * @param callback A CallbackStatus containing the force status
+ */
+ void SetRingAnalog(const Common::Input::CallbackStatus& callback);
+
+ /**
* Triggers a callback that something has changed on the device status
* @param type Input type of the event to trigger
*/
@@ -193,13 +224,17 @@ private:
bool is_configuring{false};
+ RingAnalogParams ring_params;
+
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseAnalogDevices mouse_analog_devices;
MouseStickDevice mouse_stick_device;
+ RingAnalogDevice ring_analog_device;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;
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 {
};
static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
+// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorFusionParameters {
- f32 parameter1;
- f32 parameter2;
+ f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
+ f32 parameter2{0.4f}; // Default 0.4
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"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:
T PopRaw();
template <class T>
- std::shared_ptr<T> PopIpcInterface() {
+ std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
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) {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
- static std::random_device device;
- static std::mt19937 gen(device());
- static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
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 @@
#include <vector>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +79,7 @@ private:
/// Lists all thread ids that aren't deleted/etc.
std::vector<KThread*> thread_list;
- Common::SpinLock global_list_guard{};
+ std::mutex global_list_guard;
};
} // 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 @@
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -25,8 +24,15 @@
namespace Kernel {
-SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
- : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
+SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
+ ServiceThreadType thread_type)
+ : kernel{kernel_} {
+ if (thread_type == ServiceThreadType::CreateNew) {
+ service_thread = kernel.CreateServiceThread(service_name_);
+ } else {
+ service_thread = kernel.GetDefaultServiceThread();
+ }
+}
SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread);
@@ -45,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
return false;
}
- return DomainHandler(object_id - 1) != nullptr;
+ return !DomainHandler(object_id - 1).expired();
} else {
return session_handler != nullptr;
}
@@ -55,7 +61,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this());
// Ensure our server session is tracked globally.
- kernel.RegisterServerSession(session);
+ kernel.RegisterServerObject(session);
}
void 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 {
class ServiceFrameworkBase;
}
+enum class ServiceThreadType {
+ Default,
+ CreateNew,
+};
+
namespace Kernel {
class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
- SessionRequestHandler(KernelCore& kernel, const char* service_name_);
+ SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
+ ServiceThreadType thread_type);
virtual ~SessionRequestHandler();
/**
@@ -94,6 +100,7 @@ protected:
std::weak_ptr<ServiceThread> service_thread;
};
+using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
@@ -139,7 +146,7 @@ public:
}
}
- SessionRequestHandlerPtr DomainHandler(std::size_t index) const {
+ SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
@@ -328,10 +335,10 @@ public:
template <typename T>
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
- return std::static_pointer_cast<T>(manager->DomainHandler(index));
+ return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
}
- void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
+ void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
manager = std::move(manager_);
}
@@ -374,7 +381,7 @@ private:
u32 handles_offset{};
u32 domain_offset{};
- std::shared_ptr<SessionRequestManager> manager;
+ std::weak_ptr<SessionRequestManager> manager;
KernelCore& kernel;
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
+#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
namespace Kernel::Init {
@@ -32,9 +36,13 @@ namespace Kernel::Init {
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
+ HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
+ HANDLER(KThreadLocalPage, \
+ (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
+ ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
namespace {
@@ -50,38 +58,46 @@ enum KSlabType : u32 {
// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
-constexpr size_t SlabCountKEvent = 700;
+constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
-constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew.
+constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
-constexpr size_t SlabCountKSession = 933;
+constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
-constexpr size_t SlabCountKAlpha = 1;
-constexpr size_t SlabCountKBeta = 6;
+constexpr size_t SlabCountKIoPool = 1;
+constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
+/// Helper function to translate from the slab virtual address to the reserved location in physical
+/// memory.
+static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
+ slab_addr -= memory_layout.GetSlabRegionAddress();
+ return slab_addr + Core::DramMemoryMap::SlabHeapBase;
+}
+
template <typename T>
VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
size_t num_objects) {
- // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for
- // kernel object type T with the backing kernel memory pointer once we emulate kernel memory.
const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
VAddr start = Common::AlignUp(address, alignof(T));
- // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with
- // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free
- // host memory.
- void* backing_kernel_memory{};
+ // This should use the virtual memory address passed in, but currently, we do not setup the
+ // kernel virtual memory layout. Instead, we simply map these at a region of physical memory
+ // that we reserve for the slab heaps.
+ // TODO(bunnei): Fix this once we support the kernel virtual memory layout.
if (size > 0) {
+ void* backing_kernel_memory{
+ system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
+
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
@@ -91,6 +107,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
return start + size;
}
+size_t CalculateSlabHeapGapSize() {
+ constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
+ static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
+ return KernelSlabHeapGapSize;
+}
+
} // namespace
KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
@@ -109,8 +131,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
- .num_KAlpha = SlabCountKAlpha,
- .num_KBeta = SlabCountKBeta,
+ .num_KIoPool = SlabCountKIoPool,
+ .num_KIoRegion = SlabCountKIoRegion,
};
}
@@ -136,11 +158,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
#undef ADD_SLAB_SIZE
// Add the reserved size.
- size += KernelSlabHeapGapsSize;
+ size += CalculateSlabHeapGapSize();
return size;
}
+void InitializeKPageBufferSlabHeap(Core::System& system) {
+ auto& kernel = system.Kernel();
+
+ const auto& counts = kernel.SlabResourceCounts();
+ const size_t num_pages =
+ counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
+ const size_t slab_size = num_pages * PageSize;
+
+ // Reserve memory from the system resource limit.
+ ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
+
+ // Allocate memory for the slab.
+ constexpr auto AllocateOption = KMemoryManager::EncodeOption(
+ KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
+ const PAddr slab_address =
+ kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
+ ASSERT(slab_address != 0);
+
+ // Initialize the slabheap.
+ KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
+ slab_size);
+}
+
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
@@ -160,13 +205,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
// Create an array to represent the gaps between the slabs.
- const size_t total_gap_size = KernelSlabHeapGapsSize;
+ const size_t total_gap_size = CalculateSlabHeapGapSize();
std::array<size_t, slab_types.size()> slab_gaps;
- for (size_t i = 0; i < slab_gaps.size(); i++) {
+ for (auto& slab_gap : slab_gaps) {
// Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
// is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
// will include it ourselves.
- slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size);
+ slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
}
// 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) {
}
}
- for (size_t i = 0; i < slab_types.size(); i++) {
+ // Track the gaps, so that we can free them to the unused slab tree.
+ VAddr gap_start = address;
+ size_t gap_size = 0;
+
+ for (size_t i = 0; i < slab_gaps.size(); i++) {
// Add the random gap to the address.
- address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
+ const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
+ address += cur_gap;
+ gap_size += cur_gap;
#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
case KSlabType_##NAME: \
- address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
+ if (COUNT > 0) { \
+ address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
+ } \
break;
// Initialize the slabheap.
@@ -192,7 +245,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
- UNREACHABLE();
+ UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
+ }
+
+ // If we've hit the end of a gap, free it.
+ if (gap_start + gap_size != address) {
+ gap_start = address;
+ gap_size = 0;
}
}
}
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 {
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
- size_t num_KAlpha;
- size_t num_KBeta;
+ size_t num_KIoPool;
+ size_t num_KIoRegion;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
+void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // 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) {
{
KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
return ResultInvalidState;
}
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
// Determine the updated value.
s32 new_value{};
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:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
RegisterWithKernel();
}
- virtual ~KAutoObject() {
- UnregisterWithKernel();
- }
+ virtual ~KAutoObject() = default;
static KAutoObject* Create(KAutoObject* ptr);
@@ -163,11 +161,12 @@ public:
do {
ASSERT(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
- std::memory_order_relaxed));
+ std::memory_order_acq_rel));
// If ref count hits zero, destroy the object.
if (cur_ref_count - 1 == 0) {
this->Destroy();
+ this->UnregisterWithKernel();
}
}
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 @@
#pragma once
-#include <atomic>
-
#include "common/bit_util.h"
#include "common/common_types.h"
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 @@
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
-#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -29,15 +28,21 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
- m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
+ m_page_group =
+ KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
// Clear the memory.
- for (const auto& block : m_page_group.Nodes()) {
- std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
- }
+ //
+ // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
+ // guest memory, and is not specifically required if the guest program is correctly
+ // written, so disable until this is further investigated.
+ //
+ // for (const auto& block : m_page_group.Nodes()) {
+ // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
+ // }
// Set remaining tracking members.
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 @@
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
@@ -244,7 +243,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
{
KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({cv_key, -1});
+ auto it = thread_tree.nfind_key({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
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) {
return true;
}
-ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
+ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -75,7 +75,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
- m_entry_infos[index].info = {.linear_id = linear_id, .type = type};
+ m_entry_infos[index].linear_id = linear_id;
m_objects[index] = obj;
obj->Open();
@@ -116,7 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
}
-void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
+void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -132,7 +132,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
// Set the entry.
ASSERT(m_objects[index] == nullptr);
- m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type};
+ m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
m_objects[index] = obj;
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:
m_free_head_index = -1;
// Free all entries.
- for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
+ for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
@@ -104,17 +104,8 @@ public:
ResultCode Reserve(Handle* out_handle);
void Unreserve(Handle handle);
- template <typename T>
- ResultCode Add(Handle* out_handle, T* obj) {
- static_assert(std::is_base_of_v<KAutoObject, T>);
- return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
- }
-
- template <typename T>
- void Register(Handle handle, T* obj) {
- static_assert(std::is_base_of_v<KAutoObject, T>);
- return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
- }
+ ResultCode Add(Handle* out_handle, KAutoObject* obj);
+ void Register(Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -160,9 +151,6 @@ public:
}
private:
- ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
- void Register(Handle handle, KAutoObject* obj, u16 type);
-
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
@@ -179,7 +167,7 @@ private:
ASSERT(m_count > 0);
m_objects[index] = nullptr;
- m_entry_infos[index].next_free_index = m_free_head_index;
+ m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
m_free_head_index = index;
@@ -278,19 +266,13 @@ private:
}
union EntryInfo {
- struct {
- u16 linear_id;
- u16 type;
- } info;
- s32 next_free_index;
+ u16 linear_id;
+ s16 next_free_index;
constexpr u16 GetLinearId() const {
- return info.linear_id;
- }
- constexpr u16 GetType() const {
- return info.type;
+ return linear_id;
}
- constexpr s32 GetNextFreeIndex() const {
+ constexpr s16 GetNextFreeIndex() const {
return next_free_index;
}
};
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
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
-constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB;
-constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
+constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
+constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
-constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB;
+constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr std::size_t KernelResourceSize =
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 @@
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
-#include "core/memory.h"
namespace Kernel {
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_page_buffer.h"
+#include "core/hle/kernel/memory_types.h"
+
+namespace Kernel {
+
+KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
+ ASSERT(Common::IsAligned(phys_addr, PageSize));
+ return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr));
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
+public:
+ KPageBuffer() = default;
+
+ static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
+
+private:
+ [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
+};
+
+static_assert(sizeof(KPageBuffer) == PageSize);
+static_assert(alignof(KPageBuffer) == PageSize);
+
+} // 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:
return ResultSuccess;
}
+ bool Empty() const {
+ return nodes.empty();
+ }
+
private:
std::list<Node> nodes;
};
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
return ResultSuccess;
}
-ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
+ // Validate the mapping request.
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
+ ResultInvalidMemoryRegion);
+
+ // Lock the table.
KScopedLightLock lk(general_lock);
- const std::size_t num_pages{size / PageSize};
+ // Verify that the source memory is normal heap.
+ KMemoryState src_state{};
+ KMemoryPermission src_perm{};
+ std::size_t num_src_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
+ src_address, size, KMemoryState::All, KMemoryState::Normal,
+ KMemoryPermission::All, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::All, KMemoryAttribute::None));
- KMemoryState state{};
- KMemoryPermission perm{};
- CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
- KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
+ // Verify that the destination memory is unmapped.
+ std::size_t num_dst_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
+ KMemoryState::Free, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryAttribute::None));
- if (IsRegionMapped(dst_addr, size)) {
- return ResultInvalidCurrentMemory;
+ // Map the code memory.
+ {
+ // Determine the number of pages being operated on.
+ const std::size_t num_pages = size / PageSize;
+
+ // Create page groups for the memory being mapped.
+ KPageLinkedList pg;
+ AddRegionToPages(src_address, num_pages, pg);
+
+ // Reprotect the source as kernel-read/not mapped.
+ const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
+ KMemoryPermission::NotMapped);
+ R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
+
+ // Ensure that we unprotect the source pages on failure.
+ auto unprot_guard = SCOPE_GUARD({
+ ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
+ .IsSuccess());
+ });
+
+ // Map the alias pages.
+ R_TRY(MapPages(dst_address, pg, new_perm));
+
+ // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
+ // failure.
+ unprot_guard.Cancel();
+
+ // Apply the memory block updates.
+ block_manager->Update(src_address, num_pages, src_state, new_perm,
+ KMemoryAttribute::Locked);
+ block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
+ KMemoryAttribute::None);
}
- KPageLinkedList page_linked_list;
- AddRegionToPages(src_addr, num_pages, page_linked_list);
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy) {
+ // Validate the mapping request.
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
+ ResultInvalidMemoryRegion);
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Verify that the source memory is locked normal heap.
+ std::size_t num_src_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
+ KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked));
+
+ // Verify that the destination memory is aliasable code.
+ std::size_t num_dst_allocator_blocks{};
+ R_TRY(this->CheckMemoryStateContiguous(
+ std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
+ KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::None));
+
+ // Determine whether any pages being unmapped are code.
+ bool any_code_pages = false;
{
- auto block_guard = detail::ScopeExit(
- [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
+ KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
+ while (true) {
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
- CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
- OperationType::ChangePermissions));
- CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
+ // Check if the memory has code flag.
+ if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
+ any_code_pages = true;
+ break;
+ }
- block_guard.Cancel();
+ // Check if we're done.
+ if (dst_address + size - 1 <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance.
+ ++it;
+ }
}
- block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
- KMemoryAttribute::Locked);
- block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
+ // Ensure that we maintain the instruction cache.
+ bool reprotected_pages = false;
+ SCOPE_EXIT({
+ if (reprotected_pages && any_code_pages) {
+ if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
+ system.InvalidateCpuInstructionCacheRange(dst_address, size);
+ } else {
+ system.InvalidateCpuInstructionCaches();
+ }
+ }
+ });
+
+ // Unmap.
+ {
+ // Determine the number of pages being operated on.
+ const std::size_t num_pages = size / PageSize;
+
+ // Unmap the aliased copy of the pages.
+ R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Try to set the permissions for the source pages back to what they should be.
+ R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
+ OperationType::ChangePermissions));
+
+ // Apply the memory block updates.
+ block_manager->Update(dst_address, num_pages, KMemoryState::None);
+ block_manager->Update(src_address, num_pages, KMemoryState::Normal,
+ KMemoryPermission::UserReadWrite);
+
+ // Note that we reprotected pages.
+ reprotected_pages = true;
+ }
return ResultSuccess;
}
-ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
+VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
+ std::size_t num_pages, std::size_t alignment, std::size_t offset,
+ std::size_t guard_pages) {
+ VAddr address = 0;
+
+ if (num_pages <= region_num_pages) {
+ if (this->IsAslrEnabled()) {
+ // Try to directly find a free area up to 8 times.
+ for (std::size_t i = 0; i < 8; i++) {
+ const std::size_t random_offset =
+ KSystemControl::GenerateRandomRange(
+ 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
+ alignment;
+ const VAddr candidate =
+ Common::AlignDown((region_start + random_offset), alignment) + offset;
+
+ KMemoryInfo info = this->QueryInfoImpl(candidate);
+
+ if (info.state != KMemoryState::Free) {
+ continue;
+ }
+ if (region_start > candidate) {
+ continue;
+ }
+ if (info.GetAddress() + guard_pages * PageSize > candidate) {
+ continue;
+ }
- if (!size) {
- return ResultSuccess;
+ const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
+ if (candidate_end > info.GetLastAddress()) {
+ continue;
+ }
+ if (candidate_end > region_start + region_num_pages * PageSize - 1) {
+ continue;
+ }
+
+ address = candidate;
+ break;
+ }
+ // Fall back to finding the first free area with a random offset.
+ if (address == 0) {
+ // NOTE: Nintendo does not account for guard pages here.
+ // This may theoretically cause an offset to be chosen that cannot be mapped. We
+ // will account for guard pages.
+ const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
+ 0, region_num_pages - num_pages - guard_pages);
+ address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
+ region_num_pages - offset_pages, num_pages,
+ alignment, offset, guard_pages);
+ }
+ }
+
+ // Find the first free area.
+ if (address == 0) {
+ address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
+ alignment, offset, guard_pages);
+ }
}
- const std::size_t num_pages{size / PageSize};
+ return address;
+}
- CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
- KMemoryPermission::None, KMemoryAttribute::Mask,
- KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
+ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
+ ASSERT(this->IsLockedByCurrentThread());
- KMemoryState state{};
- CASCADE_CODE(CheckMemoryState(
- &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
- KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
- CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
- KMemoryPermission::None, KMemoryAttribute::Mask,
- KMemoryAttribute::None));
- CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
+ const size_t size = num_pages * PageSize;
- block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
- block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
- KMemoryPermission::UserReadWrite);
+ // We're making a new group, not adding to an existing one.
+ R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
+
+ // Begin traversal.
+ Common::PageTable::TraversalContext context;
+ Common::PageTable::TraversalEntry next_entry;
+ R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
+
+ // Prepare tracking variables.
+ PAddr cur_addr = next_entry.phys_addr;
+ size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
+ size_t tot_size = cur_size;
+
+ // Iterate, adding to group as we go.
+ const auto& memory_layout = system.Kernel().MemoryLayout();
+ while (tot_size < size) {
+ R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
+ ResultInvalidCurrentMemory);
+
+ if (next_entry.phys_addr != (cur_addr + cur_size)) {
+ const size_t cur_pages = cur_size / PageSize;
+
+ R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
+ R_TRY(pg.AddBlock(cur_addr, cur_pages));
+
+ cur_addr = next_entry.phys_addr;
+ cur_size = next_entry.block_size;
+ } else {
+ cur_size += next_entry.block_size;
+ }
+
+ tot_size += next_entry.block_size;
+ }
+
+ // Ensure we add the right amount for the last block.
+ if (tot_size > size) {
+ cur_size -= (tot_size - size);
+ }
- system.InvalidateCpuInstructionCacheRange(dst_addr, size);
+ // Add the last block.
+ const size_t cur_pages = cur_size / PageSize;
+ R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
+ R_TRY(pg.AddBlock(cur_addr, cur_pages));
return ResultSuccess;
}
@@ -380,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
+ system.InvalidateCpuInstructionCaches();
+
return ResultSuccess;
}
@@ -986,6 +1176,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
return ResultSuccess;
}
+ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+ PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+ std::size_t region_num_pages, KMemoryState state,
+ KMemoryPermission perm) {
+ ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
+
+ // Ensure this is a valid map request.
+ R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
+ ResultInvalidCurrentMemory);
+ R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Find a random address to map at.
+ VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
+ this->GetNumGuardPages());
+ R_UNLESS(addr != 0, ResultOutOfMemory);
+ ASSERT(Common::IsAligned(addr, alignment));
+ ASSERT(this->CanContain(addr, num_pages * PageSize, state));
+ ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
+ KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::None, KMemoryAttribute::None)
+ .IsSuccess());
+
+ // Perform mapping operation.
+ if (is_pa_valid) {
+ R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
+ } else {
+ UNIMPLEMENTED();
+ }
+
+ // Update the blocks.
+ block_manager->Update(addr, num_pages, state, perm);
+
+ // We successfully mapped the pages.
+ *out_addr = addr;
+ return ResultSuccess;
+}
+
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
@@ -1028,6 +1258,55 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
+ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
+ // Check that the unmap is in range.
+ const std::size_t size = num_pages * PageSize;
+ R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check the memory state.
+ std::size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
+ KMemoryState::All, state, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ // Perform the unmap.
+ R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Update the blocks.
+ block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
+
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) {
+ // Ensure that the page group isn't null.
+ ASSERT(out != nullptr);
+
+ // Make sure that the region we're mapping is valid for the table.
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check if state allows us to create the group.
+ R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Create a new page group for the region.
+ R_TRY(this->MakePageGroup(*out, address, num_pages));
+
+ return ResultSuccess;
+}
+
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
@@ -1410,57 +1689,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
-
- KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
-
- KMemoryPermission old_perm{};
-
- if (const ResultCode result{CheckMemoryState(
- nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
- result.IsError()) {
- return result;
- }
-
- new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
-
- block_manager->UpdateLock(
- addr, size / PageSize,
- [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
- block->ShareToDevice(permission);
- },
- new_perm);
-
- return ResultSuccess;
+ return this->LockMemoryAndOpen(
+ nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::All, KMemoryAttribute::None,
+ static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
+ KMemoryPermission::KernelReadWrite),
+ KMemoryAttribute::Locked);
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
-
- KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
-
- KMemoryPermission old_perm{};
-
- if (const ResultCode result{CheckMemoryState(
- nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::All, KMemoryAttribute::Locked)};
- result.IsError()) {
- return result;
- }
-
- new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
-
- block_manager->UpdateLock(
- addr, size / PageSize,
- [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
- block->UnshareToDevice(permission);
- },
- new_perm);
-
- return ResultSuccess;
+ return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::Locked, nullptr);
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1796,4 +2039,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
+ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
+ size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
+ // Validate basic preconditions.
+ ASSERT((lock_attr & attr) == KMemoryAttribute::None);
+ ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
+ KMemoryAttribute::None);
+
+ // Validate the lock request.
+ const size_t num_pages = size / PageSize;
+ R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check that the output page group is empty, if it exists.
+ if (out_pg) {
+ ASSERT(out_pg->GetNumPages() == 0);
+ }
+
+ // Check the state.
+ KMemoryState old_state{};
+ KMemoryPermission old_perm{};
+ KMemoryAttribute old_attr{};
+ size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
+ std::addressof(old_attr), std::addressof(num_allocator_blocks),
+ addr, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Get the physical address, if we're supposed to.
+ if (out_paddr != nullptr) {
+ ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
+ }
+
+ // Make the page group, if we're supposed to.
+ if (out_pg != nullptr) {
+ R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
+ }
+
+ // Decide on new perm and attr.
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+ KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
+
+ // Update permission, if we need to.
+ if (new_perm != old_perm) {
+ R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
+ }
+
+ // Apply the memory block updates.
+ block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
+
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr, KMemoryPermission new_perm,
+ KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
+ // Validate basic preconditions.
+ ASSERT((attr_mask & lock_attr) == lock_attr);
+ ASSERT((attr & lock_attr) == lock_attr);
+
+ // Validate the unlock request.
+ const size_t num_pages = size / PageSize;
+ R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check the state.
+ KMemoryState old_state{};
+ KMemoryPermission old_perm{};
+ KMemoryAttribute old_attr{};
+ size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
+ std::addressof(old_attr), std::addressof(num_allocator_blocks),
+ addr, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Check the page group.
+ if (pg != nullptr) {
+ UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
+ }
+
+ // Decide on new perm and attr.
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+ KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
+
+ // Update permission, if we need to.
+ if (new_perm != old_perm) {
+ R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
+ }
+
+ // Apply the memory block updates.
+ block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
+
+ return ResultSuccess;
+}
+
} // 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 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
@@ -25,6 +26,8 @@ class KMemoryBlockManager;
class KPageTable final {
public:
+ enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
+
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
@@ -36,8 +39,9 @@ public:
KMemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
KMemoryPermission perm);
- ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
+ ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy);
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -46,7 +50,14 @@ public:
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
+ ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+ PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
+ return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
+ this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
+ state, perm);
+ }
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
+ ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
@@ -64,6 +75,10 @@ public:
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
+ ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -91,6 +106,9 @@ private:
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm);
+ ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+ PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+ std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -105,6 +123,9 @@ private:
VAddr GetRegionAddress(KMemoryState state) const;
std::size_t GetRegionSize(KMemoryState state) const;
+ VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
+ std::size_t alignment, std::size_t offset, std::size_t guard_pages);
+
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
@@ -137,7 +158,7 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
- ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
+ ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
@@ -146,10 +167,37 @@ private:
attr_mask, attr, ignore_attr);
}
+ ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr);
+ ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr,
+ const KPageLinkedList* pg);
+
+ ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
+
bool IsLockedByCurrentThread() const {
return general_lock.IsLockedByCurrentThread();
}
+ bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
+ }
+
+ bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ *out = GetPhysicalAddr(virt_addr);
+
+ return *out != 0;
+ }
+
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
@@ -210,7 +258,7 @@ public:
constexpr VAddr GetAliasCodeRegionSize() const {
return alias_code_region_end - alias_code_region_start;
}
- size_t GetNormalMemorySize() {
+ std::size_t GetNormalMemorySize() {
KScopedLightLock lk(general_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
@@ -253,7 +301,9 @@ public:
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
-
+ constexpr std::size_t GetNumGuardPages() const {
+ return IsKernel() ? 1 : 4;
+ }
PAddr GetPhysicalAddr(VAddr addr) const {
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
ASSERT(backing_addr);
@@ -275,10 +325,6 @@ private:
return is_aslr_enabled;
}
- constexpr std::size_t GetNumGuardPages() const {
- return IsKernel() ? 1 : 4;
- }
-
constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
return (address_space_start <= addr) &&
(num_pages <= (address_space_end - address_space_start) / PageSize) &&
@@ -311,6 +357,7 @@ private:
bool is_aslr_enabled{};
u32 heap_fill_value{};
+ const KMemoryRegion* cached_physical_heap_region{};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
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) {
R_UNLESS(state == State::Normal, ResultPortClosed);
server.EnqueueSession(session);
- server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession());
+
+ if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
+ session_ptr->ClientConnected(server.AcceptSession());
+ } else {
+ UNREACHABLE();
+ }
return ResultSuccess;
}
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 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
-#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_memory_block_manager.h"
@@ -24,7 +23,6 @@
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
-#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
@@ -70,58 +68,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
}
} // Anonymous namespace
-// Represents a page used for thread-local storage.
-//
-// Each TLS page contains slots that may be used by processes and threads.
-// Every process and thread is created with a slot in some arbitrary page
-// (whichever page happens to have an available slot).
-class TLSPage {
-public:
- static constexpr std::size_t num_slot_entries =
- Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
-
- explicit TLSPage(VAddr address) : base_address{address} {}
-
- bool HasAvailableSlots() const {
- return !is_slot_used.all();
- }
-
- VAddr GetBaseAddress() const {
- return base_address;
- }
-
- std::optional<VAddr> ReserveSlot() {
- for (std::size_t i = 0; i < is_slot_used.size(); i++) {
- if (is_slot_used[i]) {
- continue;
- }
-
- is_slot_used[i] = true;
- return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
- }
-
- return std::nullopt;
- }
-
- void ReleaseSlot(VAddr address) {
- // Ensure that all given addresses are consistent with how TLS pages
- // are intended to be used when releasing slots.
- ASSERT(IsWithinPage(address));
- ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
-
- const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
- is_slot_used[index] = false;
- }
-
-private:
- bool IsWithinPage(VAddr address) const {
- return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
- }
-
- VAddr base_address;
- std::bitset<num_slot_entries> is_slot_used;
-};
-
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
ProcessType type, KResourceLimit* res_limit) {
auto& kernel = system.Kernel();
@@ -404,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
}
// Create TLS region
- tls_region_address = CreateTLSRegion();
+ R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
memory_reservation.Commit();
return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -444,7 +390,7 @@ void KProcess::PrepareForTermination() {
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
- FreeTLSRegion(tls_region_address);
+ this->DeleteThreadLocalRegion(tls_region_address);
tls_region_address = 0;
if (resource_limit) {
@@ -456,9 +402,6 @@ void KProcess::PrepareForTermination() {
}
void KProcess::Finalize() {
- // Finalize the handle table and close any open handles.
- handle_table.Finalize();
-
// Free all shared memory infos.
{
auto it = shared_memory_list.begin();
@@ -483,67 +426,110 @@ void KProcess::Finalize() {
resource_limit = nullptr;
}
+ // Finalize the page table.
+ page_table.reset();
+
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
}
-/**
- * Attempts to find a TLS page that contains a free slot for
- * use by a thread.
- *
- * @returns If a page with an available slot is found, then an iterator
- * pointing to the page is returned. Otherwise the end iterator
- * is returned instead.
- */
-static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
- return std::find_if(tls_pages.begin(), tls_pages.end(),
- [](const auto& page) { return page.HasAvailableSlots(); });
-}
+ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
+ KThreadLocalPage* tlp = nullptr;
+ VAddr tlr = 0;
-VAddr KProcess::CreateTLSRegion() {
- KScopedSchedulerLock lock(kernel);
- if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
- tls_page_iter != tls_pages.cend()) {
- return *tls_page_iter->ReserveSlot();
- }
+ // See if we can get a region from a partially used TLP.
+ {
+ KScopedSchedulerLock sl{kernel};
- Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
- ASSERT(tls_page_ptr);
+ if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
+ tlr = it->Reserve();
+ ASSERT(tlr != 0);
- const VAddr start{page_table->GetKernelMapRegionStart()};
- const VAddr size{page_table->GetKernelMapRegionEnd() - start};
- const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
- const VAddr tls_page_addr{page_table
- ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
- KMemoryState::ThreadLocal,
- KMemoryPermission::UserReadWrite,
- tls_map_addr)
- .ValueOr(0)};
+ if (it->IsAllUsed()) {
+ tlp = std::addressof(*it);
+ partially_used_tlp_tree.erase(it);
+ fully_used_tlp_tree.insert(*tlp);
+ }
- ASSERT(tls_page_addr);
+ *out = tlr;
+ return ResultSuccess;
+ }
+ }
+
+ // Allocate a new page.
+ tlp = KThreadLocalPage::Allocate(kernel);
+ R_UNLESS(tlp != nullptr, ResultOutOfMemory);
+ auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
+
+ // Initialize the new page.
+ R_TRY(tlp->Initialize(kernel, this));
- std::memset(tls_page_ptr, 0, PageSize);
- tls_pages.emplace_back(tls_page_addr);
+ // Reserve a TLR.
+ tlr = tlp->Reserve();
+ ASSERT(tlr != 0);
- const auto reserve_result{tls_pages.back().ReserveSlot()};
- ASSERT(reserve_result.has_value());
+ // Insert into our tree.
+ {
+ KScopedSchedulerLock sl{kernel};
+ if (tlp->IsAllUsed()) {
+ fully_used_tlp_tree.insert(*tlp);
+ } else {
+ partially_used_tlp_tree.insert(*tlp);
+ }
+ }
- return *reserve_result;
+ // We succeeded!
+ tlp_guard.Cancel();
+ *out = tlr;
+ return ResultSuccess;
}
-void KProcess::FreeTLSRegion(VAddr tls_address) {
- KScopedSchedulerLock lock(kernel);
- const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
- auto iter =
- std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
- return page.GetBaseAddress() == aligned_address;
- });
+ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
+ KThreadLocalPage* page_to_free = nullptr;
- // Something has gone very wrong if we're freeing a region
- // with no actual page available.
- ASSERT(iter != tls_pages.cend());
+ // Release the region.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ // Try to find the page in the partially used list.
+ auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
+ if (it == partially_used_tlp_tree.end()) {
+ // If we don't find it, it has to be in the fully used list.
+ it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
+ R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
+
+ // Release the region.
+ it->Release(addr);
+
+ // Move the page out of the fully used list.
+ KThreadLocalPage* tlp = std::addressof(*it);
+ fully_used_tlp_tree.erase(it);
+ if (tlp->IsAllFree()) {
+ page_to_free = tlp;
+ } else {
+ partially_used_tlp_tree.insert(*tlp);
+ }
+ } else {
+ // Release the region.
+ it->Release(addr);
+
+ // Handle the all-free case.
+ KThreadLocalPage* tlp = std::addressof(*it);
+ if (tlp->IsAllFree()) {
+ partially_used_tlp_tree.erase(it);
+ page_to_free = tlp;
+ }
+ }
+ }
+
+ // If we should free the page it was in, do so.
+ if (page_to_free != nullptr) {
+ page_to_free->Finalize();
- iter->ReleaseSlot(tls_address);
+ KThreadLocalPage::Free(kernel, page_to_free);
+ }
+
+ return ResultSuccess;
}
void 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 @@
#include <cstddef>
#include <list>
#include <string>
-#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/slab_helpers.h"
@@ -362,10 +362,10 @@ public:
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
- [[nodiscard]] VAddr CreateTLSRegion();
+ [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
// Frees a used TLS slot identified by the given address
- void FreeTLSRegion(VAddr tls_address);
+ ResultCode DeleteThreadLocalRegion(VAddr addr);
private:
void PinThread(s32 core_id, KThread* thread) {
@@ -413,13 +413,6 @@ private:
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
- /// The Thread Local Storage area is allocated as processes create threads,
- /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
- /// holds the TLS for a specific thread. This vector contains which parts are in use for each
- /// page as a bitmask.
- /// This vector will grow as more pages are allocated for new threads.
- std::vector<TLSPage> tls_pages;
-
/// Contains the parsed process capability descriptors.
ProcessCapabilities capabilities;
@@ -429,7 +422,7 @@ private:
bool is_64bit_process = true;
/// Total running time for the process in ticks.
- u64 total_process_running_time_ticks = 0;
+ std::atomic<u64> total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
KHandleTable handle_table;
@@ -482,6 +475,12 @@ private:
KThread* exception_thread{};
KLightLock state_lock;
+
+ using TLPTree =
+ Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
+ using TLPIterator = TLPTree::iterator;
+ TLPTree fully_used_tlp_tree;
+ TLPTree partially_used_tlp_tree;
};
} // 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 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -706,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
prev_thread = nullptr;
}
- thread->context_guard.Unlock();
+ thread->context_guard.unlock();
}
void KScheduler::Reload(KThread* thread) {
@@ -795,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
do {
auto next_thread = current_thread.load();
if (next_thread != nullptr) {
- const auto locked = next_thread->context_guard.TryLock();
+ const auto locked = next_thread->context_guard.try_lock();
if (state.needs_scheduling.load()) {
- next_thread->context_guard.Unlock();
+ next_thread->context_guard.unlock();
break;
}
if (next_thread->GetActiveCore() != core_id) {
- next_thread->context_guard.Unlock();
+ next_thread->context_guard.unlock();
break;
}
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 @@
#pragma once
+#include <atomic>
#include "common/assert.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
@@ -75,7 +76,7 @@ private:
KernelCore& kernel;
KAlignedSpinLock spin_lock{};
s32 lock_count{};
- KThread* owner_thread{};
+ std::atomic<KThread*> owner_thread{};
};
} // 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() {
// Close our reference to our parent.
parent->Close();
+
+ // Release host emulation members.
+ session_handler.reset();
+
+ // Ensure that the global list tracking server objects does not hold on to a reference.
+ kernel.UnregisterServerObject(this);
}
bool 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:
/// Whether or not this server port has an HLE handler available.
bool HasSessionRequestHandler() const {
- return session_handler != nullptr;
+ return !session_handler.expired();
}
/// Gets the HLE handler for this port.
- SessionRequestHandlerPtr GetSessionRequestHandler() const {
+ SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
return session_handler;
}
@@ -42,7 +42,7 @@ public:
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
- void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
+ void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
session_handler = std::move(handler);
}
@@ -66,7 +66,7 @@ private:
void CleanupSessions();
SessionList session_list;
- SessionRequestHandlerPtr session_handler;
+ SessionRequestHandlerWeakPtr session_handler;
KPort* parent{};
};
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 {
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
-KServerSession::~KServerSession() {
- // Ensure that the global list tracking server sessions does not hold on to a reference.
- kernel.UnregisterServerSession(this);
-}
+KServerSession::~KServerSession() = default;
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_) {
@@ -49,6 +46,12 @@ void KServerSession::Destroy() {
parent->OnServerClosed();
parent->Close();
+
+ // Release host emulation members.
+ manager.reset();
+
+ // Ensure that the global list tracking server objects does not hold on to a reference.
+ kernel.UnregisterServerObject(this);
}
void KServerSession::OnClientClosed() {
@@ -98,7 +101,12 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
UNREACHABLE();
return ResultSuccess; // Ignore error if asserts are off
}
- return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context);
+ if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
+ return strong_ptr->HandleSyncRequest(*this, context);
+ } else {
+ UNREACHABLE();
+ return ResultSuccess;
+ }
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
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;
namespace impl {
-class KSlabHeapImpl final {
-public:
+class KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
+public:
struct Node {
Node* next{};
};
+public:
constexpr KSlabHeapImpl() = default;
- constexpr ~KSlabHeapImpl() = default;
- void Initialize(std::size_t size) {
- ASSERT(head == nullptr);
- obj_size = size;
- }
-
- constexpr std::size_t GetObjectSize() const {
- return obj_size;
+ void Initialize() {
+ ASSERT(m_head == nullptr);
}
Node* GetHead() const {
- return head;
+ return m_head;
}
void* Allocate() {
- Node* ret = head.load();
+ Node* ret = m_head.load();
do {
if (ret == nullptr) {
break;
}
- } while (!head.compare_exchange_weak(ret, ret->next));
+ } while (!m_head.compare_exchange_weak(ret, ret->next));
return ret;
}
@@ -56,170 +51,157 @@ public:
void Free(void* obj) {
Node* node = static_cast<Node*>(obj);
- Node* cur_head = head.load();
+ Node* cur_head = m_head.load();
do {
node->next = cur_head;
- } while (!head.compare_exchange_weak(cur_head, node));
+ } while (!m_head.compare_exchange_weak(cur_head, node));
}
private:
- std::atomic<Node*> head{};
- std::size_t obj_size{};
+ std::atomic<Node*> m_head{};
};
} // namespace impl
-class KSlabHeapBase {
-public:
+template <bool SupportDynamicExpansion>
+class KSlabHeapBase : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
- constexpr KSlabHeapBase() = default;
- constexpr ~KSlabHeapBase() = default;
+private:
+ size_t m_obj_size{};
+ uintptr_t m_peak{};
+ uintptr_t m_start{};
+ uintptr_t m_end{};
- constexpr bool Contains(uintptr_t addr) const {
- return start <= addr && addr < end;
- }
+private:
+ void UpdatePeakImpl(uintptr_t obj) {
+ static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
+ std::atomic_ref<uintptr_t> peak_ref(m_peak);
- constexpr std::size_t GetSlabHeapSize() const {
- return (end - start) / GetObjectSize();
+ const uintptr_t alloc_peak = obj + this->GetObjectSize();
+ uintptr_t cur_peak = m_peak;
+ do {
+ if (alloc_peak <= cur_peak) {
+ break;
+ }
+ } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
- constexpr std::size_t GetObjectSize() const {
- return impl.GetObjectSize();
- }
+public:
+ constexpr KSlabHeapBase() = default;
- constexpr uintptr_t GetSlabHeapAddress() const {
- return start;
+ bool Contains(uintptr_t address) const {
+ return m_start <= address && address < m_end;
}
- std::size_t GetObjectIndexImpl(const void* obj) const {
- return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
+ void Initialize(size_t obj_size, void* memory, size_t memory_size) {
+ // Ensure we don't initialize a slab using null memory.
+ ASSERT(memory != nullptr);
+
+ // Set our object size.
+ m_obj_size = obj_size;
+
+ // Initialize the base allocator.
+ KSlabHeapImpl::Initialize();
+
+ // Set our tracking variables.
+ const size_t num_obj = (memory_size / obj_size);
+ m_start = reinterpret_cast<uintptr_t>(memory);
+ m_end = m_start + num_obj * obj_size;
+ m_peak = m_start;
+
+ // Free the objects.
+ u8* cur = reinterpret_cast<u8*>(m_end);
+
+ for (size_t i = 0; i < num_obj; i++) {
+ cur -= obj_size;
+ KSlabHeapImpl::Free(cur);
+ }
}
- std::size_t GetPeakIndex() const {
- return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
+ size_t GetSlabHeapSize() const {
+ return (m_end - m_start) / this->GetObjectSize();
}
- void* AllocateImpl() {
- return impl.Allocate();
+ size_t GetObjectSize() const {
+ return m_obj_size;
}
- void FreeImpl(void* obj) {
- // Don't allow freeing an object that wasn't allocated from this heap
- ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
+ void* Allocate() {
+ void* obj = KSlabHeapImpl::Allocate();
- impl.Free(obj);
+ return obj;
}
- void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
- // Ensure we don't initialize a slab using null memory
- ASSERT(memory != nullptr);
-
- // Initialize the base allocator
- impl.Initialize(obj_size);
+ void Free(void* obj) {
+ // Don't allow freeing an object that wasn't allocated from this heap.
+ const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
+ ASSERT(contained);
+ KSlabHeapImpl::Free(obj);
+ }
- // Set our tracking variables
- const std::size_t num_obj = (memory_size / obj_size);
- start = reinterpret_cast<uintptr_t>(memory);
- end = start + num_obj * obj_size;
- peak = start;
+ size_t GetObjectIndex(const void* obj) const {
+ if constexpr (SupportDynamicExpansion) {
+ if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
+ return std::numeric_limits<size_t>::max();
+ }
+ }
- // Free the objects
- u8* cur = reinterpret_cast<u8*>(end);
+ return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
+ }
- for (std::size_t i{}; i < num_obj; i++) {
- cur -= obj_size;
- impl.Free(cur);
- }
+ size_t GetPeakIndex() const {
+ return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
-private:
- using Impl = impl::KSlabHeapImpl;
+ uintptr_t GetSlabHeapAddress() const {
+ return m_start;
+ }
- Impl impl;
- uintptr_t peak{};
- uintptr_t start{};
- uintptr_t end{};
+ size_t GetNumRemaining() const {
+ // Only calculate the number of remaining objects under debug configuration.
+ return 0;
+ }
};
template <typename T>
-class KSlabHeap final : public KSlabHeapBase {
-public:
- enum class AllocationType {
- Host,
- Guest,
- };
+class KSlabHeap final : public KSlabHeapBase<false> {
+private:
+ using BaseHeap = KSlabHeapBase<false>;
- explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host)
- : KSlabHeapBase(), allocation_type{allocation_type_} {}
+public:
+ constexpr KSlabHeap() = default;
- void Initialize(void* memory, std::size_t memory_size) {
- if (allocation_type == AllocationType::Guest) {
- InitializeImpl(sizeof(T), memory, memory_size);
- }
+ void Initialize(void* memory, size_t memory_size) {
+ BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- return new T;
-
- case AllocationType::Guest:
- T* obj = static_cast<T*>(AllocateImpl());
- if (obj != nullptr) {
- new (obj) T();
- }
- return obj;
- }
+ T* obj = static_cast<T*>(BaseHeap::Allocate());
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
- return nullptr;
+ if (obj != nullptr) [[likely]] {
+ std::construct_at(obj);
+ }
+ return obj;
}
- T* AllocateWithKernel(KernelCore& kernel) {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- return new T(kernel);
+ T* Allocate(KernelCore& kernel) {
+ T* obj = static_cast<T*>(BaseHeap::Allocate());
- case AllocationType::Guest:
- T* obj = static_cast<T*>(AllocateImpl());
- if (obj != nullptr) {
- new (obj) T(kernel);
- }
- return obj;
+ if (obj != nullptr) [[likely]] {
+ std::construct_at(obj, kernel);
}
-
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
- return nullptr;
+ return obj;
}
void Free(T* obj) {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- delete obj;
- return;
-
- case AllocationType::Guest:
- FreeImpl(obj);
- return;
- }
-
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
+ BaseHeap::Free(obj);
}
- constexpr std::size_t GetObjectIndex(const T* obj) const {
- return GetObjectIndexImpl(obj);
+ size_t GetObjectIndex(const T* obj) const {
+ return BaseHeap::GetObjectIndex(obj);
}
-
-private:
- const AllocationType allocation_type;
};
} // 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 @@
#include "core/hle/kernel/k_spin_lock.h"
-#if _MSC_VER
-#include <intrin.h>
-#if _M_AMD64
-#define __x86_64__ 1
-#endif
-#if _M_ARM64
-#define __aarch64__ 1
-#endif
-#else
-#if __x86_64__
-#include <xmmintrin.h>
-#endif
-#endif
-
-namespace {
-
-void ThreadPause() {
-#if __x86_64__
- _mm_pause();
-#elif __aarch64__ && _MSC_VER
- __yield();
-#elif __aarch64__
- asm("yield");
-#endif
-}
-
-} // namespace
-
namespace Kernel {
void KSpinLock::Lock() {
- while (lck.test_and_set(std::memory_order_acquire)) {
- ThreadPause();
- }
+ lck.lock();
}
void KSpinLock::Unlock() {
- lck.clear(std::memory_order_release);
+ lck.unlock();
}
bool KSpinLock::TryLock() {
- if (lck.test_and_set(std::memory_order_acquire)) {
- return false;
- }
- return true;
+ return lck.try_lock();
}
} // 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 @@
#pragma once
-#include <atomic>
+#include <mutex>
#include "core/hle/kernel/k_scoped_lock.h"
@@ -25,7 +25,7 @@ public:
[[nodiscard]] bool TryLock();
private:
- std::atomic_flag lck = ATOMIC_FLAG_INIT;
+ std::mutex lck;
};
// 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 @@
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
-#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -33,7 +31,6 @@
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
-#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -210,7 +207,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
if (owner != nullptr) {
// Setup the TLS, if needed.
if (type == ThreadType::User) {
- tls_address = owner->CreateTLSRegion();
+ R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
}
parent = owner;
@@ -305,7 +302,7 @@ void KThread::Finalize() {
// If the thread has a local region, delete it.
if (tls_address != 0) {
- parent->FreeTLSRegion(tls_address);
+ ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
}
// Release any waiters.
@@ -326,6 +323,9 @@ void KThread::Finalize() {
}
}
+ // Release host emulation members.
+ host_context.reset();
+
// Perform inherited finalization.
KSynchronizationObject::Finalize();
}
@@ -723,10 +723,10 @@ void KThread::UpdateState() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Set our suspend flags in state.
- const auto old_state = thread_state;
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
const auto new_state =
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
- thread_state = new_state;
+ thread_state.store(new_state, std::memory_order_relaxed);
// Note the state change in scheduler.
if (new_state != old_state) {
@@ -738,8 +738,8 @@ void KThread::Continue() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Clear our suspend flags in state.
- const auto old_state = thread_state;
- thread_state = old_state & ThreadState::Mask;
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
+ thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
// Note the state change in scheduler.
KScheduler::OnThreadStateChanged(kernel, this, old_state);
@@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
return;
}
- // Block until we can grab the lock.
- KScopedSpinLock lk{dummy_wait_lock};
-}
-
-void KThread::IfDummyThreadBeginWait() {
- if (!IsDummyThread()) {
- return;
- }
-
- // Ensure the thread will block when IfDummyThreadTryWait is called.
- dummy_wait_lock.Lock();
+ // Block until we are no longer waiting.
+ std::unique_lock lk(dummy_wait_lock);
+ dummy_wait_cv.wait(
+ lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
}
void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
return;
}
- // Ensure the thread will no longer block.
- dummy_wait_lock.Unlock();
+ // Wake up the waiting thread.
+ dummy_wait_cv.notify_one();
}
void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
// Set our wait queue.
wait_queue = queue;
-
- // Special case for dummy threads to ensure they block.
- IfDummyThreadBeginWait();
}
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
SetMutexWaitAddressForDebugging({});
SetWaitReasonForDebugging({});
- const ThreadState old_state = thread_state;
- thread_state =
- static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
- if (thread_state != old_state) {
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
+ thread_state.store(
+ static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
+ std::memory_order_relaxed);
+ if (thread_state.load(std::memory_order_relaxed) != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
}
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 @@
#pragma once
#include <array>
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
#include <span>
#include <string>
#include <utility>
@@ -14,6 +17,7 @@
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
+#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
@@ -255,11 +259,11 @@ public:
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
[[nodiscard]] ThreadState GetState() const {
- return thread_state & ThreadState::Mask;
+ return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
}
[[nodiscard]] ThreadState GetRawState() const {
- return thread_state;
+ return thread_state.load(std::memory_order_relaxed);
}
void SetState(ThreadState state);
@@ -641,7 +645,6 @@ public:
// blocking as needed.
void IfDummyThreadTryWait();
- void IfDummyThreadBeginWait();
void IfDummyThreadEndWait();
private:
@@ -656,7 +659,7 @@ private:
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
- struct LightCompareType {
+ struct RedBlackKeyType {
u64 cv_key{};
s32 priority{};
@@ -672,8 +675,8 @@ private:
template <typename T>
requires(
std::same_as<T, KThread> ||
- std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
- const KThread& rhs) {
+ std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
+ const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();
@@ -751,7 +754,7 @@ private:
KAffinityMask original_physical_affinity_mask{};
s32 original_physical_ideal_core_id{};
s32 num_core_migration_disables{};
- ThreadState thread_state{};
+ std::atomic<ThreadState> thread_state{};
std::atomic<bool> termination_requested{};
bool wait_cancelled{};
bool cancellable{};
@@ -761,13 +764,14 @@ private:
s8 priority_inheritance_count{};
bool resource_limit_release_hint{};
StackParameters stack_parameters{};
- KSpinLock context_guard{};
- KSpinLock dummy_wait_lock{};
+ Common::SpinLock context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
ThreadType thread_type{};
+ std::mutex dummy_wait_lock;
+ std::condition_variable dummy_wait_cv;
// For debugging
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+
+#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_page_buffer.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread_local_page.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
+ // Set that this process owns us.
+ m_owner = process;
+ m_kernel = &kernel;
+
+ // Allocate a new page.
+ KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
+ R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
+ auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
+
+ // Map the address in.
+ const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
+ R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
+ KMemoryState::ThreadLocal,
+ KMemoryPermission::UserReadWrite));
+
+ // We succeeded.
+ page_buf_guard.Cancel();
+
+ return ResultSuccess;
+}
+
+ResultCode KThreadLocalPage::Finalize() {
+ // Get the physical address of the page.
+ const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
+ ASSERT(phys_addr);
+
+ // Unmap the page.
+ R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
+
+ // Free the page.
+ KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
+
+ return ResultSuccess;
+}
+
+VAddr KThreadLocalPage::Reserve() {
+ for (size_t i = 0; i < m_is_region_free.size(); i++) {
+ if (m_is_region_free[i]) {
+ m_is_region_free[i] = false;
+ return this->GetRegionAddress(i);
+ }
+ }
+
+ return 0;
+}
+
+void KThreadLocalPage::Release(VAddr addr) {
+ m_is_region_free[this->GetRegionIndex(addr)] = true;
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
+ public KSlabAllocated<KThreadLocalPage> {
+public:
+ static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
+ static_assert(RegionsPerPage > 0);
+
+public:
+ constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
+ m_is_region_free.fill(true);
+ }
+
+ constexpr VAddr GetAddress() const {
+ return m_virt_addr;
+ }
+
+ ResultCode Initialize(KernelCore& kernel, KProcess* process);
+ ResultCode Finalize();
+
+ VAddr Reserve();
+ void Release(VAddr addr);
+
+ bool IsAllUsed() const {
+ return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+ [](bool is_free) { return !is_free; });
+ }
+
+ bool IsAllFree() const {
+ return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+ [](bool is_free) { return is_free; });
+ }
+
+ bool IsAnyUsed() const {
+ return !this->IsAllFree();
+ }
+
+ bool IsAnyFree() const {
+ return !this->IsAllUsed();
+ }
+
+public:
+ using RedBlackKeyType = VAddr;
+
+ static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
+ return v;
+ }
+ static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
+ return v.GetAddress();
+ }
+
+ template <typename T>
+ requires(std::same_as<T, KThreadLocalPage> ||
+ std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
+ const KThreadLocalPage&
+ rhs) {
+ const VAddr lval = GetRedBlackKey(lhs);
+ const VAddr rval = GetRedBlackKey(rhs);
+
+ if (lval < rval) {
+ return -1;
+ } else if (lval == rval) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+private:
+ constexpr VAddr GetRegionAddress(size_t i) const {
+ return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
+ }
+
+ constexpr bool Contains(VAddr addr) const {
+ return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
+ }
+
+ constexpr size_t GetRegionIndex(VAddr addr) const {
+ ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
+ ASSERT(this->Contains(addr));
+ return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
+ }
+
+private:
+ VAddr m_virt_addr{};
+ KProcess* m_owner{};
+ KernelCore* m_kernel{};
+ std::array<bool, RegionsPerPage> m_is_region_free{};
+};
+
+} // 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 @@
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
-#include "core/core_timing_util.h"
#include "core/cpu_manager.h"
-#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_client_port.h"
@@ -35,7 +33,6 @@
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
-#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
@@ -52,7 +49,7 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_)
- : time_manager{system_}, object_list_container{kernel_},
+ : time_manager{system_},
service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
void SetMulticore(bool is_multi) {
@@ -60,9 +57,11 @@ struct KernelCore::Impl {
}
void Initialize(KernelCore& kernel) {
+ global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
+ default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
is_phantom_mode_for_singlecore = false;
@@ -76,7 +75,7 @@ struct KernelCore::Impl {
// Initialize kernel memory and resources.
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
- InitializePageSlab();
+ Init::InitializeKPageBufferSlabHeap(system);
InitializeSchedulers();
InitializeSuspendThreads();
InitializePreemption(kernel);
@@ -86,7 +85,7 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- cores[core_id].Initialize(current_process->Is64BitProcess());
+ cores[core_id].Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -97,30 +96,17 @@ struct KernelCore::Impl {
process_list.clear();
- // Close all open server ports.
- std::unordered_set<KServerPort*> server_ports_;
+ // Close all open server sessions and ports.
+ std::unordered_set<KAutoObject*> server_objects_;
{
- std::lock_guard lk(server_ports_lock);
- server_ports_ = server_ports;
- server_ports.clear();
+ std::scoped_lock lk(server_objects_lock);
+ server_objects_ = server_objects;
+ server_objects.clear();
}
- for (auto* server_port : server_ports_) {
- server_port->Close();
- }
- // Close all open server sessions.
- std::unordered_set<KServerSession*> server_sessions_;
- {
- std::lock_guard lk(server_sessions_lock);
- server_sessions_ = server_sessions;
- server_sessions.clear();
- }
- for (auto* server_session : server_sessions_) {
- server_session->Close();
+ for (auto* server_object : server_objects_) {
+ server_object->Close();
}
- // Ensure that the object list container is finalized and properly shutdown.
- object_list_container.Finalize();
-
// Ensures all service threads gracefully shutdown.
ClearServiceThreads();
@@ -154,6 +140,7 @@ struct KernelCore::Impl {
CleanupObject(font_shared_mem);
CleanupObject(irs_shared_mem);
CleanupObject(time_shared_mem);
+ CleanupObject(hidbus_shared_mem);
CleanupObject(system_resource_limit);
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -171,7 +158,7 @@ struct KernelCore::Impl {
// Close kernel objects that were not freed on shutdown
{
- std::lock_guard lk(registered_in_use_objects_lock);
+ std::scoped_lock lk{registered_in_use_objects_lock};
if (registered_in_use_objects.size()) {
for (auto& object : registered_in_use_objects) {
object->Close();
@@ -182,23 +169,27 @@ struct KernelCore::Impl {
// Shutdown all processes.
if (current_process) {
- current_process->Finalize();
+ (*current_process).Finalize();
// current_process->Close();
// TODO: The current process should be destroyed based on accurate ref counting after
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
- current_process->Destroy();
+ (*current_process).Destroy();
current_process = nullptr;
}
// Track kernel objects that were not freed on shutdown
{
- std::lock_guard lk(registered_objects_lock);
+ std::scoped_lock lk{registered_objects_lock};
if (registered_objects.size()) {
- LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!",
- registered_objects.size());
+ LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
+ registered_objects.size());
registered_objects.clear();
}
}
+
+ // Ensure that the object list container is finalized and properly shutdown.
+ global_object_list_container->Finalize();
+ global_object_list_container.reset();
}
void InitializePhysicalCores() {
@@ -291,15 +282,16 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
- auto make_thread = [this]() {
- KThread* thread = KThread::Create(system.Kernel());
+ auto initialize = [this](KThread* thread) {
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
};
- thread_local KThread* saved_thread = make_thread();
- return saved_thread;
+ thread_local auto raw_thread = KThread(system.Kernel());
+ thread_local auto thread = initialize(&raw_thread);
+
+ return thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -631,16 +623,20 @@ struct KernelCore::Impl {
constexpr std::size_t font_size{0x1100000};
constexpr std::size_t irs_size{0x8000};
constexpr std::size_t time_size{0x1000};
+ constexpr std::size_t hidbus_size{0x1000};
const PAddr hid_phys_addr{system_pool.GetAddress()};
const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
+ const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
+ time_size};
hid_shared_mem = KSharedMemory::Create(system.Kernel());
font_shared_mem = KSharedMemory::Create(system.Kernel());
irs_shared_mem = KSharedMemory::Create(system.Kernel());
time_shared_mem = KSharedMemory::Create(system.Kernel());
+ hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
{hid_phys_addr, hid_size / PageSize},
@@ -658,22 +654,10 @@ struct KernelCore::Impl {
{time_phys_addr, time_size / PageSize},
Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
time_phys_addr, time_size, "Time:SharedMemory");
- }
-
- void InitializePageSlab() {
- // Allocate slab heaps
- user_slab_heap_pages =
- std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
-
- // TODO(ameerj): This should be derived, not hardcoded within the kernel
- constexpr u64 user_slab_heap_size{0x3de000};
- // Reserve slab heaps
- ASSERT(
- system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
- // Initialize slab heap
- user_slab_heap_pages->Initialize(
- system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
- user_slab_heap_size);
+ hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
+ {hidbus_phys_addr, hidbus_size / PageSize},
+ Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+ hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
}
KClientPort* CreateNamedServicePort(std::string name) {
@@ -684,13 +668,20 @@ struct KernelCore::Impl {
}
KClientPort* port = &search->second(system.ServiceManager(), system);
- {
- std::lock_guard lk(server_ports_lock);
- server_ports.insert(&port->GetParent()->GetServerPort());
- }
+ RegisterServerObject(&port->GetParent()->GetServerPort());
return port;
}
+ void RegisterServerObject(KAutoObject* server_object) {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects.insert(server_object);
+ }
+
+ void UnregisterServerObject(KAutoObject* server_object) {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects.erase(server_object);
+ }
+
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -703,6 +694,12 @@ struct KernelCore::Impl {
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
+ if (strong_ptr == default_service_thread.lock()) {
+ // Nothing to do here, the service is using default_service_thread, which will be
+ // released on shutdown.
+ return;
+ }
+
service_threads_manager.QueueWork(
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
}
@@ -712,8 +709,7 @@ struct KernelCore::Impl {
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
}
- std::mutex server_ports_lock;
- std::mutex server_sessions_lock;
+ std::mutex server_objects_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
@@ -724,7 +720,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<KProcess*> process_list;
- KProcess* current_process{};
+ std::atomic<KProcess*> current_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
Kernel::TimeManager time_manager;
@@ -737,14 +733,13 @@ struct KernelCore::Impl {
// stores all the objects in place.
std::unique_ptr<KHandleTable> global_handle_table;
- KAutoObjectWithListContainer object_list_container;
+ std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
NamedPortTable named_ports;
- std::unordered_set<KServerPort*> server_ports;
- std::unordered_set<KServerSession*> server_sessions;
+ std::unordered_set<KAutoObject*> server_objects;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
@@ -756,19 +751,20 @@ struct KernelCore::Impl {
// Kernel memory management
std::unique_ptr<KMemoryManager> memory_manager;
- std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
// Shared memory for services
Kernel::KSharedMemory* hid_shared_mem{};
Kernel::KSharedMemory* font_shared_mem{};
Kernel::KSharedMemory* irs_shared_mem{};
Kernel::KSharedMemory* time_shared_mem{};
+ Kernel::KSharedMemory* hidbus_shared_mem{};
// Memory layout
std::unique_ptr<KMemoryLayout> memory_layout;
// Threads used for services
- std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
+ std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
+ std::weak_ptr<ServiceThread> default_service_thread;
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -915,11 +911,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
}
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
- return impl->object_list_container;
+ return *impl->global_object_list_container;
}
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
- return impl->object_list_container;
+ return *impl->global_object_list_container;
}
void KernelCore::InvalidateAllInstructionCaches() {
@@ -949,33 +945,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
return impl->CreateNamedServicePort(std::move(name));
}
-void KernelCore::RegisterServerSession(KServerSession* server_session) {
- std::lock_guard lk(impl->server_sessions_lock);
- impl->server_sessions.insert(server_session);
+void KernelCore::RegisterServerObject(KAutoObject* server_object) {
+ impl->RegisterServerObject(server_object);
}
-void KernelCore::UnregisterServerSession(KServerSession* server_session) {
- std::lock_guard lk(impl->server_sessions_lock);
- impl->server_sessions.erase(server_session);
+void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
+ impl->UnregisterServerObject(server_object);
}
void KernelCore::RegisterKernelObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_objects_lock);
+ std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.insert(object);
}
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_objects_lock);
+ std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.erase(object);
}
void KernelCore::RegisterInUseObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_in_use_objects_lock);
+ std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.insert(object);
}
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_in_use_objects_lock);
+ std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.erase(object);
}
@@ -1031,14 +1025,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
-KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
- return *impl->user_slab_heap_pages;
-}
-
-const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
- return *impl->user_slab_heap_pages;
-}
-
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}
@@ -1071,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
return *impl->time_shared_mem;
}
+Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
+ return *impl->hidbus_shared_mem;
+}
+
+const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
+ return *impl->hidbus_shared_mem;
+}
+
void KernelCore::Suspend(bool in_suspention) {
const bool should_suspend = exception_exited || in_suspention;
{
@@ -1112,6 +1106,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
return impl->CreateServiceThread(*this, name);
}
+std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
+ return impl->default_service_thread;
+}
+
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
impl->ReleaseServiceThread(service_thread);
}
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 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
-#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
@@ -43,6 +42,7 @@ class KHandleTable;
class KLinkedListNode;
class KMemoryLayout;
class KMemoryManager;
+class KPageBuffer;
class KPort;
class KProcess;
class KResourceLimit;
@@ -52,6 +52,7 @@ class KSession;
class KSharedMemory;
class KSharedMemoryInfo;
class KThread;
+class KThreadLocalPage;
class KTransferMemory;
class KWorkerTaskManager;
class KWritableEvent;
@@ -194,13 +195,13 @@ public:
/// Opens a port to a service previously registered with RegisterNamedService.
KClientPort* CreateNamedServicePort(std::string name);
- /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
- /// necessary because we do not emulate processes for HLE sessions.
- void RegisterServerSession(KServerSession* server_session);
+ /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
+ /// This is necessary because we do not emulate processes for HLE sessions and ports.
+ void RegisterServerObject(KAutoObject* server_object);
- /// Unregisters a server session previously registered with RegisterServerSession when it was
- /// destroyed during the current emulation session.
- void UnregisterServerSession(KServerSession* server_session);
+ /// Unregisters a server session or port previously registered with RegisterServerSession when
+ /// it was destroyed during the current emulation session.
+ void UnregisterServerObject(KAutoObject* server_object);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
@@ -239,12 +240,6 @@ public:
/// Gets the virtual memory manager for the kernel.
const KMemoryManager& MemoryManager() const;
- /// Gets the slab heap allocated for user space pages.
- KSlabHeap<Page>& GetUserSlabHeapPages();
-
- /// Gets the slab heap allocated for user space pages.
- const KSlabHeap<Page>& GetUserSlabHeapPages() const;
-
/// Gets the shared memory object for HID services.
Kernel::KSharedMemory& GetHidSharedMem();
@@ -269,6 +264,12 @@ public:
/// Gets the shared memory object for Time services.
const Kernel::KSharedMemory& GetTimeSharedMem() const;
+ /// Gets the shared memory object for HIDBus services.
+ Kernel::KSharedMemory& GetHidBusSharedMem();
+
+ /// Gets the shared memory object for HIDBus services.
+ const Kernel::KSharedMemory& GetHidBusSharedMem() const;
+
/// Suspend/unsuspend the OS.
void Suspend(bool in_suspention);
@@ -284,9 +285,11 @@ public:
void ExitSVCProfile();
/**
- * Creates an HLE service thread, which are used to execute service routines asynchronously.
- * While these are allocated per ServerSession, these need to be owned and managed outside
- * of ServerSession to avoid a circular dependency.
+ * Creates a host thread to execute HLE service requests, which are used to execute service
+ * routines asynchronously. While these are allocated per ServerSession, these need to be owned
+ * and managed outside of ServerSession to avoid a circular dependency. In general, most
+ * services can just use the default service thread, and not need their own host service thread.
+ * See GetDefaultServiceThread.
* @param name String name for the ServerSession creating this thread, used for debug
* purposes.
* @returns The a weak pointer newly created service thread.
@@ -294,6 +297,14 @@ public:
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
/**
+ * Gets the default host service thread, which executes HLE service requests. Unless service
+ * requests need to block on the host, the default service thread should be used in favor of
+ * creating a new service thread.
+ * @returns The a weak pointer for the default service thread.
+ */
+ std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
+
+ /**
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
* the ServerSession associated with the thread is destroyed.
* @param service_thread Service thread to release.
@@ -336,6 +347,10 @@ public:
return slab_heap_container->writeable_event;
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
return slab_heap_container->code_memory;
+ } else if constexpr (std::is_same_v<T, KPageBuffer>) {
+ return slab_heap_container->page_buffer;
+ } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
+ return slab_heap_container->thread_local_page;
}
}
@@ -397,6 +412,8 @@ private:
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
KSlabHeap<KCodeMemory> code_memory;
+ KSlabHeap<KPageBuffer> page_buffer;
+ KSlabHeap<KThreadLocalPage> thread_local_page;
};
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/spin_lock.h"
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -16,7 +15,7 @@ namespace Kernel {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
Core::CPUInterrupts& interrupts_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
- interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {
+ interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
@@ -58,6 +57,7 @@ bool PhysicalCore::IsInterrupted() const {
void PhysicalCore::Interrupt() {
guard->lock();
interrupts[core_index].SetInterrupt(true);
+ arm_interface->SignalInterrupt();
guard->unlock();
}
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 @@
#include <cstddef>
#include <memory>
+#include <mutex>
#include "core/arm/arm_interface.h"
-namespace Common {
-class SpinLock;
-}
-
namespace Kernel {
class KScheduler;
} // namespace Kernel
@@ -91,7 +88,7 @@ private:
Core::System& system;
Kernel::KScheduler& scheduler;
Core::CPUInterrupts& interrupts;
- std::unique_ptr<Common::SpinLock> guard;
+ std::unique_ptr<std::mutex> guard;
std::unique_ptr<Core::ARM_Interface> arm_interface;
};
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
return;
}
+ // Allocate a dummy guest thread for this host thread.
kernel.RegisterHostThread();
- // Ensure the dummy thread allocated for this host thread is closed on exit.
- auto* dummy_thread = kernel.GetCurrentEmuThread();
- SCOPE_EXIT({ dummy_thread->Close(); });
-
while (true) {
std::function<void()> task;
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 {
private:
static Derived* Allocate(KernelCore& kernel) {
- return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel);
+ return kernel.SlabHeap<Derived>().Allocate(kernel);
}
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
ResultInvalidMemoryRegion);
// Create a new page group.
- KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
- KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+ KPageLinkedList pg;
+ R_TRY(src_pt.MakeAndOpenPageGroup(
+ std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::None));
// Map the group.
R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
}
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
- LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
- static_cast<void*>(out), address, size);
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
+
// Get kernel instance.
auto& kernel = system.Kernel();
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidAddress;
}
- if (size == 0 || Common::Is4KBAligned(size)) {
+ if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
return ResultInvalidSize;
}
@@ -1710,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidMemoryRegion;
}
- return page_table.UnmapCodeMemory(dst_address, src_address, size);
+ return page_table.UnmapCodeMemory(dst_address, src_address, size,
+ KPageTable::ICacheInvalidationStrategy::InvalidateAll);
}
/// 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;
constexpr inline s32 LowestThreadPriority = 63;
constexpr inline s32 HighestThreadPriority = 0;
+constexpr inline size_t ThreadLocalRegionSize = 0x200;
+
} // 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_} {
}
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
if (nanoseconds > 0) {
ASSERT(thread);
ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
}
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
reinterpret_cast<uintptr_t>(thread));
}
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 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#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 @@
#include <array>
#include <optional>
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#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:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
- applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
+ applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1007,7 +1007,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
- applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
+ applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
ASSERT(applet->IsInitialized());
applet->ExecuteInteractive();
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{200, nullptr, "GetLastApplicationExitReason"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
- {1001, nullptr, "PrepareForJit"},
+ {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
};
// clang-format on
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
}
+void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
Core::System& system) {
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:
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
+ void PrepareForJit(Kernel::HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/frontend/applets/mii_edit.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applet_mii_edit.h"
+#include "core/hle/service/mii/mii_manager.h"
+
+namespace Service::AM::Applets {
+
+MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
+ const Core::Frontend::MiiEditApplet& frontend_)
+ : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
+
+MiiEdit::~MiiEdit() = default;
+
+void MiiEdit::Initialize() {
+ // Note: MiiEdit is not initialized with common arguments.
+ // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
+ // Do NOT call Applet::Initialize() here.
+
+ const auto storage = broker.PopNormalDataToApplet();
+ ASSERT(storage != nullptr);
+
+ const auto applet_input_data = storage->GetData();
+ ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
+
+ std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
+
+ LOG_INFO(Service_AM,
+ "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
+ applet_input_common.version, applet_input_common.applet_mode);
+
+ switch (applet_input_common.version) {
+ case MiiEditAppletVersion::Version3:
+ ASSERT(applet_input_data.size() ==
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
+ std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV3));
+ break;
+ case MiiEditAppletVersion::Version4:
+ ASSERT(applet_input_data.size() ==
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
+ std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV4));
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
+ applet_input_common.version, applet_input_data.size());
+ ASSERT(applet_input_data.size() >=
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
+ std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV4));
+ break;
+ }
+}
+
+bool MiiEdit::TransactionComplete() const {
+ return is_complete;
+}
+
+ResultCode MiiEdit::GetStatus() const {
+ return ResultSuccess;
+}
+
+void MiiEdit::ExecuteInteractive() {
+ UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+}
+
+void MiiEdit::Execute() {
+ if (is_complete) {
+ return;
+ }
+
+ // This is a default stub for each of the MiiEdit applet modes.
+ switch (applet_input_common.applet_mode) {
+ case MiiEditAppletMode::ShowMiiEdit:
+ case MiiEditAppletMode::AppendMii:
+ case MiiEditAppletMode::AppendMiiImage:
+ case MiiEditAppletMode::UpdateMiiImage:
+ MiiEditOutput(MiiEditResult::Success, 0);
+ break;
+ case MiiEditAppletMode::CreateMii:
+ case MiiEditAppletMode::EditMii: {
+ Service::Mii::MiiManager mii_manager;
+
+ const MiiEditCharInfo char_info{
+ .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
+ ? applet_input_v4.char_info.mii_info
+ : mii_manager.BuildDefault(0)},
+ };
+
+ MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
+
+ MiiEditOutput(MiiEditResult::Success, 0);
+ break;
+ }
+}
+
+void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
+ const MiiEditAppletOutput applet_output{
+ .result{result},
+ .index{index},
+ };
+
+ std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
+ std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
+
+ is_complete = true;
+
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
+ const MiiEditCharInfo& char_info) {
+ const MiiEditAppletOutputForCharInfoEditing applet_output{
+ .result{result},
+ .char_info{char_info},
+ };
+
+ std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
+ std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
+
+ is_complete = true;
+
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/applets/applet_mii_edit_types.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Service::AM::Applets {
+
+class MiiEdit final : public Applet {
+public:
+ explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
+ const Core::Frontend::MiiEditApplet& frontend_);
+ ~MiiEdit() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void MiiEditOutput(MiiEditResult result, s32 index);
+
+ void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
+
+private:
+ const Core::Frontend::MiiEditApplet& frontend;
+ Core::System& system;
+
+ MiiEditAppletInputCommon applet_input_common{};
+ MiiEditAppletInputV3 applet_input_v3{};
+ MiiEditAppletInputV4 applet_input_v4{};
+
+ bool is_complete{false};
+};
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/service/mii/types.h"
+
+namespace Service::AM::Applets {
+
+enum class MiiEditAppletVersion : s32 {
+ Version3 = 0x3, // 1.0.0 - 10.1.1
+ Version4 = 0x4, // 10.2.0+
+};
+
+// This is nn::mii::AppletMode
+enum class MiiEditAppletMode : u32 {
+ ShowMiiEdit = 0,
+ AppendMii = 1,
+ AppendMiiImage = 2,
+ UpdateMiiImage = 3,
+ CreateMii = 4,
+ EditMii = 5,
+};
+
+enum class MiiEditResult : u32 {
+ Success,
+ Cancel,
+};
+
+struct MiiEditCharInfo {
+ Service::Mii::MiiInfo mii_info{};
+};
+static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
+
+struct MiiEditAppletInputCommon {
+ MiiEditAppletVersion version{};
+ MiiEditAppletMode applet_mode{};
+};
+static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
+ "MiiEditAppletInputCommon has incorrect size.");
+
+struct MiiEditAppletInputV3 {
+ u32 special_mii_key_code{};
+ std::array<Common::UUID, 8> valid_uuids{};
+ Common::UUID used_uuid{};
+ INSERT_PADDING_BYTES(0x64);
+};
+static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
+ "MiiEditAppletInputV3 has incorrect size.");
+
+struct MiiEditAppletInputV4 {
+ u32 special_mii_key_code{};
+ MiiEditCharInfo char_info{};
+ INSERT_PADDING_BYTES(0x28);
+ Common::UUID used_uuid{};
+ INSERT_PADDING_BYTES(0x64);
+};
+static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
+ "MiiEditAppletInputV4 has incorrect size.");
+
+// This is nn::mii::AppletOutput
+struct MiiEditAppletOutput {
+ MiiEditResult result{};
+ s32 index{};
+ INSERT_PADDING_BYTES(0x18);
+};
+static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
+
+// This is nn::mii::AppletOutputForCharInfoEditing
+struct MiiEditAppletOutputForCharInfoEditing {
+ MiiEditResult result{};
+ MiiEditCharInfo char_info{};
+ INSERT_PADDING_BYTES(0x24);
+};
+static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
+ "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
+
+} // 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() {
ASSERT(work_buffer_storage != nullptr);
if (swkbd_config_common.initial_string_length == 0) {
- InitializeFrontendKeyboard();
+ InitializeFrontendNormalKeyboard();
return;
}
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
- InitializeFrontendKeyboard();
+ InitializeFrontendNormalKeyboard();
}
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
ReplyDefault();
}
-void SoftwareKeyboard::InitializeFrontendKeyboard() {
- if (is_background) {
- const auto& appear_arg = swkbd_calc_arg.appear_arg;
-
- std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- appear_arg.ok_text.data(), appear_arg.ok_text.size());
-
- const u32 max_text_length =
- appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
- ? appear_arg.max_text_length
- : DEFAULT_MAX_TEXT_LENGTH;
-
- const u32 min_text_length =
- appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
-
- const s32 initial_cursor_position =
- current_cursor_position > 0 ? current_cursor_position : 0;
-
- const auto text_draw_type =
- max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
-
- Core::Frontend::KeyboardInitializeParameters initialize_parameters{
- .ok_text{std::move(ok_text)},
- .header_text{},
- .sub_text{},
- .guide_text{},
- .initial_text{current_text},
- .max_text_length{max_text_length},
- .min_text_length{min_text_length},
- .initial_cursor_position{initial_cursor_position},
- .type{appear_arg.type},
- .password_mode{SwkbdPasswordMode::Disabled},
- .text_draw_type{text_draw_type},
- .key_disable_flags{appear_arg.key_disable_flags},
- .use_blur_background{false},
- .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
- .enable_return_button{appear_arg.enable_return_button},
- .disable_cancel_button{appear_arg.disable_cancel_button},
- };
-
- frontend.InitializeKeyboard(
- true, std::move(initialize_parameters), {},
- [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
- SubmitTextInline(reply_type, submitted_text, cursor_position);
- });
- } else {
- std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
-
- std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
-
- std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
-
- std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
-
- const u32 max_text_length =
- swkbd_config_common.max_text_length > 0 &&
- swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
- ? swkbd_config_common.max_text_length
- : DEFAULT_MAX_TEXT_LENGTH;
-
- const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
- ? swkbd_config_common.min_text_length
- : 0;
-
- const s32 initial_cursor_position = [this] {
- switch (swkbd_config_common.initial_cursor_position) {
- case SwkbdInitialCursorPosition::Start:
- default:
- return 0;
- case SwkbdInitialCursorPosition::End:
- return static_cast<s32>(initial_text.size());
- }
- }();
-
- const auto text_draw_type = [this, max_text_length] {
- switch (swkbd_config_common.text_draw_type) {
- case SwkbdTextDrawType::Line:
- default:
- return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
- case SwkbdTextDrawType::Box:
- case SwkbdTextDrawType::DownloadCode:
- return swkbd_config_common.text_draw_type;
- }
- }();
-
- const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
- ? swkbd_config_common.enable_return_button
- : false;
-
- const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
- ? swkbd_config_new.disable_cancel_button
- : false;
-
- Core::Frontend::KeyboardInitializeParameters initialize_parameters{
- .ok_text{std::move(ok_text)},
- .header_text{std::move(header_text)},
- .sub_text{std::move(sub_text)},
- .guide_text{std::move(guide_text)},
- .initial_text{initial_text},
- .max_text_length{max_text_length},
- .min_text_length{min_text_length},
- .initial_cursor_position{initial_cursor_position},
- .type{swkbd_config_common.type},
- .password_mode{swkbd_config_common.password_mode},
- .text_draw_type{text_draw_type},
- .key_disable_flags{swkbd_config_common.key_disable_flags},
- .use_blur_background{swkbd_config_common.use_blur_background},
- .enable_backspace_button{true},
- .enable_return_button{enable_return_button},
- .disable_cancel_button{disable_cancel_button},
- };
-
- frontend.InitializeKeyboard(
- false, std::move(initialize_parameters),
- [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
- SubmitTextNormal(result, submitted_text, confirmed);
- },
- {});
- }
+void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
+
+ std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
+
+ std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
+
+ std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
+
+ const u32 max_text_length =
+ swkbd_config_common.max_text_length > 0 &&
+ swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? swkbd_config_common.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
+ ? swkbd_config_common.min_text_length
+ : 0;
+
+ const s32 initial_cursor_position = [this] {
+ switch (swkbd_config_common.initial_cursor_position) {
+ case SwkbdInitialCursorPosition::Start:
+ default:
+ return 0;
+ case SwkbdInitialCursorPosition::End:
+ return static_cast<s32>(initial_text.size());
+ }
+ }();
+
+ const auto text_draw_type = [this, max_text_length] {
+ switch (swkbd_config_common.text_draw_type) {
+ case SwkbdTextDrawType::Line:
+ default:
+ return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+ case SwkbdTextDrawType::Box:
+ case SwkbdTextDrawType::DownloadCode:
+ return swkbd_config_common.text_draw_type;
+ }
+ }();
+
+ const auto enable_return_button =
+ text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
+
+ const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
+ ? swkbd_config_new.disable_cancel_button
+ : false;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{std::move(header_text)},
+ .sub_text{std::move(sub_text)},
+ .guide_text{std::move(guide_text)},
+ .initial_text{initial_text},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{swkbd_config_common.type},
+ .password_mode{swkbd_config_common.password_mode},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{swkbd_config_common.key_disable_flags},
+ .use_blur_background{swkbd_config_common.use_blur_background},
+ .enable_backspace_button{true},
+ .enable_return_button{enable_return_button},
+ .disable_cancel_button{disable_cancel_button},
+ };
+
+ frontend.InitializeKeyboard(
+ false, std::move(initialize_parameters),
+ [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
+ SubmitTextNormal(result, submitted_text, confirmed);
+ },
+ {});
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
+ frontend.InitializeKeyboard(
+ true, std::move(initialize_parameters), {},
+ [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
+ SubmitTextInline(reply_type, submitted_text, cursor_position);
+ });
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
+ const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
+
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ appear_arg.ok_text.data(), appear_arg.ok_text.size());
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
+
+ const auto text_draw_type =
+ max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{},
+ .sub_text{},
+ .guide_text{},
+ .initial_text{current_text},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{appear_arg.type},
+ .password_mode{SwkbdPasswordMode::Disabled},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .use_blur_background{false},
+ .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
+ const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
+
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ appear_arg.ok_text.data(), appear_arg.ok_text.size());
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
+
+ const auto text_draw_type =
+ max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{},
+ .sub_text{},
+ .guide_text{},
+ .initial_text{current_text},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{appear_arg.type},
+ .password_mode{SwkbdPasswordMode::Disabled},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .use_blur_background{false},
+ .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
}
-void SoftwareKeyboard::ShowInlineKeyboard() {
+void SoftwareKeyboard::ShowInlineKeyboard(
+ Core::Frontend::InlineAppearParameters appear_parameters) {
+ frontend.ShowInlineKeyboard(std::move(appear_parameters));
+
+ ChangeState(SwkbdState::InitializedIsShown);
+}
+
+void SoftwareKeyboard::ShowInlineKeyboardOld() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
- const auto& appear_arg = swkbd_calc_arg.appear_arg;
+ const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
- .key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
- .key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
- .key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
- .key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
+ .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
+ .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
+ .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
+ .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
- .key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
- .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
+ .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
+ .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
- frontend.ShowInlineKeyboard(std::move(appear_parameters));
+ ShowInlineKeyboard(std::move(appear_parameters));
+}
- ChangeState(SwkbdState::InitializedIsShown);
+void SoftwareKeyboard::ShowInlineKeyboardNew() {
+ if (swkbd_state != SwkbdState::InitializedIsHidden) {
+ return;
+ }
+
+ ChangeState(SwkbdState::InitializedIsAppearing);
+
+ const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ Core::Frontend::InlineAppearParameters appear_parameters{
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
+ .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
+ .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
+ .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
+ .type{appear_arg.type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
+ .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ ShowInlineKeyboard(std::move(appear_parameters));
}
void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
+
+ ReplyReleasedUserWordInfo();
}
void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
LOG_DEBUG(Service_AM, "Processing Request: Calc");
- ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
+ ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
- std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
- sizeof(SwkbdCalcArg));
+ std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
+ sizeof(SwkbdCalcArgCommon));
+
+ switch (swkbd_calc_arg_common.calc_arg_size) {
+ case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
+ ASSERT(request_data.size() ==
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
+ std::memcpy(&swkbd_calc_arg_old,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgOld));
+ RequestCalcOld();
+ break;
+ case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
+ ASSERT(request_data.size() ==
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
+ std::memcpy(&swkbd_calc_arg_new,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgNew));
+ RequestCalcNew();
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
+ ASSERT(request_data.size() >=
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
+ std::memcpy(&swkbd_calc_arg_new,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgNew));
+ RequestCalcNew();
+ break;
+ }
+}
+
+void SoftwareKeyboard::RequestCalcOld() {
+ if (swkbd_calc_arg_common.flags.set_input_text) {
+ current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
+ }
+
+ if (swkbd_calc_arg_common.flags.set_cursor_position) {
+ current_cursor_position = swkbd_calc_arg_old.cursor_position;
+ }
+
+ if (swkbd_calc_arg_common.flags.set_utf8_mode) {
+ inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
+ }
+
+ if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+ swkbd_calc_arg_common.flags.unset_customize_dic) {
+ ReplyUnsetCustomizeDic();
+ }
+
+ if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+ swkbd_calc_arg_common.flags.unset_user_word_info) {
+ ReplyReleasedUserWordInfo();
+ }
+
+ if (swkbd_state == SwkbdState::NotInitialized &&
+ swkbd_calc_arg_common.flags.set_initialize_arg) {
+ InitializeFrontendInlineKeyboardOld();
+
+ ChangeState(SwkbdState::InitializedIsHidden);
+
+ ReplyFinishedInitialize();
+ }
+
+ if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
+ (swkbd_calc_arg_common.flags.set_input_text ||
+ swkbd_calc_arg_common.flags.set_cursor_position)) {
+ InlineTextChanged();
+ }
+
+ if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
+ ShowInlineKeyboardOld();
+ return;
+ }
+
+ if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
+ HideInlineKeyboard();
+ return;
+ }
+}
- if (swkbd_calc_arg.flags.set_input_text) {
+void SoftwareKeyboard::RequestCalcNew() {
+ if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
+ swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
}
- if (swkbd_calc_arg.flags.set_cursor_position) {
- current_cursor_position = swkbd_calc_arg.cursor_position;
+ if (swkbd_calc_arg_common.flags.set_cursor_position) {
+ current_cursor_position = swkbd_calc_arg_new.cursor_position;
}
- if (swkbd_calc_arg.flags.set_utf8_mode) {
- inline_use_utf8 = swkbd_calc_arg.utf8_mode;
+ if (swkbd_calc_arg_common.flags.set_utf8_mode) {
+ inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
- swkbd_calc_arg.flags.unset_customize_dic) {
+ swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
- swkbd_calc_arg.flags.unset_user_word_info) {
+ swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
- if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
- InitializeFrontendKeyboard();
+ if (swkbd_state == SwkbdState::NotInitialized &&
+ swkbd_calc_arg_common.flags.set_initialize_arg) {
+ InitializeFrontendInlineKeyboardNew();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
- if (!swkbd_calc_arg.flags.set_initialize_arg &&
- (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
+ if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
+ (swkbd_calc_arg_common.flags.set_input_text ||
+ swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
- if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
- ShowInlineKeyboard();
+ if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
+ ShowInlineKeyboardNew();
return;
}
- if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
+ if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}
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 {
class System;
}
+namespace Core::Frontend {
+struct KeyboardInitializeParameters;
+struct InlineAppearParameters;
+} // namespace Core::Frontend
+
namespace Service::AM::Applets {
class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
void ChangeState(SwkbdState state);
/**
- * Signals the frontend to initialize the software keyboard with common parameters.
- * This initializes either the normal software keyboard or the inline software keyboard
- * depending on the state of is_background.
+ * Signals the frontend to initialize the normal software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
- * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
+ * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
*/
- void InitializeFrontendKeyboard();
+ void InitializeFrontendNormalKeyboard();
+
+ /**
+ * Signals the frontend to initialize the inline software keyboard with common parameters.
+ * Note that this does not cause the keyboard to appear.
+ * Use the ShowInlineKeyboard() to cause the keyboard to appear.
+ */
+ void InitializeFrontendInlineKeyboard(
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters);
+
+ void InitializeFrontendInlineKeyboardOld();
+ void InitializeFrontendInlineKeyboardNew();
/// Signals the frontend to show the normal software keyboard.
void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
std::u16string text_check_message);
/// Signals the frontend to show the inline software keyboard.
- void ShowInlineKeyboard();
+ void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
+
+ void ShowInlineKeyboardOld();
+ void ShowInlineKeyboardNew();
/// Signals the frontend to hide the inline software keyboard.
void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
void RequestCalc(const std::vector<u8>& request_data);
+ void RequestCalcOld();
+ void RequestCalcNew();
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
SwkbdState swkbd_state{SwkbdState::NotInitialized};
SwkbdInitializeArg swkbd_initialize_arg;
- SwkbdCalcArg swkbd_calc_arg;
+ SwkbdCalcArgCommon swkbd_calc_arg_common;
+ SwkbdCalcArgOld swkbd_calc_arg_old;
+ SwkbdCalcArgNew swkbd_calc_arg_new;
bool use_changed_string_v2{false};
bool use_moved_cursor_v2{false};
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "common/uuid.h"
namespace Service::AM::Applets {
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
};
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
-struct SwkbdAppearArg {
+struct SwkbdAppearArgOld {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
@@ -229,19 +230,76 @@ struct SwkbdAppearArg {
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
- INSERT_PADDING_WORDS(6);
+ bool is_use_save_data{};
+ INSERT_PADDING_BYTES(7);
+ Common::UUID user_id{};
};
-static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
+static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
-struct SwkbdCalcArg {
+struct SwkbdAppearArgNew {
+ SwkbdType type{};
+ std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
+ char16_t left_optional_symbol_key{};
+ char16_t right_optional_symbol_key{};
+ bool use_prediction{};
+ bool disable_cancel_button{};
+ SwkbdKeyDisableFlags key_disable_flags{};
+ u32 max_text_length{};
+ u32 min_text_length{};
+ bool enable_return_button{};
+ INSERT_PADDING_BYTES(3);
+ u32 flags{};
+ bool is_use_save_data{};
+ INSERT_PADDING_BYTES(7);
+ Common::UUID user_id{};
+ u64 start_sampling_number{};
+ INSERT_PADDING_WORDS(8);
+};
+static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
+
+struct SwkbdCalcArgCommon {
u32 unknown{};
u16 calc_arg_size{};
INSERT_PADDING_BYTES(2);
SwkbdCalcArgFlags flags{};
SwkbdInitializeArg initialize_arg{};
+};
+static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
+
+struct SwkbdCalcArgOld {
+ f32 volume{};
+ s32 cursor_position{};
+ SwkbdAppearArgOld appear_arg{};
+ std::array<char16_t, 0x1FA> input_text{};
+ bool utf8_mode{};
+ INSERT_PADDING_BYTES(1);
+ bool enable_backspace_button{};
+ INSERT_PADDING_BYTES(3);
+ bool key_top_as_floating{};
+ bool footer_scalable{};
+ bool alpha_enabled_in_input_mode{};
+ u8 input_mode_fade_type{};
+ bool disable_touch{};
+ bool disable_hardware_keyboard{};
+ INSERT_PADDING_BYTES(8);
+ f32 key_top_scale_x{};
+ f32 key_top_scale_y{};
+ f32 key_top_translate_x{};
+ f32 key_top_translate_y{};
+ f32 key_top_bg_alpha{};
+ f32 footer_bg_alpha{};
+ f32 balloon_scale{};
+ INSERT_PADDING_WORDS(4);
+ u8 se_group{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
+ "SwkbdCalcArgOld has incorrect size.");
+
+struct SwkbdCalcArgNew {
+ SwkbdAppearArgNew appear_arg{};
f32 volume{};
s32 cursor_position{};
- SwkbdAppearArg appear_arg{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
@@ -264,8 +322,10 @@ struct SwkbdCalcArg {
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
+ INSERT_PADDING_WORDS(8);
};
-static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
+static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
+ "SwkbdCalcArgNew has incorrect size.");
struct SwkbdChangedStringArg {
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() {
}
void WebBrowser::ExecuteOffline() {
+ // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
+ // Super Mario 3D All-Stars.
+ // TODO (Morph): Implement WebSession.
+ if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
+ LOG_WARNING(Service_AM, "WebSession is not implemented");
+ return;
+ }
+
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
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 @@
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
@@ -19,6 +20,7 @@
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_error.h"
#include "core/hle/service/am/applets/applet_general_backend.h"
+#include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_software_keyboard.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -171,11 +173,12 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
+ MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
- parental_controls{std::move(parental_controls_applet)},
+ mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
@@ -202,6 +205,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
frontend.error = std::move(set.error);
}
+ if (set.mii_edit != nullptr) {
+ frontend.mii_edit = std::move(set.mii_edit);
+ }
+
if (set.parental_controls != nullptr) {
frontend.parental_controls = std::move(set.parental_controls);
}
@@ -238,6 +245,10 @@ void AppletManager::SetDefaultAppletsIfMissing() {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
+ if (frontend.mii_edit == nullptr) {
+ frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
+ }
+
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
@@ -277,6 +288,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
case AppletId::SoftwareKeyboard:
return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
+ case AppletId::MiiEdit:
+ return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
case AppletId::Web:
case AppletId::Shop:
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 {
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
+class MiiEditApplet;
class ParentalControlsApplet;
class PhotoViewerApplet;
class ProfileSelectApplet;
@@ -178,6 +179,7 @@ protected:
struct AppletFrontendSet {
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
@@ -186,9 +188,9 @@ struct AppletFrontendSet {
AppletFrontendSet();
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
- ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
- ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
- WebBrowser web_browser_);
+ MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
+ PhotoViewer photo_viewer_, ProfileSelect profile_select_,
+ SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -199,6 +201,7 @@ struct AppletFrontendSet {
ControllerApplet controller;
ErrorApplet error;
+ MiiEdit mii_edit;
ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
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:
explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
AudioCore::AudioOut& audio_core_, std::string&& device_name_,
std::string&& unique_name)
- : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
- device_name{std::move(device_name_)}, audio_params{audio_params_},
- main_memory{system.Memory()}, service_context{system_, "IAudioOut"} {
+ : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
+ audio_core{audio_core_}, device_name{std::move(device_name_)},
+ audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
+ "IAudioOut"} {
// clang-format off
static const FunctionInfo functions[] = {
{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:
explicit IAudioRenderer(Core::System& system_,
const AudioCommon::AudioRendererParameter& audren_params,
const std::size_t instance_number)
- : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} {
+ : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioRenderer"} {
// clang-format off
static const FunctionInfo functions[] = {
{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_,
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
- "Could not write all of the bytes but everything else has succeded.");
+ "Could not write all of the bytes but everything else has succeeded.");
if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// 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 {
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"},
{1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"},
{1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
- : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
- : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
- size_)} {
+ : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
+ backend{std::move(backend_)}, size{std::move(size_)} {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#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 @@
#include "common/common_types.h"
#include "common/quaternion.h"
-#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
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 @@
#pragma once
#include "common/common_types.h"
-#include "common/swap.h"
namespace Core::Timing {
class 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 @@
#pragma once
-#include <array>
#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
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 @@
#pragma once
-#include <array>
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
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 @@
#pragma once
-#include <array>
-#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
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 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/errors.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
@@ -48,15 +49,17 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
}
bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
- return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
- device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
- device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+ const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ return npad_id && npad_type && device_index;
}
bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
- return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
- device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
- device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+ const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ return npad_id && npad_type && device_index;
}
Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
@@ -262,11 +265,6 @@ void Controller_NPad::OnInit() {
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
}
- if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
- // We want to support all controllers
- hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
- }
-
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +286,6 @@ void Controller_NPad::OnInit() {
WriteEmptyEntry(npad);
}
}
-
- // Connect controllers
- for (auto& controller : controller_data) {
- const auto& device = controller.device;
- if (device->IsConnected()) {
- AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
- }
- }
}
void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +310,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
}
void Controller_NPad::OnRelease() {
+ is_controller_initialized = false;
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +321,7 @@ void Controller_NPad::OnRelease() {
}
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
auto& controller = GetControllerFromNpadIdType(npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
if (!controller.device->IsConnected()) {
@@ -651,9 +642,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
hid_core.SetSupportedStyleTag(style_set);
+
+ if (is_controller_initialized) {
+ return;
+ }
+
+ // Once SetSupportedStyleSet is called controllers are fully initialized
+ is_controller_initialized = true;
+
+ // Connect all active controllers
+ for (auto& controller : controller_data) {
+ const auto& device = controller.device;
+ if (device->IsConnected()) {
+ AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
+ }
+ }
}
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
+ if (!is_controller_initialized) {
+ return {Core::HID::NpadStyleSet::None};
+ }
return hid_core.GetSupportedStyleTag();
}
@@ -1001,87 +1010,271 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
WriteEmptyEntry(controller.shared_memory_entry);
}
-void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode) {
+ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ return NpadInvalidHandle;
+ }
+
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ }
+ controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return NpadInvalidHandle;
}
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.gyroscope_zero_drift_mode = drift_mode;
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
+ break;
+ }
+ drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
}
-Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
- Core::HID::SixAxisSensorHandle sixaxis_handle) const {
+ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool& is_at_rest) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- // Return the default value
- return GyroscopeZeroDriftMode::Standard;
+ return NpadInvalidHandle;
}
const auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.gyroscope_zero_drift_mode;
+ is_at_rest = controller.sixaxis_at_rest;
+ return ResultSuccess;
}
-bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
+ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
+ Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- // Return the default value
- return true;
+ return NpadInvalidHandle;
}
- const auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.sixaxis_at_rest;
+
+ // We don't support joycon firmware updates
+ is_firmware_available = false;
+ return ResultSuccess;
}
-void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_status) {
+ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool sixaxis_status) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ return NpadInvalidHandle;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
+ return ResultSuccess;
}
-void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_fusion_status) {
+ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
+ Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ return NpadInvalidHandle;
}
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
-}
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
+ break;
+ }
+ is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
-void Controller_NPad::SetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
- Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
+ return ResultSuccess;
+}
+ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool is_fusion_enabled) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ return NpadInvalidHandle;
}
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.sixaxis_fusion = sixaxis_fusion_parameters;
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
+ break;
+ }
+ controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
}
-Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle) {
+ResultCode Controller_NPad::SetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- // Since these parameters are unknow just return zeros
- return {};
+ return NpadInvalidHandle;
+ }
+ const auto param1 = sixaxis_fusion_parameters.parameter1;
+ if (param1 < 0.0f || param1 > 1.0f) {
+ return InvalidSixAxisFusionRange;
}
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.sixaxis_fusion;
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
+ break;
+ }
+ controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
}
-void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
+ResultCode Controller_NPad::GetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters& parameters) const {
if (!IsDeviceHandleValid(sixaxis_handle)) {
LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ return NpadInvalidHandle;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- // Since these parameters are unknow just fill with zeros
- controller.sixaxis_fusion = {};
+
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ parameters = controller.sixaxis_fullkey.fusion;
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ parameters = controller.sixaxis_handheld.fusion;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::GameCube:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ parameters = controller.sixaxis_dual_left.fusion;
+ break;
+ }
+ parameters = controller.sixaxis_dual_right.fusion;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ parameters = controller.sixaxis_left.fusion;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ parameters = controller.sixaxis_right.fusion;
+ break;
+ default:
+ LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
}
void 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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/quaternion.h"
+#include "common/vector_math.h"
+
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -27,7 +28,9 @@ class KReadableEvent;
namespace Service::KernelHelpers {
class ServiceContext;
-}
+} // namespace Service::KernelHelpers
+
+union ResultCode;
namespace Service::HID {
@@ -142,20 +145,26 @@ public:
void DisconnectNpad(Core::HID::NpadIdType npad_id);
- void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode);
- GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
- Core::HID::SixAxisSensorHandle sixaxis_handle) const;
- bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
- void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
- void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_fusion_status);
- void SetSixAxisFusionParameters(
+ ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode);
+ ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const;
+ ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool& is_at_rest) const;
+ ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
+ Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
+ ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool sixaxis_status);
+ ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool& is_fusion_enabled) const;
+ ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool is_fusion_enabled);
+ ResultCode SetSixAxisFusionParameters(
Core::HID::SixAxisSensorHandle sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
- Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle);
- void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
+ ResultCode GetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters& parameters) const;
Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
@@ -190,16 +199,16 @@ private:
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
- ColorAttribute attribute;
- Core::HID::NpadControllerColor fullkey;
+ ColorAttribute attribute{ColorAttribute::NoController};
+ Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
- ColorAttribute attribute;
- Core::HID::NpadControllerColor left;
- Core::HID::NpadControllerColor right;
+ ColorAttribute attribute{ColorAttribute::NoController};
+ Core::HID::NpadControllerColor left{};
+ Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
@@ -225,11 +234,11 @@ private:
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
- s64_le sampling_number;
- Core::HID::NpadButtonState npad_buttons;
- Core::HID::AnalogStickState l_stick;
- Core::HID::AnalogStickState r_stick;
- NpadAttribute connection_status;
+ s64_le sampling_number{};
+ Core::HID::NpadButtonState npad_buttons{};
+ Core::HID::AnalogStickState l_stick{};
+ Core::HID::AnalogStickState r_stick{};
+ NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -252,7 +261,7 @@ private:
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
- SixAxisSensorAttribute attribute;
+ SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -324,11 +333,11 @@ private:
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
- u64 handle;
- bool is_available;
- bool is_activated;
+ u64 handle{};
+ bool is_available{};
+ bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
- u64 sampling_number;
+ u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -365,8 +374,8 @@ private:
};
struct AppletFooterUi {
- AppletFooterUiAttributes attributes;
- AppletFooterUiType type;
+ AppletFooterUiAttributes attributes{};
+ AppletFooterUiType type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
};
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -403,41 +412,41 @@ private:
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
- Core::HID::NpadStyleTag style_tag;
- NpadJoyAssignmentMode assignment_mode;
- NpadFullKeyColorState fullkey_color;
- NpadJoyColorState joycon_color;
- Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
- Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
- Lifo<NPadGenericState, hid_entry_count> palma_lifo;
- Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
- DeviceType device_type;
+ Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
+ NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
+ NpadFullKeyColorState fullkey_color{};
+ NpadJoyColorState joycon_color{};
+ Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
+ DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
- NPadSystemProperties system_properties;
- NpadSystemButtonProperties button_properties;
- Core::HID::NpadBatteryLevel battery_level_dual;
- Core::HID::NpadBatteryLevel battery_level_left;
- Core::HID::NpadBatteryLevel battery_level_right;
+ NPadSystemProperties system_properties{};
+ NpadSystemButtonProperties button_properties{};
+ Core::HID::NpadBatteryLevel battery_level_dual{};
+ Core::HID::NpadBatteryLevel battery_level_left{};
+ Core::HID::NpadBatteryLevel battery_level_right{};
union {
- Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
- AppletFooterUi applet_footer;
+ AppletFooterUi applet_footer{};
+ Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
};
INSERT_PADDING_BYTES(0x20); // Unknown
- Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
- NpadLarkType lark_type_l_and_main;
- NpadLarkType lark_type_r;
- NpadLuciaType lucia_type;
- NpadLagonType lagon_type;
- NpadLagerType lager_type;
+ Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
+ NpadLarkType lark_type_l_and_main{};
+ NpadLarkType lark_type_r{};
+ NpadLuciaType lucia_type{};
+ NpadLagonType lagon_type{};
+ NpadLagerType lager_type{};
// FW 13.x Investigate there is some sort of bitflag related to joycons
INSERT_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -450,6 +459,12 @@ private:
std::chrono::steady_clock::time_point last_vibration_timepoint{};
};
+ struct SixaxisParameters {
+ bool is_fusion_enabled{true};
+ Core::HID::SixAxisSensorFusionParameters fusion{};
+ GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ };
+
struct NpadControllerData {
Core::HID::EmulatedController* device;
Kernel::KEvent* styleset_changed_event{};
@@ -466,9 +481,12 @@ private:
// Motion parameters
bool sixaxis_at_rest{true};
bool sixaxis_sensor_enabled{true};
- bool sixaxis_fusion_enabled{false};
- Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
- GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ SixaxisParameters sixaxis_fullkey{};
+ SixaxisParameters sixaxis_handheld{};
+ SixaxisParameters sixaxis_dual_left{};
+ SixaxisParameters sixaxis_dual_right{};
+ SixaxisParameters sixaxis_left{};
+ SixaxisParameters sixaxis_right{};
// Current pad state
NPadGenericState npad_pad_state{};
@@ -480,6 +498,7 @@ private:
SixAxisSensorState sixaxis_dual_right_state{};
SixAxisSensorState sixaxis_left_lifo_state{};
SixAxisSensorState sixaxis_right_lifo_state{};
+
int callback_key;
};
@@ -510,7 +529,8 @@ private:
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
bool permit_vibration_session_enabled{false};
- bool analog_stick_use_center_clamp{};
+ bool analog_stick_use_center_clamp{false};
bool is_in_lr_assignment_mode{false};
+ bool is_controller_initialized{false};
};
} // 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 @@
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
-#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#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 @@
#pragma once
-#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/point.h"
-#include "common/swap.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#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 @@
#pragma once
#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#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 @@
namespace Service::HID {
-constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710};
+constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
+constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
+constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
} // 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 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/xcd.h"
#include "core/memory.h"
@@ -247,7 +248,7 @@ Hid::Hid(Core::System& system_)
{65, nullptr, "GetJoySixAxisSensorLifoHandle"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
- {68, nullptr, "IsSixAxisSensorFusionEnabled"},
+ {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
{70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
{71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
@@ -527,8 +528,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisEnabled(parameters.sixaxis_handle, true);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -536,7 +537,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -550,8 +551,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisEnabled(parameters.sixaxis_handle, false);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,7 +560,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
+}
+
+void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ bool is_enabled{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(is_enabled);
}
void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
@@ -574,9 +601,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
- parameters.enable_sixaxis_sensor_fusion);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
+ parameters.enable_sixaxis_sensor_fusion);
LOG_DEBUG(Service_HID,
"called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -586,7 +613,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -601,8 +628,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -612,7 +640,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -626,9 +654,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- const auto sixaxis_fusion_parameters =
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetSixAxisFusionParameters(parameters.sixaxis_handle);
+ Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
+ const auto& controller =
+ GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -636,8 +666,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushRaw(sixaxis_fusion_parameters);
+ rb.Push(result);
+ rb.PushRaw(fusion_parameters);
}
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -651,8 +681,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
+ // Since these parameters are unknow just use what HW outputs
+ const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
+ .parameter1 = 0.03f,
+ .parameter2 = 0.4f,
+ };
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result1 =
+ controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
+ const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,6 +697,14 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
+ if (result1.IsError()) {
+ rb.Push(result1);
+ return;
+ }
+ if (result2.IsError()) {
+ rb.Push(result2);
+ return;
+ }
rb.Push(ResultSuccess);
}
@@ -669,8 +714,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -679,7 +724,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
drift_mode, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -693,15 +738,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
+ auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
+
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
+ rb.Push(result);
+ rb.PushEnum(drift_mode);
}
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -714,10 +762,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
+ const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -725,7 +773,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
@@ -739,15 +787,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_at_rest{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
+
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
+ rb.Push(result);
+ rb.Push(is_at_rest);
}
void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -761,6 +812,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_firmware_available{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
+ parameters.sixaxis_handle, is_firmware_available);
+
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -768,8 +824,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(false);
+ rb.Push(result);
+ rb.Push(is_firmware_available);
}
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
@@ -878,6 +934,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
+ // Games expect this event to be signaled after calling this function
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SignalStyleSetChangedEvent(parameters.npad_id);
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1115,7 +1175,7 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
} else {
LOG_ERROR(Service_HID, "Npads are not connected!");
- rb.Push(ERR_NPAD_NOT_CONNECTED);
+ rb.Push(NpadNotConnected);
}
}
@@ -2124,32 +2184,6 @@ public:
}
};
-class HidBus final : public ServiceFramework<HidBus> {
-public:
- explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1, nullptr, "GetBusHandle"},
- {2, nullptr, "IsExternalDeviceConnected"},
- {3, nullptr, "Initialize"},
- {4, nullptr, "Finalize"},
- {5, nullptr, "EnableExternalDevice"},
- {6, nullptr, "GetExternalDeviceId"},
- {7, nullptr, "SendCommandAsync"},
- {8, nullptr, "GetSendCommandAsynceResult"},
- {9, nullptr, "SetEventForSendCommandAsycResult"},
- {10, nullptr, "GetSharedMemoryHandle"},
- {11, nullptr, "EnableJoyPollingReceiveMode"},
- {12, nullptr, "DisableJoyPollingReceiveMode"},
- {13, nullptr, "GetPollingData"},
- {14, nullptr, "SetStatusManagerType"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<Hid>(system)->InstallAsService(service_manager);
std::make_shared<HidBus>(system)->InstallAsService(service_manager);
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:
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/hid/hidbus.h"
+#include "core/hle/service/hid/hidbus/ringcon.h"
+#include "core/hle/service/hid/hidbus/starlink.h"
+#include "core/hle/service/hid/hidbus/stubbed.h"
+#include "core/hle/service/service.h"
+#include "core/memory.h"
+
+namespace Service::HID {
+// (15ms, 66Hz)
+constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
+
+HidBus::HidBus(Core::System& system_)
+ : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
+
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, &HidBus::GetBusHandle, "GetBusHandle"},
+ {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
+ {3, &HidBus::Initialize, "Initialize"},
+ {4, &HidBus::Finalize, "Finalize"},
+ {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
+ {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
+ {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
+ {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
+ {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
+ {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
+ {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
+ {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
+ {13, nullptr, "GetPollingData"},
+ {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ // Register update callbacks
+ hidbus_update_event = Core::Timing::CreateEvent(
+ "Hidbus::UpdateCallback",
+ [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ const auto guard = LockService();
+ UpdateHidbus(user_data, ns_late);
+ });
+
+ system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
+}
+
+HidBus::~HidBus() {
+ system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
+}
+
+void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ auto& core_timing = system.CoreTiming();
+
+ if (is_hidbus_enabled) {
+ for (std::size_t i = 0; i < devices.size(); ++i) {
+ if (!devices[i].is_device_initializated) {
+ continue;
+ }
+ auto& device = devices[i].device;
+ device->OnUpdate();
+ auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
+ cur_entry.is_polling_mode = device->IsPollingMode();
+ cur_entry.polling_mode = device->GetPollingMode();
+ cur_entry.is_enabled = device->IsEnabled();
+
+ u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
+ std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
+ sizeof(HidbusStatusManagerEntry));
+ }
+ }
+
+ // If ns_late is higher than the update rate ignore the delay
+ if (ns_late > hidbus_update_ns) {
+ ns_late = {};
+ }
+
+ core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
+}
+
+std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
+ for (std::size_t i = 0; i < devices.size(); ++i) {
+ const auto& device_handle = devices[i].handle;
+ if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
+ handle.internal_index == device_handle.internal_index &&
+ handle.player_number == device_handle.player_number &&
+ handle.bus_type == device_handle.bus_type &&
+ handle.is_valid == device_handle.is_valid) {
+ return i;
+ }
+ }
+ return std::nullopt;
+}
+
+void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::NpadIdType npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ BusType bus_type;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
+
+ bool is_handle_found = 0;
+ std::size_t handle_index = 0;
+
+ for (std::size_t i = 0; i < devices.size(); i++) {
+ const auto& handle = devices[i].handle;
+ if (!handle.is_valid) {
+ continue;
+ }
+ if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
+ handle.bus_type == parameters.bus_type) {
+ is_handle_found = true;
+ handle_index = i;
+ break;
+ }
+ }
+
+ // Handle not found. Create a new one
+ if (!is_handle_found) {
+ for (std::size_t i = 0; i < devices.size(); i++) {
+ if (devices[i].handle.is_valid) {
+ continue;
+ }
+ devices[i].handle = {
+ .abstracted_pad_id = static_cast<u8>(i),
+ .internal_index = static_cast<u8>(i),
+ .player_number = static_cast<u8>(parameters.npad_id),
+ .bus_type = parameters.bus_type,
+ .is_valid = true,
+ };
+ handle_index = i;
+ break;
+ }
+ }
+
+ struct OutData {
+ bool is_valid;
+ INSERT_PADDING_BYTES(7);
+ BusHandle handle;
+ };
+ static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
+
+ const OutData out_data{
+ .is_valid = true,
+ .handle = devices[handle_index].handle,
+ };
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out_data);
+}
+
+void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ const bool is_attached = device->IsDeviceActivated();
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(is_attached);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={} bus_type={} internal_index={} "
+ "player_number={} is_valid={}, applet_resource_user_id={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
+
+ is_hidbus_enabled = true;
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto entry_index = devices[device_index.value()].handle.internal_index;
+ auto& cur_entry = hidbus_status.entries[entry_index];
+
+ if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
+ MakeDevice<RingController>(bus_handle_);
+ devices[device_index.value()].is_device_initializated = true;
+ devices[device_index.value()].device->ActivateDevice();
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = true;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ } else {
+ MakeDevice<HidbusStubbed>(bus_handle_);
+ devices[device_index.value()].is_device_initializated = true;
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = false;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ }
+
+ std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
+ sizeof(hidbus_status));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}, applet_resource_user_id={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto entry_index = devices[device_index.value()].handle.internal_index;
+ auto& cur_entry = hidbus_status.entries[entry_index];
+ auto& device = devices[device_index.value()].device;
+ devices[device_index.value()].is_device_initializated = false;
+ device->DeactivateDevice();
+
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = false;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
+ sizeof(hidbus_status));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enable;
+ INSERT_PADDING_BYTES_NOINIT(7);
+ BusHandle bus_handle;
+ u64 inval;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_INFO(Service_HID,
+ "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
+ parameters.enable, parameters.bus_handle.abstracted_pad_id,
+ parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
+ parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
+ parameters.applet_resource_user_id);
+
+ const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->Enable(parameters.enable);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ u32 device_id = device->GetDeviceId();
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(device_id);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto data = ctx.ReadBuffer();
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}",
+ data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
+ bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->SetCommand(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ const std::vector<u8> data = device->GetReply();
+ const u64 data_size = ctx.WriteBuffer(data);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(data_size);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(device->GetSendCommandAsycEvent());
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
+}
+
+void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto t_mem_size{rp.Pop<u32>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+ const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
+
+ LOG_INFO(Service_HID,
+ "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
+ "internal_index={}, player_number={}, is_valid={}",
+ t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
+ bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->SetPollingMode(polling_mode_);
+ device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->DisablePollingMode();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto manager_type{rp.PopEnum<StatusManagerType>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+};
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Core::Timing {
+struct EventType;
+} // namespace Core::Timing
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Service::HID {
+
+class HidBus final : public ServiceFramework<HidBus> {
+public:
+ explicit HidBus(Core::System& system_);
+ ~HidBus() override;
+
+private:
+ static const std::size_t max_number_of_handles = 0x13;
+
+ enum class HidBusDeviceId : std::size_t {
+ RingController = 0x20,
+ FamicomRight = 0x21,
+ Starlink = 0x28,
+ };
+
+ // This is nn::hidbus::detail::StatusManagerType
+ enum class StatusManagerType : u32 {
+ None,
+ Type16,
+ Type32,
+ };
+
+ // This is nn::hidbus::BusType
+ enum class BusType : u8 {
+ LeftJoyRail,
+ RightJoyRail,
+ InternalBus, // Lark microphone
+
+ MaxBusType,
+ };
+
+ // This is nn::hidbus::BusHandle
+ struct BusHandle {
+ u32 abstracted_pad_id;
+ u8 internal_index;
+ u8 player_number;
+ BusType bus_type;
+ bool is_valid;
+ };
+ static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
+
+ // This is nn::hidbus::JoyPollingReceivedData
+ struct JoyPollingReceivedData {
+ std::array<u8, 0x30> data;
+ u64 out_size;
+ u64 sampling_number;
+ };
+ static_assert(sizeof(JoyPollingReceivedData) == 0x40,
+ "JoyPollingReceivedData is an invalid size");
+
+ struct HidbusStatusManagerEntry {
+ u8 is_connected{};
+ INSERT_PADDING_BYTES(0x3);
+ ResultCode is_connected_result{0};
+ u8 is_enabled{};
+ u8 is_in_focus{};
+ u8 is_polling_mode{};
+ u8 reserved{};
+ JoyPollingMode polling_mode{};
+ INSERT_PADDING_BYTES(0x70); // Unknown
+ };
+ static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
+ "HidbusStatusManagerEntry is an invalid size");
+
+ struct HidbusStatusManager {
+ std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
+ INSERT_PADDING_BYTES(0x680); // Unused
+ };
+ static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
+
+ struct HidbusDevice {
+ bool is_device_initializated{};
+ BusHandle handle{};
+ std::unique_ptr<HidbusBase> device{nullptr};
+ };
+
+ void GetBusHandle(Kernel::HLERequestContext& ctx);
+ void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
+ void Initialize(Kernel::HLERequestContext& ctx);
+ void Finalize(Kernel::HLERequestContext& ctx);
+ void EnableExternalDevice(Kernel::HLERequestContext& ctx);
+ void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
+ void SendCommandAsync(Kernel::HLERequestContext& ctx);
+ void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
+ void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
+ void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
+ void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
+ void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
+ void SetStatusManagerType(Kernel::HLERequestContext& ctx);
+
+ void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
+
+ template <typename T>
+ void MakeDevice(BusHandle handle) {
+ const auto device_index = GetDeviceIndexFromHandle(handle);
+ if (device_index) {
+ devices[device_index.value()].device =
+ std::make_unique<T>(system.HIDCore(), service_context);
+ }
+ }
+
+ bool is_hidbus_enabled{false};
+ HidbusStatusManager hidbus_status{};
+ std::array<HidbusDevice, max_number_of_handles> devices{};
+ std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
+ KernelHelpers::ServiceContext service_context;
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hid/hid_core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+#include "core/hle/service/kernel_helpers.h"
+
+namespace Service::HID {
+
+HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
+ : service_context(service_context_) {
+ send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
+}
+HidbusBase::~HidbusBase() = default;
+
+void HidbusBase::ActivateDevice() {
+ if (is_activated) {
+ return;
+ }
+ is_activated = true;
+ OnInit();
+}
+
+void HidbusBase::DeactivateDevice() {
+ if (is_activated) {
+ OnRelease();
+ }
+ is_activated = false;
+}
+
+bool HidbusBase::IsDeviceActivated() const {
+ return is_activated;
+}
+
+void HidbusBase::Enable(bool enable) {
+ device_enabled = enable;
+}
+
+bool HidbusBase::IsEnabled() const {
+ return device_enabled;
+}
+
+bool HidbusBase::IsPollingMode() const {
+ return polling_mode_enabled;
+}
+
+JoyPollingMode HidbusBase::GetPollingMode() const {
+ return polling_mode;
+}
+
+void HidbusBase::SetPollingMode(JoyPollingMode mode) {
+ polling_mode = mode;
+ polling_mode_enabled = true;
+}
+
+void HidbusBase::DisablePollingMode() {
+ polling_mode_enabled = false;
+}
+
+void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
+ return send_command_async_event->GetReadableEvent();
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::KernelHelpers {
+class ServiceContext;
+}
+
+namespace Service::HID {
+
+// This is nn::hidbus::JoyPollingMode
+enum class JoyPollingMode : u32 {
+ SixAxisSensorDisable,
+ SixAxisSensorEnable,
+ ButtonOnly,
+};
+
+struct DataAccessorHeader {
+ ResultCode result{ResultUnknown};
+ INSERT_PADDING_WORDS(0x1);
+ std::array<u8, 0x18> unused{};
+ u64 latest_entry{};
+ u64 total_entries{};
+};
+static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
+
+struct JoyDisableSixAxisPollingData {
+ std::array<u8, 0x26> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x1);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
+ "JoyDisableSixAxisPollingData is an invalid size");
+
+struct JoyEnableSixAxisPollingData {
+ std::array<u8, 0x8> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x7);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
+ "JoyEnableSixAxisPollingData is an invalid size");
+
+struct JoyButtonOnlyPollingData {
+ std::array<u8, 0x2c> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x3);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
+ "JoyButtonOnlyPollingData is an invalid size");
+
+struct JoyDisableSixAxisPollingEntry {
+ u64 sampling_number;
+ JoyDisableSixAxisPollingData polling_data;
+};
+static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
+ "JoyDisableSixAxisPollingEntry is an invalid size");
+
+struct JoyEnableSixAxisPollingEntry {
+ u64 sampling_number;
+ JoyEnableSixAxisPollingData polling_data;
+};
+static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
+ "JoyEnableSixAxisPollingEntry is an invalid size");
+
+struct JoyButtonOnlyPollingEntry {
+ u64 sampling_number;
+ JoyButtonOnlyPollingData polling_data;
+};
+static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
+ "JoyButtonOnlyPollingEntry is an invalid size");
+
+struct JoyDisableSixAxisDataAccessor {
+ DataAccessorHeader header{};
+ std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
+};
+static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
+ "JoyDisableSixAxisDataAccessor is an invalid size");
+
+struct JoyEnableSixAxisDataAccessor {
+ DataAccessorHeader header{};
+ std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
+};
+static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
+ "JoyEnableSixAxisDataAccessor is an invalid size");
+
+struct ButtonOnlyPollingDataAccessor {
+ DataAccessorHeader header;
+ std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
+};
+static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
+ "ButtonOnlyPollingDataAccessor is an invalid size");
+
+class HidbusBase {
+public:
+ explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
+ virtual ~HidbusBase();
+
+ void ActivateDevice();
+
+ void DeactivateDevice();
+
+ bool IsDeviceActivated() const;
+
+ // Enables/disables the device
+ void Enable(bool enable);
+
+ // returns true if device is enabled
+ bool IsEnabled() const;
+
+ // returns true if polling mode is enabled
+ bool IsPollingMode() const;
+
+ // returns polling mode
+ JoyPollingMode GetPollingMode() const;
+
+ // Sets and enables JoyPollingMode
+ void SetPollingMode(JoyPollingMode mode);
+
+ // Disables JoyPollingMode
+ void DisablePollingMode();
+
+ // Called on EnableJoyPollingReceiveMode
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
+
+ virtual void OnInit() {}
+
+ virtual void OnRelease() {}
+
+ // Updates device transfer memory
+ virtual void OnUpdate() {}
+
+ // Returns the device ID of the joycon
+ virtual u8 GetDeviceId() const {
+ return {};
+ }
+
+ // Assigns a command from data
+ virtual bool SetCommand(const std::vector<u8>& data) {
+ return {};
+ }
+
+ // Returns a reply from a command
+ virtual std::vector<u8> GetReply() const {
+ return {};
+ }
+
+protected:
+ bool is_activated{};
+ bool device_enabled{};
+ bool polling_mode_enabled{};
+ JoyPollingMode polling_mode = {};
+ // TODO(German77): All data accessors need to be replaced with a ring lifo object
+ JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
+ JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
+ ButtonOnlyPollingDataAccessor button_only_data{};
+
+ u8* transfer_memory{nullptr};
+ bool is_transfer_memory_set{};
+
+ Kernel::KEvent* send_command_async_event;
+ KernelHelpers::ServiceContext& service_context;
+};
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/hidbus/ringcon.h"
+
+namespace Service::HID {
+
+RingController::RingController(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {
+ input = hid_core_.GetEmulatedDevices();
+}
+
+RingController::~RingController() = default;
+
+void RingController::OnInit() {
+ return;
+}
+
+void RingController::OnRelease() {
+ return;
+};
+
+void RingController::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+
+ if (!device_enabled) {
+ return;
+ }
+
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ // TODO: Increment multitasking counters from motion and sensor data
+
+ switch (polling_mode) {
+ case JoyPollingMode::SixAxisSensorEnable: {
+ enable_sixaxis_data.header.total_entries = 10;
+ enable_sixaxis_data.header.result = ResultSuccess;
+ const auto& last_entry =
+ enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
+
+ enable_sixaxis_data.header.latest_entry =
+ (enable_sixaxis_data.header.latest_entry + 1) % 10;
+ auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
+
+ curr_entry.sampling_number = last_entry.sampling_number + 1;
+ curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
+
+ const RingConData ringcon_value = GetSensorValue();
+ curr_entry.polling_data.out_size = sizeof(ringcon_value);
+ std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
+
+ std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
+ break;
+ }
+ default:
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+ break;
+ }
+}
+
+RingController::RingConData RingController::GetSensorValue() const {
+ RingConData ringcon_sensor_value{
+ .status = DataValid::Valid,
+ .data = 0,
+ };
+
+ const f32 force_value = input->GetRingSensorForce().force * range;
+ ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
+
+ return ringcon_sensor_value;
+}
+
+u8 RingController::GetDeviceId() const {
+ return device_id;
+}
+
+std::vector<u8> RingController::GetReply() const {
+ const RingConCommands current_command = command;
+
+ switch (current_command) {
+ case RingConCommands::GetFirmwareVersion:
+ return GetFirmwareVersionReply();
+ case RingConCommands::ReadId:
+ return GetReadIdReply();
+ case RingConCommands::c20105:
+ return GetC020105Reply();
+ case RingConCommands::ReadUnkCal:
+ return GetReadUnkCalReply();
+ case RingConCommands::ReadFactoryCal:
+ return GetReadFactoryCalReply();
+ case RingConCommands::ReadUserCal:
+ return GetReadUserCalReply();
+ case RingConCommands::ReadRepCount:
+ return GetReadRepCountReply();
+ case RingConCommands::ReadTotalPushCount:
+ return GetReadTotalPushCountReply();
+ case RingConCommands::ResetRepCount:
+ return GetResetRepCountReply();
+ case RingConCommands::SaveCalData:
+ return GetSaveDataReply();
+ default:
+ return GetErrorReply();
+ }
+}
+
+bool RingController::SetCommand(const std::vector<u8>& data) {
+ if (data.size() < 4) {
+ LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
+ command = RingConCommands::Error;
+ return false;
+ }
+
+ std::memcpy(&command, data.data(), sizeof(RingConCommands));
+
+ switch (command) {
+ case RingConCommands::GetFirmwareVersion:
+ case RingConCommands::ReadId:
+ case RingConCommands::c20105:
+ case RingConCommands::ReadUnkCal:
+ case RingConCommands::ReadFactoryCal:
+ case RingConCommands::ReadUserCal:
+ case RingConCommands::ReadRepCount:
+ case RingConCommands::ReadTotalPushCount:
+ ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ case RingConCommands::ResetRepCount:
+ ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
+ total_rep_count = 0;
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ case RingConCommands::SaveCalData: {
+ ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
+
+ SaveCalData save_info{};
+ std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
+ user_calibration = save_info.calibration;
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ }
+ default:
+ LOG_ERROR(Service_HID, "Command not implemented {}", command);
+ command = RingConCommands::Error;
+ // Signal a reply to avoid softlocking the game
+ send_command_async_event->GetWritableEvent().Signal();
+ return false;
+ }
+}
+
+std::vector<u8> RingController::GetFirmwareVersionReply() const {
+ const FirmwareVersionReply reply{
+ .status = DataValid::Valid,
+ .firmware = version,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadIdReply() const {
+ // The values are hardcoded from a real joycon
+ const ReadIdReply reply{
+ .status = DataValid::Valid,
+ .id_l_x0 = 8,
+ .id_l_x0_2 = 41,
+ .id_l_x4 = 22294,
+ .id_h_x0 = 19777,
+ .id_h_x0_2 = 13621,
+ .id_h_x4 = 8245,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetC020105Reply() const {
+ const Cmd020105Reply reply{
+ .status = DataValid::Valid,
+ .data = 1,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadUnkCalReply() const {
+ const ReadUnkCalReply reply{
+ .status = DataValid::Valid,
+ .data = 0,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadFactoryCalReply() const {
+ const ReadFactoryCalReply reply{
+ .status = DataValid::Valid,
+ .calibration = factory_calibration,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadUserCalReply() const {
+ const ReadUserCalReply reply{
+ .status = DataValid::Valid,
+ .calibration = user_calibration,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadRepCountReply() const {
+ const GetThreeByteReply reply{
+ .status = DataValid::Valid,
+ .data = {total_rep_count, 0, 0},
+ .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadTotalPushCountReply() const {
+ const GetThreeByteReply reply{
+ .status = DataValid::Valid,
+ .data = {total_push_count, 0, 0},
+ .crc = GetCrcValue({total_push_count, 0, 0, 0}),
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetResetRepCountReply() const {
+ return GetReadRepCountReply();
+}
+
+std::vector<u8> RingController::GetSaveDataReply() const {
+ const StatusReply reply{
+ .status = DataValid::Valid,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetErrorReply() const {
+ const ErrorReply reply{
+ .status = DataValid::BadCRC,
+ };
+
+ return GetDataVector(reply);
+}
+
+u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
+ u8 crc = 0;
+ for (std::size_t index = 0; index < data.size(); index++) {
+ for (u8 i = 0x80; i > 0; i >>= 1) {
+ bool bit = (crc & 0x80) != 0;
+ if ((data[index] & i) != 0) {
+ bit = !bit;
+ }
+ crc <<= 1;
+ if (bit) {
+ crc ^= 0x8d;
+ }
+ }
+ }
+ return crc;
+}
+
+template <typename T>
+std::vector<u8> RingController::GetDataVector(const T& reply) const {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::vector<u8> data;
+ data.resize(sizeof(reply));
+ std::memcpy(data.data(), &reply, sizeof(reply));
+ return data;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedDevices;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class RingController final : public HidbusBase {
+public:
+ explicit RingController(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~RingController() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+
+private:
+ // These values are obtained from a real ring controller
+ static constexpr s16 idle_value = 2280;
+ static constexpr s16 idle_deadzone = 120;
+ static constexpr s16 range = 2500;
+
+ // Most missing command names are leftovers from other firmware versions
+ enum class RingConCommands : u32 {
+ GetFirmwareVersion = 0x00020000,
+ ReadId = 0x00020100,
+ JoyPolling = 0x00020101,
+ Unknown1 = 0x00020104,
+ c20105 = 0x00020105,
+ Unknown2 = 0x00020204,
+ Unknown3 = 0x00020304,
+ Unknown4 = 0x00020404,
+ ReadUnkCal = 0x00020504,
+ ReadFactoryCal = 0x00020A04,
+ Unknown5 = 0x00021104,
+ Unknown6 = 0x00021204,
+ Unknown7 = 0x00021304,
+ ReadUserCal = 0x00021A04,
+ ReadRepCount = 0x00023104,
+ ReadTotalPushCount = 0x00023204,
+ ResetRepCount = 0x04013104,
+ Unknown8 = 0x04011104,
+ Unknown9 = 0x04011204,
+ Unknown10 = 0x04011304,
+ SaveCalData = 0x10011A04,
+ Error = 0xFFFFFFFF,
+ };
+
+ enum class DataValid : u32 {
+ Valid,
+ BadCRC,
+ Cal,
+ };
+
+ struct FirmwareVersion {
+ u8 sub;
+ u8 main;
+ };
+ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
+
+ struct FactoryCalibration {
+ s32_le os_max;
+ s32_le hk_max;
+ s32_le zero_min;
+ s32_le zero_max;
+ };
+ static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
+
+ struct CalibrationValue {
+ s16 value;
+ u16 crc;
+ };
+ static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
+
+ struct UserCalibration {
+ CalibrationValue os_max;
+ CalibrationValue hk_max;
+ CalibrationValue zero;
+ };
+ static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
+
+ struct SaveCalData {
+ RingConCommands command;
+ UserCalibration calibration;
+ INSERT_PADDING_BYTES_NOINIT(4);
+ };
+ static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
+ static_assert(std::is_trivially_copyable_v<SaveCalData>,
+ "SaveCalData must be trivially copyable");
+
+ struct FirmwareVersionReply {
+ DataValid status;
+ FirmwareVersion firmware;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
+
+ struct Cmd020105Reply {
+ DataValid status;
+ u8 data;
+ INSERT_PADDING_BYTES(0x3);
+ };
+ static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
+
+ struct StatusReply {
+ DataValid status;
+ };
+ static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
+
+ struct GetThreeByteReply {
+ DataValid status;
+ std::array<u8, 3> data;
+ u8 crc;
+ };
+ static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
+
+ struct ReadUnkCalReply {
+ DataValid status;
+ u16 data;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
+
+ struct ReadFactoryCalReply {
+ DataValid status;
+ FactoryCalibration calibration;
+ };
+ static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
+
+ struct ReadUserCalReply {
+ DataValid status;
+ UserCalibration calibration;
+ INSERT_PADDING_BYTES(0x4);
+ };
+ static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
+
+ struct ReadIdReply {
+ DataValid status;
+ u16 id_l_x0;
+ u16 id_l_x0_2;
+ u16 id_l_x4;
+ u16 id_h_x0;
+ u16 id_h_x0_2;
+ u16 id_h_x4;
+ };
+ static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
+
+ struct ErrorReply {
+ DataValid status;
+ INSERT_PADDING_BYTES(0x3);
+ };
+ static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
+
+ struct RingConData {
+ DataValid status;
+ s16_le data;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
+
+ // Returns RingConData struct with pressure sensor values
+ RingConData GetSensorValue() const;
+
+ // Returns 8 byte reply with firmware version
+ std::vector<u8> GetFirmwareVersionReply() const;
+
+ // Returns 16 byte reply with ID values
+ std::vector<u8> GetReadIdReply() const;
+
+ // (STUBBED) Returns 8 byte reply
+ std::vector<u8> GetC020105Reply() const;
+
+ // (STUBBED) Returns 8 byte empty reply
+ std::vector<u8> GetReadUnkCalReply() const;
+
+ // Returns 20 byte reply with factory calibration values
+ std::vector<u8> GetReadFactoryCalReply() const;
+
+ // Returns 20 byte reply with user calibration values
+ std::vector<u8> GetReadUserCalReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetReadRepCountReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetReadTotalPushCountReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetResetRepCountReply() const;
+
+ // Returns 4 byte save data reply
+ std::vector<u8> GetSaveDataReply() const;
+
+ // Returns 8 byte error reply
+ std::vector<u8> GetErrorReply() const;
+
+ // Returns 8 bit redundancy check from provided data
+ u8 GetCrcValue(const std::vector<u8>& data) const;
+
+ // Converts structs to an u8 vector equivalent
+ template <typename T>
+ std::vector<u8> GetDataVector(const T& reply) const;
+
+ RingConCommands command{RingConCommands::Error};
+
+ // These counters are used in multitasking mode while the switch is sleeping
+ // Total steps taken
+ u8 total_rep_count = 0;
+ // Total times the ring was pushed
+ u8 total_push_count = 0;
+
+ const u8 device_id = 0x20;
+ const FirmwareVersion version = {
+ .sub = 0x0,
+ .main = 0x2c,
+ };
+ const FactoryCalibration factory_calibration = {
+ .os_max = idle_value + range + idle_deadzone,
+ .hk_max = idle_value - range - idle_deadzone,
+ .zero_min = idle_value - idle_deadzone,
+ .zero_max = idle_value + idle_deadzone,
+ };
+ UserCalibration user_calibration = {
+ .os_max = {.value = range, .crc = 228},
+ .hk_max = {.value = -range, .crc = 239},
+ .zero = {.value = idle_value, .crc = 225},
+ };
+
+ Core::HID::EmulatedDevices* input;
+};
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/hidbus/starlink.h"
+
+namespace Service::HID {
+constexpr u8 DEVICE_ID = 0x28;
+
+Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {}
+Starlink::~Starlink() = default;
+
+void Starlink::OnInit() {
+ return;
+}
+
+void Starlink::OnRelease() {
+ return;
+};
+
+void Starlink::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+ if (!device_enabled) {
+ return;
+ }
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+}
+
+u8 Starlink::GetDeviceId() const {
+ return DEVICE_ID;
+}
+
+std::vector<u8> Starlink::GetReply() const {
+ return {};
+}
+
+bool Starlink::SetCommand(const std::vector<u8>& data) {
+ LOG_ERROR(Service_HID, "Command not implemented");
+ return false;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class Starlink final : public HidbusBase {
+public:
+ explicit Starlink(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~Starlink() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/hidbus/stubbed.h"
+
+namespace Service::HID {
+constexpr u8 DEVICE_ID = 0xFF;
+
+HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {}
+HidbusStubbed::~HidbusStubbed() = default;
+
+void HidbusStubbed::OnInit() {
+ return;
+}
+
+void HidbusStubbed::OnRelease() {
+ return;
+};
+
+void HidbusStubbed::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+ if (!device_enabled) {
+ return;
+ }
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+}
+
+u8 HidbusStubbed::GetDeviceId() const {
+ return DEVICE_ID;
+}
+
+std::vector<u8> HidbusStubbed::GetReply() const {
+ return {};
+}
+
+bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
+ LOG_ERROR(Service_HID, "Command not implemented");
+ return false;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class HidbusStubbed final : public HidbusBase {
+public:
+ explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~HidbusStubbed() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+};
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/result.h"
+#include "core/hle/service/jit/jit.h"
+#include "core/hle/service/jit/jit_context.h"
+#include "core/hle/service/service.h"
+#include "core/memory.h"
+
+namespace Service::JIT {
+
+struct CodeRange {
+ u64 offset;
+ u64 size;
+};
+
+class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
+public:
+ explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
+ : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
+ context{system_.Memory()} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
+ {1, &IJitEnvironment::Control, "Control"},
+ {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
+ {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ // Identity map user code range into sysmodule context
+ configuration.user_ro_memory = user_ro;
+ configuration.user_rx_memory = user_rx;
+ configuration.sys_ro_memory = user_ro;
+ configuration.sys_rx_memory = user_rx;
+ }
+
+ void GenerateCode(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ u32 data_size;
+ u64 command;
+ CodeRange cr1;
+ CodeRange cr2;
+ Struct32 data;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters{rp.PopRaw<Parameters>()};
+ std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
+ std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
+
+ const VAddr return_ptr{context.AddHeap(0u)};
+ const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
+ const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
+ const VAddr cr1_out_ptr{
+ context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
+ const VAddr cr2_out_ptr{
+ context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
+ const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
+ const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
+ const VAddr data_ptr{context.AddHeap(parameters.data)};
+ const VAddr configuration_ptr{context.AddHeap(configuration)};
+
+ context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
+ configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
+ cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
+ output_buffer.size());
+
+ const s32 return_value{context.GetHeap<s32>(return_ptr)};
+
+ if (return_value == 0) {
+ system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
+ configuration.user_rx_memory.size);
+
+ if (ctx.CanWriteBuffer()) {
+ context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
+ ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
+ }
+ const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
+ const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
+
+ IPC::ResponseBuilder rb{ctx, 8};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(return_value);
+ rb.PushRaw(cr1_out);
+ rb.PushRaw(cr2_out);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ }
+ };
+
+ void Control(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto command{rp.PopRaw<u64>()};
+ const auto input_buffer{ctx.ReadBuffer()};
+ std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
+
+ const VAddr return_ptr{context.AddHeap(0u)};
+ const VAddr configuration_ptr{context.AddHeap(configuration)};
+ const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
+ const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
+ const u64 wrapper_value{
+ context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
+ input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
+ const s32 return_value{context.GetHeap<s32>(return_ptr)};
+
+ if (wrapper_value == 0 && return_value == 0) {
+ if (ctx.CanWriteBuffer()) {
+ context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
+ ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
+ }
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(return_value);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin Control callback failed");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ }
+ }
+
+ void LoadPlugin(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto tmem_size{rp.PopRaw<u64>()};
+ if (tmem_size == 0) {
+ LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ const auto tmem_handle{ctx.GetCopyHandle(0)};
+ auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ tmem_handle)};
+ if (tmem.IsNull()) {
+ LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ configuration.work_memory.offset = tmem->GetSourceAddress();
+ configuration.work_memory.size = tmem_size;
+
+ const auto nro_plugin{ctx.ReadBuffer(1)};
+ auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
+ const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
+
+ callbacks =
+ GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
+ .rtld_init = GetSymbol("_init"),
+ .Control = GetSymbol("nnjitpluginControl"),
+ .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
+ .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
+ .Configure = GetSymbol("nnjitpluginConfigure"),
+ .GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
+ .GetVersion = GetSymbol("nnjitpluginGetVersion"),
+ .Keeper = GetSymbol("nnjitpluginKeeper"),
+ .OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
+
+ if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
+ callbacks.OnPrepared == 0) {
+ LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ if (!context.LoadNRO(nro_plugin)) {
+ LOG_ERROR(Service_JIT, "failed to load plugin");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ context.MapProcessMemory(configuration.sys_ro_memory.offset,
+ configuration.sys_ro_memory.size);
+ context.MapProcessMemory(configuration.sys_rx_memory.offset,
+ configuration.sys_rx_memory.size);
+ context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
+
+ if (callbacks.rtld_init != 0) {
+ context.CallFunction(callbacks.rtld_init);
+ }
+
+ const auto version{context.CallFunction(callbacks.GetVersion)};
+ if (version != 1) {
+ LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ const auto resolve{context.GetHelper("_resolve")};
+ if (callbacks.ResolveBasicSymbols != 0) {
+ context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
+ }
+ const auto resolve_ptr{context.AddHeap(resolve)};
+ if (callbacks.SetupDiagnostics != 0) {
+ context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
+ }
+
+ context.CallFunction(callbacks.Configure, 0u);
+ const auto configuration_ptr{context.AddHeap(configuration)};
+ context.CallFunction(callbacks.OnPrepared, configuration_ptr);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetCodeAddress(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(ResultSuccess);
+ rb.Push(configuration.user_rx_memory.offset);
+ rb.Push(configuration.user_ro_memory.offset);
+ }
+
+private:
+ using Struct32 = std::array<u8, 32>;
+
+ struct GuestCallbacks {
+ VAddr rtld_fini;
+ VAddr rtld_init;
+ VAddr Control;
+ VAddr ResolveBasicSymbols;
+ VAddr SetupDiagnostics;
+ VAddr Configure;
+ VAddr GenerateCode;
+ VAddr GetVersion;
+ VAddr Keeper;
+ VAddr OnPrepared;
+ };
+
+ struct JITConfiguration {
+ CodeRange user_rx_memory;
+ CodeRange user_ro_memory;
+ CodeRange work_memory;
+ CodeRange sys_rx_memory;
+ CodeRange sys_ro_memory;
+ };
+
+ GuestCallbacks callbacks;
+ JITConfiguration configuration;
+ JITContext context;
+};
+
+class JITU final : public ServiceFramework<JITU> {
+public:
+ explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+ void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ struct Parameters {
+ u64 rx_size;
+ u64 ro_size;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto executable_mem_handle{ctx.GetCopyHandle(1)};
+ const auto readable_mem_handle{ctx.GetCopyHandle(2)};
+
+ if (parameters.rx_size == 0 || parameters.ro_size == 0) {
+ LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ // The copy handle at index 0 is the process handle, but handle tables are
+ // per-process, so there is no point reading it here until we are multiprocess
+ const auto& process{*system.CurrentProcess()};
+
+ auto executable_mem{
+ process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
+ if (executable_mem.IsNull()) {
+ LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
+ executable_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ auto readable_mem{
+ process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
+ if (readable_mem.IsNull()) {
+ LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ const CodeRange user_rx{
+ .offset = executable_mem->GetSourceAddress(),
+ .size = parameters.rx_size,
+ };
+
+ const CodeRange user_ro{
+ .offset = readable_mem->GetSourceAddress(),
+ .size = parameters.ro_size,
+ };
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<JITU>(system)->InstallAsService(sm);
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::JIT {
+
+/// Registers all JIT services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <map>
+#include <span>
+#include <boost/icl/interval_set.hpp>
+#include <dynarmic/interface/A64/a64.h>
+#include <dynarmic/interface/A64/config.h>
+
+#include "common/alignment.h"
+#include "common/common_funcs.h"
+#include "common/div_ceil.h"
+#include "common/logging/log.h"
+#include "core/hle/service/jit/jit_context.h"
+#include "core/memory.h"
+
+namespace Service::JIT {
+
+constexpr std::array<u8, 4> STOP_ARM64 = {
+ 0x01, 0x00, 0x00, 0xd4, // svc #0
+};
+
+constexpr std::array<u8, 8> RESOLVE_ARM64 = {
+ 0x21, 0x00, 0x00, 0xd4, // svc #1
+ 0xc0, 0x03, 0x5f, 0xd6, // ret
+};
+
+constexpr std::array<u8, 4> PANIC_ARM64 = {
+ 0x41, 0x00, 0x00, 0xd4, // svc #2
+};
+
+constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
+ 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
+ 0x83, 0x01, 0x00, 0x54, // b.lo #+34
+ 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
+ 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
+ 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
+ 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
+ 0xfc, 0xff, 0xff, 0x17, // b #-16
+ 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
+ 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
+ 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
+ 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
+ 0x8b, 0xff, 0xff, 0x54, // b.lt #-16
+ 0xc0, 0x03, 0x5f, 0xd6, // ret
+ 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
+ 0xfc, 0xff, 0xff, 0x17, // b #-16
+};
+
+constexpr std::array<u8, 28> MEMSET_ARM64 = {
+ 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
+ 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
+ 0x4b, 0x00, 0x00, 0x54, // b.lt #+8
+ 0xc0, 0x03, 0x5f, 0xd6, // ret
+ 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
+ 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
+ 0xfb, 0xff, 0xff, 0x17, // b #-20
+};
+
+struct HelperFunction {
+ const char* name;
+ const std::span<const u8> data;
+};
+
+constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
+ {"_stop", STOP_ARM64},
+ {"_resolve", RESOLVE_ARM64},
+ {"_panic", PANIC_ARM64},
+ {"memcpy", MEMMOVE_ARM64},
+ {"memmove", MEMMOVE_ARM64},
+ {"memset", MEMSET_ARM64},
+}};
+
+struct Elf64_Dyn {
+ u64 d_tag;
+ u64 d_un;
+};
+
+struct Elf64_Rela {
+ u64 r_offset;
+ u64 r_info;
+ s64 r_addend;
+};
+
+static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
+ return static_cast<u32>(rela->r_info);
+}
+
+constexpr int DT_RELA = 7; /* Address of Rela relocs */
+constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
+constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
+
+constexpr size_t STACK_ALIGN = 16;
+
+class JITContextImpl;
+
+using IntervalSet = boost::icl::interval_set<VAddr>::type;
+using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
+
+class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
+public:
+ explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
+ IntervalSet& mapped_ranges_, JITContextImpl& parent_)
+ : memory{memory_}, local_memory{local_memory_},
+ mapped_ranges{mapped_ranges_}, parent{parent_} {}
+
+ u8 MemoryRead8(u64 vaddr) override {
+ return ReadMemory<u8>(vaddr);
+ }
+ u16 MemoryRead16(u64 vaddr) override {
+ return ReadMemory<u16>(vaddr);
+ }
+ u32 MemoryRead32(u64 vaddr) override {
+ return ReadMemory<u32>(vaddr);
+ }
+ u64 MemoryRead64(u64 vaddr) override {
+ return ReadMemory<u64>(vaddr);
+ }
+ u128 MemoryRead128(u64 vaddr) override {
+ return ReadMemory<u128>(vaddr);
+ }
+ std::string MemoryReadCString(u64 vaddr) {
+ std::string result;
+ u8 next;
+
+ while ((next = MemoryRead8(vaddr++)) != 0) {
+ result += next;
+ }
+
+ return result;
+ }
+
+ void MemoryWrite8(u64 vaddr, u8 value) override {
+ WriteMemory<u8>(vaddr, value);
+ }
+ void MemoryWrite16(u64 vaddr, u16 value) override {
+ WriteMemory<u16>(vaddr, value);
+ }
+ void MemoryWrite32(u64 vaddr, u32 value) override {
+ WriteMemory<u32>(vaddr, value);
+ }
+ void MemoryWrite64(u64 vaddr, u64 value) override {
+ WriteMemory<u64>(vaddr, value);
+ }
+ void MemoryWrite128(u64 vaddr, u128 value) override {
+ WriteMemory<u128>(vaddr, value);
+ }
+
+ bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
+ return WriteMemory<u8>(vaddr, value);
+ }
+ bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
+ return WriteMemory<u16>(vaddr, value);
+ }
+ bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
+ return WriteMemory<u32>(vaddr, value);
+ }
+ bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
+ return WriteMemory<u64>(vaddr, value);
+ }
+ bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
+ return WriteMemory<u128>(vaddr, value);
+ }
+
+ void CallSVC(u32 swi) override;
+ void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
+ void InterpreterFallback(u64 pc, size_t num_instructions) override;
+
+ void AddTicks(u64 ticks) override {}
+ u64 GetTicksRemaining() override {
+ return std::numeric_limits<u32>::max();
+ }
+ u64 GetCNTPCT() override {
+ return 0;
+ }
+
+ template <class T>
+ T ReadMemory(u64 vaddr) {
+ T ret{};
+ if (boost::icl::contains(mapped_ranges, vaddr)) {
+ memory.ReadBlock(vaddr, &ret, sizeof(T));
+ } else if (vaddr + sizeof(T) > local_memory.size()) {
+ LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
+ } else {
+ std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
+ }
+ return ret;
+ }
+
+ template <class T>
+ bool WriteMemory(u64 vaddr, const T value) {
+ if (boost::icl::contains(mapped_ranges, vaddr)) {
+ memory.WriteBlock(vaddr, &value, sizeof(T));
+ } else if (vaddr + sizeof(T) > local_memory.size()) {
+ LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
+ } else {
+ std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
+ }
+ return true;
+ }
+
+private:
+ Core::Memory::Memory& memory;
+ std::vector<u8>& local_memory;
+ IntervalSet& mapped_ranges;
+ JITContextImpl& parent;
+};
+
+class JITContextImpl {
+public:
+ explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
+ callbacks =
+ std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
+ user_config.callbacks = callbacks.get();
+ jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
+ }
+
+ bool LoadNRO(std::span<const u8> data) {
+ local_memory.clear();
+ local_memory.insert(local_memory.end(), data.begin(), data.end());
+
+ if (FixupRelocations()) {
+ InsertHelperFunctions();
+ InsertStack();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool FixupRelocations() {
+ const VAddr mod_offset{callbacks->MemoryRead32(4)};
+ if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return false;
+ }
+
+ VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
+ VAddr rela_dyn = 0;
+ size_t num_rela = 0;
+ while (true) {
+ const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
+ dynamic_offset += sizeof(Elf64_Dyn);
+
+ if (!dyn.d_tag) {
+ break;
+ }
+ if (dyn.d_tag == DT_RELA) {
+ rela_dyn = dyn.d_un;
+ }
+ if (dyn.d_tag == DT_RELASZ) {
+ num_rela = dyn.d_un / sizeof(Elf64_Rela);
+ }
+ }
+
+ for (size_t i = 0; i < num_rela; i++) {
+ const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
+ if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
+ continue;
+ }
+ const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
+ callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
+ }
+
+ return true;
+ }
+
+ void InsertHelperFunctions() {
+ for (const auto& [name, contents] : HELPER_FUNCTIONS) {
+ helpers[name] = local_memory.size();
+ local_memory.insert(local_memory.end(), contents.begin(), contents.end());
+ }
+ }
+
+ void InsertStack() {
+ const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
+ local_memory.size()};
+ local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
+ top_of_stack = local_memory.size();
+ heap_pointer = top_of_stack;
+ }
+
+ void MapProcessMemory(VAddr dest_address, std::size_t size) {
+ mapped_ranges.add(IntervalType{dest_address, dest_address + size});
+ }
+
+ void PushArgument(const void* data, size_t size) {
+ const size_t num_words = Common::DivCeil(size, sizeof(u64));
+ const size_t current_pos = argument_stack.size();
+ argument_stack.insert(argument_stack.end(), num_words, 0);
+ std::memcpy(argument_stack.data() + current_pos, data, size);
+ }
+
+ void SetupArguments() {
+ for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
+ jit->SetRegister(i, argument_stack[i]);
+ }
+ if (argument_stack.size() > 8) {
+ const VAddr new_sp = Common::AlignDown(
+ top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
+ for (size_t i = 8; i < argument_stack.size(); i++) {
+ callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
+ }
+ jit->SetSP(new_sp);
+ }
+ argument_stack.clear();
+ heap_pointer = top_of_stack;
+ }
+
+ u64 CallFunction(VAddr func) {
+ jit->SetRegister(30, helpers["_stop"]);
+ jit->SetSP(top_of_stack);
+ SetupArguments();
+
+ jit->SetPC(func);
+ jit->Run();
+ return jit->GetRegister(0);
+ }
+
+ VAddr GetHelper(const std::string& name) {
+ return helpers[name];
+ }
+
+ VAddr AddHeap(const void* data, size_t size) {
+ const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
+ if (heap_pointer + num_bytes > local_memory.size()) {
+ local_memory.insert(local_memory.end(),
+ (heap_pointer + num_bytes) - local_memory.size(), 0);
+ }
+ const VAddr location{heap_pointer};
+ std::memcpy(local_memory.data() + location, data, size);
+ heap_pointer += num_bytes;
+ return location;
+ }
+
+ void GetHeap(VAddr location, void* data, size_t size) {
+ std::memcpy(data, local_memory.data() + location, size);
+ }
+
+ std::unique_ptr<DynarmicCallbacks64> callbacks;
+ std::vector<u8> local_memory;
+ std::vector<u64> argument_stack;
+ IntervalSet mapped_ranges;
+ Dynarmic::A64::UserConfig user_config;
+ std::unique_ptr<Dynarmic::A64::Jit> jit;
+ std::map<std::string, VAddr, std::less<>> helpers;
+ Core::Memory::Memory& memory;
+ VAddr top_of_stack;
+ VAddr heap_pointer;
+};
+
+void DynarmicCallbacks64::CallSVC(u32 swi) {
+ switch (swi) {
+ case 0:
+ parent.jit->HaltExecution();
+ break;
+
+ case 1: {
+ // X0 contains a char* for a symbol to resolve
+ std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
+ const auto helper{parent.helpers[name]};
+
+ if (helper != 0) {
+ parent.jit->SetRegister(0, helper);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
+ parent.jit->SetRegister(0, parent.helpers["_panic"]);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ LOG_CRITICAL(Service_JIT, "plugin panicked!");
+ parent.jit->HaltExecution();
+ break;
+ }
+}
+
+void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
+ LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
+ parent.jit->HaltExecution();
+}
+
+void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
+ LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
+ parent.jit->HaltExecution();
+}
+
+JITContext::JITContext(Core::Memory::Memory& memory)
+ : impl{std::make_unique<JITContextImpl>(memory)} {}
+
+JITContext::~JITContext() {}
+
+bool JITContext::LoadNRO(std::span<const u8> data) {
+ return impl->LoadNRO(data);
+}
+
+void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
+ impl->MapProcessMemory(dest_address, size);
+}
+
+u64 JITContext::CallFunction(VAddr func) {
+ return impl->CallFunction(func);
+}
+
+void JITContext::PushArgument(const void* data, size_t size) {
+ impl->PushArgument(data, size);
+}
+
+VAddr JITContext::GetHelper(const std::string& name) {
+ return impl->GetHelper(name);
+}
+
+VAddr JITContext::AddHeap(const void* data, size_t size) {
+ return impl->AddHeap(data, size);
+}
+
+void JITContext::GetHeap(VAddr location, void* data, size_t size) {
+ impl->GetHeap(location, data, size);
+}
+
+} // 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Service::JIT {
+
+class JITContextImpl;
+
+class JITContext {
+public:
+ explicit JITContext(Core::Memory::Memory& memory);
+ ~JITContext();
+
+ [[nodiscard]] bool LoadNRO(std::span<const u8> data);
+ void MapProcessMemory(VAddr dest_address, std::size_t size);
+
+ template <typename T, typename... Ts>
+ u64 CallFunction(VAddr func, T argument, Ts... rest) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ PushArgument(&argument, sizeof(argument));
+
+ if constexpr (sizeof...(rest) > 0) {
+ return CallFunction(func, rest...);
+ } else {
+ return CallFunction(func);
+ }
+ }
+
+ u64 CallFunction(VAddr func);
+ VAddr GetHelper(const std::string& name);
+
+ template <typename T>
+ VAddr AddHeap(T argument) {
+ return AddHeap(&argument, sizeof(argument));
+ }
+ VAddr AddHeap(const void* data, size_t size);
+
+ template <typename T>
+ T GetHeap(VAddr location) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T result;
+ GetHeap(location, &result, sizeof(result));
+ return result;
+ }
+ void GetHeap(VAddr location, void* data, size_t size);
+
+private:
+ std::unique_ptr<JITContextImpl> impl;
+
+ void PushArgument(const void* data, size_t size);
+};
+
+} // 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 {
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
: kernel(system_.Kernel()) {
-
- // Create a resource limit for the process.
- const auto physical_memory_size =
- kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
- auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
-
// Create the process.
process = Kernel::KProcess::Create(kernel);
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
Kernel::KProcess::ProcessType::KernelInternal,
- resource_limit)
+ kernel.GetSystemResourceLimit())
.IsSuccess());
-
- // Close reference to our resource limit, as the process opens one.
- resource_limit->Close();
}
ServiceContext::~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 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/ldr/ldr.h"
@@ -161,7 +160,8 @@ public:
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
public:
- explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} {
+ explicit RelocatableObject(Core::System& system_)
+ : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadModule, "LoadModule"},
@@ -288,7 +288,7 @@ public:
}
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
- constexpr std::size_t padding_size{4 * Kernel::PageSize};
+ const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
const auto start_info{page_table.QueryInfo(start - 1)};
if (start_info.state != Kernel::KMemoryState::Free) {
@@ -308,31 +308,69 @@ public:
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
- VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const {
- VAddr addr{};
- const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
- Kernel::PageBits};
- do {
- addr = page_table.GetAliasCodeRegionStart() +
- (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits);
- } while (!page_table.IsInsideAddressSpace(addr, size) ||
- page_table.IsInsideHeapRegion(addr, size) ||
- page_table.IsInsideAliasRegion(addr, size));
- return addr;
+ ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
+ size = Common::AlignUp(size, Kernel::PageSize);
+ size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
+
+ const auto is_region_available = [&](VAddr addr) {
+ const auto end_addr = addr + size;
+ while (addr < end_addr) {
+ if (system.Memory().IsValidVirtualAddress(addr)) {
+ return false;
+ }
+
+ if (!page_table.IsInsideAddressSpace(out_addr, size)) {
+ return false;
+ }
+
+ if (page_table.IsInsideHeapRegion(out_addr, size)) {
+ return false;
+ }
+
+ if (page_table.IsInsideAliasRegion(out_addr, size)) {
+ return false;
+ }
+
+ addr += Kernel::PageSize;
+ }
+ return true;
+ };
+
+ bool succeeded = false;
+ const auto map_region_end =
+ page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
+ while (current_map_addr < map_region_end) {
+ if (is_region_available(current_map_addr)) {
+ succeeded = true;
+ break;
+ }
+ current_map_addr += 0x100000;
+ }
+
+ if (!succeeded) {
+ UNREACHABLE_MSG("Out of address space!");
+ return Kernel::ResultOutOfMemory;
+ }
+
+ out_addr = current_map_addr;
+ current_map_addr += size;
+
+ return ResultSuccess;
}
- ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress,
- u64 size) const {
+ ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
+ auto& page_table{process->PageTable()};
+ VAddr addr{};
+
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
- auto& page_table{process->PageTable()};
- const VAddr addr{GetRandomMapRegion(page_table, size)};
- const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
+ R_TRY(GetAvailableMapRegion(page_table, size, addr));
+ const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
- CASCADE_CODE(result);
+ R_TRY(result);
if (ValidateRegionForMap(page_table, addr, size)) {
return addr;
@@ -343,7 +381,7 @@ public:
}
ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
- VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
+ VAddr bss_addr, std::size_t bss_size, std::size_t size) {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
VAddr addr{};
@@ -352,8 +390,12 @@ public:
if (bss_size) {
auto block_guard = detail::ScopeExit([&] {
- page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
- page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
+ page_table.UnmapCodeMemory(
+ addr + nro_size, bss_addr, bss_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
+ page_table.UnmapCodeMemory(
+ addr, nro_addr, nro_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
});
const ResultCode result{
@@ -533,17 +575,21 @@ public:
auto& page_table{system.CurrentProcess()->PageTable()};
if (info.bss_size != 0) {
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size +
- info.ro_size + info.data_size,
- info.bss_address, info.bss_size));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
+ info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
}
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
- info.src_addr + info.text_size + info.ro_size,
- info.data_size));
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
- info.src_addr + info.text_size, info.ro_size));
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size + info.ro_size,
+ info.src_addr + info.text_size + info.ro_size, info.data_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address, info.src_addr, info.text_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
return ResultSuccess;
}
@@ -597,6 +643,7 @@ public:
LOG_WARNING(Service_LDR, "(STUBBED) called");
initialized = true;
+ current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -607,6 +654,7 @@ private:
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
+ VAddr current_map_addr{};
bool IsValidNROHash(const SHA256Hash& hash) const {
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 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -12,7 +12,6 @@
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/raw_data.h"
-#include "core/hle/service/mii/types.h"
namespace Service::Mii {
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 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
-#include <array>
#include <vector>
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/uuid.h"
+
#include "core/hle/result.h"
#include "core/hle/service/mii/types.h"
namespace Service::Mii {
-enum class Source : u32 {
- Database = 0,
- Default = 1,
- Account = 2,
- Friend = 3,
-};
-
-enum class SourceFlag : u32 {
- None = 0,
- Database = 1 << 0,
- Default = 1 << 1,
-};
-DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
-
-struct MiiInfo {
- Common::UUID uuid;
- std::array<char16_t, 11> name;
- u8 font_region;
- u8 favorite_color;
- u8 gender;
- u8 height;
- u8 build;
- u8 type;
- u8 region_move;
- u8 faceline_type;
- u8 faceline_color;
- u8 faceline_wrinkle;
- u8 faceline_make;
- u8 hair_type;
- u8 hair_color;
- u8 hair_flip;
- u8 eye_type;
- u8 eye_color;
- u8 eye_scale;
- u8 eye_aspect;
- u8 eye_rotate;
- u8 eye_x;
- u8 eye_y;
- u8 eyebrow_type;
- u8 eyebrow_color;
- u8 eyebrow_scale;
- u8 eyebrow_aspect;
- u8 eyebrow_rotate;
- u8 eyebrow_x;
- u8 eyebrow_y;
- u8 nose_type;
- u8 nose_scale;
- u8 nose_y;
- u8 mouth_type;
- u8 mouth_color;
- u8 mouth_scale;
- u8 mouth_aspect;
- u8 mouth_y;
- u8 beard_color;
- u8 beard_type;
- u8 mustache_type;
- u8 mustache_scale;
- u8 mustache_y;
- u8 glasses_type;
- u8 glasses_color;
- u8 glasses_scale;
- u8 glasses_y;
- u8 mole_type;
- u8 mole_scale;
- u8 mole_x;
- u8 mole_y;
- u8 padding;
-
- std::u16string Name() const;
-};
-static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
-static_assert(std::has_unique_object_representations_v<MiiInfo>,
- "All bits of MiiInfo must contribute to its value.");
-
-#pragma pack(push, 4)
-
-struct MiiInfoElement {
- MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
-
- MiiInfo info{};
- Source source{};
-};
-static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
-
-struct MiiStoreBitFields {
- union {
- u32 word_0{};
-
- BitField<0, 8, u32> hair_type;
- BitField<8, 7, u32> height;
- BitField<15, 1, u32> mole_type;
- BitField<16, 7, u32> build;
- BitField<23, 1, HairFlip> hair_flip;
- BitField<24, 7, u32> hair_color;
- BitField<31, 1, u32> type;
- };
-
- union {
- u32 word_1{};
-
- BitField<0, 7, u32> eye_color;
- BitField<7, 1, Gender> gender;
- BitField<8, 7, u32> eyebrow_color;
- BitField<16, 7, u32> mouth_color;
- BitField<24, 7, u32> beard_color;
- };
-
- union {
- u32 word_2{};
-
- BitField<0, 7, u32> glasses_color;
- BitField<8, 6, u32> eye_type;
- BitField<14, 2, u32> region_move;
- BitField<16, 6, u32> mouth_type;
- BitField<22, 2, FontRegion> font_region;
- BitField<24, 5, u32> eye_y;
- BitField<29, 3, u32> glasses_scale;
- };
-
- union {
- u32 word_3{};
-
- BitField<0, 5, u32> eyebrow_type;
- BitField<5, 3, MustacheType> mustache_type;
- BitField<8, 5, u32> nose_type;
- BitField<13, 3, BeardType> beard_type;
- BitField<16, 5, u32> nose_y;
- BitField<21, 3, u32> mouth_aspect;
- BitField<24, 5, u32> mouth_y;
- BitField<29, 3, u32> eyebrow_aspect;
- };
-
- union {
- u32 word_4{};
-
- BitField<0, 5, u32> mustache_y;
- BitField<5, 3, u32> eye_rotate;
- BitField<8, 5, u32> glasses_y;
- BitField<13, 3, u32> eye_aspect;
- BitField<16, 5, u32> mole_x;
- BitField<21, 3, u32> eye_scale;
- BitField<24, 5, u32> mole_y;
- };
-
- union {
- u32 word_5{};
-
- BitField<0, 5, u32> glasses_type;
- BitField<8, 4, u32> favorite_color;
- BitField<12, 4, u32> faceline_type;
- BitField<16, 4, u32> faceline_color;
- BitField<20, 4, u32> faceline_wrinkle;
- BitField<24, 4, u32> faceline_makeup;
- BitField<28, 4, u32> eye_x;
- };
-
- union {
- u32 word_6{};
-
- BitField<0, 4, u32> eyebrow_scale;
- BitField<4, 4, u32> eyebrow_rotate;
- BitField<8, 4, u32> eyebrow_x;
- BitField<12, 4, u32> eyebrow_y;
- BitField<16, 4, u32> nose_scale;
- BitField<20, 4, u32> mouth_scale;
- BitField<24, 4, u32> mustache_scale;
- BitField<28, 4, u32> mole_scale;
- };
-};
-static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
-static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
- "MiiStoreBitFields is not trivially copyable.");
-
-struct MiiStoreData {
- using Name = std::array<char16_t, 10>;
-
- MiiStoreData();
- MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
- const Common::UUID& user_id);
-
- // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
- // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
- // not suitable for our uses.
- struct {
- std::array<u8, 0x1C> data{};
- static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
-
- Name name{};
- Common::UUID uuid{};
- } data;
-
- u16 data_crc{};
- u16 device_crc{};
-};
-static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
-
-struct MiiStoreDataElement {
- MiiStoreData data{};
- Source source{};
-};
-static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
-
-struct MiiDatabase {
- u32 magic{}; // 'NFDB'
- std::array<MiiStoreData, 0x64> miis{};
- INSERT_PADDING_BYTES(1);
- u8 count{};
- u16 crc{};
-};
-static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
-
-struct RandomMiiValues {
- std::array<u8, 0xbc> values{};
-};
-static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
-
-struct RandomMiiData4 {
- Gender gender{};
- Age age{};
- Race race{};
- u32 values_count{};
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
-
-struct RandomMiiData3 {
- u32 arg_1;
- u32 arg_2;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
-
-struct RandomMiiData2 {
- u32 arg_1;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
-
-struct DefaultMii {
- u32 face_type{};
- u32 face_color{};
- u32 face_wrinkle{};
- u32 face_makeup{};
- u32 hair_type{};
- u32 hair_color{};
- u32 hair_flip{};
- u32 eye_type{};
- u32 eye_color{};
- u32 eye_scale{};
- u32 eye_aspect{};
- u32 eye_rotate{};
- u32 eye_x{};
- u32 eye_y{};
- u32 eyebrow_type{};
- u32 eyebrow_color{};
- u32 eyebrow_scale{};
- u32 eyebrow_aspect{};
- u32 eyebrow_rotate{};
- u32 eyebrow_x{};
- u32 eyebrow_y{};
- u32 nose_type{};
- u32 nose_scale{};
- u32 nose_y{};
- u32 mouth_type{};
- u32 mouth_color{};
- u32 mouth_scale{};
- u32 mouth_aspect{};
- u32 mouth_y{};
- u32 mustache_type{};
- u32 beard_type{};
- u32 beard_color{};
- u32 mustache_scale{};
- u32 mustache_y{};
- u32 glasses_type{};
- u32 glasses_color{};
- u32 glasses_scale{};
- u32 glasses_y{};
- u32 mole_type{};
- u32 mole_scale{};
- u32 mole_x{};
- u32 mole_y{};
- u32 height{};
- u32 weight{};
- Gender gender{};
- u32 favorite_color{};
- u32 region{};
- FontRegion font_region{};
- u32 type{};
- INSERT_PADDING_WORDS(5);
-};
-static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
-
-#pragma pack(pop)
-
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class 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 @@
#include <array>
-#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/mii/types.h"
namespace Service::Mii::RawData {
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 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
+#include <array>
+#include <type_traits>
+
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/uuid.h"
namespace Service::Mii {
@@ -25,7 +30,11 @@ enum class BeardType : u32 {
Beard5,
};
-enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
+enum class BeardAndMustacheFlag : u32 {
+ Beard = 1,
+ Mustache,
+ All = Beard | Mustache,
+};
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
enum class FontRegion : u32 {
@@ -64,4 +73,298 @@ enum class Race : u32 {
All,
};
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+enum class SourceFlag : u32 {
+ None = 0,
+ Database = 1 << 0,
+ Default = 1 << 1,
+};
+DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
+
+struct MiiInfo {
+ Common::UUID uuid;
+ std::array<char16_t, 11> name;
+ u8 font_region;
+ u8 favorite_color;
+ u8 gender;
+ u8 height;
+ u8 build;
+ u8 type;
+ u8 region_move;
+ u8 faceline_type;
+ u8 faceline_color;
+ u8 faceline_wrinkle;
+ u8 faceline_make;
+ u8 hair_type;
+ u8 hair_color;
+ u8 hair_flip;
+ u8 eye_type;
+ u8 eye_color;
+ u8 eye_scale;
+ u8 eye_aspect;
+ u8 eye_rotate;
+ u8 eye_x;
+ u8 eye_y;
+ u8 eyebrow_type;
+ u8 eyebrow_color;
+ u8 eyebrow_scale;
+ u8 eyebrow_aspect;
+ u8 eyebrow_rotate;
+ u8 eyebrow_x;
+ u8 eyebrow_y;
+ u8 nose_type;
+ u8 nose_scale;
+ u8 nose_y;
+ u8 mouth_type;
+ u8 mouth_color;
+ u8 mouth_scale;
+ u8 mouth_aspect;
+ u8 mouth_y;
+ u8 beard_color;
+ u8 beard_type;
+ u8 mustache_type;
+ u8 mustache_scale;
+ u8 mustache_y;
+ u8 glasses_type;
+ u8 glasses_color;
+ u8 glasses_scale;
+ u8 glasses_y;
+ u8 mole_type;
+ u8 mole_scale;
+ u8 mole_x;
+ u8 mole_y;
+ u8 padding;
+};
+static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<MiiInfo>,
+ "All bits of MiiInfo must contribute to its value.");
+
+#pragma pack(push, 4)
+
+struct MiiInfoElement {
+ MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
+
+ MiiInfo info{};
+ Source source{};
+};
+static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
+
+struct MiiStoreBitFields {
+ union {
+ u32 word_0{};
+
+ BitField<0, 8, u32> hair_type;
+ BitField<8, 7, u32> height;
+ BitField<15, 1, u32> mole_type;
+ BitField<16, 7, u32> build;
+ BitField<23, 1, HairFlip> hair_flip;
+ BitField<24, 7, u32> hair_color;
+ BitField<31, 1, u32> type;
+ };
+
+ union {
+ u32 word_1{};
+
+ BitField<0, 7, u32> eye_color;
+ BitField<7, 1, Gender> gender;
+ BitField<8, 7, u32> eyebrow_color;
+ BitField<16, 7, u32> mouth_color;
+ BitField<24, 7, u32> beard_color;
+ };
+
+ union {
+ u32 word_2{};
+
+ BitField<0, 7, u32> glasses_color;
+ BitField<8, 6, u32> eye_type;
+ BitField<14, 2, u32> region_move;
+ BitField<16, 6, u32> mouth_type;
+ BitField<22, 2, FontRegion> font_region;
+ BitField<24, 5, u32> eye_y;
+ BitField<29, 3, u32> glasses_scale;
+ };
+
+ union {
+ u32 word_3{};
+
+ BitField<0, 5, u32> eyebrow_type;
+ BitField<5, 3, MustacheType> mustache_type;
+ BitField<8, 5, u32> nose_type;
+ BitField<13, 3, BeardType> beard_type;
+ BitField<16, 5, u32> nose_y;
+ BitField<21, 3, u32> mouth_aspect;
+ BitField<24, 5, u32> mouth_y;
+ BitField<29, 3, u32> eyebrow_aspect;
+ };
+
+ union {
+ u32 word_4{};
+
+ BitField<0, 5, u32> mustache_y;
+ BitField<5, 3, u32> eye_rotate;
+ BitField<8, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_aspect;
+ BitField<16, 5, u32> mole_x;
+ BitField<21, 3, u32> eye_scale;
+ BitField<24, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5{};
+
+ BitField<0, 5, u32> glasses_type;
+ BitField<8, 4, u32> favorite_color;
+ BitField<12, 4, u32> faceline_type;
+ BitField<16, 4, u32> faceline_color;
+ BitField<20, 4, u32> faceline_wrinkle;
+ BitField<24, 4, u32> faceline_makeup;
+ BitField<28, 4, u32> eye_x;
+ };
+
+ union {
+ u32 word_6{};
+
+ BitField<0, 4, u32> eyebrow_scale;
+ BitField<4, 4, u32> eyebrow_rotate;
+ BitField<8, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> nose_scale;
+ BitField<20, 4, u32> mouth_scale;
+ BitField<24, 4, u32> mustache_scale;
+ BitField<28, 4, u32> mole_scale;
+ };
+};
+static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
+ "MiiStoreBitFields is not trivially copyable.");
+
+struct MiiStoreData {
+ using Name = std::array<char16_t, 10>;
+
+ MiiStoreData();
+ MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
+ const Common::UUID& user_id);
+
+ // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
+ // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
+ // not suitable for our uses.
+ struct {
+ std::array<u8, 0x1C> data{};
+ static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
+
+ Name name{};
+ Common::UUID uuid{};
+ } data;
+
+ u16 data_crc{};
+ u16 device_crc{};
+};
+static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
+
+struct MiiStoreDataElement {
+ MiiStoreData data{};
+ Source source{};
+};
+static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
+
+struct MiiDatabase {
+ u32 magic{}; // 'NFDB'
+ std::array<MiiStoreData, 0x64> miis{};
+ INSERT_PADDING_BYTES(1);
+ u8 count{};
+ u16 crc{};
+};
+static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
+
+struct RandomMiiValues {
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
+
+struct RandomMiiData4 {
+ Gender gender{};
+ Age age{};
+ Race race{};
+ u32 values_count{};
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
+
+struct RandomMiiData3 {
+ u32 arg_1;
+ u32 arg_2;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
+
+struct RandomMiiData2 {
+ u32 arg_1;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
+
+struct DefaultMii {
+ u32 face_type{};
+ u32 face_color{};
+ u32 face_wrinkle{};
+ u32 face_makeup{};
+ u32 hair_type{};
+ u32 hair_color{};
+ u32 hair_flip{};
+ u32 eye_type{};
+ u32 eye_color{};
+ u32 eye_scale{};
+ u32 eye_aspect{};
+ u32 eye_rotate{};
+ u32 eye_x{};
+ u32 eye_y{};
+ u32 eyebrow_type{};
+ u32 eyebrow_color{};
+ u32 eyebrow_scale{};
+ u32 eyebrow_aspect{};
+ u32 eyebrow_rotate{};
+ u32 eyebrow_x{};
+ u32 eyebrow_y{};
+ u32 nose_type{};
+ u32 nose_scale{};
+ u32 nose_y{};
+ u32 mouth_type{};
+ u32 mouth_color{};
+ u32 mouth_scale{};
+ u32 mouth_aspect{};
+ u32 mouth_y{};
+ u32 mustache_type{};
+ u32 beard_type{};
+ u32 beard_color{};
+ u32 mustache_scale{};
+ u32 mustache_y{};
+ u32 glasses_type{};
+ u32 glasses_color{};
+ u32 glasses_scale{};
+ u32 glasses_y{};
+ u32 mole_type{};
+ u32 mole_scale{};
+ u32 mole_x{};
+ u32 mole_y{};
+ u32 height{};
+ u32 weight{};
+ Gender gender{};
+ u32 favorite_color{};
+ u32 region{};
+ FontRegion font_region{};
+ u32 type{};
+ INSERT_PADDING_WORDS(5);
+};
+static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
+
+#pragma pack(pop)
+
} // 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 @@
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
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 @@
#include "common/common_funcs.h"
#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/mii/types.h"
#include "core/hle/service/service.h"
namespace 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 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nifm/nifm.h"
#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 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/pdm_qry.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
namespace Service::NS {
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>&
void nvdisp_disp0::OnOpen(DeviceFD fd) {}
void nvdisp_disp0::OnClose(DeviceFD fd) {}
-void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
- u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
+void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
+ u32 height, u32 stride, android::BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect) {
const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);
- const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
- const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform);
- const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
- stride, pixel_format, transform_flags, crop_rect};
+ const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
+ stride, format, transform, crop_rect};
system.GetPerfStats().EndSystemFrame();
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 @@
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/buffer_transform_flags.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
namespace Service::Nvidia::Devices {
@@ -31,8 +32,8 @@ public:
void OnClose(DeviceFD fd) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
- void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
- NVFlinger::BufferQueue::BufferTransformFlags transform,
+ void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
+ u32 stride, android::BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect);
private:
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
}
EventState status = events_interface.status[event_id];
- const bool bad_parameter = status != EventState::Free && status != EventState::Registered;
+ const bool bad_parameter = status == EventState::Busy;
if (bad_parameter) {
std::memcpy(output.data(), &params, sizeof(params));
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 @@
#pragma once
#include <vector>
+
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#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
return NvResult::Success;
}
-static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
+static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
return {
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
Tegra::SubmissionMode::Increasing),
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
};
}
-static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
+static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
+ u32 add_increment) {
std::vector<Tegra::CommandHeader> result{
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
Tegra::SubmissionMode::Increasing),
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
return result;
}
-static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
+static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
u32 add_increment) {
std::vector<Tegra::CommandHeader> result{
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 @@
#include <memory>
#include <vector>
#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -108,7 +109,7 @@ private:
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
- static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
+ static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries{};
@@ -126,7 +127,7 @@ private:
u32_le num_entries{}; // in
u32_le flags{}; // in
u32_le unk0{}; // in (1 works)
- Fence fence_out{}; // out
+ NvFence fence_out{}; // out
u32_le unk1{}; // in
u32_le unk2{}; // in
u32_le unk3{}; // in
@@ -152,13 +153,13 @@ private:
BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
BitField<8, 1, u32_le> increment; // increment the returned fence
} flags;
- Fence fence_out{}; // returned new fence object for others to wait on
+ NvFence fence_out{}; // returned new fence object for others to wait on
u32 AddIncrementValue() const {
return flags.add_increment.Value() << 1;
}
};
- static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
+ static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
@@ -193,7 +194,7 @@ private:
std::shared_ptr<nvmap> nvmap_dev;
SyncpointManager& syncpoint_manager;
- Fence channel_fence;
+ NvFence channel_fence;
};
} // 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;
constexpr DeviceFD INVALID_NVDRV_FD = -1;
-struct Fence {
+struct NvFence {
s32 id;
u32 value;
};
-
-static_assert(sizeof(Fence) == 8, "Fence has wrong size");
-
-struct MultiFence {
- u32 num_fences;
- std::array<Fence, 4> fences;
-};
+static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
enum class NvResult : u32 {
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 @@
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -37,7 +38,7 @@ class nvdevice;
/// Represents an Nvidia event
struct NvEvent {
Kernel::KEvent* event{};
- Fence fence{};
+ NvFence fence{};
};
struct 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) {
rb.Push<DeviceFD>(0);
rb.PushEnum(NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
}
NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
- : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} {
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
{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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class HLERequestContext;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::android {
+
+enum class TransactionId {
+ RequestBuffer = 1,
+ SetBufferCount = 2,
+ DequeueBuffer = 3,
+ DetachBuffer = 4,
+ DetachNextBuffer = 5,
+ AttachBuffer = 6,
+ QueueBuffer = 7,
+ CancelBuffer = 8,
+ Query = 9,
+ Connect = 10,
+ Disconnect = 11,
+ AllocateBuffers = 13,
+ SetPreallocatedBuffer = 14,
+ GetBufferHistory = 17,
+};
+
+class IBinder {
+public:
+ virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
+ u32 flags) = 0;
+ virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class GraphicBuffer;
+
+class BufferItem final {
+public:
+ constexpr BufferItem() = default;
+
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ Fence fence;
+ Common::Rectangle<s32> crop;
+ NativeWindowTransform transform{};
+ u32 scaling_mode{};
+ s64 timestamp{};
+ bool is_auto_timestamp{};
+ u64 frame_number{};
+
+ // The default value for buf, used to indicate this doesn't correspond to a slot.
+ static constexpr s32 INVALID_BUFFER_SLOT = -1;
+ union {
+ s32 slot{INVALID_BUFFER_SLOT};
+ s32 buf;
+ };
+
+ bool is_droppable{};
+ bool acquire_called{};
+ bool transform_to_display_inverse{};
+ s32 swap_interval{};
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2012 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+
+namespace Service::android {
+
+BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
+ : ConsumerBase{std::move(consumer_)} {}
+
+Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
+ bool wait_for_fence) {
+ if (!item) {
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{mutex};
+
+ if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
+ if (status != Status::NoBufferAvailable) {
+ LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
+ }
+ return status;
+ }
+
+ if (wait_for_fence) {
+ UNIMPLEMENTED();
+ }
+
+ item->graphic_buffer = slots[item->slot].graphic_buffer;
+
+ return Status::NoError;
+}
+
+Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
+ std::scoped_lock lock{mutex};
+
+ if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
+ status != Status::NoError) {
+ LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
+ }
+
+ if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
+ status != Status::NoError) {
+ LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
+ return status;
+ }
+
+ return Status::NoError;
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2012 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/consumer_base.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::android {
+
+class BufferItem;
+
+class BufferItemConsumer final : public ConsumerBase {
+public:
+ explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
+ Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
+ bool wait_for_fence = true);
+ Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
+};
+
+} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/k_writable_event.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
-
-namespace Service::NVFlinger {
-
-BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
- KernelHelpers::ServiceContext& service_context_)
- : id(id_), layer_id(layer_id_), service_context{service_context_} {
- buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
-}
-
-BufferQueue::~BufferQueue() {
- service_context.CloseEvent(buffer_wait_event);
-}
-
-void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
- ASSERT(slot < buffer_slots);
- LOG_WARNING(Service, "Adding graphics buffer {}", slot);
-
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffers[slot] = {
- .slot = slot,
- .status = Buffer::Status::Free,
- .igbp_buffer = igbp_buffer,
- .transform = {},
- .crop_rect = {},
- .swap_interval = 0,
- .multi_fence = {},
- };
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
- u32 height) {
- // Wait for first request before trying to dequeue
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
- }
-
- if (!is_connect) {
- // Buffer was disconnected while the thread was blocked, this is most likely due to
- // emulation being stopped
- return std::nullopt;
- }
-
- std::unique_lock lock{free_buffers_mutex};
-
- auto f_itr = free_buffers.begin();
- auto slot = buffers.size();
-
- while (f_itr != free_buffers.end()) {
- const Buffer& buffer = buffers[*f_itr];
- if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
- buffer.igbp_buffer.height == height) {
- slot = *f_itr;
- free_buffers.erase(f_itr);
- break;
- }
- ++f_itr;
- }
- if (slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[slot].status = Buffer::Status::Dequeued;
- return {{buffers[slot].slot, &buffers[slot].multi_fence}};
-}
-
-const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
-
- return buffers[slot].igbp_buffer;
-}
-
-void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect, u32 swap_interval,
- Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Queued;
- buffers[slot].transform = transform;
- buffers[slot].crop_rect = crop_rect;
- buffers[slot].swap_interval = swap_interval;
- buffers[slot].multi_fence = multi_fence;
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.push_back(slot);
-}
-
-void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status != Buffer::Status::Free);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Free;
- buffers[slot].multi_fence = multi_fence;
- buffers[slot].swap_interval = 0;
-
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
- std::unique_lock lock{queue_sequence_mutex};
- std::size_t buffer_slot = buffers.size();
- // Iterate to find a queued buffer matching the requested slot.
- while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
- const auto slot = static_cast<std::size_t>(queue_sequence.front());
- ASSERT(slot < buffers.size());
- if (buffers[slot].status == Buffer::Status::Queued) {
- ASSERT(buffers[slot].slot == slot);
- buffer_slot = slot;
- }
- queue_sequence.pop_front();
- }
- if (buffer_slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[buffer_slot].status = Buffer::Status::Acquired;
- return {{buffers[buffer_slot]}};
-}
-
-void BufferQueue::ReleaseBuffer(u32 slot) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Acquired);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Free;
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-void BufferQueue::Connect() {
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.clear();
- is_connect = true;
-}
-
-void BufferQueue::Disconnect() {
- buffers.fill({});
- {
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.clear();
- }
- buffer_wait_event->GetWritableEvent().Signal();
- is_connect = false;
- free_buffers_condition.notify_one();
-}
-
-u32 BufferQueue::Query(QueryType type) {
- LOG_WARNING(Service, "(STUBBED) called type={}", type);
-
- switch (type) {
- case QueryType::NativeWindowFormat:
- return static_cast<u32>(PixelFormat::RGBA8888);
- case QueryType::NativeWindowWidth:
- case QueryType::NativeWindowHeight:
- break;
- case QueryType::NativeWindowMinUndequeuedBuffers:
- return 0;
- case QueryType::NativeWindowConsumerUsageBits:
- return 0;
- }
- UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
- return 0;
-}
-
-Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
- return buffer_wait_event->GetWritableEvent();
-}
-
-Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
- return buffer_wait_event->GetReadableEvent();
-}
-
-} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <condition_variable>
-#include <list>
-#include <mutex>
-#include <optional>
-
-#include "common/common_funcs.h"
-#include "common/math_util.h"
-#include "common/swap.h"
-#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/service/nvdrv/nvdata.h"
-
-namespace Kernel {
-class KernelCore;
-class KEvent;
-class KReadableEvent;
-class KWritableEvent;
-} // namespace Kernel
-
-namespace Service::KernelHelpers {
-class ServiceContext;
-} // namespace Service::KernelHelpers
-
-namespace Service::NVFlinger {
-
-constexpr u32 buffer_slots = 0x40;
-struct IGBPBuffer {
- u32_le magic;
- u32_le width;
- u32_le height;
- u32_le stride;
- u32_le format;
- u32_le usage;
- INSERT_PADDING_WORDS(1);
- u32_le index;
- INSERT_PADDING_WORDS(3);
- u32_le gpu_buffer_id;
- INSERT_PADDING_WORDS(6);
- u32_le external_format;
- INSERT_PADDING_WORDS(10);
- u32_le nvmap_handle;
- u32_le offset;
- INSERT_PADDING_WORDS(60);
-};
-
-static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
-
-class BufferQueue final {
-public:
- enum class QueryType {
- NativeWindowWidth = 0,
- NativeWindowHeight = 1,
- NativeWindowFormat = 2,
- /// The minimum number of buffers that must remain un-dequeued after a buffer has been
- /// queued
- NativeWindowMinUndequeuedBuffers = 3,
- /// The consumer gralloc usage bits currently set by the consumer
- NativeWindowConsumerUsageBits = 10,
- };
-
- explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
- KernelHelpers::ServiceContext& service_context_);
- ~BufferQueue();
-
- enum class BufferTransformFlags : u32 {
- /// No transform flags are set
- Unset = 0x00,
- /// Flip source image horizontally (around the vertical axis)
- FlipH = 0x01,
- /// Flip source image vertically (around the horizontal axis)
- FlipV = 0x02,
- /// Rotate source image 90 degrees clockwise
- Rotate90 = 0x04,
- /// Rotate source image 180 degrees
- Rotate180 = 0x03,
- /// Rotate source image 270 degrees clockwise
- Rotate270 = 0x07,
- };
-
- enum class PixelFormat : u32 {
- RGBA8888 = 1,
- RGBX8888 = 2,
- RGB888 = 3,
- RGB565 = 4,
- BGRA8888 = 5,
- RGBA5551 = 6,
- RRGBA4444 = 7,
- };
-
- struct Buffer {
- enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
-
- u32 slot;
- Status status = Status::Free;
- IGBPBuffer igbp_buffer;
- BufferTransformFlags transform;
- Common::Rectangle<int> crop_rect;
- u32 swap_interval;
- Service::Nvidia::MultiFence multi_fence;
- };
-
- void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
- std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
- u32 height);
- const IGBPBuffer& RequestBuffer(u32 slot) const;
- void QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect, u32 swap_interval,
- Service::Nvidia::MultiFence& multi_fence);
- void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
- std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
- void ReleaseBuffer(u32 slot);
- void Connect();
- void Disconnect();
- u32 Query(QueryType type);
-
- u32 GetId() const {
- return id;
- }
-
- bool IsConnected() const {
- return is_connect;
- }
-
- Kernel::KWritableEvent& GetWritableBufferWaitEvent();
-
- Kernel::KReadableEvent& GetBufferWaitEvent();
-
-private:
- BufferQueue(const BufferQueue&) = delete;
-
- u32 id{};
- u64 layer_id{};
- std::atomic_bool is_connect{};
-
- std::list<u32> free_buffers;
- std::array<Buffer, buffer_slots> buffers;
- std::list<u32> queue_sequence;
- Kernel::KEvent* buffer_wait_event{};
-
- std::mutex free_buffers_mutex;
- std::condition_variable free_buffers_condition;
-
- std::mutex queue_sequence_mutex;
-
- KernelHelpers::ServiceContext& service_context;
-};
-
-} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
+
+#include "common/logging/log.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/producer_listener.h"
+
+namespace Service::android {
+
+BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
+ : core{std::move(core_)}, slots{core->slots} {}
+
+BufferQueueConsumer::~BufferQueueConsumer() = default;
+
+Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
+ std::chrono::nanoseconds expected_present) {
+ std::scoped_lock lock{core->mutex};
+
+ // Check that the consumer doesn't currently have the maximum number of buffers acquired.
+ const s32 num_acquired_buffers{
+ static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
+ return slot.buffer_state == BufferState::Acquired;
+ }))};
+
+ if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
+ LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
+ num_acquired_buffers, core->max_acquired_buffer_count);
+ return Status::InvalidOperation;
+ }
+
+ // Check if the queue is empty.
+ if (core->queue.empty()) {
+ return Status::NoBufferAvailable;
+ }
+
+ auto front(core->queue.begin());
+
+ // If expected_present is specified, we may not want to return a buffer yet.
+ if (expected_present.count() != 0) {
+ constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
+
+ // The expected_present argument indicates when the buffer is expected to be presented
+ // on-screen.
+ while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
+ const auto& buffer_item{core->queue[1]};
+
+ // If entry[1] is timely, drop entry[0] (and repeat).
+ const auto desired_present = buffer_item.timestamp;
+ if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
+ desired_present > expected_present.count()) {
+ // This buffer is set to display in the near future, or desired_present is garbage.
+ LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
+ expected_present.count());
+ break;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
+ expected_present.count(), core->queue.size());
+
+ if (core->StillTracking(*front)) {
+ // Front buffer is still in mSlots, so mark the slot as free
+ slots[front->slot].buffer_state = BufferState::Free;
+ }
+
+ core->queue.erase(front);
+ front = core->queue.begin();
+ }
+
+ // See if the front buffer is ready to be acquired.
+ const auto desired_present = front->timestamp;
+ if (desired_present > expected_present.count() &&
+ desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
+ LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
+ expected_present.count());
+ return Status::PresentLater;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
+ expected_present.count());
+ }
+
+ const auto slot = front->slot;
+ *out_buffer = *front;
+
+ LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
+
+ // If the front buffer is still being tracked, update its slot state
+ if (core->StillTracking(*front)) {
+ slots[slot].acquire_called = true;
+ slots[slot].needs_cleanup_on_release = false;
+ slots[slot].buffer_state = BufferState::Acquired;
+ slots[slot].fence = Fence::NoFence();
+ }
+
+ // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
+ // avoid unnecessarily remapping this buffer on the consumer side.
+ if (out_buffer->acquire_called) {
+ out_buffer->graphic_buffer = nullptr;
+ }
+
+ core->queue.erase(front);
+
+ // We might have freed a slot while dropping old buffers, or the producer may be blocked
+ // waiting for the number of buffers in the queue to decrease.
+ core->SignalDequeueCondition();
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
+ return Status::BadValue;
+ }
+
+ std::shared_ptr<IProducerListener> listener;
+ {
+ std::scoped_lock lock{core->mutex};
+
+ // If the frame number has changed because the buffer has been reallocated, we can ignore
+ // this ReleaseBuffer for the old buffer.
+ if (frame_number != slots[slot].frame_number) {
+ return Status::StaleBufferSlot;
+ }
+
+ // Make sure this buffer hasn't been queued while acquired by the consumer.
+ auto current(core->queue.begin());
+ while (current != core->queue.end()) {
+ if (current->slot == slot) {
+ LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
+ slot);
+ return Status::BadValue;
+ }
+ ++current;
+ }
+
+ if (slots[slot].buffer_state == BufferState::Acquired) {
+ slots[slot].fence = release_fence;
+ slots[slot].buffer_state = BufferState::Free;
+
+ listener = core->connected_producer_listener;
+
+ LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
+ } else if (slots[slot].needs_cleanup_on_release) {
+ LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
+ slots[slot].buffer_state);
+
+ slots[slot].needs_cleanup_on_release = false;
+
+ return Status::StaleBufferSlot;
+ } else {
+ LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
+ slot, slots[slot].buffer_state);
+
+ return Status::BadValue;
+ }
+
+ core->SignalDequeueCondition();
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBufferReleased();
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
+ bool controlled_by_app) {
+ if (consumer_listener == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ core->consumer_listener = consumer_listener;
+ core->consumer_controlled_by_app = controlled_by_app;
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
+ if (out_slot_mask == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ u64 mask = 0;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (!slots[s].acquire_called) {
+ mask |= (1ULL << s);
+ }
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called, since the consumer
+ // will not receive their buffer addresses and so must retain their cached information
+ auto current(core->queue.begin());
+ while (current != core->queue.end()) {
+ if (current->acquire_called) {
+ mask &= ~(1ULL << current->slot);
+ }
+ ++current;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
+ *out_slot_mask = mask;
+ return Status::NoError;
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::android {
+
+class BufferItem;
+class BufferQueueCore;
+class IConsumerListener;
+
+class BufferQueueConsumer final {
+public:
+ explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
+ ~BufferQueueConsumer();
+
+ Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
+ Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
+ Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
+ Status GetReleasedBuffers(u64* out_slot_mask);
+
+private:
+ std::shared_ptr<BufferQueueCore> core;
+ BufferQueueDefs::SlotsType& slots;
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
+
+#include "common/assert.h"
+
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+
+namespace Service::android {
+
+BufferQueueCore::BufferQueueCore() = default;
+
+BufferQueueCore::~BufferQueueCore() = default;
+
+void BufferQueueCore::NotifyShutdown() {
+ std::scoped_lock lock{mutex};
+
+ is_shutting_down = true;
+
+ SignalDequeueCondition();
+}
+
+void BufferQueueCore::SignalDequeueCondition() {
+ dequeue_condition.notify_all();
+}
+
+bool BufferQueueCore::WaitForDequeueCondition() {
+ if (is_shutting_down) {
+ return false;
+ }
+
+ dequeue_condition.wait(mutex);
+
+ return true;
+}
+
+s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
+ // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
+ if (!use_async_buffer) {
+ return max_acquired_buffer_count;
+ }
+
+ if (dequeue_buffer_cannot_block || async) {
+ return max_acquired_buffer_count + 1;
+ }
+
+ return max_acquired_buffer_count;
+}
+
+s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
+ return GetMinUndequeuedBufferCountLocked(async) + 1;
+}
+
+s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
+ const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
+ auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
+
+ if (override_max_buffer_count != 0) {
+ ASSERT(override_max_buffer_count >= min_buffer_count);
+ max_buffer_count = override_max_buffer_count;
+ }
+
+ // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
+ // need to have their slots preserved.
+ for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ const auto state = slots[slot].buffer_state;
+ if (state == BufferState::Queued || state == BufferState::Dequeued) {
+ max_buffer_count = slot + 1;
+ }
+ }
+
+ return max_buffer_count;
+}
+
+s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
+ return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
+ [](const auto& slot) { return slot.is_preallocated; }));
+}
+
+void BufferQueueCore::FreeBufferLocked(s32 slot) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ slots[slot].graphic_buffer.reset();
+
+ if (slots[slot].buffer_state == BufferState::Acquired) {
+ slots[slot].needs_cleanup_on_release = true;
+ }
+
+ slots[slot].buffer_state = BufferState::Free;
+ slots[slot].frame_number = UINT32_MAX;
+ slots[slot].acquire_called = false;
+ slots[slot].fence = Fence::NoFence();
+}
+
+void BufferQueueCore::FreeAllBuffersLocked() {
+ buffer_has_been_queued = false;
+
+ for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ FreeBufferLocked(slot);
+ }
+}
+
+bool BufferQueueCore::StillTracking(const BufferItem& item) const {
+ const BufferSlot& slot = slots[item.slot];
+
+ return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
+}
+
+void BufferQueueCore::WaitWhileAllocatingLocked() const {
+ while (is_allocating) {
+ is_allocating_condition.wait(mutex);
+ }
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
+
+#pragma once
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <vector>
+
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+#include "core/hle/service/nvflinger/status.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class IConsumerListener;
+class IProducerListener;
+
+class BufferQueueCore final {
+ friend class BufferQueueProducer;
+ friend class BufferQueueConsumer;
+
+public:
+ static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
+
+ BufferQueueCore();
+ ~BufferQueueCore();
+
+ void NotifyShutdown();
+
+private:
+ void SignalDequeueCondition();
+ bool WaitForDequeueCondition();
+
+ s32 GetMinUndequeuedBufferCountLocked(bool async) const;
+ s32 GetMinMaxBufferCountLocked(bool async) const;
+ s32 GetMaxBufferCountLocked(bool async) const;
+ s32 GetPreallocatedBufferCountLocked() const;
+ void FreeBufferLocked(s32 slot);
+ void FreeAllBuffersLocked();
+ bool StillTracking(const BufferItem& item) const;
+ void WaitWhileAllocatingLocked() const;
+
+private:
+ mutable std::mutex mutex;
+ bool is_abandoned{};
+ bool consumer_controlled_by_app{};
+ std::shared_ptr<IConsumerListener> consumer_listener;
+ u32 consumer_usage_bit{};
+ NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
+ std::shared_ptr<IProducerListener> connected_producer_listener;
+ BufferQueueDefs::SlotsType slots{};
+ std::vector<BufferItem> queue;
+ s32 override_max_buffer_count{};
+ mutable std::condition_variable_any dequeue_condition;
+ const bool use_async_buffer{}; // This is always disabled on HOS
+ bool dequeue_buffer_cannot_block{};
+ PixelFormat default_buffer_format{PixelFormat::Rgba8888};
+ u32 default_width{1};
+ u32 default_height{1};
+ s32 default_max_buffer_count{2};
+ const s32 max_acquired_buffer_count{}; // This is always zero on HOS
+ bool buffer_has_been_queued{};
+ u64 frame_counter{};
+ u32 transform_hint{};
+ bool is_allocating{};
+ mutable std::condition_variable_any is_allocating_condition;
+ bool is_shutting_down{};
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_slot.h"
+
+namespace Service::android::BufferQueueDefs {
+
+// BufferQueue will keep track of at most this value of buffers.
+constexpr s32 NUM_BUFFER_SLOTS = 64;
+
+using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "core/core.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/consumer_listener.h"
+#include "core/hle/service/nvflinger/parcel.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
+#include "core/hle/service/nvflinger/window.h"
+#include "core/hle/service/vi/vi.h"
+
+namespace Service::android {
+
+BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
+ std::shared_ptr<BufferQueueCore> buffer_queue_core_)
+ : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
+ buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
+}
+
+BufferQueueProducer::~BufferQueueProducer() {
+ service_context.CloseEvent(buffer_wait_event);
+}
+
+Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return Status::BadValue;
+ }
+
+ slots[slot].request_buffer_called = true;
+ *buf = slots[slot].graphic_buffer;
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
+ LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
+
+ std::shared_ptr<IConsumerListener> listener;
+ {
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ }
+
+ // There must be no dequeued buffers when changing the buffer count.
+ for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (slots[s].buffer_state == BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
+ return Status::BadValue;
+ }
+ }
+
+ if (buffer_count == 0) {
+ core->override_max_buffer_count = 0;
+ core->SignalDequeueCondition();
+ return Status::NoError;
+ }
+
+ const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
+ if (buffer_count < min_buffer_slots) {
+ LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
+ buffer_count, min_buffer_slots);
+ return Status::BadValue;
+ }
+
+ // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
+ // release all of its buffer references.
+ if (core->GetPreallocatedBufferCountLocked() <= 0) {
+ core->FreeAllBuffersLocked();
+ }
+
+ core->override_max_buffer_count = buffer_count;
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+ listener = core->consumer_listener;
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBuffersReleased();
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
+ Status* return_flags) const {
+ bool try_again = true;
+
+ while (try_again) {
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
+ if (async && core->override_max_buffer_count) {
+ if (core->override_max_buffer_count < max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
+ return Status::BadValue;
+ }
+ }
+
+ // Free up any buffers that are in slots beyond the max buffer count
+ for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ ASSERT(slots[s].buffer_state == BufferState::Free);
+ if (slots[s].graphic_buffer != nullptr) {
+ core->FreeBufferLocked(s);
+ *return_flags |= Status::ReleaseAllBuffers;
+ }
+ }
+
+ // Look for a free buffer to give to the client
+ *found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ s32 dequeued_count{};
+ s32 acquired_count{};
+ for (s32 s{}; s < max_buffer_count; ++s) {
+ switch (slots[s].buffer_state) {
+ case BufferState::Dequeued:
+ ++dequeued_count;
+ break;
+ case BufferState::Acquired:
+ ++acquired_count;
+ break;
+ case BufferState::Free:
+ // We return the oldest of the free buffers to avoid stalling the producer if
+ // possible, since the consumer may still have pending reads of in-flight buffers
+ if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ slots[s].frame_number < slots[*found].frame_number) {
+ *found = s;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
+ // count
+ if (!core->override_max_buffer_count && dequeued_count) {
+ LOG_ERROR(Service_NVFlinger,
+ "can't dequeue multiple buffers without setting the buffer count");
+ return Status::InvalidOperation;
+ }
+
+ // See whether a buffer has been queued since the last SetBufferCount so we know whether to
+ // perform the min undequeued buffers check below
+ if (core->buffer_has_been_queued) {
+ // Make sure the producer is not trying to dequeue more buffers than allowed
+ const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
+ const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
+ if (new_undequeued_count < min_undequeued_count) {
+ LOG_ERROR(Service_NVFlinger,
+ "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
+ min_undequeued_count, dequeued_count, new_undequeued_count);
+ return Status::InvalidOperation;
+ }
+ }
+
+ // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
+ // but we have many buffers in the queue. This can cause us to run out of memory if we
+ // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
+ const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
+ if (too_many_buffers) {
+ LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
+ }
+
+ // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
+ // buffer to be acquired or released, or for the max buffer count to change.
+ try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
+ if (try_again) {
+ // Return an error if we're in non-blocking mode (producer and consumer are controlled
+ // by the application).
+ if (core->dequeue_buffer_cannot_block &&
+ (acquired_count <= core->max_acquired_buffer_count)) {
+ return Status::WouldBlock;
+ }
+
+ if (!core->WaitForDequeueCondition()) {
+ // We are no longer running
+ return Status::NoError;
+ }
+ }
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
+ u32 height, PixelFormat format, u32 usage) {
+ LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
+ width, height, format, usage);
+
+ if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
+ LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
+ return Status::BadValue;
+ }
+
+ Status return_flags = Status::NoError;
+ bool attached_by_consumer = false;
+ {
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (format == PixelFormat::NoFormat) {
+ format = core->default_buffer_format;
+ }
+
+ // Enable the usage bits the consumer requested
+ usage |= core->consumer_usage_bit;
+
+ s32 found{};
+ Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
+ if (status != Status::NoError) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ LOG_ERROR(Service_NVFlinger, "no available buffer slots");
+ return Status::Busy;
+ }
+
+ *out_slot = found;
+
+ attached_by_consumer = slots[found].attached_by_consumer;
+
+ const bool use_default_size = !width && !height;
+ if (use_default_size) {
+ width = core->default_width;
+ height = core->default_height;
+ }
+
+ slots[found].buffer_state = BufferState::Dequeued;
+
+ const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
+ if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
+ (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
+ slots[found].acquire_called = false;
+ slots[found].graphic_buffer = nullptr;
+ slots[found].request_buffer_called = false;
+ slots[found].fence = Fence::NoFence();
+
+ return_flags |= Status::BufferNeedsReallocation;
+ }
+
+ *out_fence = slots[found].fence;
+ slots[found].fence = Fence::NoFence();
+ }
+
+ if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
+ LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
+
+ auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
+ if (graphic_buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
+ return Status::NoMemory;
+ }
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ slots[*out_slot].frame_number = UINT32_MAX;
+ slots[*out_slot].graphic_buffer = graphic_buffer;
+ }
+ }
+
+ if (attached_by_consumer) {
+ return_flags |= Status::BufferNeedsReallocation;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
+ slots[*out_slot].frame_number, return_flags);
+
+ return return_flags;
+}
+
+Status BufferQueueProducer::DetachBuffer(s32 slot) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return Status::BadValue;
+ } else if (!slots[slot].request_buffer_called) {
+ LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
+ return Status::BadValue;
+ }
+
+ core->FreeBufferLocked(slot);
+ core->SignalDequeueCondition();
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
+ Fence* out_fence) {
+ if (out_buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
+ return Status::BadValue;
+ } else if (out_fence == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ // Find the oldest valid slot
+ int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ slots[s].frame_number < slots[found].frame_number) {
+ found = s;
+ }
+ }
+ }
+
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ return Status::NoMemory;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
+
+ *out_buffer = slots[found].graphic_buffer;
+ *out_fence = slots[found].fence;
+
+ core->FreeBufferLocked(found);
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::AttachBuffer(s32* out_slot,
+ const std::shared_ptr<GraphicBuffer>& buffer) {
+ if (out_slot == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
+ return Status::BadValue;
+ } else if (buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ Status return_flags = Status::NoError;
+ s32 found{};
+
+ const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
+ if (status != Status::NoError) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ LOG_ERROR(Service_NVFlinger, "No available buffer slots");
+ return Status::Busy;
+ }
+
+ *out_slot = found;
+
+ LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
+
+ slots[*out_slot].graphic_buffer = buffer;
+ slots[*out_slot].buffer_state = BufferState::Dequeued;
+ slots[*out_slot].fence = Fence::NoFence();
+ slots[*out_slot].request_buffer_called = true;
+
+ return return_flags;
+}
+
+Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) {
+ s64 timestamp{};
+ bool is_auto_timestamp{};
+ Common::Rectangle<s32> crop;
+ NativeWindowScalingMode scaling_mode{};
+ NativeWindowTransform transform;
+ u32 sticky_transform_{};
+ bool async{};
+ s32 swap_interval{};
+ Fence fence{};
+
+ input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
+ &sticky_transform_, &async, &swap_interval, &fence);
+
+ switch (scaling_mode) {
+ case NativeWindowScalingMode::Freeze:
+ case NativeWindowScalingMode::ScaleToWindow:
+ case NativeWindowScalingMode::ScaleCrop:
+ case NativeWindowScalingMode::NoScaleCrop:
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
+ return Status::BadValue;
+ }
+
+ std::shared_ptr<IConsumerListener> frame_available_listener;
+ std::shared_ptr<IConsumerListener> frame_replaced_listener;
+ s32 callback_ticket{};
+ BufferItem item;
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
+ if (async && core->override_max_buffer_count) {
+ if (core->override_max_buffer_count < max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
+ "buffer count override");
+ return Status::BadValue;
+ }
+ }
+
+ if (slot < 0 || slot >= max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ max_buffer_count);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger,
+ "slot {} is not owned by the producer "
+ "(state = {})",
+ slot, slots[slot].buffer_state);
+ return Status::BadValue;
+ } else if (!slots[slot].request_buffer_called) {
+ LOG_ERROR(Service_NVFlinger,
+ "slot {} was queued without requesting "
+ "a buffer",
+ slot);
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger,
+ "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
+ core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
+ crop.Bottom(), transform, scaling_mode);
+
+ const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
+ Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
+ Common::Rectangle<s32> cropped_rect;
+ [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
+
+ if (cropped_rect != crop) {
+ LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
+ slot);
+ return Status::BadValue;
+ }
+
+ slots[slot].fence = fence;
+ slots[slot].buffer_state = BufferState::Queued;
+ ++core->frame_counter;
+ slots[slot].frame_number = core->frame_counter;
+
+ item.acquire_called = slots[slot].acquire_called;
+ item.graphic_buffer = slots[slot].graphic_buffer;
+ item.crop = crop;
+ item.transform = transform & ~NativeWindowTransform::InverseDisplay;
+ item.transform_to_display_inverse =
+ (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
+ item.scaling_mode = static_cast<u32>(scaling_mode);
+ item.timestamp = timestamp;
+ item.is_auto_timestamp = is_auto_timestamp;
+ item.frame_number = core->frame_counter;
+ item.slot = slot;
+ item.fence = fence;
+ item.is_droppable = core->dequeue_buffer_cannot_block || async;
+ item.swap_interval = swap_interval;
+
+ sticky_transform = sticky_transform_;
+
+ if (core->queue.empty()) {
+ // When the queue is empty, we can simply queue this buffer
+ core->queue.push_back(item);
+ frame_available_listener = core->consumer_listener;
+ } else {
+ // When the queue is not empty, we need to look at the front buffer
+ // state to see if we need to replace it
+ auto front(core->queue.begin());
+
+ if (front->is_droppable) {
+ // If the front queued buffer is still being tracked, we first
+ // mark it as freed
+ if (core->StillTracking(*front)) {
+ slots[front->slot].buffer_state = BufferState::Free;
+ // Reset the frame number of the freed buffer so that it is the first in line to
+ // be dequeued again
+ slots[front->slot].frame_number = 0;
+ }
+ // Overwrite the droppable buffer with the incoming one
+ *front = item;
+ frame_replaced_listener = core->consumer_listener;
+ } else {
+ core->queue.push_back(item);
+ frame_available_listener = core->consumer_listener;
+ }
+ }
+
+ core->buffer_has_been_queued = true;
+ core->SignalDequeueCondition();
+ output->Inflate(core->default_width, core->default_height, core->transform_hint,
+ static_cast<u32>(core->queue.size()));
+
+ // Take a ticket for the callback functions
+ callback_ticket = next_callback_ticket++;
+ }
+
+ // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
+ // consumer shouldn't need it
+ item.graphic_buffer.reset();
+ item.slot = BufferItem::INVALID_BUFFER_SLOT;
+
+ // Call back without the main BufferQueue lock held, but with the callback lock held so we can
+ // ensure that callbacks occur in order
+ {
+ std::scoped_lock lock{callback_mutex};
+ while (callback_ticket != current_callback_ticket) {
+ callback_condition.wait(callback_mutex);
+ }
+
+ if (frame_available_listener != nullptr) {
+ frame_available_listener->OnFrameAvailable(item);
+ } else if (frame_replaced_listener != nullptr) {
+ frame_replaced_listener->OnFrameReplaced(item);
+ }
+
+ ++current_callback_ticket;
+ callback_condition.notify_all();
+ }
+
+ return Status::NoError;
+}
+
+void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return;
+ }
+
+ slots[slot].buffer_state = BufferState::Free;
+ slots[slot].frame_number = 0;
+ slots[slot].fence = fence;
+
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+}
+
+Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
+ std::scoped_lock lock{core->mutex};
+
+ if (out_value == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
+ return Status::BadValue;
+ }
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ u32 value{};
+ switch (what) {
+ case NativeWindow::Width:
+ value = core->default_width;
+ break;
+ case NativeWindow::Height:
+ value = core->default_height;
+ break;
+ case NativeWindow::Format:
+ value = static_cast<u32>(core->default_buffer_format);
+ break;
+ case NativeWindow::MinUndequeedBuffers:
+ value = core->GetMinUndequeuedBufferCountLocked(false);
+ break;
+ case NativeWindow::StickyTransform:
+ value = sticky_transform;
+ break;
+ case NativeWindow::ConsumerRunningBehind:
+ value = (core->queue.size() > 1);
+ break;
+ case NativeWindow::ConsumerUsageBits:
+ value = core->consumer_usage_bit;
+ break;
+ default:
+ UNREACHABLE();
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
+
+ *out_value = static_cast<s32>(value);
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
+ NativeWindowApi api, bool producer_controlled_by_app,
+ QueueBufferOutput* output) {
+ std::scoped_lock lock{core->mutex};
+
+ LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
+ producer_controlled_by_app);
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (core->consumer_listener == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
+ return Status::NoInit;
+ }
+
+ if (output == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "output was nullptr");
+ return Status::BadValue;
+ }
+
+ if (core->connected_api != NativeWindowApi::NoConnectedApi) {
+ LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
+ api);
+ return Status::BadValue;
+ }
+
+ Status status = Status::NoError;
+ switch (api) {
+ case NativeWindowApi::Egl:
+ case NativeWindowApi::Cpu:
+ case NativeWindowApi::Media:
+ case NativeWindowApi::Camera:
+ core->connected_api = api;
+ output->Inflate(core->default_width, core->default_height, core->transform_hint,
+ static_cast<u32>(core->queue.size()));
+ core->connected_producer_listener = listener;
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
+ status = Status::BadValue;
+ break;
+ }
+
+ core->buffer_has_been_queued = false;
+ core->dequeue_buffer_cannot_block =
+ core->consumer_controlled_by_app && producer_controlled_by_app;
+
+ return status;
+}
+
+Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
+ LOG_DEBUG(Service_NVFlinger, "api = {}", api);
+
+ Status status = Status::NoError;
+ std::shared_ptr<IConsumerListener> listener;
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ // Disconnecting after the surface has been abandoned is a no-op.
+ return Status::NoError;
+ }
+
+ switch (api) {
+ case NativeWindowApi::Egl:
+ case NativeWindowApi::Cpu:
+ case NativeWindowApi::Media:
+ case NativeWindowApi::Camera:
+ if (core->connected_api == api) {
+ core->FreeAllBuffersLocked();
+ core->connected_producer_listener = nullptr;
+ core->connected_api = NativeWindowApi::NoConnectedApi;
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+ listener = core->consumer_listener;
+ } else {
+ LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
+ core->connected_api, api);
+ status = Status::BadValue;
+ }
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
+ status = Status::BadValue;
+ break;
+ }
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBuffersReleased();
+ }
+
+ return status;
+}
+
+Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
+ const std::shared_ptr<GraphicBuffer>& buffer) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+
+ slots[slot] = {};
+ slots[slot].graphic_buffer = buffer;
+ slots[slot].frame_number = 0;
+
+ // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
+ // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
+ if (buffer) {
+ slots[slot].is_preallocated = true;
+
+ core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
+ core->default_width = buffer->Width();
+ core->default_height = buffer->Height();
+ core->default_buffer_format = buffer->Format();
+ }
+
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+
+ return Status::NoError;
+}
+
+void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
+ Status status{Status::NoError};
+ Parcel parcel_in{ctx.ReadBuffer()};
+ Parcel parcel_out{};
+
+ switch (code) {
+ case TransactionId::Connect: {
+ const auto enable_listener = parcel_in.Read<bool>();
+ const auto api = parcel_in.Read<NativeWindowApi>();
+ const auto producer_controlled_by_app = parcel_in.Read<bool>();
+
+ UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
+
+ std::shared_ptr<IProducerListener> listener;
+ QueueBufferOutput output{};
+
+ status = Connect(listener, api, producer_controlled_by_app, &output);
+
+ parcel_out.Write(output);
+ break;
+ }
+ case TransactionId::SetPreallocatedBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+ const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
+
+ status = SetPreallocatedBuffer(slot, buffer);
+ break;
+ }
+ case TransactionId::DequeueBuffer: {
+ const auto is_async = parcel_in.Read<bool>();
+ const auto width = parcel_in.Read<u32>();
+ const auto height = parcel_in.Read<u32>();
+ const auto pixel_format = parcel_in.Read<PixelFormat>();
+ const auto usage = parcel_in.Read<u32>();
+
+ s32 slot{};
+ Fence fence{};
+
+ status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
+
+ parcel_out.Write(slot);
+ parcel_out.WriteObject(&fence);
+ break;
+ }
+ case TransactionId::RequestBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ std::shared_ptr<GraphicBuffer> buf;
+
+ status = RequestBuffer(slot, &buf);
+
+ parcel_out.WriteObject(buf);
+ break;
+ }
+ case TransactionId::QueueBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ QueueBufferInput input{parcel_in};
+ QueueBufferOutput output;
+
+ status = QueueBuffer(slot, input, &output);
+
+ parcel_out.Write(output);
+ break;
+ }
+ case TransactionId::Query: {
+ const auto what = parcel_in.Read<NativeWindow>();
+
+ s32 value{};
+
+ status = Query(what, &value);
+
+ parcel_out.Write(value);
+ break;
+ }
+ case TransactionId::CancelBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+ const auto fence = parcel_in.ReadFlattened<Fence>();
+
+ CancelBuffer(slot, fence);
+ break;
+ }
+ case TransactionId::Disconnect: {
+ const auto api = parcel_in.Read<NativeWindowApi>();
+
+ status = Disconnect(api);
+ break;
+ }
+ case TransactionId::DetachBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ status = DetachBuffer(slot);
+ break;
+ }
+ case TransactionId::SetBufferCount: {
+ const auto buffer_count = parcel_in.Read<s32>();
+
+ status = SetBufferCount(buffer_count);
+ break;
+ }
+ case TransactionId::GetBufferHistory:
+ LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
+ break;
+ default:
+ ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
+ break;
+ }
+
+ parcel_out.Write(status);
+
+ ctx.WriteBuffer(parcel_out.Serialize());
+}
+
+Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
+ return buffer_wait_event->GetReadableEvent();
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
+
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/common_funcs.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvflinger/binder.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/buffer_slot.h"
+#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+#include "core/hle/service/nvflinger/status.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Kernel {
+class KernelCore;
+class KEvent;
+class KReadableEvent;
+class KWritableEvent;
+} // namespace Kernel
+
+namespace Service::KernelHelpers {
+class ServiceContext;
+} // namespace Service::KernelHelpers
+
+namespace Service::android {
+
+class BufferQueueCore;
+class IProducerListener;
+
+class BufferQueueProducer final : public IBinder {
+public:
+ explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
+ std::shared_ptr<BufferQueueCore> buffer_queue_core_);
+ ~BufferQueueProducer();
+
+ void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
+
+ Kernel::KReadableEvent& GetNativeHandle() override;
+
+public:
+ Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
+ Status SetBufferCount(s32 buffer_count);
+ Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
+ u32 height, PixelFormat format, u32 usage);
+ Status DetachBuffer(s32 slot);
+ Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
+ Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
+ Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
+ void CancelBuffer(s32 slot, const Fence& fence);
+ Status Query(NativeWindow what, s32* out_value);
+ Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
+ bool producer_controlled_by_app, QueueBufferOutput* output);
+
+ Status Disconnect(NativeWindowApi api);
+ Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
+
+private:
+ BufferQueueProducer(const BufferQueueProducer&) = delete;
+
+ Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
+
+ Kernel::KEvent* buffer_wait_event{};
+ Service::KernelHelpers::ServiceContext& service_context;
+
+ std::shared_ptr<BufferQueueCore> core;
+ BufferQueueDefs::SlotsType& slots;
+ u32 sticky_transform{};
+ std::mutex callback_mutex;
+ s32 next_callback_ticket{};
+ s32 current_callback_ticket{};
+ std::condition_variable_any callback_condition;
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+
+namespace Service::android {
+
+class GraphicBuffer;
+
+enum class BufferState : u32 {
+ Free = 0,
+ Dequeued = 1,
+ Queued = 2,
+ Acquired = 3,
+};
+
+struct BufferSlot final {
+ constexpr BufferSlot() = default;
+
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ BufferState buffer_state{BufferState::Free};
+ bool request_buffer_called{};
+ u64 frame_number{};
+ Fence fence;
+ bool acquire_called{};
+ bool needs_cleanup_on_release{};
+ bool attached_by_consumer{};
+ bool is_preallocated{};
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class BufferTransformFlags : u32 {
+ /// No transform flags are set
+ Unset = 0x00,
+ /// Flip source image horizontally (around the vertical axis)
+ FlipH = 0x01,
+ /// Flip source image vertically (around the horizontal axis)
+ FlipV = 0x02,
+ /// Rotate source image 90 degrees clockwise
+ Rotate90 = 0x04,
+ /// Rotate source image 180 degrees
+ Rotate180 = 0x03,
+ /// Rotate source image 270 degrees clockwise
+ Rotate270 = 0x07,
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2010 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/consumer_base.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
+
+namespace Service::android {
+
+ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
+ : consumer{std::move(consumer_)} {}
+
+ConsumerBase::~ConsumerBase() {
+ std::scoped_lock lock{mutex};
+
+ ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
+}
+
+void ConsumerBase::Connect(bool controlled_by_app) {
+ consumer->Connect(shared_from_this(), controlled_by_app);
+}
+
+void ConsumerBase::FreeBufferLocked(s32 slot_index) {
+ LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
+
+ slots[slot_index].graphic_buffer = nullptr;
+ slots[slot_index].fence = Fence::NoFence();
+ slots[slot_index].frame_number = 0;
+}
+
+void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
+ LOG_DEBUG(Service_NVFlinger, "called");
+}
+
+void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
+ LOG_DEBUG(Service_NVFlinger, "called");
+}
+
+void ConsumerBase::OnBuffersReleased() {
+ std::scoped_lock lock{mutex};
+
+ LOG_DEBUG(Service_NVFlinger, "called");
+
+ if (is_abandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
+ u64 mask = 0;
+ consumer->GetReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ FreeBufferLocked(i);
+ }
+ }
+}
+
+void ConsumerBase::OnSidebandStreamChanged() {}
+
+Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
+ Status err = consumer->AcquireBuffer(item, present_when);
+ if (err != Status::NoError) {
+ return err;
+ }
+
+ if (item->graphic_buffer != nullptr) {
+ slots[item->slot].graphic_buffer = item->graphic_buffer;
+ }
+
+ slots[item->slot].frame_number = item->frame_number;
+ slots[item->slot].fence = item->fence;
+
+ LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
+
+ return Status::NoError;
+}
+
+Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer,
+ const Fence& fence) {
+ LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
+
+ // If consumer no longer tracks this graphic_buffer, we can safely
+ // drop this fence, as it will never be received by the producer.
+
+ if (!StillTracking(slot, graphic_buffer)) {
+ return Status::NoError;
+ }
+
+ slots[slot].fence = fence;
+
+ return Status::NoError;
+}
+
+Status ConsumerBase::ReleaseBufferLocked(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer) {
+ // If consumer no longer tracks this graphic_buffer (we received a new
+ // buffer on the same slot), the buffer producer is definitely no longer
+ // tracking it.
+
+ if (!StillTracking(slot, graphic_buffer)) {
+ return Status::NoError;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
+ Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
+ if (err == Status::StaleBufferSlot) {
+ FreeBufferLocked(slot);
+ }
+
+ slots[slot].fence = Fence::NoFence();
+
+ return err;
+}
+
+bool ConsumerBase::StillTracking(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ return false;
+ }
+
+ return (slots[slot].graphic_buffer != nullptr &&
+ slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2010 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
+
+#pragma once
+
+#include <array>
+#include <chrono>
+#include <memory>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/consumer_listener.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::android {
+
+class BufferItem;
+class BufferQueueConsumer;
+
+class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
+public:
+ void Connect(bool controlled_by_app);
+
+protected:
+ explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
+ virtual ~ConsumerBase();
+
+ virtual void OnFrameAvailable(const BufferItem& item) override;
+ virtual void OnFrameReplaced(const BufferItem& item) override;
+ virtual void OnBuffersReleased() override;
+ virtual void OnSidebandStreamChanged() override;
+
+ void FreeBufferLocked(s32 slot_index);
+ Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
+ Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
+ bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
+ Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
+ const Fence& fence);
+
+ struct Slot final {
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ Fence fence;
+ u64 frame_number{};
+ };
+
+protected:
+ std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
+
+ bool is_abandoned{};
+
+ std::unique_ptr<BufferQueueConsumer> consumer;
+
+ mutable std::mutex mutex;
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
+
+#pragma once
+
+namespace Service::android {
+
+class BufferItem;
+
+/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
+/// that the consumer may wish to react to.
+class IConsumerListener {
+public:
+ IConsumerListener() = default;
+ virtual ~IConsumerListener() = default;
+
+ virtual void OnFrameAvailable(const BufferItem& item) = 0;
+ virtual void OnFrameReplaced(const BufferItem& item) = 0;
+ virtual void OnBuffersReleased() = 0;
+ virtual void OnSidebandStreamChanged() = 0;
+};
+
+}; // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2010 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
+
+#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
+#include "core/hle/service/nvflinger/parcel.h"
+
+namespace Service::android {
+
+QueueBufferInput::QueueBufferInput(Parcel& parcel) {
+ parcel.ReadFlattened(*this);
+}
+
+QueueBufferOutput::QueueBufferOutput() = default;
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2010 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class Parcel;
+
+#pragma pack(push, 1)
+struct QueueBufferInput final {
+ explicit QueueBufferInput(Parcel& parcel);
+
+ void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
+ NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
+ u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
+ *timestamp_ = timestamp;
+ *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
+ *crop_ = crop;
+ *scaling_mode_ = scaling_mode;
+ *transform_ = transform;
+ *sticky_transform_ = sticky_transform;
+ *async_ = static_cast<bool>(async);
+ *swap_interval_ = swap_interval;
+ *fence_ = fence;
+ }
+
+private:
+ s64 timestamp{};
+ s32 is_auto_timestamp{};
+ Common::Rectangle<s32> crop{};
+ NativeWindowScalingMode scaling_mode{};
+ NativeWindowTransform transform{};
+ u32 sticky_transform{};
+ s32 async{};
+ s32 swap_interval{};
+ Fence fence{};
+};
+#pragma pack(pop)
+static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
+
+struct QueueBufferOutput final {
+ QueueBufferOutput();
+
+ void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
+ *width_ = width;
+ *height_ = height;
+ *transform_hint_ = transform_hint;
+ *num_pending_buffers_ = num_pending_buffers;
+ }
+
+ void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
+ width = width_;
+ height = height_;
+ transform_hint = transform_hint_;
+ num_pending_buffers = num_pending_buffers_;
+ }
+
+private:
+ u32 width{};
+ u32 height{};
+ u32 transform_hint{};
+ u32 num_pending_buffers{};
+};
+static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
+
+namespace Service::NVFlinger {
+
+HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
+ : service_context(system_, "HosBinderDriverServer") {}
+
+HosBinderDriverServer::~HosBinderDriverServer() {}
+
+u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
+ std::scoped_lock lk{lock};
+
+ last_id++;
+
+ producers[last_id] = std::move(binder);
+
+ return last_id;
+}
+
+android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
+ std::scoped_lock lk{lock};
+
+ if (auto search = producers.find(id); search != producers.end()) {
+ return search->second.get();
+ }
+
+ return {};
+}
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvflinger/binder.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::NVFlinger {
+
+class HosBinderDriverServer final {
+public:
+ explicit HosBinderDriverServer(Core::System& system_);
+ ~HosBinderDriverServer();
+
+ u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
+
+ android::IBinder* TryGetProducer(u64 id);
+
+private:
+ KernelHelpers::ServiceContext service_context;
+
+ std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
+ std::mutex lock;
+ u64 last_id{};
+};
+
+} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
#include <algorithm>
#include <optional>
@@ -16,8 +15,11 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
#include "video_core/gpu.h"
@@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
}
}
-NVFlinger::NVFlinger(Core::System& system_)
- : system(system_), service_context(system_, "nvflinger") {
- displays.emplace_back(0, "Default", service_context, system);
- displays.emplace_back(1, "External", service_context, system);
- displays.emplace_back(2, "Edid", service_context, system);
- displays.emplace_back(3, "Internal", service_context, system);
- displays.emplace_back(4, "Null", service_context, system);
+NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
+ : system(system_), service_context(system_, "nvflinger"),
+ hos_binder_driver_server(hos_binder_driver_server_) {
+ displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
guard = std::make_shared<std::mutex>();
// Schedule the screen composition events
@@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_)
}
NVFlinger::~NVFlinger() {
- for (auto& buffer_queue : buffer_queues) {
- buffer_queue->Disconnect();
- }
if (!system.IsMulticore()) {
system.CoreTiming().UnscheduleEvent(composition_event, 0);
}
+
+ for (auto& display : displays) {
+ for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
+ display.GetLayer(layer).Core().NotifyShutdown();
+ }
+ }
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -98,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto lock_guard = Lock();
- LOG_DEBUG(Service, "Opening \"{}\" display", name);
+ LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
const auto itr =
std::find_if(displays.begin(), displays.end(),
@@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
}
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
- const u32 buffer_queue_id = next_buffer_queue_id++;
- buffer_queues.emplace_back(
- std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
- display.CreateLayer(layer_id, *buffer_queues.back());
+ const auto buffer_id = next_buffer_queue_id++;
+ display.CreateLayer(layer_id, buffer_id);
}
void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
return std::nullopt;
}
- return layer->GetBufferQueue().GetId();
+ return layer->GetBinderId();
}
Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
@@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
return &display->GetVSyncEvent();
}
-BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
- const auto lock_guard = Lock();
- const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
- [id](const auto& queue) { return queue->GetId() == id; });
-
- if (itr == buffer_queues.end()) {
- return nullptr;
- }
-
- return itr->get();
-}
-
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
const auto itr =
std::find_if(displays.begin(), displays.end(),
@@ -227,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
auto* layer = display->FindLayer(layer_id);
if (layer == nullptr) {
- LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id);
+ LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
CreateLayerAtId(*display, layer_id);
return display->FindLayer(layer_id);
}
@@ -246,23 +238,22 @@ void NVFlinger::Compose() {
// TODO(Subv): Support more than 1 layer.
VI::Layer& layer = display.GetLayer(0);
- auto& buffer_queue = layer.GetBufferQueue();
- // Search for a queued buffer and acquire it
- auto buffer = buffer_queue.AcquireBuffer();
+ android::BufferItem buffer{};
+ const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
- if (!buffer) {
+ if (status != android::Status::NoError) {
continue;
}
- const auto& igbp_buffer = buffer->get().igbp_buffer;
+ const auto& igbp_buffer = *buffer.graphic_buffer;
if (!system.IsPoweredOn()) {
return; // We are likely shutting down
}
auto& gpu = system.GPU();
- const auto& multi_fence = buffer->get().multi_fence;
+ const auto& multi_fence = buffer.fence;
guard->unlock();
for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
const auto& fence = multi_fence.fences[fence_id];
@@ -278,12 +269,18 @@ void NVFlinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
ASSERT(nvdisp);
- nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format,
- igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
- buffer->get().transform, buffer->get().crop_rect);
+ Common::Rectangle<int> crop_rect{
+ static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
+ static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
+
+ nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
+ igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
+ static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect);
+
+ swap_interval = buffer.swap_interval;
- swap_interval = buffer->get().swap_interval;
- buffer_queue.ReleaseBuffer(buffer->get().slot);
+ auto fence = android::Fence::NoFence();
+ layer.GetConsumer().ReleaseBuffer(buffer, fence);
}
}
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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
#pragma once
@@ -37,13 +36,16 @@ class Display;
class Layer;
} // namespace Service::VI
-namespace Service::NVFlinger {
+namespace Service::android {
+class BufferQueueCore;
+class BufferQueueProducer;
+} // namespace Service::android
-class BufferQueue;
+namespace Service::NVFlinger {
class NVFlinger final {
public:
- explicit NVFlinger(Core::System& system_);
+ explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
~NVFlinger();
/// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -72,9 +74,6 @@ public:
/// If an invalid display ID is provided, then nullptr is returned.
[[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
- /// Obtains a buffer queue identified by the ID.
- [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
-
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
@@ -82,6 +81,12 @@ public:
[[nodiscard]] s64 GetNextTicks() const;
private:
+ struct Layer {
+ std::unique_ptr<android::BufferQueueCore> core;
+ std::unique_ptr<android::BufferQueueProducer> producer;
+ };
+
+private:
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
return std::unique_lock{*guard};
}
@@ -111,7 +116,6 @@ private:
std::shared_ptr<Nvidia::Module> nvdrv;
std::list<VI::Display> displays;
- std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
u64 next_layer_id = 1;
@@ -131,6 +135,8 @@ private:
std::jthread vsync_thread;
KernelHelpers::ServiceContext service_context;
+
+ HosBinderDriverServer& hos_binder_driver_server;
};
} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+class Parcel final {
+public:
+ static constexpr std::size_t DefaultBufferSize = 0x40;
+
+ Parcel() : buffer(DefaultBufferSize) {}
+
+ template <typename T>
+ explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
+ Write(out_data);
+ }
+
+ explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
+ DeserializeHeader();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
+ }
+
+ template <typename T>
+ void Read(T& val) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+ ASSERT(read_index + sizeof(T) <= buffer.size());
+
+ std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ read_index += sizeof(T);
+ read_index = Common::AlignUp(read_index, 4);
+ }
+
+ template <typename T>
+ T Read() {
+ T val;
+ Read(val);
+ return val;
+ }
+
+ template <typename T>
+ void ReadFlattened(T& val) {
+ const auto flattened_size = Read<s64>();
+ ASSERT(sizeof(T) == flattened_size);
+ Read(val);
+ }
+
+ template <typename T>
+ T ReadFlattened() {
+ T val;
+ ReadFlattened(val);
+ return val;
+ }
+
+ template <typename T>
+ T ReadUnaligned() {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+ ASSERT(read_index + sizeof(T) <= buffer.size());
+
+ T val;
+ std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ read_index += sizeof(T);
+ return val;
+ }
+
+ template <typename T>
+ const std::shared_ptr<T> ReadObject() {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ const auto is_valid{Read<bool>()};
+
+ if (is_valid) {
+ auto result = std::make_shared<T>();
+ ReadFlattened(*result);
+ return result;
+ }
+
+ return {};
+ }
+
+ std::u16string ReadInterfaceToken() {
+ [[maybe_unused]] const u32 unknown = Read<u32>();
+ const u32 length = Read<u32>();
+
+ std::u16string token;
+ token.reserve(length + 1);
+
+ for (u32 ch = 0; ch < length + 1; ++ch) {
+ token.push_back(ReadUnaligned<u16>());
+ }
+
+ read_index = Common::AlignUp(read_index, 4);
+
+ return token;
+ }
+
+ template <typename T>
+ void Write(const T& val) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ if (buffer.size() < write_index + sizeof(T)) {
+ buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
+ }
+
+ std::memcpy(buffer.data() + write_index, &val, sizeof(T));
+ write_index += sizeof(T);
+ write_index = Common::AlignUp(write_index, 4);
+ }
+
+ template <typename T>
+ void WriteObject(const T* ptr) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ if (!ptr) {
+ Write<u32>(0);
+ return;
+ }
+
+ Write<u32>(1);
+ Write<s64>(sizeof(T));
+ Write(*ptr);
+ }
+
+ template <typename T>
+ void WriteObject(const std::shared_ptr<T> ptr) {
+ WriteObject(ptr.get());
+ }
+
+ void DeserializeHeader() {
+ ASSERT(buffer.size() > sizeof(Header));
+
+ Header header{};
+ std::memcpy(&header, buffer.data(), sizeof(Header));
+
+ read_index = header.data_offset;
+ }
+
+ std::vector<u8> Serialize() const {
+ ASSERT(read_index == 0);
+
+ Header header{};
+ header.data_size = static_cast<u32>(write_index - sizeof(Header));
+ header.data_offset = sizeof(Header);
+ header.objects_size = 4;
+ header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
+ std::memcpy(buffer.data(), &header, sizeof(Header));
+
+ return buffer;
+ }
+
+private:
+ struct Header {
+ u32 data_size;
+ u32 data_offset;
+ u32 objects_size;
+ u32 objects_offset;
+ };
+ static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
+
+ mutable std::vector<u8> buffer;
+ std::size_t read_index = 0;
+ std::size_t write_index = sizeof(Header);
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class PixelFormat : u32 {
+ NoFormat = 0,
+ Rgba8888 = 1,
+ Rgbx8888 = 2,
+ Rgb888 = 3,
+ Rgb565 = 4,
+ Bgra8888 = 5,
+ Rgba5551 = 6,
+ Rgba4444 = 7,
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2014 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
+
+#pragma once
+
+namespace Service::android {
+
+class IProducerListener {
+public:
+ virtual void OnBufferReleased() = 0;
+};
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class Status : s32 {
+ None = 0,
+ NoError = 0,
+ StaleBufferSlot = 1,
+ NoBufferAvailable = 2,
+ PresentLater = 3,
+ WouldBlock = -11,
+ NoMemory = -12,
+ Busy = -16,
+ NoInit = -19,
+ BadValue = -22,
+ InvalidOperation = -37,
+ BufferNeedsReallocation = 1,
+ ReleaseAllBuffers = 2,
+};
+DECLARE_ENUM_FLAG_OPERATORS(Status);
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2012 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Service::android {
+
+class Fence {
+public:
+ constexpr Fence() = default;
+
+ static constexpr Fence NoFence() {
+ Fence fence;
+ fence.fences[0].id = -1;
+ return fence;
+ }
+
+public:
+ u32 num_fences{};
+ std::array<Service::Nvidia::NvFence, 4> fences{};
+};
+static_assert(sizeof(Fence) == 36, "Fence has wrong size");
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+// Copyright 2007 The Android Open Source Project
+// Parts of this implementation were base on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+
+namespace Service::android {
+
+class GraphicBuffer final {
+public:
+ constexpr GraphicBuffer() = default;
+
+ constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
+ : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
+ usage{static_cast<s32>(usage_)} {}
+
+ constexpr u32 Width() const {
+ return static_cast<u32>(width);
+ }
+
+ constexpr u32 Height() const {
+ return static_cast<u32>(height);
+ }
+
+ constexpr u32 Stride() const {
+ return static_cast<u32>(stride);
+ }
+
+ constexpr u32 Usage() const {
+ return static_cast<u32>(usage);
+ }
+
+ constexpr PixelFormat Format() const {
+ return format;
+ }
+
+ constexpr u32 BufferId() const {
+ return buffer_id;
+ }
+
+ constexpr PixelFormat ExternalFormat() const {
+ return external_format;
+ }
+
+ constexpr u32 Handle() const {
+ return handle;
+ }
+
+ constexpr u32 Offset() const {
+ return offset;
+ }
+
+ constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
+ u32 usage_) const {
+ if (static_cast<s32>(width_) != width) {
+ return true;
+ }
+
+ if (static_cast<s32>(height_) != height) {
+ return true;
+ }
+
+ if (format_ != format) {
+ return true;
+ }
+
+ if ((static_cast<u32>(usage) & usage_) != usage_) {
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ u32 magic{};
+ s32 width{};
+ s32 height{};
+ s32 stride{};
+ PixelFormat format{};
+ s32 usage{};
+ INSERT_PADDING_WORDS(1);
+ u32 index{};
+ INSERT_PADDING_WORDS(3);
+ u32 buffer_id{};
+ INSERT_PADDING_WORDS(6);
+ PixelFormat external_format{};
+ INSERT_PADDING_WORDS(10);
+ u32 handle{};
+ u32 offset{};
+ INSERT_PADDING_WORDS(60);
+};
+static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
+
+} // 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 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright 2021 yuzu Emulator Project
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+/// Attributes queryable with Query
+enum class NativeWindow : s32 {
+ Width = 0,
+ Height = 1,
+ Format = 2,
+ MinUndequeedBuffers = 3,
+ QueuesToWindowComposer = 4,
+ ConcreteType = 5,
+ DefaultWidth = 6,
+ DefaultHeight = 7,
+ TransformHint = 8,
+ ConsumerRunningBehind = 9,
+ ConsumerUsageBits = 10,
+ StickyTransform = 11,
+ DefaultDataSpace = 12,
+ BufferAge = 13,
+};
+
+/// Parameter for Connect/Disconnect
+enum class NativeWindowApi : s32 {
+ NoConnectedApi = 0,
+ Egl = 1,
+ Cpu = 2,
+ Media = 3,
+ Camera = 4,
+};
+
+/// Scaling mode parameter for QueueBuffer
+enum class NativeWindowScalingMode : s32 {
+ Freeze = 0,
+ ScaleToWindow = 1,
+ ScaleCrop = 2,
+ NoScaleCrop = 3,
+};
+
+/// Transform parameter for QueueBuffer
+enum class NativeWindowTransform : u32 {
+ None = 0x0,
+ InverseDisplay = 0x08,
+};
+DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
+
+} // 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 @@
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/jit/jit.h"
#include "core/hle/service/lbl/lbl.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldr/ldr.h"
@@ -49,6 +50,7 @@
#include "core/hle/service/npns/npns.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/pcie/pcie.h"
@@ -90,8 +92,9 @@ namespace Service {
}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
- u32 max_sessions_, InvokerFn* handler_invoker_)
- : SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
+ ServiceThreadType thread_type, u32 max_sessions_,
+ InvokerFn* handler_invoker_)
+ : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -230,7 +233,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
/// Initialize Services
Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
- : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
+ : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
+ nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
@@ -259,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm, system);
HID::InstallInterfaces(*sm, system);
+ JIT::InstallInterfaces(*sm, system);
LBL::InstallInterfaces(*sm, system);
LDN::InstallInterfaces(*sm, system);
LDR::InstallInterfaces(*sm, system);
@@ -290,7 +295,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
SSL::InstallInterfaces(*sm, system);
Time::InstallInterfaces(system);
USB::InstallInterfaces(*sm, system);
- VI::InstallInterfaces(*sm, system, *nv_flinger);
+ VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
WLAN::InstallInterfaces(*sm, system);
}
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 @@
#include <string>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "core/hle/kernel/hle_ipc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,8 +32,9 @@ class FileSystemController;
}
namespace NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace NVFlinger
namespace SM {
class ServiceManager;
@@ -89,7 +89,7 @@ protected:
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
- [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
+ [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
return std::scoped_lock{lock_service};
}
@@ -113,7 +113,8 @@ private:
Kernel::HLERequestContext& ctx);
explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
- u32 max_sessions_, InvokerFn* handler_invoker_);
+ ServiceThreadType thread_type, u32 max_sessions_,
+ InvokerFn* handler_invoker_);
~ServiceFrameworkBase() override;
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -133,7 +134,7 @@ private:
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
- Common::SpinLock lock_service;
+ std::mutex lock_service;
};
/**
@@ -175,14 +176,17 @@ protected:
/**
* Initializes the handler with no functions installed.
*
- * @param system_ The system context to construct this service under.
+ * @param system_ The system context to construct this service under.
* @param service_name_ Name of the service.
- * @param max_sessions_ Maximum number of sessions that can be
- * connected to this service at the same time.
+ * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
+ * it creates a new thread for it, otherwise this uses the default thread.
+ * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
+ * same time.
*/
explicit ServiceFramework(Core::System& system_, const char* service_name_,
+ ServiceThreadType thread_type = ServiceThreadType::Default,
u32 max_sessions_ = ServerSessionCountMax)
- : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
+ : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>
@@ -236,6 +240,7 @@ public:
~Services();
private:
+ std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
};
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
}
auto* port = Kernel::KPort::Create(kernel);
+ SCOPE_EXIT({ port->Close(); });
+
port->Initialize(ServerSessionCountMax, false, name);
auto handler = it->second;
port->GetServerPort().SetSessionHandler(std::move(handler));
@@ -151,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
auto& port = port_result.Unwrap();
SCOPE_EXIT({ port->GetClientPort().Close(); });
- server_ports.emplace_back(&port->GetServerPort());
+ kernel.RegisterServerObject(&port->GetServerPort());
// Create a new session.
Kernel::KClientSession* session{};
@@ -204,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
}
SM::SM(ServiceManager& service_manager_, Core::System& system_)
- : ServiceFramework{system_, "sm:", 4},
+ : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
service_manager{service_manager_}, kernel{system_.Kernel()} {
RegisterHandlers({
{0, &SM::Initialize, "Initialize"},
@@ -222,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
});
}
-SM::~SM() {
- for (auto& server_port : server_ports) {
- server_port->Close();
- }
-}
+SM::~SM() = default;
} // 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;
class KClientSession;
class KernelCore;
class KPort;
-class KServerPort;
class SessionRequestHandler;
} // namespace Kernel
@@ -48,7 +47,6 @@ private:
ServiceManager& service_manager;
bool is_initialized{};
Kernel::KernelCore& kernel;
- std::vector<Kernel::KServerPort*> server_ports;
};
class 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 @@
#include <fmt/format.h>
#include "common/microprofile.h"
-#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
@@ -569,9 +568,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
- ASSERT(write_buffer.size() == sizeof(SockAddrIn));
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
- std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
+ const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
+ std::memcpy(write_buffer.data(), &guest_addr_in, length);
return {new_fd, Errno::SUCCESS};
}
@@ -690,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
+ case OptName::KEEPALIVE:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
@@ -838,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
rb.PushEnum(bsd_errno);
}
-BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
+BSD::BSD(Core::System& system_, const char* name)
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
// clang-format off
static const FunctionInfo functions[] = {
{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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "common/string_util.h"
+#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/sockets/sfdnsres.h"
+#include "core/memory.h"
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#elif YUZU_UNIX
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#ifndef EAI_NODATA
+#define EAI_NODATA EAI_NONAME
+#endif
+#endif
namespace Service::Sockets {
@@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
{9, nullptr, "CancelRequest"},
{10, nullptr, "GetHostByNameRequestWithOptions"},
{11, nullptr, "GetHostByAddrRequestWithOptions"},
- {12, nullptr, "GetAddrInfoRequestWithOptions"},
+ {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
{13, nullptr, "GetNameInfoRequestWithOptions"},
{14, nullptr, "ResolverSetOptionRequest"},
{15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
SFDNSRES::~SFDNSRES() = default;
-void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
+enum class NetDbError : s32 {
+ Internal = -1,
+ Success = 0,
+ HostNotFound = 1,
+ TryAgain = 2,
+ NoRecovery = 3,
+ NoData = 4,
+};
+
+static NetDbError AddrInfoErrorToNetDbError(s32 result) {
+ // Best effort guess to map errors
+ switch (result) {
+ case 0:
+ return NetDbError::Success;
+ case EAI_AGAIN:
+ return NetDbError::TryAgain;
+ case EAI_NODATA:
+ return NetDbError::NoData;
+ default:
+ return NetDbError::HostNotFound;
+ }
+}
+
+static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
+ std::string_view host) {
+ // Adapted from
+ // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
+ std::vector<u8> data;
+
+ auto* current = addrinfo;
+ while (current != nullptr) {
+ struct SerializedResponseHeader {
+ u32 magic;
+ s32 flags;
+ s32 family;
+ s32 socket_type;
+ s32 protocol;
+ u32 address_length;
+ };
+ static_assert(sizeof(SerializedResponseHeader) == 0x18,
+ "Response header size must be 0x18 bytes");
+
+ constexpr auto header_size = sizeof(SerializedResponseHeader);
+ const auto addr_size =
+ current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
+ const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
+
+ const auto last_size = data.size();
+ data.resize(last_size + header_size + addr_size + canonname_size);
+
+ // Header in network byte order
+ SerializedResponseHeader header{};
+
+ constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
+ header.magic = htonl(HEADER_MAGIC);
+ header.family = htonl(current->ai_family);
+ header.flags = htonl(current->ai_flags);
+ header.socket_type = htonl(current->ai_socktype);
+ header.protocol = htonl(current->ai_protocol);
+ header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
+
+ auto* header_ptr = data.data() + last_size;
+ std::memcpy(header_ptr, &header, header_size);
+
+ if (header.address_length == 0) {
+ std::memset(header_ptr + header_size, 0, 4);
+ } else {
+ switch (current->ai_family) {
+ case AF_INET: {
+ struct SockAddrIn {
+ s16 sin_family;
+ u16 sin_port;
+ u32 sin_addr;
+ u8 sin_zero[8];
+ };
+
+ SockAddrIn serialized_addr{};
+ const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
+ serialized_addr.sin_port = htons(addr.sin_port);
+ serialized_addr.sin_family = htons(addr.sin_family);
+ serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
+ std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
+
+ char addr_string_buf[64]{};
+ inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
+ LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
+ break;
+ }
+ case AF_INET6: {
+ struct SockAddrIn6 {
+ s16 sin6_family;
+ u16 sin6_port;
+ u32 sin6_flowinfo;
+ u8 sin6_addr[16];
+ u32 sin6_scope_id;
+ };
+
+ SockAddrIn6 serialized_addr{};
+ const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
+ serialized_addr.sin6_family = htons(addr.sin6_family);
+ serialized_addr.sin6_port = htons(addr.sin6_port);
+ serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
+ serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
+ std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
+ sizeof(SockAddrIn6::sin6_addr));
+ std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
+
+ char addr_string_buf[64]{};
+ inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
+ LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
+ break;
+ }
+ default:
+ std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
+ break;
+ }
+ }
+ if (current->ai_canonname) {
+ std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
+ } else {
+ *(header_ptr + header_size + addr_size) = 0;
+ }
+
+ current = current->ai_next;
+ }
+
+ // 4-byte sentinel value
+ data.push_back(0);
+ data.push_back(0);
+ data.push_back(0);
+ data.push_back(0);
+
+ return data;
+}
+
+static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
struct Parameters {
u8 use_nsd_resolve;
u32 unknown;
@@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
const auto parameters = rp.PopRaw<Parameters>();
LOG_WARNING(Service,
- "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
+ "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
- IPC::ResponseBuilder rb{ctx, 2};
+ const auto host_buffer = ctx.ReadBuffer(0);
+ const std::string host = Common::StringFromBuffer(host_buffer);
+
+ const auto service_buffer = ctx.ReadBuffer(1);
+ const std::string service = Common::StringFromBuffer(service_buffer);
+
+ addrinfo* addrinfo;
+ // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
+ s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
+
+ u32 data_size = 0;
+ if (result_code == 0 && addrinfo != nullptr) {
+ const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
+ data_size = static_cast<u32>(data.size());
+ freeaddrinfo(addrinfo);
+
+ ctx.WriteBuffer(data, 0);
+ }
+
+ return std::make_pair(data_size, result_code);
+}
+
+void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
+ auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
+ rb.Push(result_code); // errno
+ rb.Push(data_size); // serialized size
+}
+
+void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
+ // Additional options are ignored
+ auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
+ rb.Push(data_size); // serialized size
+ rb.Push(result_code); // errno
+ rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
+ rb.Push(0);
}
-} // namespace Service::Sockets
+} // 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:
private:
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
+ void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
};
} // 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 {
enum class OptName : u32 {
REUSEADDR = 0x4,
+ KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
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 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::VI {
-Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
- Core::System& system_)
- : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
+struct BufferQueue {
+ std::shared_ptr<android::BufferQueueCore> core;
+ std::unique_ptr<android::BufferQueueProducer> producer;
+ std::unique_ptr<android::BufferQueueConsumer> consumer;
+};
+
+static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) {
+ auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
+ return {buffer_queue_core,
+ std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core),
+ std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
+}
+
+Display::Display(u64 id, std::string name_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
+ KernelHelpers::ServiceContext& service_context_, Core::System& system_)
+ : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
+ service_context{service_context_} {
vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
}
@@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() {
vsync_event->GetWritableEvent().Signal();
}
-void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
- // TODO(Subv): Support more than 1 layer.
+void Display::CreateLayer(u64 layer_id, u32 binder_id) {
ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
- layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue));
+ auto [core, producer, consumer] = CreateBufferQueue(service_context);
+
+ auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
+ buffer_item_consumer->Connect(false);
+
+ layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
+ std::move(buffer_item_consumer)));
+
+ hos_binder_driver_server.RegisterProducer(std::move(producer));
}
void Display::CloseLayer(u64 layer_id) {
- std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; });
+ std::erase_if(layers,
+ [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
}
Layer* Display::FindLayer(u64 layer_id) {
const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) {
- return layer->GetID() == layer_id;
+ std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
+ return layer->GetLayerId() == layer_id;
});
if (itr == layers.end()) {
@@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) {
const Layer* Display::FindLayer(u64 layer_id) const {
const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) {
- return layer->GetID() == layer_id;
+ std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
+ return layer->GetLayerId() == layer_id;
});
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 {
class KEvent;
}
-namespace Service::NVFlinger {
-class BufferQueue;
+namespace Service::android {
+class BufferQueueProducer;
}
+
namespace Service::KernelHelpers {
class ServiceContext;
-} // namespace Service::KernelHelpers
+}
+
+namespace Service::NVFlinger {
+class HosBinderDriverServer;
+}
namespace Service::VI {
@@ -35,12 +40,13 @@ public:
/// Constructs a display with a given unique ID and name.
///
/// @param id The unique ID for this display.
+ /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
/// @param service_context_ The ServiceContext for the owning service.
/// @param name_ The name for this display.
/// @param system_ The global system instance.
///
- Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
- Core::System& system_);
+ Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
+ KernelHelpers::ServiceContext& service_context_, Core::System& system_);
~Display();
/// Gets the unique ID assigned to this display.
@@ -64,6 +70,10 @@ public:
/// Gets a layer for this display based off an index.
const Layer& GetLayer(std::size_t index) const;
+ std::size_t GetNumLayers() const {
+ return layers.size();
+ }
+
/// Gets the readable vsync event.
Kernel::KReadableEvent& GetVSyncEvent();
@@ -72,10 +82,10 @@ public:
/// Creates and adds a layer to this display with the given ID.
///
- /// @param layer_id The ID to assign to the created layer.
- /// @param buffer_queue The buffer queue for the layer instance to use.
+ /// @param layer_id The ID to assign to the created layer.
+ /// @param binder_id The ID assigned to the buffer queue.
///
- void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue);
+ void CreateLayer(u64 layer_id, u32 binder_id);
/// Closes and removes a layer from this display with the given ID.
///
@@ -104,9 +114,10 @@ public:
private:
u64 display_id;
std::string name;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
KernelHelpers::ServiceContext& service_context;
- std::vector<std::shared_ptr<Layer>> layers;
+ std::vector<std::unique_ptr<Layer>> layers;
Kernel::KEvent* vsync_event{};
};
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 @@
namespace Service::VI {
-Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {}
+Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
+ android::BufferQueueProducer& binder_,
+ std::shared_ptr<android::BufferItemConsumer>&& consumer_)
+ : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
+ consumer_)} {}
Layer::~Layer() = default;
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 @@
#pragma once
+#include <memory>
+
#include "common/common_types.h"
-namespace Service::NVFlinger {
-class BufferQueue;
-}
+namespace Service::android {
+class BufferItemConsumer;
+class BufferQueueCore;
+class BufferQueueProducer;
+} // namespace Service::android
namespace Service::VI {
@@ -17,10 +21,13 @@ class Layer {
public:
/// Constructs a layer with a given ID and buffer queue.
///
- /// @param id The ID to assign to this layer.
- /// @param queue The buffer queue for this layer to use.
+ /// @param layer_id_ The ID to assign to this layer.
+ /// @param binder_id_ The binder ID to assign to this layer.
+ /// @param binder_ The buffer producer queue for this layer to use.
///
- Layer(u64 id, NVFlinger::BufferQueue& queue);
+ Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
+ android::BufferQueueProducer& binder_,
+ std::shared_ptr<android::BufferItemConsumer>&& consumer_);
~Layer();
Layer(const Layer&) = delete;
@@ -30,23 +37,47 @@ public:
Layer& operator=(Layer&&) = delete;
/// Gets the ID for this layer.
- u64 GetID() const {
+ u64 GetLayerId() const {
return layer_id;
}
+ /// Gets the binder ID for this layer.
+ u32 GetBinderId() const {
+ return binder_id;
+ }
+
/// Gets a reference to the buffer queue this layer is using.
- NVFlinger::BufferQueue& GetBufferQueue() {
- return buffer_queue;
+ android::BufferQueueProducer& GetBufferQueue() {
+ return binder;
}
/// Gets a const reference to the buffer queue this layer is using.
- const NVFlinger::BufferQueue& GetBufferQueue() const {
- return buffer_queue;
+ const android::BufferQueueProducer& GetBufferQueue() const {
+ return binder;
+ }
+
+ android::BufferItemConsumer& GetConsumer() {
+ return *consumer;
+ }
+
+ const android::BufferItemConsumer& GetConsumer() const {
+ return *consumer;
+ }
+
+ android::BufferQueueCore& Core() {
+ return core;
+ }
+
+ const android::BufferQueueCore& Core() const {
+ return core;
}
private:
- u64 layer_id;
- NVFlinger::BufferQueue& buffer_queue;
+ const u64 layer_id;
+ const u32 binder_id;
+ android::BufferQueueCore& core;
+ android::BufferQueueProducer& binder;
+ std::shared_ptr<android::BufferItemConsumer> consumer;
};
} // 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 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/binder.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/nvflinger/parcel.h"
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
@@ -57,447 +60,25 @@ struct DisplayInfo {
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
-class Parcel {
+class NativeWindow final {
public:
- // This default size was chosen arbitrarily.
- static constexpr std::size_t DefaultBufferSize = 0x40;
- Parcel() : buffer(DefaultBufferSize) {}
- explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
- virtual ~Parcel() = default;
-
- template <typename T>
- T Read() {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
-
- T val;
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
- read_index += sizeof(T);
- read_index = Common::AlignUp(read_index, 4);
- return val;
- }
-
- template <typename T>
- T ReadUnaligned() {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
-
- T val;
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
- read_index += sizeof(T);
- return val;
- }
-
- std::vector<u8> ReadBlock(std::size_t length) {
- ASSERT(read_index + length <= buffer.size());
- const u8* const begin = buffer.data() + read_index;
- const u8* const end = begin + length;
- std::vector<u8> data(begin, end);
- read_index += length;
- read_index = Common::AlignUp(read_index, 4);
- return data;
- }
-
- std::u16string ReadInterfaceToken() {
- [[maybe_unused]] const u32 unknown = Read<u32_le>();
- const u32 length = Read<u32_le>();
-
- std::u16string token{};
-
- for (u32 ch = 0; ch < length + 1; ++ch) {
- token.push_back(ReadUnaligned<u16_le>());
- }
-
- read_index = Common::AlignUp(read_index, 4);
-
- return token;
- }
-
- template <typename T>
- void Write(const T& val) {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
-
- if (buffer.size() < write_index + sizeof(T)) {
- buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
- }
-
- std::memcpy(buffer.data() + write_index, &val, sizeof(T));
- write_index += sizeof(T);
- write_index = Common::AlignUp(write_index, 4);
- }
-
- template <typename T>
- void WriteObject(const T& val) {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
-
- const u32_le size = static_cast<u32>(sizeof(val));
- Write(size);
- // TODO(Subv): Support file descriptors.
- Write<u32_le>(0); // Fd count.
- Write(val);
- }
-
- void Deserialize() {
- ASSERT(buffer.size() > sizeof(Header));
-
- Header header{};
- std::memcpy(&header, buffer.data(), sizeof(Header));
-
- read_index = header.data_offset;
- DeserializeData();
- }
-
- std::vector<u8> Serialize() {
- ASSERT(read_index == 0);
- write_index = sizeof(Header);
-
- SerializeData();
-
- Header header{};
- header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
- header.data_offset = sizeof(Header);
- header.objects_size = 4;
- header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
- std::memcpy(buffer.data(), &header, sizeof(Header));
-
- return buffer;
- }
-
-protected:
- virtual void SerializeData() {}
-
- virtual void DeserializeData() {}
-
-private:
- struct Header {
- u32_le data_size;
- u32_le data_offset;
- u32_le objects_size;
- u32_le objects_offset;
- };
- static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
-
- std::vector<u8> buffer;
- std::size_t read_index = 0;
- std::size_t write_index = 0;
-};
-
-class NativeWindow : public Parcel {
-public:
- explicit NativeWindow(u32 id) {
- data.id = id;
- }
- ~NativeWindow() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le magic = 2;
- u32_le process_id = 1;
- u32_le id;
- INSERT_PADDING_WORDS(3);
- std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
- INSERT_PADDING_WORDS(2);
- };
- static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
-
- Data data{};
-};
-
-class IGBPConnectRequestParcel : public Parcel {
-public:
- explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le unk;
- u32_le api;
- u32_le producer_controlled_by_app;
- };
-
- Data data;
-};
-
-class IGBPConnectResponseParcel : public Parcel {
-public:
- explicit IGBPConnectResponseParcel(u32 width, u32 height) {
- data.width = width;
- data.height = height;
- }
- ~IGBPConnectResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le width;
- u32_le height;
- u32_le transform_hint;
- u32_le num_pending_buffers;
- u32_le status;
- };
- static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
-
- Data data{};
-};
-
-/// Represents a parcel containing one int '0' as its data
-/// Used by DetachBuffer and Disconnect
-class IGBPEmptyResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le unk_0{};
- };
-
- Data data{};
-};
-
-class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
-public:
- explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
- : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- if (data.contains_object != 0) {
- buffer_container = Read<BufferContainer>();
- }
- }
-
- struct Data {
- u32_le slot;
- u32_le contains_object;
- };
-
- struct BufferContainer {
- u32_le graphic_buffer_length;
- INSERT_PADDING_WORDS(1);
- NVFlinger::IGBPBuffer buffer{};
- };
-
- Data data{};
- BufferContainer buffer_container{};
-};
-
-class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- // TODO(Subv): Find out what this means
- Write<u32>(0);
- }
-};
-
-class IGBPCancelBufferRequestParcel : public Parcel {
-public:
- explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le slot;
- Service::Nvidia::MultiFence multi_fence;
- };
-
- Data data;
-};
-
-class IGBPCancelBufferResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- Write<u32>(0); // Success
- }
-};
-
-class IGBPDequeueBufferRequestParcel : public Parcel {
-public:
- explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le pixel_format;
- u32_le width;
- u32_le height;
- u32_le get_frame_timestamps;
- u32_le usage;
- };
-
- Data data;
-};
-
-class IGBPDequeueBufferResponseParcel : public Parcel {
-public:
- explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
- : slot(slot_), multi_fence(multi_fence_) {}
-
-protected:
- void SerializeData() override {
- Write(slot);
- Write<u32_le>(1);
- WriteObject(multi_fence);
- Write<u32_le>(0);
- }
-
- u32_le slot;
- Service::Nvidia::MultiFence multi_fence;
-};
-
-class IGBPRequestBufferRequestParcel : public Parcel {
-public:
- explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- slot = Read<u32_le>();
- }
-
- u32_le slot;
-};
-
-class IGBPRequestBufferResponseParcel : public Parcel {
-public:
- explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
- ~IGBPRequestBufferResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
- // try to read an IGBPBuffer object from the parcel.
- Write<u32_le>(1);
- WriteObject(buffer);
- Write<u32_le>(0);
- }
-
- NVFlinger::IGBPBuffer buffer;
-};
-
-class IGBPQueueBufferRequestParcel : public Parcel {
-public:
- explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le slot;
- INSERT_PADDING_WORDS(3);
- u32_le timestamp;
- s32_le is_auto_timestamp;
- s32_le crop_top;
- s32_le crop_left;
- s32_le crop_right;
- s32_le crop_bottom;
- s32_le scaling_mode;
- NVFlinger::BufferQueue::BufferTransformFlags transform;
- u32_le sticky_transform;
- INSERT_PADDING_WORDS(1);
- u32_le swap_interval;
- Service::Nvidia::MultiFence multi_fence;
-
- Common::Rectangle<int> GetCropRect() const {
- return {crop_left, crop_top, crop_right, crop_bottom};
- }
- };
- static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
-
- Data data;
-};
-
-class IGBPQueueBufferResponseParcel : public Parcel {
-public:
- explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
- data.width = width;
- data.height = height;
- }
- ~IGBPQueueBufferResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le width;
- u32_le height;
- u32_le transform_hint;
- u32_le num_pending_buffers;
- u32_le status;
- };
- static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
-
- Data data{};
-};
-
-class IGBPQueryRequestParcel : public Parcel {
-public:
- explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- type = Read<u32_le>();
- }
-
- u32 type;
-};
-
-class IGBPQueryResponseParcel : public Parcel {
-public:
- explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
- ~IGBPQueryResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(value);
- }
+ constexpr explicit NativeWindow(u32 id_) : id{id_} {}
private:
- u32_le value;
+ const u32 magic = 2;
+ const u32 process_id = 1;
+ const u32 id;
+ INSERT_PADDING_WORDS(3);
+ std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
+ INSERT_PADDING_WORDS(2);
};
+static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
public:
- explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) {
+ explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
+ : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
+ server(server_) {
static const FunctionInfo functions[] = {
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +89,16 @@ public:
}
private:
- enum class TransactionId {
- RequestBuffer = 1,
- SetBufferCount = 2,
- DequeueBuffer = 3,
- DetachBuffer = 4,
- DetachNextBuffer = 5,
- AttachBuffer = 6,
- QueueBuffer = 7,
- CancelBuffer = 8,
- Query = 9,
- Connect = 10,
- Disconnect = 11,
-
- AllocateBuffers = 13,
- SetPreallocatedBuffer = 14,
-
- GetBufferHistory = 17
- };
-
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 id = rp.Pop<u32>();
- const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
+ const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
transaction, flags);
- auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-
- switch (transaction) {
- case TransactionId::Connect: {
- IGBPConnectRequestParcel request{ctx.ReadBuffer()};
- IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
- static_cast<u32>(DisplayResolution::UndockedHeight)};
-
- buffer_queue.Connect();
-
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::SetPreallocatedBuffer: {
- IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
-
- IGBPSetPreallocatedBufferResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::DequeueBuffer: {
- IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
- const u32 width{request.data.width};
- const u32 height{request.data.height};
-
- do {
- if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
- // Buffer is available
- IGBPDequeueBufferResponseParcel response{result->first, *result->second};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- } while (buffer_queue.IsConnected());
-
- break;
- }
- case TransactionId::RequestBuffer: {
- IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
-
- auto& buffer = buffer_queue.RequestBuffer(request.slot);
- IGBPRequestBufferResponseParcel response{buffer};
- ctx.WriteBuffer(response.Serialize());
-
- break;
- }
- case TransactionId::QueueBuffer: {
- IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
- request.data.GetCropRect(), request.data.swap_interval,
- request.data.multi_fence);
-
- IGBPQueueBufferResponseParcel response{1280, 720};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::Query: {
- IGBPQueryRequestParcel request{ctx.ReadBuffer()};
-
- const u32 value =
- buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
-
- IGBPQueryResponseParcel response{value};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::CancelBuffer: {
- IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
-
- IGBPCancelBufferResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::Disconnect: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
- const auto buffer = ctx.ReadBuffer();
-
- buffer_queue.Disconnect();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::DetachBuffer: {
- const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::SetBufferCount: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
- [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::GetBufferHistory: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
- [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- default:
- ASSERT_MSG(false, "Unimplemented");
- }
+ server.TryGetProducer(id)->Transact(ctx, transaction, flags);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -674,13 +124,13 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
- // TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent());
+ rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
}
- NVFlinger::NVFlinger& nv_flinger;
+private:
+ NVFlinger::HosBinderDriverServer& server;
};
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -937,7 +387,40 @@ private:
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
- explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
+ hos_binder_driver_server{hos_binder_driver_server_} {
+
+ static const FunctionInfo functions[] = {
+ {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
+ {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
+ {102, &IApplicationDisplayService::GetManagerDisplayService,
+ "GetManagerDisplayService"},
+ {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
+ "GetIndirectDisplayTransactionService"},
+ {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
+ {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
+ {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
+ {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
+ {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
+ {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
+ {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
+ {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
+ {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
+ {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
+ {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
+ {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
+ {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
+ "GetIndirectLayerImageMap"},
+ {2451, nullptr, "GetIndirectLayerImageCropMap"},
+ {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
+ "GetIndirectLayerImageRequiredMemoryInfo"},
+ {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
+ {5203, nullptr, "GetDisplayVsyncEventForDebug"},
+ };
+ RegisterHandlers(functions);
+ }
private:
enum class ConvertedScaleMode : u64 {
@@ -961,7 +444,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger);
+ rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
}
void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +468,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger);
+ rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
}
void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1089,7 +572,7 @@ private:
void ListDisplays(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
- DisplayInfo display_info;
+ const DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1124,8 +607,8 @@ private:
return;
}
- NativeWindow native_window{*buffer_queue_id};
- const auto buffer_size = ctx.WriteBuffer(native_window.Serialize());
+ const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1170,8 +653,8 @@ private:
return;
}
- NativeWindow native_window{*buffer_queue_id};
- const auto buffer_size = ctx.WriteBuffer(native_window.Serialize());
+ const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
@@ -1287,39 +770,9 @@ private:
}
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
-IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
- NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
- static const FunctionInfo functions[] = {
- {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
- {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
- {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
- {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
- "GetIndirectDisplayTransactionService"},
- {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
- {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
- {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
- {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
- {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
- {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
- {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
- {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
- {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
- {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
- {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
- {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
- {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
- {2451, nullptr, "GetIndirectLayerImageCropMap"},
- {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
- "GetIndirectLayerImageRequiredMemoryInfo"},
- {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
- {5203, nullptr, "GetDisplayVsyncEventForDebug"},
- };
- RegisterHandlers(functions);
-}
-
static bool IsValidServiceAccess(Permission permission, Policy policy) {
if (permission == Permission::User) {
return policy == Policy::User;
@@ -1333,7 +786,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
}
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger, Permission permission) {
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
+ Permission permission) {
IPC::RequestParser rp{ctx};
const auto policy = rp.PopEnum<Policy>();
@@ -1346,14 +801,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger);
+ rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
}
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger) {
- std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
+ std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
+ std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
+ std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
}
} // 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;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::SM {
class ServiceManager;
@@ -47,11 +48,14 @@ enum class Policy {
namespace detail {
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger, Permission permission);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
+ Permission permission);
} // namespace detail
/// Registers all VI services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
} // 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 @@
namespace Service::VI {
-VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} {
+VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_M::~VI_M() = default;
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::Manager);
}
} // 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;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_M final : public ServiceFramework<VI_M> {
public:
- explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_M() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // 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 @@
namespace Service::VI {
-VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} {
+VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{1, &VI_S::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_S::~VI_S() = default;
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::System);
}
} // 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;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_S final : public ServiceFramework<VI_S> {
public:
- explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_S() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // 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 @@
namespace Service::VI {
-VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} {
+VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_U::~VI_U() = default;
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::User);
}
} // 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;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_U final : public ServiceFramework<VI_U> {
public:
- explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_U() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // 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) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
+Errno Socket::SetKeepAlive(bool enable) {
+ return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
+}
+
Errno Socket::SetBroadcast(bool enable) {
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}
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 @@
#include <utility>
#if defined(_WIN32)
-#include <winsock.h>
#elif !YUZU_UNIX
#error "Platform not implemented"
#endif
@@ -68,6 +67,8 @@ public:
Errno SetReuseAddr(bool enable);
+ Errno SetKeepAlive(bool enable);
+
Errno SetBroadcast(bool enable);
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() {
}
void PerfStats::BeginSystemFrame() {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
auto frame_end = Clock::now();
const auto frame_time = frame_end - frame_begin;
@@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
}
double PerfStats::GetMeanFrametime() const {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
@@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
const auto now = Clock::now();
// Walltime elapsed since stats were reset
@@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
}
double PerfStats::GetLastFrameTimeScale() const {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;
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 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
-#include <fmt/ostream.h>
#include <nlohmann/json.hpp>
#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 {
}
void Freezer::Clear() {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
@@ -88,7 +88,7 @@ void Freezer::Clear() {
}
u64 Freezer::Freeze(VAddr address, u32 width) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto current_value = MemoryReadWidth(memory, width, address);
entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
}
void Freezer::Unfreeze(VAddr address) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
}
bool Freezer::IsFrozen(VAddr address) const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
return FindEntry(address) != entries.cend();
}
void Freezer::SetFrozenValue(VAddr address, u64 value) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
}
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
}
std::vector<Freezer::Entry> Freezer::GetEntries() const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
return entries;
}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
return;
}
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
for (const auto& entry : entries) {
LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
}
void Freezer::FillEntryReads() {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
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
return Common::Input::ButtonNames::Invalid;
}
+bool GCAdapter::IsStickInverted(const Common::ParamPackage& params) {
+ if (!params.Has("port")) {
+ return false;
+ }
+
+ const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0));
+ const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0));
+ if (x_axis != PadAxes::StickY && x_axis != PadAxes::SubstickY) {
+ return false;
+ }
+ if (y_axis != PadAxes::StickX && y_axis != PadAxes::SubstickX) {
+ return false;
+ }
+ return true;
+}
+
} // 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 @@
#include <array>
#include <memory>
-#include <mutex>
#include <stop_token>
#include <string>
#include <thread>
@@ -36,6 +35,8 @@ public:
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
+ bool IsStickInverted(const Common::ParamPackage& params) override;
+
private:
enum class PadButton {
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:
bool UpdateMotion(SDL_ControllerSensorEvent event) {
constexpr float gravity_constant = 9.80665f;
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const u64 time_difference = event.timestamp - last_motion_update;
last_motion_update = event.timestamp;
switch (event.sensor) {
@@ -241,7 +241,7 @@ private:
};
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
- std::lock_guard lock{joystick_map_mutex};
+ std::scoped_lock lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
@@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
- std::lock_guard lock{joystick_map_mutex};
+ std::scoped_lock lock{joystick_map_mutex};
const auto map_it = joystick_map.find(guid);
if (map_it == joystick_map.end()) {
@@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
const std::string guid = GetGUID(sdl_joystick);
- std::lock_guard lock{joystick_map_mutex};
+ std::scoped_lock lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
PreSetController(joystick->GetPadIdentifier());
@@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
const std::string guid = GetGUID(sdl_joystick);
- std::lock_guard lock{joystick_map_mutex};
+ std::scoped_lock lock{joystick_map_mutex};
// This call to guid is safe since the joystick is guaranteed to be in the map
const auto& joystick_guid_list = joystick_map[guid];
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) {
}
void SDLDriver::CloseJoysticks() {
- std::lock_guard lock{joystick_map_mutex};
+ std::scoped_lock lock{joystick_map_mutex};
joystick_map.clear();
}
@@ -934,4 +934,37 @@ u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const {
return direction;
}
+bool SDLDriver::IsStickInverted(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return false;
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ if (joystick == nullptr) {
+ return false;
+ }
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return false;
+ }
+
+ const auto& axis_x = params.Get("axis_x", 0);
+ const auto& axis_y = params.Get("axis_y", 0);
+ const auto& binding_left_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
+ const auto& binding_right_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
+ const auto& binding_left_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
+ const auto& binding_right_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
+
+ if (axis_x != binding_left_y.value.axis && axis_x != binding_right_y.value.axis) {
+ return false;
+ }
+ if (axis_y != binding_left_x.value.axis && axis_y != binding_right_x.value.axis) {
+ return false;
+ }
+ return true;
+}
+
} // 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:
std::string GetHatButtonName(u8 direction_value) const override;
u8 GetHatButtonId(const std::string& direction_name) const override;
+ bool IsStickInverted(const Common::ParamPackage& params) override;
+
Common::Input::VibrationError SetRumble(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
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
return Common::Input::ButtonNames::Invalid;
}
+bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
+ return false;
+ }
+
+ const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0));
+ const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0));
+ if (x_axis != PadAxes::LeftStickY && x_axis != PadAxes::RightStickY) {
+ return false;
+ }
+ if (y_axis != PadAxes::LeftStickX && y_axis != PadAxes::RightStickX) {
+ return false;
+ }
+ return true;
+}
+
void TestCommunication(const std::string& host, u16 port,
const std::function<void()>& success_callback,
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:
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
+ bool IsStickInverted(const Common::ParamPackage& params) override;
+
private:
enum class PadButton {
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 @@
#include <optional>
#include <type_traits>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
+#endif
+
#include <boost/crc.hpp>
-#include "common/bit_field.h"
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#include "common/swap.h"
namespace 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 @@
// Refer to the license.txt file included
#include "common/logging/log.h"
-#include "common/param_package.h"
#include "input_common/input_engine.h"
namespace InputCommon {
void InputEngine::PreSetController(const PadIdentifier& identifier) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
controller_list.try_emplace(identifier);
}
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.buttons.try_emplace(button, false);
}
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.hat_buttons.try_emplace(button, u8{0});
}
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.axes.try_emplace(axis, 0.0f);
}
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.motions.try_emplace(motion);
}
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.buttons.insert_or_assign(button, value);
@@ -50,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.hat_buttons.insert_or_assign(button, value);
@@ -61,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.axes.insert_or_assign(axis, value);
@@ -72,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.battery = value;
@@ -83,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.motions.insert_or_assign(motion, value);
@@ -93,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
}
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -110,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
}
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -127,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
}
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -144,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
}
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -156,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
}
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -187,7 +186,7 @@ void InputEngine::ResetAnalogState() {
}
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
@@ -215,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
}
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
@@ -244,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
}
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
@@ -271,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
[[maybe_unused]] Common::Input::BatteryLevel value) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
@@ -285,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
const BasicMotion& value) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
@@ -347,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const {
}
int InputEngine::SetCallback(InputIdentifier input_identifier) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
return last_callback_key++;
}
void InputEngine::SetMappingCallback(MappingCallback callback) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
mapping_callback = std::move(callback);
}
void InputEngine::DeleteCallback(int key) {
- std::lock_guard lock{mutex_callback};
+ std::scoped_lock lock{mutex_callback};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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:
return 0;
}
+ /// Returns true if axis of a stick aren't mapped in the correct direction
+ virtual bool IsStickInverted([[maybe_unused]] const Common::ParamPackage& params) {
+ return false;
+ }
+
void PreSetController(const PadIdentifier& identifier);
void PreSetButton(const PadIdentifier& identifier, int button);
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 @@
// Refer to the license.txt file included.
#include <memory>
-#include <thread>
#include "common/input.h"
#include "common/param_package.h"
#include "input_common/drivers/gc_adapter.h"
@@ -242,6 +241,28 @@ struct InputSubsystem::Impl {
return Common::Input::ButtonNames::Invalid;
}
+ bool IsStickInverted(const Common::ParamPackage& params) {
+ const std::string engine = params.Get("engine", "");
+ if (engine == mouse->GetEngineName()) {
+ return mouse->IsStickInverted(params);
+ }
+ if (engine == gcadapter->GetEngineName()) {
+ return gcadapter->IsStickInverted(params);
+ }
+ if (engine == udp_client->GetEngineName()) {
+ return udp_client->IsStickInverted(params);
+ }
+ if (engine == tas_input->GetEngineName()) {
+ return tas_input->IsStickInverted(params);
+ }
+#ifdef HAVE_SDL2
+ if (engine == sdl->GetEngineName()) {
+ return sdl->IsStickInverted(params);
+ }
+#endif
+ return false;
+ }
+
bool IsController(const Common::ParamPackage& params) {
const std::string engine = params.Get("engine", "");
if (engine == mouse->GetEngineName()) {
@@ -385,6 +406,13 @@ bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
return impl->IsController(params);
}
+bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
+ if (params.Has("axis_x") && params.Has("axis_y")) {
+ return impl->IsStickInverted(params);
+ }
+ return false;
+}
+
void InputSubsystem::ReloadInputDevices() {
impl->udp_client.get()->ReloadSockets();
}
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:
/// Returns true if device is a controller.
[[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
+ /// Returns true if axis of a stick aren't mapped in the correct direction
+ [[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const;
+
/// Reloads the input devices.
void ReloadInputDevices();
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
-#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string>
-
#include <fmt/format.h>
-#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/backend/glasm/reg_alloc.h"
#include "shader_recompiler/exception.h"
#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 @@
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
-#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLSL {
void 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) {
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
}
+std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
+ if (binding.IsImmediate()) {
+ return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
+ } else {
+ const auto binding_var{ctx.var_alloc.Consume(binding)};
+ return fmt::format("GetCbufIndirect({},{})", binding_var, index);
+ }
+}
+
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
std::string_view bit_offset = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
: fmt::format("[({}>>2)%4]", offset_var)};
- const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
- const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
+ const auto cbuf{ChooseCbuf(ctx, binding, index)};
+ const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
const auto extraction{num_bits == 32 ? cbuf_cast
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
bit_offset, num_bits)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
- const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
if (offset.IsImmediate()) {
+ const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
static constexpr u32 cbuf_size{0x10000};
const u32 u32_offset{offset.U32()};
const s32 signed_offset{static_cast<s32>(offset.U32())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
return;
}
const auto offset_var{ctx.var_alloc.Consume(offset)};
+ const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
if (!ctx.profile.has_gl_component_indexing_bug) {
- ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
- cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
+ ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
+ offset_var, cast, cbuf, offset_var);
return;
}
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
- ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
- swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
- "xyzw"[(swizzle + 1) % 4]);
+ ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
+ cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
}
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
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
header += "layout(location=0) uniform vec4 scaling;";
}
DefineConstantBuffers(bindings);
+ DefineConstantBufferIndirect();
DefineStorageBuffers(bindings);
SetupImages(bindings);
SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
}
}
+void EmitContext::DefineConstantBufferIndirect() {
+ if (!info.uses_cbuf_indirect) {
+ return;
+ }
+
+ header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
+ header += "GetCbufIndirect(uint binding, uint offset){"
+ "switch(binding){"
+ "default:";
+
+ for (const auto& desc : info.constant_buffer_descriptors) {
+ header +=
+ fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
+ }
+
+ header += "}}";
+}
+
void EmitContext::DefineStorageBuffers(Bindings& bindings) {
if (info.storage_buffers_descriptors.empty()) {
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:
private:
void SetupExtensions();
void DefineConstantBuffers(Bindings& bindings);
+ void DefineConstantBufferIndirect();
void DefineStorageBuffers(Bindings& bindings);
void DefineGenericOutput(size_t index, u32 invocations);
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 =
struct RescalingLayout {
alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
- alignas(16) u32 down_factor;
+ u32 down_factor;
};
constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
constexpr 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <bit>
+
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
-#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
namespace {
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
-#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <bit>
#include <tuple>
#include <utility>
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
@@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
}
Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size,
- const IR::Value& binding, const IR::Value& offset) {
+ const IR::Value& binding, const IR::Value& offset, const Id indirect_func) {
+ Id buffer_offset;
+ const Id uniform_type{ctx.uniform_types.*member_ptr};
+ if (offset.IsImmediate()) {
+ // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
+ const Id imm_offset{ctx.Const(offset.U32() / element_size)};
+ buffer_offset = imm_offset;
+ } else if (element_size > 1) {
+ const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
+ const Id shift{ctx.Const(log2_element_size)};
+ buffer_offset = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
+ } else {
+ buffer_offset = ctx.Def(offset);
+ }
if (!binding.IsImmediate()) {
- throw NotImplementedException("Constant buffer indexing");
+ return ctx.OpFunctionCall(result_type, indirect_func, ctx.Def(binding), buffer_offset);
}
const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
- const Id uniform_type{ctx.uniform_types.*member_ptr};
- if (!offset.IsImmediate()) {
- Id index{ctx.Def(offset)};
- if (element_size > 1) {
- const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
- const Id shift{ctx.Const(log2_element_size)};
- index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
- }
- const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
- return ctx.OpLoad(result_type, access_chain);
- }
- // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
- const Id imm_offset{ctx.Const(offset.U32() / element_size)};
- const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
+ const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, buffer_offset)};
return ctx.OpLoad(result_type, access_chain);
}
Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
- return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset);
+ return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset,
+ ctx.load_const_func_u32);
}
Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
- return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset);
+ return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset,
+ ctx.load_const_func_u32x4);
}
Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
@@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
- const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)};
+ const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
+ ctx.load_const_func_u8)};
return ctx.OpUConvert(ctx.U32[1], load);
}
Id element{};
@@ -217,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
- const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)};
+ const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
+ ctx.load_const_func_u8)};
return ctx.OpSConvert(ctx.U32[1], load);
}
Id element{};
@@ -233,8 +237,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
- const Id load{
- GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)};
+ const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
+ ctx.load_const_func_u16)};
return ctx.OpUConvert(ctx.U32[1], load);
}
Id element{};
@@ -250,8 +254,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
- const Id load{
- GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)};
+ const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
+ ctx.load_const_func_u16)};
return ctx.OpSConvert(ctx.U32[1], load);
}
Id element{};
@@ -276,7 +280,8 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing) {
- return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset);
+ return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset,
+ ctx.load_const_func_f32);
} else {
const Id vector{GetCbufU32x4(ctx, binding, offset)};
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
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing) {
- return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding,
- offset);
+ return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset,
+ ctx.load_const_func_u32x2);
} else {
const Id vector{GetCbufU32x4(ctx, binding, offset)};
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 @@
#include <algorithm>
#include <array>
+#include <bit>
#include <climits>
-#include <string_view>
#include <boost/container/static_vector.hpp>
@@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
DefineSharedMemory(program);
DefineSharedMemoryFunctions(program);
DefineConstantBuffers(program.info, uniform_binding);
+ DefineConstantBufferIndirectFunctions(program.info);
DefineStorageBuffers(program.info, storage_binding);
DefineTextureBuffers(program.info, texture_binding);
DefineImageBuffers(program.info, image_binding);
@@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
}
return;
}
- IR::Type types{info.used_constant_buffer_types};
+ IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
if (True(types & IR::Type::U8)) {
if (profile.support_int8) {
DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
@@ -1027,6 +1028,63 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
binding += static_cast<u32>(info.constant_buffer_descriptors.size());
}
+void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
+ if (!info.uses_cbuf_indirect) {
+ return;
+ }
+ const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) {
+ const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])};
+ const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)};
+ const Id binding{OpFunctionParameter(U32[1])};
+ const Id offset{OpFunctionParameter(U32[1])};
+
+ AddLabel();
+
+ const Id merge_label{OpLabel()};
+ const Id uniform_type{uniform_types.*member_ptr};
+
+ std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels;
+ std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals;
+ for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
+ buf_labels[i] = OpLabel();
+ buf_literals[i] = Sirit::Literal{i};
+ }
+ OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
+ OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
+ for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
+ AddLabel(buf_labels[i]);
+ const Id cbuf{cbufs[i].*member_ptr};
+ const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
+ const Id result{OpLoad(buffer_type, access_chain)};
+ OpReturnValue(result);
+ }
+ AddLabel(merge_label);
+ OpUnreachable();
+ OpFunctionEnd();
+ return func;
+ }};
+ IR::Type types{info.used_indirect_cbuf_types};
+ bool supports_aliasing = profile.support_descriptor_aliasing;
+ if (supports_aliasing && True(types & IR::Type::U8)) {
+ load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
+ }
+ if (supports_aliasing && True(types & IR::Type::U16)) {
+ load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
+ }
+ if (supports_aliasing && True(types & IR::Type::F32)) {
+ load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
+ }
+ if (supports_aliasing && True(types & IR::Type::U32)) {
+ load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
+ }
+ if (supports_aliasing && True(types & IR::Type::U32x2)) {
+ load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
+ }
+ if (!supports_aliasing || True(types & IR::Type::U32x4)) {
+ load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
+ }
+}
+
void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
if (info.storage_buffers_descriptors.empty()) {
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 @@
#pragma once
#include <array>
-#include <string_view>
#include <sirit/sirit.h>
@@ -294,6 +293,13 @@ public:
std::vector<Id> interfaces;
+ Id load_const_func_u8{};
+ Id load_const_func_u16{};
+ Id load_const_func_u32{};
+ Id load_const_func_f32{};
+ Id load_const_func_u32x2{};
+ Id load_const_func_u32x4{};
+
private:
void DefineCommonTypes(const Info& info);
void DefineCommonConstants();
@@ -302,6 +308,7 @@ private:
void DefineSharedMemory(const IR::Program& program);
void DefineSharedMemoryFunctions(const IR::Program& program);
void DefineConstantBuffers(const Info& info, u32& binding);
+ void DefineConstantBufferIndirectFunctions(const Info& info);
void DefineStorageBuffers(const Info& info, u32& binding);
void DefineTextureBuffers(const Info& info, u32& binding);
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 @@
#include <exception>
#include <string>
-#include <string_view>
#include <utility>
#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 @@
#include <algorithm>
#include <initializer_list>
#include <map>
-#include <memory>
-#include "common/bit_cast.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#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 @@
#pragma once
-#include <compare>
#include <string>
#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) {
phi_args.emplace_back(predecessor, value);
}
+void Inst::ErasePhiOperand(size_t index) {
+ const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
+ phi_args.erase(operand_it);
+}
+
void Inst::OrderPhiArgs() {
if (op != Opcode::Phi) {
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string_view>
-
#include "shader_recompiler/frontend/ir/opcodes.h"
namespace 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 @@
#include <algorithm>
#include <array>
-#include <string_view>
#include <fmt/format.h>
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "shader_recompiler/frontend/ir/opcodes.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace 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:
/// Get a pointer to the block of a phi argument.
[[nodiscard]] Block* PhiBlock(size_t index) const;
+
/// Add phi operand to a phi instruction.
void AddPhiOperand(Block* predecessor, const Value& value);
+ // Erase the phi operand at the given index.
+ void ErasePhiOperand(size_t index);
+
/// Orders the Phi arguments from farthest away to nearest.
void OrderPhiArgs();
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 @@
#pragma once
-#include <compare>
#include <optional>
#include <span>
#include <string>
@@ -15,6 +14,7 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/condition.h"
+#include "shader_recompiler/frontend/ir/reg.h"
#include "shader_recompiler/frontend/maxwell/instruction.h"
#include "shader_recompiler/frontend/maxwell/location.h"
#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 @@
#include <array>
#include <bit>
#include <memory>
-#include <string_view>
#include "common/common_types.h"
#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 @@
#include <optional>
-#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/environment.h"
#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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/flow_test.h"
-#include "shader_recompiler/frontend/ir/reg.h"
namespace Shader::Maxwell {
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 @@
#pragma once
-#include <compare>
-#include <iterator>
-
#include <fmt/format.h>
#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 @@
#include <unordered_map>
#include <utility>
#include <vector>
-#include <version>
#include <fmt/format.h>
@@ -17,7 +16,6 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
-#include "shader_recompiler/frontend/maxwell/decode.h"
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
#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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "shader_recompiler/frontend/ir/modifiers.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
#pragma once
-#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
// Refer to the license.txt file included.
#include "common/common_types.h"
-#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace Shader::Maxwell {
@@ -12,9 +11,13 @@ void ExitFragment(TranslatorVisitor& v) {
const ProgramHeader sph{v.env.SPH()};
IR::Reg src_reg{IR::Reg::R0};
for (u32 render_target = 0; render_target < 8; ++render_target) {
+ if (!sph.ps.HasOutputComponents(render_target)) {
+ continue;
+ }
const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)};
for (u32 component = 0; component < 4; ++component) {
if (!mask[component]) {
+ ++src_reg;
continue;
}
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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
-#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h"
-#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"
namespace 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 {
using namespace LDC;
namespace {
std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index,
- const IR::U32& reg, const IR::U32& imm) {
+ const IR::U32& reg, const IR::U32& imm_offset) {
switch (mode) {
case Mode::Default:
- return {imm_index, ir.IAdd(reg, imm)};
+ return {imm_index, ir.IAdd(reg, imm_offset)};
+ case Mode::IS: {
+ // Segmented addressing mode
+ // Ra+imm_offset points into a flat mapping of const buffer
+ // address space
+ const IR::U32 address{ir.IAdd(reg, imm_offset)};
+ const IR::U32 index{ir.BitFieldExtract(address, ir.Imm32(16), ir.Imm32(16))};
+ const IR::U32 offset{ir.BitFieldExtract(address, ir.Imm32(0), ir.Imm32(16))};
+
+ return {ir.IAdd(index, imm_index), offset};
+ }
default:
break;
}
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 @@
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 {
// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table)
IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c,
u64 ttbl) {
- IR::U32 r{ir.Imm32(0)};
- const IR::U32 not_a{ir.BitwiseNot(a)};
- const IR::U32 not_b{ir.BitwiseNot(b)};
- const IR::U32 not_c{ir.BitwiseNot(c)};
- if (ttbl & 0x01) {
- // r |= ~a & ~b & ~c;
- const auto lhs{ir.BitwiseAnd(not_a, not_b)};
- const auto rhs{ir.BitwiseAnd(lhs, not_c)};
- r = ir.BitwiseOr(r, rhs);
+ switch (ttbl) {
+ // generated code, do not edit manually
+ case 0:
+ return ir.Imm32(0);
+ case 1:
+ return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c)));
+ case 2:
+ return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
+ case 3:
+ return ir.BitwiseNot(ir.BitwiseOr(a, b));
+ case 4:
+ return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
+ case 5:
+ return ir.BitwiseNot(ir.BitwiseOr(a, c));
+ case 6:
+ return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
+ case 7:
+ return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c)));
+ case 8:
+ return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
+ case 9:
+ return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c)));
+ case 10:
+ return ir.BitwiseAnd(c, ir.BitwiseNot(a));
+ case 11:
+ return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b)));
+ case 12:
+ return ir.BitwiseAnd(b, ir.BitwiseNot(a));
+ case 13:
+ return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c)));
+ case 14:
+ return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
+ case 15:
+ return ir.BitwiseNot(a);
+ case 16:
+ return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
+ case 17:
+ return ir.BitwiseNot(ir.BitwiseOr(b, c));
+ case 18:
+ return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
+ case 19:
+ return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c)));
+ case 20:
+ return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
+ case 21:
+ return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
+ case 22:
+ return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
+ case 23:
+ return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)),
+ ir.BitwiseNot(a));
+ case 24:
+ return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
+ case 25:
+ return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)));
+ case 26:
+ return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
+ case 27:
+ return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
+ case 28:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
+ case 29:
+ return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
+ case 30:
+ return ir.BitwiseXor(a, ir.BitwiseOr(b, c));
+ case 31:
+ return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)));
+ case 32:
+ return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
+ case 33:
+ return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
+ case 34:
+ return ir.BitwiseAnd(c, ir.BitwiseNot(b));
+ case 35:
+ return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
+ case 36:
+ return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c));
+ case 37:
+ return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)));
+ case 38:
+ return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
+ case 39:
+ return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
+ case 40:
+ return ir.BitwiseAnd(c, ir.BitwiseXor(a, b));
+ case 41:
+ return ir.BitwiseXor(ir.BitwiseOr(a, b),
+ ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)));
+ case 42:
+ return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
+ case 43:
+ return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)),
+ ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
+ case 44:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b));
+ case 45:
+ return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
+ case 46:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c));
+ case 47:
+ return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a));
+ case 48:
+ return ir.BitwiseAnd(a, ir.BitwiseNot(b));
+ case 49:
+ return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c)));
+ case 50:
+ return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
+ case 51:
+ return ir.BitwiseNot(b);
+ case 52:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
+ case 53:
+ return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
+ case 54:
+ return ir.BitwiseXor(b, ir.BitwiseOr(a, c));
+ case 55:
+ return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)));
+ case 56:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b));
+ case 57:
+ return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
+ case 58:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c));
+ case 59:
+ return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b));
+ case 60:
+ return ir.BitwiseXor(a, b);
+ case 61:
+ return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b));
+ case 62:
+ return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b));
+ case 63:
+ return ir.BitwiseNot(ir.BitwiseAnd(a, b));
+ case 64:
+ return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
+ case 65:
+ return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
+ case 66:
+ return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c));
+ case 67:
+ return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)));
+ case 68:
+ return ir.BitwiseAnd(b, ir.BitwiseNot(c));
+ case 69:
+ return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
+ case 70:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
+ case 71:
+ return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
+ case 72:
+ return ir.BitwiseAnd(b, ir.BitwiseXor(a, c));
+ case 73:
+ return ir.BitwiseXor(ir.BitwiseOr(a, c),
+ ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)));
+ case 74:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c));
+ case 75:
+ return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
+ case 76:
+ return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
+ case 77:
+ return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)),
+ ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
+ case 78:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c));
+ case 79:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a));
+ case 80:
+ return ir.BitwiseAnd(a, ir.BitwiseNot(c));
+ case 81:
+ return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b)));
+ case 82:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
+ case 83:
+ return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
+ case 84:
+ return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
+ case 85:
+ return ir.BitwiseNot(c);
+ case 86:
+ return ir.BitwiseXor(c, ir.BitwiseOr(a, b));
+ case 87:
+ return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)));
+ case 88:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c));
+ case 89:
+ return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
+ case 90:
+ return ir.BitwiseXor(a, c);
+ case 91:
+ return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c));
+ case 92:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b));
+ case 93:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c));
+ case 94:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c));
+ case 95:
+ return ir.BitwiseNot(ir.BitwiseAnd(a, c));
+ case 96:
+ return ir.BitwiseAnd(a, ir.BitwiseXor(b, c));
+ case 97:
+ return ir.BitwiseXor(ir.BitwiseOr(b, c),
+ ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)));
+ case 98:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c));
+ case 99:
+ return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
+ case 100:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c));
+ case 101:
+ return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
+ case 102:
+ return ir.BitwiseXor(b, c);
+ case 103:
+ return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c));
+ case 104:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b)));
+ case 105:
+ return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
+ case 106:
+ return ir.BitwiseXor(c, ir.BitwiseAnd(a, b));
+ case 107:
+ return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)),
+ ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 108:
+ return ir.BitwiseXor(b, ir.BitwiseAnd(a, c));
+ case 109:
+ return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)),
+ ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 110:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
+ case 111:
+ return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
+ case 112:
+ return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
+ case 113:
+ return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)),
+ ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
+ case 114:
+ return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c));
+ case 115:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b));
+ case 116:
+ return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b));
+ case 117:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c));
+ case 118:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
+ case 119:
+ return ir.BitwiseNot(ir.BitwiseAnd(b, c));
+ case 120:
+ return ir.BitwiseXor(a, ir.BitwiseAnd(b, c));
+ case 121:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)),
+ ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 122:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
+ case 123:
+ return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
+ case 124:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
+ case 125:
+ return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
+ case 126:
+ return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
+ case 127:
+ return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)));
+ case 128:
+ return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c));
+ case 129:
+ return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
+ case 130:
+ return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 131:
+ return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 132:
+ return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 133:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 134:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
+ case 135:
+ return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
+ case 136:
+ return ir.BitwiseAnd(b, c);
+ case 137:
+ return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 138:
+ return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
+ case 139:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
+ case 140:
+ return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
+ case 141:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c)));
+ case 142:
+ return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
+ case 143:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
+ case 144:
+ return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 145:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 146:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
+ case 147:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
+ case 148:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
+ case 149:
+ return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
+ case 150:
+ return ir.BitwiseXor(a, ir.BitwiseXor(b, c));
+ case 151:
+ return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)),
+ ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
+ case 152:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 153:
+ return ir.BitwiseXor(b, ir.BitwiseNot(c));
+ case 154:
+ return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
+ case 155:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)));
+ case 156:
+ return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
+ case 157:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)));
+ case 158:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c)));
+ case 159:
+ return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c)));
+ case 160:
+ return ir.BitwiseAnd(a, c);
+ case 161:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 162:
+ return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
+ case 163:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
+ case 164:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 165:
+ return ir.BitwiseXor(a, ir.BitwiseNot(c));
+ case 166:
+ return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
+ case 167:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)));
+ case 168:
+ return ir.BitwiseAnd(c, ir.BitwiseOr(a, b));
+ case 169:
+ return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
+ case 170:
+ return c;
+ case 171:
+ return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
+ case 172:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
+ case 173:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 174:
+ return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
+ case 175:
+ return ir.BitwiseOr(c, ir.BitwiseNot(a));
+ case 176:
+ return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
+ case 177:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c)));
+ case 178:
+ return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
+ case 179:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
+ case 180:
+ return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
+ case 181:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)));
+ case 182:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c)));
+ case 183:
+ return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c)));
+ case 184:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
+ case 185:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 186:
+ return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
+ case 187:
+ return ir.BitwiseOr(c, ir.BitwiseNot(b));
+ case 188:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b));
+ case 189:
+ return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 190:
+ return ir.BitwiseOr(c, ir.BitwiseXor(a, b));
+ case 191:
+ return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
+ case 192:
+ return ir.BitwiseAnd(a, b);
+ case 193:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 194:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 195:
+ return ir.BitwiseXor(a, ir.BitwiseNot(b));
+ case 196:
+ return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
+ case 197:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c)));
+ case 198:
+ return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
+ case 199:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)));
+ case 200:
+ return ir.BitwiseAnd(b, ir.BitwiseOr(a, c));
+ case 201:
+ return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
+ case 202:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
+ case 203:
+ return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 204:
+ return b;
+ case 205:
+ return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
+ case 206:
+ return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
+ case 207:
+ return ir.BitwiseOr(b, ir.BitwiseNot(a));
+ case 208:
+ return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
+ case 209:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c)));
+ case 210:
+ return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
+ case 211:
+ return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)));
+ case 212:
+ return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
+ case 213:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
+ case 214:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b)));
+ case 215:
+ return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b)));
+ case 216:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
+ case 217:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 218:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c));
+ case 219:
+ return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 220:
+ return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
+ case 221:
+ return ir.BitwiseOr(b, ir.BitwiseNot(c));
+ case 222:
+ return ir.BitwiseOr(b, ir.BitwiseXor(a, c));
+ case 223:
+ return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
+ case 224:
+ return ir.BitwiseAnd(a, ir.BitwiseOr(b, c));
+ case 225:
+ return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
+ case 226:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
+ case 227:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 228:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
+ case 229:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 230:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c));
+ case 231:
+ return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
+ case 232:
+ return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
+ case 233:
+ return ir.BitwiseOr(ir.BitwiseAnd(a, b),
+ ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)));
+ case 234:
+ return ir.BitwiseOr(c, ir.BitwiseAnd(a, b));
+ case 235:
+ return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
+ case 236:
+ return ir.BitwiseOr(b, ir.BitwiseAnd(a, c));
+ case 237:
+ return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
+ case 238:
+ return ir.BitwiseOr(b, c);
+ case 239:
+ return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
+ case 240:
+ return a;
+ case 241:
+ return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
+ case 242:
+ return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
+ case 243:
+ return ir.BitwiseOr(a, ir.BitwiseNot(b));
+ case 244:
+ return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
+ case 245:
+ return ir.BitwiseOr(a, ir.BitwiseNot(c));
+ case 246:
+ return ir.BitwiseOr(a, ir.BitwiseXor(b, c));
+ case 247:
+ return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
+ case 248:
+ return ir.BitwiseOr(a, ir.BitwiseAnd(b, c));
+ case 249:
+ return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
+ case 250:
+ return ir.BitwiseOr(a, c);
+ case 251:
+ return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
+ case 252:
+ return ir.BitwiseOr(a, b);
+ case 253:
+ return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
+ case 254:
+ return ir.BitwiseOr(a, ir.BitwiseOr(b, c));
+ case 255:
+ return ir.Imm32(0xFFFFFFFF);
+ // end of generated code
}
- if (ttbl & 0x02) {
- // r |= ~a & ~b & c;
- const auto lhs{ir.BitwiseAnd(not_a, not_b)};
- const auto rhs{ir.BitwiseAnd(lhs, c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x04) {
- // r |= ~a & b & ~c;
- const auto lhs{ir.BitwiseAnd(not_a, b)};
- const auto rhs{ir.BitwiseAnd(lhs, not_c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x08) {
- // r |= ~a & b & c;
- const auto lhs{ir.BitwiseAnd(not_a, b)};
- const auto rhs{ir.BitwiseAnd(lhs, c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x10) {
- // r |= a & ~b & ~c;
- const auto lhs{ir.BitwiseAnd(a, not_b)};
- const auto rhs{ir.BitwiseAnd(lhs, not_c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x20) {
- // r |= a & ~b & c;
- const auto lhs{ir.BitwiseAnd(a, not_b)};
- const auto rhs{ir.BitwiseAnd(lhs, c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x40) {
- // r |= a & b & ~c;
- const auto lhs{ir.BitwiseAnd(a, b)};
- const auto rhs{ir.BitwiseAnd(lhs, not_c)};
- r = ir.BitwiseOr(r, rhs);
- }
- if (ttbl & 0x80) {
- // r |= a & b & c;
- const auto lhs{ir.BitwiseAnd(a, b)};
- const auto rhs{ir.BitwiseAnd(lhs, c)};
- r = ir.BitwiseOr(r, rhs);
- }
- return r;
+ throw NotImplementedException("LOP3 with out of range ttbl");
}
IR::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 @@
+# Copyright © 2022 degasus <markus@selfnet.de>
+# This work is free. You can redistribute it and/or modify it under the
+# terms of the Do What The Fuck You Want To Public License, Version 2,
+# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
+
+from itertools import product
+
+# The primitive instructions
+OPS = {
+ 'ir.BitwiseAnd({}, {})' : (2, 1, lambda a,b: a&b),
+ 'ir.BitwiseOr({}, {})' : (2, 1, lambda a,b: a|b),
+ 'ir.BitwiseXor({}, {})' : (2, 1, lambda a,b: a^b),
+ 'ir.BitwiseNot({})' : (1, 0.1, lambda a: (~a) & 255), # Only tiny cost, as this can often inlined in other instructions
+}
+
+# Our database of combination of instructions
+optimized_calls = {}
+def cmp(lhs, rhs):
+ if lhs is None: # new entry
+ return True
+ if lhs[3] > rhs[3]: # costs
+ return True
+ if lhs[3] < rhs[3]: # costs
+ return False
+ if len(lhs[0]) > len(rhs[0]): # string len
+ return True
+ if len(lhs[0]) < len(rhs[0]): # string len
+ return False
+ if lhs[0] > rhs[0]: # string sorting
+ return True
+ if lhs[0] < rhs[0]: # string sorting
+ return False
+ assert lhs == rhs, "redundant instruction, bug in brute force"
+ return False
+def register(imm, instruction, count, latency):
+ # Use the sum of instruction count and latency as costs to evaluate which combination is best
+ costs = count + latency
+
+ old = optimized_calls.get(imm, None)
+ new = (instruction, count, latency, costs)
+
+ # Update if new or better
+ if cmp(old, new):
+ optimized_calls[imm] = new
+ return True
+
+ return False
+
+# Constants: 0, 1 (for free)
+register(0, 'ir.Imm32(0)', 0, 0)
+register(255, 'ir.Imm32(0xFFFFFFFF)', 0, 0)
+
+# Inputs: a, b, c (for free)
+ta = 0xF0
+tb = 0xCC
+tc = 0xAA
+inputs = {
+ ta : 'a',
+ tb : 'b',
+ tc : 'c',
+}
+for imm, instruction in inputs.items():
+ register(imm, instruction, 0, 0)
+ register((~imm) & 255, 'ir.BitwiseNot({})'.format(instruction), 0.099, 0.099) # slightly cheaper NEG on inputs
+
+# Try to combine two values from the db with an instruction.
+# If it is better than the old method, update it.
+while True:
+ registered = 0
+ calls_copy = optimized_calls.copy()
+ for OP, (argc, cost, f) in OPS.items():
+ for args in product(calls_copy.items(), repeat=argc):
+ # unpack(transponse) the arrays
+ imm = [arg[0] for arg in args]
+ value = [arg[1][0] for arg in args]
+ count = [arg[1][1] for arg in args]
+ latency = [arg[1][2] for arg in args]
+
+ registered += register(
+ f(*imm),
+ OP.format(*value),
+ sum(count) + cost,
+ max(latency) + cost)
+ if registered == 0:
+ # No update at all? So terminate
+ break
+
+# Hacky output. Please improve me to output valid C++ instead.
+s = """ case {imm}:
+ return {op};"""
+for imm in range(256):
+ 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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
-#include "shader_recompiler/frontend/maxwell/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <array>
-#include <bit>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <utility>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <optional>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <utility>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <optional>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <optional>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
@@ -134,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
multisample = v.X(meta_reg++);
}
if (tld.clamp != 0) {
- throw NotImplementedException("TLD.CL - CLAMP is not implmented");
+ throw NotImplementedException("TLD.CL - CLAMP is not implemented");
}
IR::TextureInstInfo info{};
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <optional>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
@@ -83,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
} const tmml{insn};
if ((tmml.mask & 0b1100) != 0) {
- throw NotImplementedException("TMML BA results are not implmented");
+ throw NotImplementedException("TMML BA results are not implemented");
}
const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)};
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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <optional>
-
#include "common/bit_field.h"
#include "common/common_types.h"
#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
}
Optimization::SsaRewritePass(program);
+ Optimization::ConstantPropagationPass(program);
+
Optimization::GlobalMemoryToStorageBufferPass(program);
Optimization::TexturePass(env, program);
- Optimization::ConstantPropagationPass(program);
-
if (Settings::values.resolution_info.active) {
Optimization::RescalingPass(program);
}
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 @@
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h"
-#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/object_pool.h"
#include "shader_recompiler/runtime_info.h"
+namespace Shader {
+struct HostTranslateInfo;
+}
+
namespace Shader::Maxwell {
[[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) {
});
}
+void AddRegisterIndexedLdc(Info& info) {
+ info.uses_cbuf_indirect = true;
+
+ for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
+ AddConstantBufferDescriptor(info, i, 1);
+
+ // The shader can use any possible access size
+ info.constant_buffer_used_sizes[i] = 0x10'000;
+ }
+}
+
+u32 GetElementSize(IR::Type& used_type, Shader::IR::Opcode opcode) {
+ switch (opcode) {
+ case IR::Opcode::GetCbufU8:
+ case IR::Opcode::GetCbufS8:
+ used_type |= IR::Type::U8;
+ return 1;
+ case IR::Opcode::GetCbufU16:
+ case IR::Opcode::GetCbufS16:
+ used_type |= IR::Type::U16;
+ return 2;
+ case IR::Opcode::GetCbufU32:
+ used_type |= IR::Type::U32;
+ return 4;
+ case IR::Opcode::GetCbufF32:
+ used_type |= IR::Type::F32;
+ return 4;
+ case IR::Opcode::GetCbufU32x2:
+ used_type |= IR::Type::U32x2;
+ return 8;
+ default:
+ throw InvalidArgument("Invalid opcode {}", opcode);
+ }
+}
+
void GetPatch(Info& info, IR::Patch patch) {
if (!IR::IsGeneric(patch)) {
throw NotImplementedException("Reading non-generic patch {}", patch);
@@ -463,42 +498,18 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::GetCbufU32x2: {
const IR::Value index{inst.Arg(0)};
const IR::Value offset{inst.Arg(1)};
- if (!index.IsImmediate()) {
- throw NotImplementedException("Constant buffer with non-immediate index");
- }
- AddConstantBufferDescriptor(info, index.U32(), 1);
- u32 element_size{};
- switch (inst.GetOpcode()) {
- case IR::Opcode::GetCbufU8:
- case IR::Opcode::GetCbufS8:
- info.used_constant_buffer_types |= IR::Type::U8;
- element_size = 1;
- break;
- case IR::Opcode::GetCbufU16:
- case IR::Opcode::GetCbufS16:
- info.used_constant_buffer_types |= IR::Type::U16;
- element_size = 2;
- break;
- case IR::Opcode::GetCbufU32:
- info.used_constant_buffer_types |= IR::Type::U32;
- element_size = 4;
- break;
- case IR::Opcode::GetCbufF32:
- info.used_constant_buffer_types |= IR::Type::F32;
- element_size = 4;
- break;
- case IR::Opcode::GetCbufU32x2:
- info.used_constant_buffer_types |= IR::Type::U32x2;
- element_size = 8;
- break;
- default:
- break;
- }
- u32& size{info.constant_buffer_used_sizes[index.U32()]};
- if (offset.IsImmediate()) {
- size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
+ if (index.IsImmediate()) {
+ AddConstantBufferDescriptor(info, index.U32(), 1);
+ u32 element_size = GetElementSize(info.used_constant_buffer_types, inst.GetOpcode());
+ u32& size{info.constant_buffer_used_sizes[index.U32()]};
+ if (offset.IsImmediate()) {
+ size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
+ } else {
+ size = 0x10'000;
+ }
} else {
- size = 0x10'000;
+ AddRegisterIndexedLdc(info);
+ GetElementSize(info.used_indirect_cbuf_types, inst.GetOpcode());
}
break;
}
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 @@
#include <type_traits>
#include "common/bit_cast.h"
-#include "common/bit_util.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
+
+#include <boost/container/small_vector.hpp>
+
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"
namespace Shader::Optimization {
-
-void DeadCodeEliminationPass(IR::Program& program) {
+namespace {
+template <bool TEST_USES>
+void DeadInstElimination(IR::Block* const block) {
// We iterate over the instructions in reverse order.
// This is because removing an instruction reduces the number of uses for earlier instructions.
- for (IR::Block* const block : program.post_order_blocks) {
- auto it{block->end()};
- while (it != block->begin()) {
- --it;
- if (!it->HasUses() && !it->MayHaveSideEffects()) {
- it->Invalidate();
- it = block->Instructions().erase(it);
+ auto it{block->end()};
+ while (it != block->begin()) {
+ --it;
+ if constexpr (TEST_USES) {
+ if (it->HasUses() || it->MayHaveSideEffects()) {
+ continue;
+ }
+ }
+ it->Invalidate();
+ it = block->Instructions().erase(it);
+ }
+}
+
+void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
+ for (IR::Block* const block : program.blocks) {
+ for (IR::Inst& phi : *block) {
+ if (!IR::IsPhi(phi)) {
+ continue;
+ }
+ for (size_t i = 0; i < phi.NumArgs(); ++i) {
+ if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
+ continue;
+ }
+ // Phi operand at this index is an unreachable block
+ phi.ErasePhiOperand(i);
+ --i;
+ }
+ }
+ }
+}
+
+void DeadBranchElimination(IR::Program& program) {
+ boost::container::small_vector<const IR::Block*, 3> dead_blocks;
+ const auto begin_it{program.syntax_list.begin()};
+ for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
+ if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
+ continue;
+ }
+ IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
+ const IR::U1 cond{cond_ref->Arg(0)};
+ if (!cond.IsImmediate()) {
+ continue;
+ }
+ if (cond.U1()) {
+ continue;
+ }
+ // False immediate condition. Remove condition ref, erase the entire branch.
+ cond_ref->Invalidate();
+ // Account for nested if-statements within the if(false) branch
+ u32 nested_ifs{1u};
+ while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
+ node_it = program.syntax_list.erase(node_it);
+ switch (node_it->type) {
+ case IR::AbstractSyntaxNode::Type::If:
+ ++nested_ifs;
+ break;
+ case IR::AbstractSyntaxNode::Type::EndIf:
+ --nested_ifs;
+ break;
+ case IR::AbstractSyntaxNode::Type::Block: {
+ IR::Block* const block{node_it->data.block};
+ DeadInstElimination<false>(block);
+ dead_blocks.push_back(block);
+ break;
+ }
+ default:
+ break;
}
}
+ // Erase EndIf node of the if(false) branch
+ node_it = program.syntax_list.erase(node_it);
+ // Account for loop increment
+ --node_it;
+ }
+ if (!dead_blocks.empty()) {
+ DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
+ }
+}
+} // namespace
+
+void DeadCodeEliminationPass(IR::Program& program) {
+ DeadBranchElimination(program);
+ for (IR::Block* const block : program.post_order_blocks) {
+ DeadInstElimination<true>(block);
}
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-#include <compare>
#include <optional>
-#include <queue>
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
@@ -334,7 +331,8 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
/// Tries to track the storage buffer address used by a global memory instruction
std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) {
const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> {
- if (inst->GetOpcode() != IR::Opcode::GetCbufU32) {
+ if (inst->GetOpcode() != IR::Opcode::GetCbufU32 &&
+ inst->GetOpcode() != IR::Opcode::GetCbufU32x2) {
return std::nullopt;
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-
-#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"
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 @@
#pragma once
-#include <span>
-
#include "shader_recompiler/environment.h"
-#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/program.h"
namespace 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/alignment.h"
#include "common/settings.h"
#include "shader_recompiler/environment.h"
#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
}
}
+void ScaleIntegerOffsetComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled,
+ size_t index) {
+ const IR::Value composite{inst.Arg(index)};
+ if (composite.IsEmpty()) {
+ return;
+ }
+ const auto info{inst.Flags<IR::TextureInstInfo>()};
+ const IR::U32 x{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 0)})};
+ const IR::U32 y{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 1)})};
+ switch (info.type) {
+ case TextureType::ColorArray2D:
+ case TextureType::Color2D:
+ inst.SetArg(index, ir.CompositeConstruct(x, y));
+ break;
+ case TextureType::Color1D:
+ case TextureType::ColorArray1D:
+ case TextureType::Color3D:
+ case TextureType::ColorCube:
+ case TextureType::ColorArrayCube:
+ case TextureType::Buffer:
+ // Nothing to patch here
+ break;
+ }
+}
+
void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) {
const auto info{inst.Flags<IR::TextureInstInfo>()};
const IR::Value coord{inst.Arg(1)};
@@ -220,7 +244,7 @@ void SubScaleImageFetch(IR::Block& block, IR::Inst& inst) {
const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))};
SubScaleCoord(ir, inst, is_scaled);
// Scale ImageFetch offset
- ScaleIntegerComposite(ir, inst, is_scaled, 2);
+ ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2);
}
void SubScaleImageRead(IR::Block& block, IR::Inst& inst) {
@@ -242,7 +266,7 @@ void PatchImageFetch(IR::Block& block, IR::Inst& inst) {
const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))};
ScaleIntegerComposite(ir, inst, is_scaled, 1);
// Scale ImageFetch offset
- ScaleIntegerComposite(ir, inst, is_scaled, 2);
+ ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2);
}
void 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 @@
#include <vector>
#include <boost/container/flat_map.hpp>
-#include <boost/container/flat_set.hpp>
#include "shader_recompiler/frontend/ir/basic_block.h"
#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 {
return {(bits & 1) != 0, (bits & 2) != 0, (bits & 4) != 0, (bits & 8) != 0};
}
+ [[nodiscard]] bool HasOutputComponents(u32 rt) const noexcept {
+ const u32 bits{omap.target >> (rt * 4)};
+ return (bits & 0xf) != 0;
+ }
+
[[nodiscard]] std::array<PixelImap, 4> GenericInputMap(u32 attribute) const {
const auto& vector{imap_generic_vector[attribute]};
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 @@
#pragma once
#include <array>
-#include <bitset>
#include <optional>
#include <vector>
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 {
using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
struct Info {
+ static constexpr size_t MAX_INDIRECT_CBUFS{14};
static constexpr size_t MAX_CBUFS{18};
static constexpr size_t MAX_SSBOS{32};
@@ -173,9 +174,11 @@ struct Info {
bool uses_atomic_image_u32{};
bool uses_shadow_lod{};
bool uses_rescaling_uniform{};
+ bool uses_cbuf_indirect{};
IR::Type used_constant_buffer_types{};
IR::Type used_storage_buffer_types{};
+ IR::Type used_indirect_cbuf_types{};
u32 constant_buffer_mask{};
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:
void FlushCachedWrites() noexcept {
flags &= ~BufferFlagBits::CachedWrites;
const u64 num_words = NumWords();
- const u64* const cached_words = Array<Type::CachedCPU>();
+ u64* const cached_words = Array<Type::CachedCPU>();
u64* const untracked_words = Array<Type::Untracked>();
u64* const cpu_words = Array<Type::CPU>();
for (u64 word_index = 0; word_index < num_words; ++word_index) {
@@ -220,6 +220,7 @@ public:
NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
untracked_words[word_index] |= cached_bits;
cpu_words[word_index] |= cached_bits;
+ cached_words[word_index] = 0;
}
}
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 @@
#include <mutex>
#include <numeric>
#include <span>
-#include <unordered_map>
#include <vector>
#include <boost/container/small_vector.hpp>
@@ -22,7 +21,6 @@
#include "common/literals.h"
#include "common/lru_cache.h"
#include "common/microprofile.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/buffer_cache/buffer_base.h"
@@ -78,8 +76,9 @@ class BufferCache {
static constexpr BufferId NULL_BUFFER_ID{0};
- static constexpr u64 EXPECTED_MEMORY = 512_MiB;
- static constexpr u64 CRITICAL_MEMORY = 1_GiB;
+ static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
+ static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
+ static constexpr s64 TARGET_THRESHOLD = 4_GiB;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
@@ -438,6 +437,8 @@ private:
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0;
u64 total_used_memory = 0;
+ u64 minimum_memory = 0;
+ u64 critical_memory = 0;
std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
};
@@ -453,11 +454,30 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
// Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{}));
common_ranges.clear();
+
+ if (!runtime.CanReportMemoryUsage()) {
+ minimum_memory = DEFAULT_EXPECTED_MEMORY;
+ critical_memory = DEFAULT_CRITICAL_MEMORY;
+ return;
+ }
+
+ const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
+ const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
+ const s64 min_spacing_critical = device_memory - 1_GiB;
+ const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
+ const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
+ const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
+ minimum_memory = static_cast<u64>(
+ std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
+ DEFAULT_EXPECTED_MEMORY));
+ critical_memory = static_cast<u64>(
+ std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
+ DEFAULT_CRITICAL_MEMORY));
}
template <class P>
void BufferCache<P>::RunGarbageCollector() {
- const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
+ const bool aggressive_gc = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
@@ -488,7 +508,11 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
- if (total_used_memory >= EXPECTED_MEMORY) {
+ // If we can obtain the memory info, use it instead of the estimate.
+ if (runtime.CanReportMemoryUsage()) {
+ total_used_memory = runtime.GetDeviceMemoryUsage();
+ }
+ if (total_used_memory >= minimum_memory) {
RunGarbageCollector();
}
++frame_tick;
@@ -1287,7 +1311,20 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
const GPUVAddr gpu_addr_begin = array.StartAddress();
const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1;
const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
- const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
+ u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
+ if (address_size >= 64_MiB) {
+ // Reported vertex buffer size is very large, cap to mapped buffer size
+ GPUVAddr submapped_addr_end = gpu_addr_begin;
+
+ const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)};
+ if (ranges.size() > 0) {
+ const auto& [addr, size] = *ranges.begin();
+ submapped_addr_end = addr + size;
+ }
+
+ address_size =
+ std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin));
+ }
const u32 size = address_size; // TODO: Analyze stride and number of vertices
if (array.enable == 0 || size == 0 || !cpu_addr) {
vertex_buffers[index] = NULL_BINDING;
@@ -1469,19 +1506,27 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
overlap_ids.push_back(overlap_id);
overlap.Pick();
const VAddr overlap_cpu_addr = overlap.CpuAddr();
- if (overlap_cpu_addr < begin) {
+ const bool expands_left = overlap_cpu_addr < begin;
+ if (expands_left) {
cpu_addr = begin = overlap_cpu_addr;
}
- end = std::max(end, overlap_cpu_addr + overlap.SizeBytes());
-
+ const VAddr overlap_end = overlap_cpu_addr + overlap.SizeBytes();
+ const bool expands_right = overlap_end > end;
+ if (overlap_end > end) {
+ end = overlap_end;
+ }
stream_score += overlap.StreamScore();
if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) {
// When this memory region has been joined a bunch of times, we assume it's being used
// as a stream buffer. Increase the size to skip constantly recreating buffers.
has_stream_leap = true;
- begin -= PAGE_SIZE * 256;
- cpu_addr = begin;
- end += PAGE_SIZE * 256;
+ if (expands_right) {
+ begin -= PAGE_SIZE * 256;
+ cpu_addr = begin;
+ }
+ if (expands_left) {
+ end += PAGE_SIZE * 256;
+ }
}
}
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 @@
#include "command_classes/nvdec.h"
#include "command_classes/vic.h"
#include "video_core/cdma_pusher.h"
-#include "video_core/command_classes/nvdec_common.h"
#include "video_core/command_classes/sync_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
-#include "video_core/memory_manager.h"
namespace Tegra {
CDmaPusher::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 @@
#include <vector>
#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
namespace 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 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <cstdio>
#include <fstream>
#include <vector>
#include "common/assert.h"
@@ -57,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi
av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
return PREFERRED_CPU_FMT;
}
+
+// List all the currently available hwcontext in ffmpeg
+std::vector<AVHWDeviceType> ListSupportedContexts() {
+ std::vector<AVHWDeviceType> contexts{};
+ AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
+ do {
+ current_device_type = av_hwdevice_iterate_types(current_device_type);
+ contexts.push_back(current_device_type);
+ } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
+ return contexts;
+}
+
} // namespace
void AVFrameDeleter(AVFrame* ptr) {
@@ -77,17 +88,6 @@ Codec::~Codec() {
av_buffer_unref(&av_gpu_decoder);
}
-// List all the currently available hwcontext in ffmpeg
-static std::vector<AVHWDeviceType> ListSupportedContexts() {
- std::vector<AVHWDeviceType> contexts{};
- AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
- do {
- current_device_type = av_hwdevice_iterate_types(current_device_type);
- contexts.push_back(current_device_type);
- } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
- return contexts;
-}
-
bool Codec::CreateGpuAvDevice() {
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
static const auto supported_contexts = ListSupportedContexts();
@@ -97,6 +97,8 @@ bool Codec::CreateGpuAvDevice() {
LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
+ // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
+ av_buffer_unref(&av_gpu_decoder);
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
if (hwdevice_res < 0) {
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
@@ -128,15 +130,19 @@ bool Codec::CreateGpuAvDevice() {
av_codec->name, av_hwdevice_get_type_name(type));
break;
}
- if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
- av_codec_ctx->pix_fmt = config->pix_fmt;
- if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
+ if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
+#if defined(__unix__)
+ // Some linux decoding backends are reported to crash with this config method
+ // TODO(ameerj): Properly support this method
+ if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
// skip zero-copy decoders, we don't currently support them
LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
av_hwdevice_get_type_name(type), config->methods);
continue;
}
+#endif
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
+ av_codec_ctx->pix_fmt = config->pix_fmt;
return true;
}
}
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 @@
#include <memory>
#include <string_view>
#include <queue>
-#include "common/common_types.h"
+
#include "video_core/command_classes/nvdec_common.h"
extern "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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <array>
#include <vector>
#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 @@
#pragma once
#include <array>
-#include <cstring>
#include <vector>
#include "common/common_funcs.h"
#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 @@
#pragma once
-#include <vector>
-#include "common/common_funcs.h"
#include "common/common_types.h"
namespace 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 @@
#include "common/microprofile.h"
#include "common/settings.h"
#include "core/core.h"
-#include "core/memory.h"
#include "video_core/dma_pusher.h"
#include "video_core/engines/maxwell_3d.h"
#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 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/math_util.h"
#include "video_core/engines/engine_interface.h"
#include "video_core/gpu.h"
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 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
-#include "video_core/renderer_base.h"
#include "video_core/textures/decoders.h"
namespace 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 @@
#include "common/common_types.h"
#include "video_core/engines/engine_interface.h"
#include "video_core/engines/engine_upload.h"
-#include "video_core/gpu.h"
#include "video_core/textures/texture.h"
namespace 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 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
-#include "video_core/renderer_base.h"
-#include "video_core/textures/decoders.h"
namespace Tegra::Engines {
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 @@
#include <array>
#include <cstddef>
-#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/engines/engine_interface.h"
#include "video_core/engines/engine_upload.h"
-#include "video_core/gpu.h"
namespace Core {
class 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 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -195,7 +196,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
- return StartCBData(method);
+ return ProcessCBData(argument);
case MAXWELL3D_REG_INDEX(cb_bind[0]):
return ProcessCBBind(0);
case MAXWELL3D_REG_INDEX(cb_bind[1]):
@@ -208,6 +209,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return ProcessCBBind(4);
case MAXWELL3D_REG_INDEX(draw.vertex_end_gl):
return DrawArrays();
+ case MAXWELL3D_REG_INDEX(small_index):
+ regs.index_array.count = regs.small_index.count;
+ regs.index_array.first = regs.small_index.first;
+ dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
+ return DrawArrays();
+ case MAXWELL3D_REG_INDEX(small_index_2):
+ regs.index_array.count = regs.small_index_2.count;
+ regs.index_array.first = regs.small_index_2.first;
+ dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
+ return DrawArrays();
+ case MAXWELL3D_REG_INDEX(topology_override):
+ use_topology_override = true;
+ return;
case MAXWELL3D_REG_INDEX(clear_buffers):
return ProcessClearBuffers();
case MAXWELL3D_REG_INDEX(query.query_get):
@@ -248,14 +262,6 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
}
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
- if (method == cb_data_state.current) {
- regs.reg_array[method] = method_argument;
- ProcessCBData(method_argument);
- return;
- } else if (cb_data_state.current != null_cb_data) {
- FinishCBData();
- }
-
// It is an error to write to a register other than the current macro's ARG register before it
// has finished execution.
if (executing_macro != 0) {
@@ -302,7 +308,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
- ProcessCBMultiData(method, base_start, amount);
+ ProcessCBMultiData(base_start, amount);
break;
default:
for (std::size_t i = 0; i < amount; i++) {
@@ -360,6 +366,35 @@ void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
}
}
+void Maxwell3D::ProcessTopologyOverride() {
+ using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
+ using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
+
+ PrimitiveTopology topology{};
+
+ switch (regs.topology_override) {
+ case PrimitiveTopologyOverride::None:
+ topology = regs.draw.topology;
+ break;
+ case PrimitiveTopologyOverride::Points:
+ topology = PrimitiveTopology::Points;
+ break;
+ case PrimitiveTopologyOverride::Lines:
+ topology = PrimitiveTopology::Lines;
+ break;
+ case PrimitiveTopologyOverride::LineStrip:
+ topology = PrimitiveTopology::LineStrip;
+ break;
+ default:
+ topology = static_cast<PrimitiveTopology>(regs.topology_override);
+ break;
+ }
+
+ if (use_topology_override) {
+ regs.draw.topology.Assign(topology);
+ }
+}
+
void Maxwell3D::FlushMMEInlineDraw() {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
regs.vertex_buffer.count);
@@ -370,6 +405,8 @@ void Maxwell3D::FlushMMEInlineDraw() {
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
"Illegal combination of instancing parameters");
+ ProcessTopologyOverride();
+
const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
if (ShouldExecute()) {
rasterizer->Draw(is_indexed, true);
@@ -529,6 +566,8 @@ void Maxwell3D::DrawArrays() {
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
"Illegal combination of instancing parameters");
+ ProcessTopologyOverride();
+
if (regs.draw.instance_next) {
// Increment the current instance *before* drawing.
state.current_instance += 1;
@@ -587,46 +626,7 @@ void Maxwell3D::ProcessCBBind(size_t stage_index) {
rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size);
}
-void Maxwell3D::ProcessCBData(u32 value) {
- const u32 id = cb_data_state.id;
- cb_data_state.buffer[id][cb_data_state.counter] = value;
- // Increment the current buffer position.
- regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
- cb_data_state.counter++;
-}
-
-void Maxwell3D::StartCBData(u32 method) {
- constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
- cb_data_state.start_pos = regs.const_buffer.cb_pos;
- cb_data_state.id = method - first_cb_data;
- cb_data_state.current = method;
- cb_data_state.counter = 0;
- ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]);
-}
-
-void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) {
- if (cb_data_state.current != method) {
- if (cb_data_state.current != null_cb_data) {
- FinishCBData();
- }
- constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
- cb_data_state.start_pos = regs.const_buffer.cb_pos;
- cb_data_state.id = method - first_cb_data;
- cb_data_state.current = method;
- cb_data_state.counter = 0;
- }
- const std::size_t id = cb_data_state.id;
- const std::size_t size = amount;
- std::size_t i = 0;
- for (; i < size; i++) {
- cb_data_state.buffer[id][cb_data_state.counter] = start_base[i];
- cb_data_state.counter++;
- }
- // Increment the current buffer position.
- regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount;
-}
-
-void Maxwell3D::FinishCBData() {
+void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
// Write the input value to the current const buffer at the current position.
const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
ASSERT(buffer_address != 0);
@@ -634,14 +634,16 @@ void Maxwell3D::FinishCBData() {
// Don't allow writing past the end of the buffer.
ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size);
- const GPUVAddr address{buffer_address + cb_data_state.start_pos};
- const std::size_t size = regs.const_buffer.cb_pos - cb_data_state.start_pos;
+ const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
+ const size_t copy_size = amount * sizeof(u32);
+ memory_manager.WriteBlock(address, start_base, copy_size);
- const u32 id = cb_data_state.id;
- memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
+ // Increment the current buffer position.
+ regs.const_buffer.cb_pos += static_cast<u32>(copy_size);
+}
- cb_data_state.id = null_cb_data;
- cb_data_state.current = null_cb_data;
+void Maxwell3D::ProcessCBData(u32 value) {
+ ProcessCBMultiData(&value, 1);
}
Texture::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 @@
#include <limits>
#include <optional>
#include <type_traits>
-#include <unordered_map>
#include <vector>
#include "common/assert.h"
@@ -367,6 +366,22 @@ public:
Patches = 0xe,
};
+ // Constants as from NVC0_3D_UNK1970_D3D
+ // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598
+ enum class PrimitiveTopologyOverride : u32 {
+ None = 0x0,
+ Points = 0x1,
+ Lines = 0x2,
+ LineStrip = 0x3,
+ Triangles = 0x4,
+ TriangleStrip = 0x5,
+ LinesAdjacency = 0xa,
+ LineStripAdjacency = 0xb,
+ TrianglesAdjacency = 0xc,
+ TriangleStripAdjacency = 0xd,
+ Patches = 0xe,
+ };
+
enum class IndexFormat : u32 {
UnsignedByte = 0x0,
UnsignedShort = 0x1,
@@ -1200,7 +1215,17 @@ public:
}
} index_array;
- INSERT_PADDING_WORDS_NOINIT(0x7);
+ union {
+ BitField<0, 16, u32> first;
+ BitField<16, 16, u32> count;
+ } small_index;
+
+ union {
+ BitField<0, 16, u32> first;
+ BitField<16, 16, u32> count;
+ } small_index_2;
+
+ INSERT_PADDING_WORDS_NOINIT(0x5);
INSERT_PADDING_WORDS_NOINIT(0x1F);
@@ -1244,7 +1269,11 @@ public:
BitField<11, 1, u32> depth_clamp_disabled;
} view_volume_clip_control;
- INSERT_PADDING_WORDS_NOINIT(0x1F);
+ INSERT_PADDING_WORDS_NOINIT(0xC);
+
+ PrimitiveTopologyOverride topology_override;
+
+ INSERT_PADDING_WORDS_NOINIT(0x12);
u32 depth_bounds_enable;
@@ -1520,10 +1549,8 @@ private:
void ProcessSyncPoint();
/// Handles a write to the CB_DATA[i] register.
- void StartCBData(u32 method);
void ProcessCBData(u32 value);
- void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount);
- void FinishCBData();
+ void ProcessCBMultiData(const u32* start_base, u32 amount);
/// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index);
@@ -1531,6 +1558,9 @@ private:
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
+ /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
+ void ProcessTopologyOverride();
+
// Handles a instance drawcall from MME
void StepInstance(MMEDrawMode expected_mode, u32 count);
@@ -1555,20 +1585,10 @@ private:
/// Interpreter for the macro codes uploaded to the GPU.
std::unique_ptr<MacroEngine> macro_engine;
- static constexpr u32 null_cb_data = 0xFFFFFFFF;
- struct CBDataState {
- static constexpr size_t inline_size = 0x4000;
- std::array<std::array<u32, inline_size>, 16> buffer;
- u32 current{null_cb_data};
- u32 id{null_cb_data};
- u32 start_pos{};
- u32 counter{};
- };
- CBDataState cb_data_state;
-
Upload::State upload_state;
bool execute_on{true};
+ bool use_topology_override{false};
};
#define ASSERT_REG_POSITION(field_name, position) \
@@ -1685,6 +1705,7 @@ ASSERT_REG_POSITION(draw, 0x585);
ASSERT_REG_POSITION(primitive_restart, 0x591);
ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1);
ASSERT_REG_POSITION(index_array, 0x5F2);
+ASSERT_REG_POSITION(small_index, 0x5F9);
ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
ASSERT_REG_POSITION(instanced_arrays, 0x620);
ASSERT_REG_POSITION(vp_point_size, 0x644);
@@ -1694,6 +1715,7 @@ ASSERT_REG_POSITION(cull_face, 0x648);
ASSERT_REG_POSITION(pixel_center_integer, 0x649);
ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
+ASSERT_REG_POSITION(topology_override, 0x65C);
ASSERT_REG_POSITION(depth_bounds_enable, 0x66F);
ASSERT_REG_POSITION(logic_op, 0x671);
ASSERT_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() {
// TODO(Subv): Perform more research and implement all features of this engine.
const LaunchDMA& launch = regs.launch_dma;
- ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
ASSERT(regs.dst_params.origin.x == 0);
@@ -79,6 +78,7 @@ void MaxwellDMA::Launch() {
CopyPitchToBlockLinear();
}
}
+ ReleaseSemaphore();
}
void MaxwellDMA::CopyPitchToPitch() {
@@ -244,4 +244,22 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
+void MaxwellDMA::ReleaseSemaphore() {
+ const auto type = regs.launch_dma.semaphore_type;
+ const GPUVAddr address = regs.semaphore.address;
+ switch (type) {
+ case LaunchDMA::SemaphoreType::NONE:
+ break;
+ case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE:
+ memory_manager.Write<u32>(address, regs.semaphore.payload);
+ break;
+ case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE:
+ memory_manager.Write<u64>(address, static_cast<u64>(regs.semaphore.payload));
+ memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
+ break;
+ default:
+ UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value()));
+ }
+}
+
} // 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 @@
#include <cstddef>
#include <vector>
#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/engines/engine_interface.h"
-#include "video_core/gpu.h"
namespace Core {
class System;
@@ -224,6 +222,8 @@ private:
void FastCopyBlockLinearToPitch();
+ void ReleaseSemaphore();
+
Core::System& system;
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 @@
#include <queue>
#include "common/common_types.h"
-#include "common/settings.h"
-#include "core/core.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/gpu.h"
#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 @@
#include "common/common_types.h"
#include "common/math_util.h"
+#include "core/hle/service/nvflinger/buffer_transform_flags.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
namespace Tegra {
+
/**
* Struct describing framebuffer configuration
*/
struct FramebufferConfig {
- enum class PixelFormat : u32 {
- A8B8G8R8_UNORM = 1,
- RGB565_UNORM = 4,
- B8G8R8A8_UNORM = 5,
- };
-
- enum class TransformFlags : u32 {
- /// No transform flags are set
- Unset = 0x00,
- /// Flip source image horizontally (around the vertical axis)
- FlipH = 0x01,
- /// Flip source image vertically (around the horizontal axis)
- FlipV = 0x02,
- /// Rotate source image 90 degrees clockwise
- Rotate90 = 0x04,
- /// Rotate source image 180 degrees
- Rotate180 = 0x03,
- /// Rotate source image 270 degrees clockwise
- Rotate270 = 0x07,
- };
-
VAddr address{};
u32 offset{};
u32 width{};
u32 height{};
u32 stride{};
- PixelFormat pixel_format{};
-
- TransformFlags transform_flags{};
+ Service::android::PixelFormat pixel_format{};
+ Service::android::BufferTransformFlags transform_flags{};
Common::Rectangle<int> crop_rect;
};
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 {
void IncrementSyncPoint(u32 syncpoint_id) {
auto& syncpoint = syncpoints.at(syncpoint_id);
syncpoint++;
- std::lock_guard lock{sync_mutex};
+ std::scoped_lock lock{sync_mutex};
sync_cv.notify_all();
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
if (!interrupt.empty()) {
@@ -252,7 +252,7 @@ struct GPU::Impl {
}
void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
- std::lock_guard lock{sync_mutex};
+ std::scoped_lock lock{sync_mutex};
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
bool contains = std::any_of(interrupt.begin(), interrupt.end(),
[value](u32 in_value) { return in_value == value; });
@@ -263,7 +263,7 @@ struct GPU::Impl {
}
[[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
- std::lock_guard lock{sync_mutex};
+ std::scoped_lock lock{sync_mutex};
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
const auto iter =
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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/cdma_pusher.h"
#include "video_core/framebuffer_config.h"
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,
if (next.block) {
// We have to lock the write_lock to ensure that the condition_variable wait not get a
// race between the check and the lock itself.
- std::lock_guard lk(state.write_lock);
+ std::scoped_lock lk{state.write_lock};
state.cv.notify_all();
}
}
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
convert_d24s8_to_abgr8.frag
convert_depth_to_float.frag
convert_float_to_depth.frag
+ convert_s8d24_to_abgr8.frag
full_screen_triangle.vert
fxaa.frag
fxaa.vert
+ opengl_convert_s8d24.comp
opengl_copy_bc4.comp
opengl_present.frag
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#version 450
+
+layout(binding = 0) uniform sampler2D depth_tex;
+layout(binding = 1) uniform isampler2D stencil_tex;
+
+layout(location = 0) out vec4 color;
+
+void main() {
+ ivec2 coord = ivec2(gl_FragCoord.xy);
+ uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
+ uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
+
+ highp uint depth_val =
+ uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
+ lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
+ highp uvec4 components =
+ uvec4((uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu, stencil_val);
+ color.rgba = vec4(components) / (exp2(8.0) - 1.0);
+}
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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#version 430 core
+
+layout(local_size_x = 16, local_size_y = 8) in;
+
+layout(binding = 0, rgba8ui) restrict uniform uimage2D destination;
+layout(location = 0) uniform uvec3 size;
+
+void main() {
+ if (any(greaterThanEqual(gl_GlobalInvocationID, size))) {
+ return;
+ }
+ uvec4 components = imageLoad(destination, ivec2(gl_GlobalInvocationID.xy));
+ imageStore(destination, ivec2(gl_GlobalInvocationID.xy), components.wxyz);
+}
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 @@
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
-#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#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 @@
#include "common/assert.h"
#include "common/settings.h"
-#include "core/core.h"
#include "video_core/engines/maxwell_3d.h"
-#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
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 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
-#include "common/settings.h"
#include "core/frontend/emu_window.h"
#include "video_core/renderer_base.h"
@@ -27,6 +26,10 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
}
+bool RendererBase::IsScreenshotPending() const {
+ return renderer_settings.screenshot_requested;
+}
+
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout) {
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:
/// Refreshes the settings common to all renderers
void RefreshBaseSettings();
+ /// Returns true if a screenshot is being processed
+ bool IsScreenshotPending() const;
+
/// Request a screenshot of the next frame
void RequestScreenshot(void* data, std::function<void(bool)> callback,
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_)
buffer.Create();
glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY);
}
+
+ device_access_memory = [this]() -> u64 {
+ if (device.CanReportMemoryUsage()) {
+ return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
+ }
+ return 2_GiB; // Return minimum requirements
+ }();
+}
+
+u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
+ if (device.CanReportMemoryUsage()) {
+ return device_access_memory - device.GetCurrentDedicatedVideoMemory();
+ }
+ return 2_GiB;
}
void 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 @@
#include <array>
#include <span>
-#include "common/alignment.h"
#include "common/common_types.h"
-#include "common/dynamic_library.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_device.h"
@@ -91,6 +89,8 @@ public:
void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
VideoCore::Surface::PixelFormat format);
+ u64 GetDeviceMemoryUsage() const;
+
void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
const GLuint handle = fast_uniforms[stage][binding_index].handle;
const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
@@ -153,6 +153,14 @@ public:
use_storage_buffers = use_storage_buffers_;
}
+ u64 GetDeviceLocalMemory() const {
+ return device_access_memory;
+ }
+
+ bool CanReportMemoryUsage() const {
+ return device.CanReportMemoryUsage();
+ }
+
private:
static constexpr std::array PABO_LUT{
GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
@@ -186,6 +194,8 @@ private:
std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms;
u32 index_buffer_offset = 0;
+
+ u64 device_access_memory;
};
struct 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 @@
#include <array>
#include <type_traits>
-#include <utility>
#include "common/common_types.h"
#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 @@
#include <array>
#include <cstddef>
#include <cstdlib>
-#include <cstring>
-#include <limits>
#include <optional>
#include <span>
#include <stdexcept>
@@ -15,13 +13,15 @@
#include <glad/glad.h>
+#include "common/literals.h"
#include "common/logging/log.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
#include "shader_recompiler/stage.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
+using namespace Common::Literals;
+
namespace OpenGL {
namespace {
constexpr std::array LIMIT_UBOS = {
@@ -168,6 +168,7 @@ Device::Device() {
has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
need_fastmath_off = is_nvidia;
+ can_report_memory = GLAD_GL_NVX_gpu_memory_info;
// At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
// uniform buffers as "push constants"
@@ -279,4 +280,10 @@ void main() {
})");
}
+u64 Device::GetCurrentDedicatedVideoMemory() const {
+ GLint cur_avail_mem_kb = 0;
+ glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb);
+ return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
+}
+
} // 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:
[[nodiscard]] std::string GetVendorName() const;
+ u64 GetCurrentDedicatedVideoMemory() const;
+
u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept {
return max_uniform_buffers[static_cast<size_t>(stage)];
}
@@ -168,6 +170,10 @@ public:
return vendor_name == "ATI Technologies Inc.";
}
+ bool CanReportMemoryUsage() const {
+ return can_report_memory;
+ }
+
private:
static bool TestVariableAoffi();
static bool TestPreciseBug();
@@ -210,6 +216,7 @@ private:
bool need_fastmath_off{};
bool has_cbuf_ftou_bug{};
bool has_bool_ref_bug{};
+ bool can_report_memory{};
std::string vendor_name;
};
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(
case Settings::ShaderBackend::GLASM:
if (!sources[stage].empty()) {
assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage));
- if (in_parallel) {
- // Make sure program is built before continuing when building in parallel
- glGetString(GL_PROGRAM_ERROR_STRING_NV);
- }
}
break;
case Settings::ShaderBackend::SPIRV:
@@ -256,20 +252,18 @@ GraphicsPipeline::GraphicsPipeline(
break;
}
}
- if (in_parallel && backend != Settings::ShaderBackend::GLASM) {
- // Make sure programs have built if we are building shaders in parallel
- for (OGLProgram& program : source_programs) {
- if (program.handle != 0) {
- GLint status{};
- glGetProgramiv(program.handle, GL_LINK_STATUS, &status);
- }
- }
+ if (in_parallel) {
+ std::scoped_lock lock{built_mutex};
+ built_fence.Create();
+ // Flush this context to ensure compilation commands and fence are in the GPU pipe.
+ glFlush();
+ built_condvar.notify_one();
+ } else {
+ is_built = true;
}
if (shader_notify) {
shader_notify->MarkShaderComplete();
}
- is_built = true;
- built_condvar.notify_one();
}};
if (thread_worker) {
thread_worker->QueueWork(std::move(func));
@@ -440,7 +434,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed);
- if (!is_built.load(std::memory_order::relaxed)) {
+ if (!IsBuilt()) {
WaitForBuild();
}
const bool use_assembly{assembly_programs[0].handle != 0};
@@ -585,8 +579,26 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
}
void GraphicsPipeline::WaitForBuild() {
- std::unique_lock lock{built_mutex};
- built_condvar.wait(lock, [this] { return is_built.load(std::memory_order::relaxed); });
+ if (built_fence.handle == 0) {
+ std::unique_lock lock{built_mutex};
+ built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
+ }
+ ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
+ is_built = true;
+}
+
+bool GraphicsPipeline::IsBuilt() noexcept {
+ if (is_built) {
+ return true;
+ }
+ if (built_fence.handle == 0) {
+ return false;
+ }
+ // Timeout of zero means this is non-blocking
+ const auto sync_status = glClientWaitSync(built_fence.handle, 0, 0);
+ ASSERT(sync_status != GL_WAIT_FAILED);
+ is_built = sync_status != GL_TIMEOUT_EXPIRED;
+ return is_built;
}
} // 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 @@
#include "common/common_types.h"
#include "shader_recompiler/shader_info.h"
#include "video_core/engines/maxwell_3d.h"
-#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
@@ -100,9 +99,7 @@ public:
return writes_global_memory;
}
- [[nodiscard]] bool IsBuilt() const noexcept {
- return is_built.load(std::memory_order::relaxed);
- }
+ [[nodiscard]] bool IsBuilt() noexcept;
template <typename Spec>
static auto MakeConfigureSpecFunc() {
@@ -154,7 +151,8 @@ private:
std::mutex built_mutex;
std::condition_variable built_condvar;
- std::atomic_bool is_built{false};
+ OGLSync built_fence{};
+ bool is_built{false};
};
} // 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 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <cstring>
#include <memory>
-#include <unordered_map>
#include <utility>
#include <vector>
#include <glad/glad.h>
-#include "common/assert.h"
#include "core/core.h"
#include "video_core/engines/maxwell_3d.h"
#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 @@
#include <array>
#include <bitset>
#include <memory>
-#include <string>
#include <string_view>
-#include <tuple>
#include <utility>
#include <glad/glad.h>
@@ -17,8 +15,9 @@
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/microprofile.h"
+#include "common/scope_exit.h"
#include "common/settings.h"
-#include "core/memory.h"
+
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@@ -212,6 +211,7 @@ void RasterizerOpenGL::Clear() {
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
+ SCOPE_EXIT({ gpu.TickWork(); });
query_cache.UpdateCounters();
GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -267,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
++num_queued_commands;
has_written_global_memory |= pipeline->WritesGlobalMemory();
-
- gpu.TickWork();
}
void RasterizerOpenGL::DispatchCompute() {
@@ -522,6 +520,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
// ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different");
// ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different");
+ screen_info.texture.width = image_view->size.width;
+ screen_info.texture.height = image_view->size.height;
screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format);
return true;
@@ -559,12 +559,19 @@ void RasterizerOpenGL::SyncViewport() {
const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports;
const bool dirty_clip_control = flags[Dirty::ClipControl];
- if (dirty_clip_control || flags[Dirty::FrontFace]) {
+ if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) {
flags[Dirty::FrontFace] = false;
GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
+ bool flip_faces = false;
if (regs.screen_y_control.triangle_rast_flip != 0 &&
regs.viewport_transform[0].scale_y < 0.0f) {
+ flip_faces = !flip_faces;
+ }
+ if (regs.viewport_transform[0].scale_z < 0.0f) {
+ flip_faces = !flip_faces;
+ }
+ if (flip_faces) {
switch (mode) {
case GL_CW:
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 @@
#pragma once
#include <array>
-#include <atomic>
#include <cstddef>
-#include <memory>
#include <optional>
-#include <tuple>
-#include <utility>
#include <boost/container/static_vector.hpp>
#include <glad/glad.h>
#include "common/common_types.h"
-#include "video_core/engines/const_buffer_info.h"
-#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/rasterizer_accelerated.h"
#include "video_core/rasterizer_interface.h"
@@ -26,12 +20,8 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_fence_manager.h"
#include "video_core/renderer_opengl/gl_query_cache.h"
-#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
-#include "video_core/renderer_opengl/gl_shader_manager.h"
-#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
-#include "video_core/textures/texture.h"
namespace Core::Memory {
class 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 @@
// Refer to the license.txt file included.
#include <string_view>
-#include <utility>
#include <glad/glad.h>
-#include "common/common_types.h"
#include "common/microprofile.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#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 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread_worker.h"
-#include "core/core.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h"
#include "shader_recompiler/backend/glsl/emit_glsl.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
@@ -29,7 +27,6 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
-#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
@@ -261,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
[this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
ctx->pools.ReleaseContents();
auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
- std::lock_guard lock{state.mutex};
+ std::scoped_lock lock{state.mutex};
if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
}
@@ -283,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
}
ctx->pools.ReleaseContents();
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
- std::lock_guard lock{state.mutex};
+ std::scoped_lock lock{state.mutex};
if (pipeline) {
graphics_cache.emplace(key, std::move(pipeline));
}
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 @@
#pragma once
-#include <array>
#include <filesystem>
#include <stop_token>
#include <unordered_map>
-#include <glad/glad.h>
-
#include "common/common_types.h"
#include "common/thread_worker.h"
-#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/host_translate_info.h"
-#include "shader_recompiler/object_pool.h"
#include "shader_recompiler/profile.h"
#include "video_core/renderer_opengl/gl_compute_pipeline.h"
#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 @@
#include <vector>
#include <glad/glad.h>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/settings.h"
#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 @@
#pragma once
#include <span>
-#include <string>
#include <string_view>
-#include <vector>
#include <glad/glad.h>
-#include "common/assert.h"
-#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
namespace 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 @@
#include <glad/glad.h>
#include "common/common_types.h"
-#include "core/core.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
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 @@
#pragma once
#include <array>
-#include <memory>
#include <span>
#include <utility>
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) {
}
}
+GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
+ switch (source) {
+ case SwizzleSource::Zero:
+ return GL_ZERO;
+ case SwizzleSource::R:
+ return GL_ALPHA;
+ case SwizzleSource::G:
+ return GL_BLUE;
+ case SwizzleSource::B:
+ return GL_GREEN;
+ case SwizzleSource::A:
+ return GL_RED;
+ case SwizzleSource::OneInt:
+ case SwizzleSource::OneFloat:
+ return GL_ONE;
+ }
+ UNREACHABLE_MSG("Invalid swizzle source={}", source);
+ return GL_NONE;
+}
+
void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) {
switch (format) {
case PixelFormat::D24_UNORM_S8_UINT:
@@ -192,6 +212,12 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
TextureMode(format, swizzle[0] == SwizzleSource::R));
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
break;
+ case PixelFormat::A5B5G5R1_UNORM: {
+ std::array<GLint, 4> gl_swizzle;
+ std::ranges::transform(swizzle, gl_swizzle.begin(), ConvertA5B5G5R1_UNORM);
+ glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
+ return;
+ }
default:
break;
}
@@ -409,8 +435,8 @@ ImageBufferMap::~ImageBufferMap() {
TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager,
StateTracker& state_tracker_)
- : device{device_}, state_tracker{state_tracker_},
- util_shaders(program_manager), resolution{Settings::values.resolution_info} {
+ : device{device_}, state_tracker{state_tracker_}, util_shaders(program_manager),
+ format_conversion_pass{util_shaders}, resolution{Settings::values.resolution_info} {
static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D};
for (size_t i = 0; i < TARGETS.size(); ++i) {
const GLenum target = TARGETS[i];
@@ -484,6 +510,13 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
rescale_read_fbos[i].Create();
}
}
+
+ device_access_memory = [this]() -> u64 {
+ if (device.CanReportMemoryUsage()) {
+ return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
+ }
+ return 2_GiB; // Return minimum requirements
+ }();
}
TextureCacheRuntime::~TextureCacheRuntime() = default;
@@ -500,13 +533,11 @@ ImageBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
return download_buffers.RequestMap(size, false);
}
-u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
- if (GLAD_GL_NVX_gpu_memory_info) {
- GLint cur_avail_mem_kb = 0;
- glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb);
- return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
+u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
+ if (device.CanReportMemoryUsage()) {
+ return device_access_memory - device.GetCurrentDedicatedVideoMemory();
}
- return 2_GiB; // Return minimum requirements
+ return 2_GiB;
}
void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
@@ -686,6 +717,7 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
}
if (IsConverted(runtime->device, info.format, info.type)) {
flags |= ImageFlagBits::Converted;
+ flags |= ImageFlagBits::CostlyLoad;
gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
@@ -1319,6 +1351,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
Framebuffer::~Framebuffer() = default;
+FormatConversionPass::FormatConversionPass(UtilShaders& util_shaders_)
+ : util_shaders{util_shaders_} {}
+
void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies) {
const GLenum dst_target = ImageTarget(dst_image.info);
@@ -1351,6 +1386,12 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
dst_origin.z, region.width, region.height, region.depth,
dst_image.GlFormat(), dst_image.GlType(), nullptr);
}
+
+ // Swap component order of S8D24 to ABGR8 reinterprets
+ if (src_image.info.format == PixelFormat::D24_UNORM_S8_UINT &&
+ dst_image.info.format == PixelFormat::A8B8G8R8_UNORM) {
+ util_shaders.ConvertS8D24(dst_image, copies);
+ }
}
} // 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 @@
#include <glad/glad.h>
#include "shader_recompiler/shader_info.h"
+#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/util_shaders.h"
#include "video_core/texture_cache/image_view_base.h"
@@ -21,7 +22,6 @@ struct ResolutionScalingInfo;
namespace OpenGL {
-class Device;
class ProgramManager;
class StateTracker;
@@ -55,13 +55,14 @@ struct FormatProperties {
class FormatConversionPass {
public:
- FormatConversionPass() = default;
+ explicit FormatConversionPass(UtilShaders& util_shaders);
~FormatConversionPass() = default;
void ConvertImage(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
private:
+ UtilShaders& util_shaders;
OGLBuffer intermediate_pbo;
size_t pbo_size{};
};
@@ -83,7 +84,15 @@ public:
ImageBufferMap DownloadStagingBuffer(size_t size);
- u64 GetDeviceLocalMemory() const;
+ u64 GetDeviceLocalMemory() const {
+ return device_access_memory;
+ }
+
+ u64 GetDeviceMemoryUsage() const;
+
+ bool CanReportMemoryUsage() const {
+ return device.CanReportMemoryUsage();
+ }
bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
return true;
@@ -172,6 +181,7 @@ private:
std::array<OGLFramebuffer, 4> rescale_draw_fbos;
std::array<OGLFramebuffer, 4> rescale_read_fbos;
const Settings::ResolutionScalingInfo& resolution;
+ u64 device_access_memory;
};
class 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
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
{GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
+ {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
{GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM
{GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT
@@ -87,6 +88,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
+ {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
{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 @@
#include <algorithm>
#include <cstddef>
#include <cstdlib>
-#include <cstring>
#include <memory>
#include <glad/glad.h>
@@ -15,11 +14,9 @@
#include "common/microprofile.h"
#include "common/settings.h"
#include "common/telemetry.h"
-#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
-#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "video_core/host_shaders/fxaa_frag.h"
#include "video_core/host_shaders/fxaa_vert.h"
@@ -211,6 +208,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// Framebuffer orientation handling
framebuffer_transform_flags = framebuffer.transform_flags;
framebuffer_crop_rect = framebuffer.crop_rect;
+ framebuffer_width = framebuffer.width;
+ framebuffer_height = framebuffer.height;
const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
screen_info.was_accelerated =
@@ -326,12 +325,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
GLint internal_format;
switch (framebuffer.pixel_format) {
- case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM:
+ case Service::android::PixelFormat::Rgba8888:
internal_format = GL_RGBA8;
texture.gl_format = GL_RGBA;
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
break;
- case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM:
+ case Service::android::PixelFormat::Rgb565:
internal_format = GL_RGB565;
texture.gl_format = GL_RGB;
texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
@@ -467,8 +466,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
const auto& texcoords = screen_info.display_texcoords;
auto left = texcoords.left;
auto right = texcoords.right;
- if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) {
- if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) {
+ if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
+ if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
// Flip the framebuffer vertically
left = texcoords.right;
right = texcoords.left;
@@ -483,9 +482,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
+ f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
+ f32 scale_v =
+ static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
+
// Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
// (e.g. handheld mode) on a 1920x1080 framebuffer.
- f32 scale_u = 1.f, scale_v = 1.f;
if (framebuffer_crop_rect.GetWidth() > 0) {
scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
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 @@
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/math_util.h"
+
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
namespace Core {
@@ -44,7 +46,7 @@ struct TextureInfo {
GLsizei height;
GLenum gl_format;
GLenum gl_type;
- Tegra::FramebufferConfig::PixelFormat pixel_format;
+ Service::android::PixelFormat pixel_format;
};
/// Structure used for storing information about the display target for the Switch screen
@@ -133,8 +135,10 @@ private:
std::vector<u8> gl_framebuffer_data;
/// Used for transforming the framebuffer orientation
- Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
+ Service::android::BufferTransformFlags framebuffer_transform_flags{};
Common::Rectangle<int> framebuffer_crop_rect;
+ u32 framebuffer_width;
+ u32 framebuffer_height;
};
} // 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 @@
#include "video_core/host_shaders/astc_decoder_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
+#include "video_core/host_shaders/opengl_convert_s8d24_comp.h"
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
#include "video_core/host_shaders/pitch_unswizzle_comp.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -50,7 +51,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
- copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
+ copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
+ convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) {
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
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
program_manager.RestoreGuestCompute();
}
+void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) {
+ static constexpr GLuint BINDING_DESTINATION = 0;
+ static constexpr GLuint LOC_SIZE = 0;
+
+ program_manager.BindComputeProgram(convert_s8d24_program.handle);
+ for (const ImageCopy& copy : copies) {
+ ASSERT(copy.src_subresource.base_layer == 0);
+ ASSERT(copy.src_subresource.num_layers == 1);
+ ASSERT(copy.dst_subresource.base_layer == 0);
+ ASSERT(copy.dst_subresource.num_layers == 1);
+
+ glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth);
+ glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(),
+ copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI);
+ glDispatchCompute(Common::DivCeil(copy.extent.width, 16u),
+ Common::DivCeil(copy.extent.height, 8u), copy.extent.depth);
+ }
+ program_manager.RestoreGuestCompute();
+}
+
GLenum StoreFormat(u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 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:
void CopyBC4(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
+ void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies);
+
private:
ProgramManager& program_manager;
@@ -49,6 +51,7 @@ private:
OGLProgram block_linear_unswizzle_3d_program;
OGLProgram pitch_unswizzle_program;
OGLProgram copy_bc4_program;
+ OGLProgram convert_s8d24_program;
};
GLenum StoreFormat(u32 bytes_per_block);
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 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 @@
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
+#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -366,16 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
PipelineLayoutCreateInfo(two_textures_set_layout.address()))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
+ blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
+ convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
- nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {
- if (device.IsExtShaderStencilExportSupported()) {
- blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV);
- }
-}
+ nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
BlitImageHelper::~BlitImageHelper() = default;
@@ -474,6 +473,13 @@ void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
+void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
+ ImageView& src_image_view) {
+ ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
+ convert_s8d24_to_abgr8_frag);
+ ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
+}
+
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
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 @@
#pragma once
-#include <compare>
-
#include "video_core/engines/fermi_2d.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/texture_cache/types.h"
@@ -56,6 +54,8 @@ public:
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
+ void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
+
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -99,6 +99,7 @@ private:
vk::ShaderModule convert_float_to_depth_frag;
vk::ShaderModule convert_abgr8_to_d24s8_frag;
vk::ShaderModule convert_d24s8_to_abgr8_frag;
+ vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -112,6 +113,7 @@ private:
vk::Pipeline convert_r16_to_d16_pipeline;
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
+ vk::Pipeline convert_s8d24_to_abgr8_pipeline;
};
} // 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 @@
#include <algorithm>
#include <cstring>
-#include <tuple>
-
-#include <boost/functional/hash.hpp>
#include "common/bit_cast.h"
#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 {
{VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
{VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
+ {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
{VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
{VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM
{VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT
@@ -184,6 +185,7 @@ struct FormatTuple {
{VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
{VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
{VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
+ {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM
{VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
{VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
{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 @@
#pragma once
-#include "common/common_types.h"
#include "shader_recompiler/stage.h"
#include "video_core/engines/maxwell_3d.h"
#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 @@
#include <boost/container/small_vector.hpp>
-#include "common/assert.h"
#include "common/common_types.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/shader_info.h"
@@ -16,7 +15,6 @@
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/texture_cache/types.h"
-#include "video_core/textures/texture.h"
#include "video_core/vulkan_common/vulkan_device.h"
namespace 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) {
stage_stats.basic_block_count = GetUint64(statistic);
}
}
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
collected_stats.push_back(stage_stats);
}
}
@@ -66,7 +66,7 @@ void PipelineStatistics::Report() const {
double num{};
Stats total;
{
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
for (const Stats& stats : collected_stats) {
total.code_size += stats.code_size;
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 @@
#include <fmt/format.h>
#include "common/logging/log.h"
+#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/telemetry.h"
-#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/telemetry_session.h"
#include "video_core/gpu.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
-#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#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 @@
#include <memory>
#include <string>
-#include <vector>
#include "common/dynamic_library.h"
#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 @@
#include <array>
#include <cstring>
#include <memory>
-#include <tuple>
#include <vector>
#include "common/assert.h"
@@ -28,7 +27,6 @@
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_fsr.h"
-#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -96,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
switch (framebuffer.pixel_format) {
- case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM:
+ case Service::android::PixelFormat::Rgba8888:
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
- case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM:
+ case Service::android::PixelFormat::Rgb565:
return VK_FORMAT_R5G6B5_UNORM_PACK16;
- case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM:
+ case Service::android::PixelFormat::Bgra8888:
return VK_FORMAT_B8G8R8A8_UNORM;
default:
UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
@@ -1392,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
auto right = texcoords.right;
switch (framebuffer_transform_flags) {
- case Tegra::FramebufferConfig::TransformFlags::Unset:
+ case Service::android::BufferTransformFlags::Unset:
break;
- case Tegra::FramebufferConfig::TransformFlags::FlipV:
+ case Service::android::BufferTransformFlags::FlipV:
// Flip the framebuffer vertically
left = texcoords.right;
right = texcoords.left;
@@ -1408,8 +1406,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0);
UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
- f32 scale_u = 1.0f;
- f32 scale_v = 1.0f;
+ f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width);
+ f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height);
+
// Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
// (e.g. handheld mode) on a 1920x1080 framebuffer.
if (!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) {
return staging_pool.Request(size, MemoryUsage::Download);
}
+u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
+ return device.GetDeviceLocalMemory();
+}
+
+u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
+ return device.GetDeviceMemoryUsage();
+}
+
+bool BufferCacheRuntime::CanReportMemoryUsage() const {
+ return device.CanReportMemoryUsage();
+}
+
void BufferCacheRuntime::Finish() {
scheduler.Finish();
}
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:
void Finish();
+ u64 GetDeviceLocalMemory() const;
+
+ u64 GetDeviceMemoryUsage() const;
+
+ bool CanReportMemoryUsage() const;
+
[[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
[[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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
+#include <array>
#include <memory>
#include <optional>
#include <utility>
-#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
@@ -22,7 +21,6 @@
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/texture_cache/accelerated_swizzle.h"
#include "video_core/texture_cache/types.h"
-#include "video_core/textures/astc.h"
#include "video_core/textures/decoders.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -292,7 +290,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
};
- const std::array push_constants{base_vertex, index_shift};
+ const std::array<u32, 2> push_constants{base_vertex, index_shift};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
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
if (pipeline_statistics) {
pipeline_statistics->Collect(*pipeline);
}
- std::lock_guard lock{build_mutex};
+ std::scoped_lock lock{build_mutex};
is_built = true;
build_condvar.notify_one();
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 @@
#include "common/common_types.h"
#include "common/thread_worker.h"
#include "shader_recompiler/shader_info.h"
-#include "video_core/memory_manager.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#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 @@
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/vulkan_common/vulkan_device.h"
-#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
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 @@
#include "video_core/fence_manager.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
-#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Core {
class 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(
pipeline_statistics->Collect(*pipeline);
}
- std::lock_guard lock{build_mutex};
+ std::scoped_lock lock{build_mutex};
is_built = true;
build_condvar.notify_one();
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 @@
#include "common/microprofile.h"
#include "common/thread_worker.h"
#include "core/core.h"
-#include "core/memory.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/program_header.h"
-#include "video_core/dirty_flags.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@@ -406,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
ShaderPools pools;
auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
- std::lock_guard lock{state.mutex};
+ std::scoped_lock lock{state.mutex};
if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
}
@@ -436,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
state.statistics.get(), false)};
- std::lock_guard lock{state.mutex};
+ std::scoped_lock lock{state.mutex};
graphics_cache.emplace(key, std::move(pipeline));
++state.built;
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 @@
#include <array>
#include <cstddef>
#include <filesystem>
-#include <iosfwd>
#include <memory>
#include <type_traits>
#include <unordered_map>
-#include <utility>
#include <vector>
#include "common/common_types.h"
@@ -29,7 +27,6 @@
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/shader_cache.h"
-#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Core {
class 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 @@
#include <array>
#include <memory>
#include <mutex>
-#include <vector>
-#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
-#include "core/core.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#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 @@
#pragma once
#include <array>
-#include <bitset>
-#include <memory>
-#include <utility>
-#include <vector>
#include <boost/container/static_vector.hpp>
@@ -17,14 +13,12 @@
#include "video_core/rasterizer_accelerated.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_vulkan/blit_image.h"
-#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_fence_manager.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
-#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#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
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
const auto [pair, is_new] = cache.try_emplace(key);
if (!is_new) {
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 @@
#include <memory>
#include <mutex>
-#include <optional>
#include <thread>
#include <utility>
@@ -74,7 +73,7 @@ void VKScheduler::DispatchWork() {
return;
}
{
- std::lock_guard lock{work_mutex};
+ std::scoped_lock lock{work_mutex};
work_queue.push(std::move(chunk));
}
work_cv.notify_one();
@@ -158,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
if (has_submit) {
AllocateWorkerCommandBuffer();
}
- std::lock_guard reserve_lock{reserve_mutex};
+ std::scoped_lock reserve_lock{reserve_mutex};
chunk_reserve.push_back(std::move(work));
} while (!stop_token.stop_requested());
}
@@ -283,7 +282,7 @@ void VKScheduler::EndRenderPass() {
}
void VKScheduler::AcquireNewChunk() {
- std::lock_guard lock{reserve_mutex};
+ std::scoped_lock lock{reserve_mutex};
if (chunk_reserve.empty()) {
chunk = std::make_unique<CommandChunk>();
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 @@
#pragma once
-#include <atomic>
#include <condition_variable>
#include <cstddef>
#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 @@
// Refer to the license.txt file included.
#include <cstring>
-#include <memory>
-#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#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
.image = nullptr,
.buffer = *stream_buffer,
};
- const auto memory_properties = device.GetPhysical().GetMemoryProperties();
+ const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
VkMemoryAllocateInfo stream_memory_info{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.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 @@
#include <algorithm>
#include <array>
#include <cstddef>
-#include <iterator>
#include "common/common_types.h"
#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 @@
#include <limits>
#include "common/common_types.h"
-#include "core/core.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
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 @@
#include <limits>
#include <vector>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
-#include "core/frontend/framebuffer_layout.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
#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) {
}
}
+[[nodiscard]] SwizzleSource SwapGreenRed(SwizzleSource value) {
+ switch (value) {
+ case SwizzleSource::R:
+ return SwizzleSource::G;
+ case SwizzleSource::G:
+ return SwizzleSource::R;
+ default:
+ return value;
+ }
+}
+
+[[nodiscard]] SwizzleSource SwapSpecial(SwizzleSource value) {
+ switch (value) {
+ case SwizzleSource::A:
+ return SwizzleSource::R;
+ case SwizzleSource::R:
+ return SwizzleSource::A;
+ case SwizzleSource::G:
+ return SwizzleSource::B;
+ case SwizzleSource::B:
+ return SwizzleSource::G;
+ default:
+ return value;
+ }
+}
+
void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
VkImageAspectFlags aspect_mask, bool is_initialized,
std::span<const VkBufferImageCopy> copies) {
@@ -554,14 +580,25 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
};
}
-[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) {
+void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
+ bool emulate_bgr565) {
switch (format) {
case PixelFormat::A1B5G5R5_UNORM:
- return true;
+ std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
+ break;
case PixelFormat::B5G6R5_UNORM:
- return emulate_bgr565;
+ if (emulate_bgr565) {
+ std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
+ }
+ break;
+ case PixelFormat::A5B5G5R1_UNORM:
+ std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
+ break;
+ case PixelFormat::R4G4_UNORM:
+ std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
+ break;
default:
- return false;
+ break;
}
}
@@ -781,11 +818,6 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
!device.IsExtShaderStencilExportSupported()) {
return true;
}
- if (VideoCore::Surface::GetFormatType(src.info.format) ==
- VideoCore::Surface::SurfaceType::DepthStencil &&
- !device.IsExtShaderStencilExportSupported()) {
- return true;
- }
if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT ||
src.info.format == PixelFormat::D32_FLOAT_S8_UINT) {
return true;
@@ -1070,6 +1102,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
}
+ if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
+ return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
+ }
break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
@@ -1191,6 +1226,14 @@ u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
return device.GetDeviceLocalMemory();
}
+u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
+ return device.GetDeviceMemoryUsage();
+}
+
+bool TextureCacheRuntime::CanReportMemoryUsage() const {
+ return device.CanReportMemoryUsage();
+}
+
void TextureCacheRuntime::TickFrame() {}
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1205,6 +1248,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
} else {
flags |= VideoCommon::ImageFlagBits::Converted;
}
+ flags |= VideoCommon::ImageFlagBits::CostlyLoad;
}
if (runtime->device.HasDebuggingToolAttached()) {
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
@@ -1444,8 +1488,7 @@ bool Image::BlitScaleHelper(bool scale_up) {
runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region,
src_region, operation, BLIT_OPERATION);
- } else if (!runtime->device.IsBlitDepthStencilSupported() &&
- aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
+ } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
if (!blit_framebuffer) {
blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent);
}
@@ -1490,9 +1533,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
};
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
- if (IsFormatFlipped(format, device->MustEmulateBGR565())) {
- std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
- }
+ TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
}
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:
u64 GetDeviceLocalMemory() const;
+ u64 GetDeviceMemoryUsage() const;
+
+ bool CanReportMemoryUsage() const;
+
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
const Region2D& dst_region, const Region2D& src_region,
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 @@
#include <variant>
#include <boost/container/static_vector.hpp>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#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 @@
#include <array>
-#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace 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) {
}
void ShaderCache::OnCPUWrite(VAddr addr, size_t size) {
- std::lock_guard lock{invalidation_mutex};
+ std::scoped_lock lock{invalidation_mutex};
InvalidatePagesInRegion(addr, size);
}
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 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <bit>
#include <filesystem>
#include <fstream>
#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 @@
#include <atomic>
#include <chrono>
-#include <optional>
#include "video_core/shader_notify.h"
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 @@
#include <atomic>
#include <chrono>
-#include <optional>
namespace VideoCore {
class 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)
}
}
-PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
+PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) {
switch (format) {
- case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM:
+ case Service::android::PixelFormat::Rgba8888:
return PixelFormat::A8B8G8R8_UNORM;
- case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM:
+ case Service::android::PixelFormat::Rgb565:
return PixelFormat::R5G6B5_UNORM;
- case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM:
+ case Service::android::PixelFormat::Bgra8888:
return PixelFormat::B8G8R8A8_UNORM;
default:
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 {
A2B10G10R10_UNORM,
A2B10G10R10_UINT,
A1B5G5R5_UNORM,
+ A5B5G5R1_UNORM,
R8_UNORM,
R8_SNORM,
R8_SINT,
@@ -82,6 +83,7 @@ enum class PixelFormat {
BC3_SRGB,
BC7_SRGB,
A4B4G4R4_UNORM,
+ R4G4_UNORM,
ASTC_2D_4X4_SRGB,
ASTC_2D_8X8_SRGB,
ASTC_2D_8X5_SRGB,
@@ -156,6 +158,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
1, // A2B10G10R10_UNORM
1, // A2B10G10R10_UINT
1, // A1B5G5R5_UNORM
+ 1, // A5B5G5R1_UNORM
1, // R8_UNORM
1, // R8_SNORM
1, // R8_SINT
@@ -213,6 +216,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
4, // BC3_SRGB
4, // BC7_SRGB
1, // A4B4G4R4_UNORM
+ 1, // R4G4_UNORM
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
8, // ASTC_2D_8X5_SRGB
@@ -256,6 +260,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
1, // A2B10G10R10_UNORM
1, // A2B10G10R10_UINT
1, // A1B5G5R5_UNORM
+ 1, // A5B5G5R1_UNORM
1, // R8_UNORM
1, // R8_SNORM
1, // R8_SINT
@@ -313,6 +318,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
4, // BC3_SRGB
4, // BC7_SRGB
1, // A4B4G4R4_UNORM
+ 1, // R4G4_UNORM
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
5, // ASTC_2D_8X5_SRGB
@@ -356,6 +362,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
32, // A2B10G10R10_UNORM
32, // A2B10G10R10_UINT
16, // A1B5G5R5_UNORM
+ 16, // A5B5G5R1_UNORM
8, // R8_UNORM
8, // R8_SNORM
8, // R8_SINT
@@ -413,6 +420,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
128, // BC3_SRGB
128, // BC7_UNORM
16, // A4B4G4R4_UNORM
+ 8, // R4G4_UNORM
128, // ASTC_2D_4X4_SRGB
128, // ASTC_2D_8X8_SRGB
128, // ASTC_2D_8X5_SRGB
@@ -460,7 +468,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
-PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format);
+PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);
SurfaceType GetFormatType(PixelFormat pixel_format);
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 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
-#include "common/logging/log.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
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,
return PixelFormat::A1B5G5R5_UNORM;
case Hash(TextureFormat::A4B4G4R4, UNORM):
return PixelFormat::A4B4G4R4_UNORM;
+ case Hash(TextureFormat::G4R4, UNORM):
+ return PixelFormat::R4G4_UNORM;
+ case Hash(TextureFormat::A5B5G5R1, UNORM):
+ return PixelFormat::A5B5G5R1_UNORM;
case Hash(TextureFormat::R8, UNORM):
return PixelFormat::R8_UNORM;
case Hash(TextureFormat::R8, SNORM):
@@ -143,6 +147,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::S8_UINT_D24_UNORM;
case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
return PixelFormat::S8_UINT_D24_UNORM;
+ case Hash(TextureFormat::D24S8, UNORM, UINT, UINT, UINT, LINEAR):
+ return PixelFormat::D24_UNORM_S8_UINT;
case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
return PixelFormat::D32_FLOAT_S8_UINT;
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
return "A2B10G10R10_UINT";
case PixelFormat::A1B5G5R5_UNORM:
return "A1B5G5R5_UNORM";
+ case PixelFormat::A5B5G5R1_UNORM:
+ return "A5B5G5R1_UNORM";
case PixelFormat::R8_UNORM:
return "R8_UNORM";
case PixelFormat::R8_SNORM:
@@ -152,6 +154,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "BC7_SRGB";
case PixelFormat::A4B4G4R4_UNORM:
return "A4B4G4R4_UNORM";
+ case PixelFormat::R4G4_UNORM:
+ return "R4G4_UNORM";
case PixelFormat::ASTC_2D_4X4_SRGB:
return "ASTC_2D_4X4_SRGB";
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 {
///< garbage collection priority
Alias = 1 << 11, ///< This image has aliases and has priority on garbage
///< collection
+ CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back.
// Rescaler
- Rescaled = 1 << 12,
- CheckingRescalable = 1 << 13,
- IsRescalable = 1 << 14,
+ Rescaled = 1 << 13,
+ CheckingRescalable = 1 << 14,
+ IsRescalable = 1 << 15,
};
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
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 @@
#include <algorithm>
#include <span>
-#include <utility>
#include "common/bit_cast.h"
#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 @@
#pragma once
#include <algorithm>
-#include <array>
#include <bit>
-#include <concepts>
#include <numeric>
#include <type_traits>
#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&
void(slot_samplers.insert(runtime, sampler_descriptor));
if constexpr (HAS_DEVICE_MEMORY_INFO) {
- const auto device_memory = runtime.GetDeviceLocalMemory();
- const u64 possible_expected_memory = (device_memory * 4) / 10;
- const u64 possible_critical_memory = (device_memory * 7) / 10;
- expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB);
- critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB);
- minimum_memory = 0;
+ const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
+ const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
+ const s64 min_spacing_critical = device_memory - 1_GiB;
+ const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
+ const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
+ const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
+ expected_memory = static_cast<u64>(
+ std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
+ DEFAULT_EXPECTED_MEMORY));
+ critical_memory = static_cast<u64>(
+ std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
+ DEFAULT_CRITICAL_MEMORY));
+ minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2);
} else {
- // On OpenGL we can be more conservatives as the driver takes care.
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0;
@@ -66,18 +72,21 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
template <class P>
void TextureCache<P>::RunGarbageCollector() {
- const bool high_priority_mode = total_used_memory >= expected_memory;
- const bool aggressive_mode = total_used_memory >= critical_memory;
- const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL;
- size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10);
- const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) {
+ bool high_priority_mode = total_used_memory >= expected_memory;
+ bool aggressive_mode = total_used_memory >= critical_memory;
+ const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
+ size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
+ const auto clean_up = [this, &num_iterations, &high_priority_mode,
+ &aggressive_mode](ImageId image_id) {
if (num_iterations == 0) {
return true;
}
--num_iterations;
auto& image = slot_images[image_id];
- const bool must_download = image.IsSafeDownload();
- if (!high_priority_mode && must_download) {
+ const bool must_download =
+ image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
+ if (!high_priority_mode &&
+ (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
return false;
}
if (must_download) {
@@ -92,6 +101,18 @@ void TextureCache<P>::RunGarbageCollector() {
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
+ if (total_used_memory < critical_memory) {
+ if (aggressive_mode) {
+ // Sink the aggresiveness.
+ num_iterations >>= 2;
+ aggressive_mode = false;
+ return false;
+ }
+ if (high_priority_mode && total_used_memory < expected_memory) {
+ num_iterations >>= 1;
+ high_priority_mode = false;
+ }
+ }
return false;
};
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
@@ -99,6 +120,10 @@ void TextureCache<P>::RunGarbageCollector() {
template <class P>
void TextureCache<P>::TickFrame() {
+ // If we can obtain the memory info, use it instead of the estimate.
+ if (runtime.CanReportMemoryUsage()) {
+ total_used_memory = runtime.GetDeviceMemoryUsage();
+ }
if (total_used_memory > minimum_memory) {
RunGarbageCollector();
}
@@ -106,6 +131,7 @@ void TextureCache<P>::TickFrame() {
sentenced_framebuffers.Tick();
sentenced_image_view.Tick();
runtime.TickFrame();
+ critical_gc = 0;
++frame_tick;
}
@@ -343,7 +369,7 @@ template <bool has_blacklists>
void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
std::span<ImageViewId> cached_image_view_ids,
std::span<ImageViewInOut> views) {
- bool has_blacklisted;
+ bool has_blacklisted = false;
do {
has_deleted_images = false;
if constexpr (has_blacklists) {
@@ -1052,6 +1078,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
for (const ImageId overlap_id : overlap_ids) {
Image& overlap = slot_images[overlap_id];
+ if (True(overlap.flags & ImageFlagBits::GpuModified)) {
+ new_image.flags |= ImageFlagBits::GpuModified;
+ }
if (overlap.info.num_samples != new_image.info.num_samples) {
LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
} else {
@@ -1414,6 +1443,10 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
}
total_used_memory += Common::AlignUp(tentative_size, 1024);
+ if (total_used_memory > critical_memory && critical_gc < GC_EMERGENCY_COUNTS) {
+ RunGarbageCollector();
+ critical_gc++;
+ }
image.lru_index = lru_cache.Insert(image_id, frame_tick);
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
@@ -1704,6 +1737,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
aliased_images.push_back(&aliased);
any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
+ if (True(aliased_image.flags & ImageFlagBits::GpuModified)) {
+ image.flags |= ImageFlagBits::GpuModified;
+ }
}
}
if (aliased_images.empty()) {
@@ -1725,7 +1761,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
});
const auto& resolution = Settings::values.resolution_info;
for (const AliasedImage* const aliased : aliased_images) {
- if (!resolution.active | !any_rescaled) {
+ if (!resolution.active || !any_rescaled) {
CopyImage(image_id, aliased->id, aliased->copies);
continue;
}
@@ -1736,19 +1772,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
continue;
}
ScaleUp(aliased_image);
-
- const bool both_2d{image.info.type == ImageType::e2D &&
- aliased_image.info.type == ImageType::e2D};
- auto copies = aliased->copies;
- for (auto copy : copies) {
- copy.extent.width = std::max<u32>(
- (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1);
- if (both_2d) {
- copy.extent.height = std::max<u32>(
- (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1);
- }
- }
- CopyImage(image_id, aliased->id, copies);
+ CopyImage(image_id, aliased->id, aliased->copies);
}
}
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 @@
#include "video_core/texture_cache/image_base.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h"
-#include "video_core/texture_cache/image_view_info.h"
#include "video_core/texture_cache/render_targets.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h"
@@ -60,8 +59,10 @@ class TextureCache {
/// True when the API can provide info about the memory of the device.
static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
- static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
- static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
+ static constexpr s64 TARGET_THRESHOLD = 4_GiB;
+ static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
+ static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
+ static constexpr size_t GC_EMERGENCY_COUNTS = 2;
using Runtime = typename P::Runtime;
using Image = typename P::Image;
@@ -373,6 +374,7 @@ private:
u64 minimum_memory;
u64 expected_memory;
u64 critical_memory;
+ size_t critical_gc;
SlotVector<Image> slot_images;
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 @@
#include "common/common_types.h"
-#include "video_core/engines/maxwell_3d.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/image_base.h"
-#include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/types.h"
#include "video_core/textures/texture.h"
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 @@
// <http://gamma.cs.unc.edu/FasTC/>
#include <algorithm>
+#include <bit>
#include <cassert>
#include <cstring>
#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 @@
#pragma once
-#include <bit>
-#include "common/common_types.h"
-
namespace Tegra::Texture::ASTC {
void 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 @@
#include <cmath>
#include <cstring>
#include <span>
-#include <utility>
#include "common/alignment.h"
#include "common/assert.h"
@@ -14,7 +13,6 @@
#include "common/div_ceil.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
-#include "video_core/textures/texture.h"
namespace Tegra::Texture {
namespace {
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <array>
#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
gpu->BindRenderer(std::move(renderer));
return gpu;
} catch (const std::runtime_error& exception) {
+ scope.Cancel();
LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
return nullptr;
}
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 @@
#pragma once
-#include <filesystem>
-#include <mutex>
#include <span>
-#include <string>
-#include <vector>
#include "common/common_types.h"
-#include "common/dynamic_library.h"
-#include "video_core/vulkan_common/vulkan_wrapper.h"
#ifdef HAS_NSIGHT_AFTERMATH
+#include <filesystem>
+#include <mutex>
+
+// Vulkan headers must be included before Aftermath
+#include "video_core/vulkan_common/vulkan_wrapper.h"
+
#include <GFSDK_Aftermath_Defines.h>
#include <GFSDK_Aftermath_GpuCrashDump.h>
#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
+
+#include "common/dynamic_library.h"
#endif
namespace 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 @@
#include <bitset>
#include <chrono>
#include <optional>
-#include <string_view>
#include <thread>
#include <unordered_set>
#include <utility>
#include <vector>
#include "common/assert.h"
+#include "common/literals.h"
#include "common/settings.h"
#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
+using namespace Common::Literals;
namespace {
namespace Alternatives {
constexpr std::array STENCIL8_UINT{
@@ -44,6 +45,12 @@ constexpr std::array B5G6R5_UNORM_PACK16{
VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
};
+
+constexpr std::array R4G4_UNORM_PACK8{
+ VK_FORMAT_R8_UNORM,
+ VK_FORMAT_UNDEFINED,
+};
+
} // namespace Alternatives
enum class NvidiaArchitecture {
@@ -94,6 +101,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
case VK_FORMAT_B5G6R5_UNORM_PACK16:
return Alternatives::B5G6R5_UNORM_PACK16.data();
+ case VK_FORMAT_R4G4_UNORM_PACK8:
+ return Alternatives::R4G4_UNORM_PACK8.data();
default:
return nullptr;
}
@@ -121,6 +130,8 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_A8B8G8R8_SRGB_PACK32,
VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_B5G6R5_UNORM_PACK16,
+ VK_FORMAT_R5G5B5A1_UNORM_PACK16,
+ VK_FORMAT_B5G5R5A1_UNORM_PACK16,
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_FORMAT_A2B10G10R10_UINT_PACK32,
VK_FORMAT_A1R5G5B5_UNORM_PACK16,
@@ -159,7 +170,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_B8G8R8A8_SRGB,
+ VK_FORMAT_R4G4_UNORM_PACK8,
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
+ VK_FORMAT_B4G4R4A4_UNORM_PACK16,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D16_UNORM,
VK_FORMAT_S8_UINT,
@@ -597,6 +610,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
+ is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
+ is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
+ is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
+ properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
+
CollectPhysicalMemoryInfo();
CollectTelemetryParameters();
CollectToolingInfo();
@@ -615,6 +633,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
khr_push_descriptor = false;
break;
}
+ const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
+ if (nv_major_version >= 510) {
+ LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
+ cant_blit_msaa = true;
+ }
}
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
if (ext_extended_dynamic_state && is_radv) {
@@ -725,7 +748,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
}
void Device::ReportLoss() const {
- LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
+ LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
// Wait for the log to flush and for Nsight Aftermath to dump the results
std::this_thread::sleep_for(std::chrono::seconds{15});
@@ -986,6 +1009,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
false);
test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
+ test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
if (Settings::values.enable_nsight_aftermath) {
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
true);
@@ -998,7 +1022,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
VkPhysicalDeviceFeatures2KHR features{};
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
- VkPhysicalDeviceProperties2KHR physical_properties;
+ VkPhysicalDeviceProperties2KHR physical_properties{};
physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
if (has_khr_shader_float16_int8) {
@@ -1268,15 +1292,50 @@ void Device::CollectTelemetryParameters() {
vendor_name = driver.driverName;
}
+u64 Device::GetDeviceMemoryUsage() const {
+ VkPhysicalDeviceMemoryBudgetPropertiesEXT budget;
+ budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
+ budget.pNext = nullptr;
+ physical.GetMemoryProperties(&budget);
+ u64 result{};
+ for (const size_t heap : valid_heap_memory) {
+ result += budget.heapUsage[heap];
+ }
+ return result;
+}
+
void Device::CollectPhysicalMemoryInfo() {
- const auto mem_properties = physical.GetMemoryProperties();
+ VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
+ budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
+ const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr);
+ const auto& mem_properties = mem_info.memoryProperties;
const size_t num_properties = mem_properties.memoryHeapCount;
device_access_memory = 0;
+ u64 device_initial_usage = 0;
+ u64 local_memory = 0;
for (size_t element = 0; element < num_properties; ++element) {
- if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) {
- device_access_memory += mem_properties.memoryHeaps[element].size;
+ const bool is_heap_local =
+ (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
+ if (!is_integrated && !is_heap_local) {
+ continue;
}
+ valid_heap_memory.push_back(element);
+ if (is_heap_local) {
+ local_memory += mem_properties.memoryHeaps[element].size;
+ }
+ if (ext_memory_budget) {
+ device_initial_usage += budget.heapUsage[element];
+ device_access_memory += budget.heapBudget[element];
+ continue;
+ }
+ device_access_memory += mem_properties.memoryHeaps[element].size;
+ }
+ if (!is_integrated) {
+ return;
}
+ const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
+ device_access_memory = static_cast<u64>(std::max<s64>(
+ std::min<s64>(available_memory - 8_GiB, 4_GiB), static_cast<s64>(local_memory)));
}
void 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 @@
#include <span>
#include <string>
-#include <string_view>
#include <unordered_map>
#include <vector>
@@ -342,6 +341,12 @@ public:
return device_access_memory;
}
+ bool CanReportMemoryUsage() const {
+ return ext_memory_budget;
+ }
+
+ u64 GetDeviceMemoryUsage() const;
+
u32 GetSetsPerPool() const {
return sets_per_pool;
}
@@ -422,6 +427,9 @@ private:
bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
///< topologies.
bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
+ bool is_integrated{}; ///< Is GPU an iGPU.
+ bool is_virtual{}; ///< Is GPU a virtual GPU.
+ bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -446,6 +454,7 @@ private:
bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
+ bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
bool has_renderdoc{}; ///< Has RenderDoc attached
@@ -457,6 +466,7 @@ private:
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.
std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions.
+ std::vector<size_t> valid_heap_memory; ///< Heaps used.
/// Format properties dictionary.
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <future>
#include <optional>
#include <span>
-#include <utility>
#include <vector>
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstdlib>
#include <string>
#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() {
}
MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
- : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()},
+ : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
export_allocations{export_allocations_},
buffer_image_granularity{
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 @@
#include <memory>
#include <span>
-#include <utility>
#include <vector>
#include "common/common_types.h"
#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 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <exception>
#include <memory>
#include <optional>
-#include <string_view>
#include <utility>
#include <vector>
@@ -239,8 +237,8 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) &&
X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) &&
X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) &&
- X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) &&
- X(vkGetPhysicalDeviceQueueFamilyProperties);
+ X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) &&
+ X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties);
#undef X
}
@@ -928,9 +926,12 @@ std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR(
return modes;
}
-VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept {
- VkPhysicalDeviceMemoryProperties properties;
- dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties);
+VkPhysicalDeviceMemoryProperties2 PhysicalDevice::GetMemoryProperties(
+ void* next_structures) const noexcept {
+ VkPhysicalDeviceMemoryProperties2 properties{};
+ properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
+ properties.pNext = next_structures;
+ dld->vkGetPhysicalDeviceMemoryProperties2(physical_device, &properties);
return properties;
}
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 @@
#pragma once
#include <exception>
-#include <iterator>
#include <limits>
#include <memory>
#include <optional>
@@ -173,6 +172,7 @@ struct InstanceDispatch {
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
+ PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{};
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
@@ -951,7 +951,8 @@ public:
std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const;
- VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept;
+ VkPhysicalDeviceMemoryProperties2 GetMemoryProperties(
+ void* next_structures = nullptr) const noexcept;
private:
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 @@
// Refer to the license.txt file included.
#include <array>
-#include <cstdlib>
#include <mutex>
#include <string>
@@ -33,7 +32,7 @@ constexpr std::size_t TIMEOUT_SECONDS = 30;
struct Client::Impl {
Impl(std::string host, std::string username, std::string token)
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
- std::lock_guard lock{jwt_cache.mutex};
+ std::scoped_lock lock{jwt_cache.mutex};
if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
jwt = jwt_cache.jwt;
}
@@ -148,7 +147,7 @@ struct Client::Impl {
if (result.result_code != WebResult::Code::Success) {
LOG_ERROR(WebService, "UpdateJWT failed");
} else {
- std::lock_guard lock{jwt_cache.mutex};
+ std::scoped_lock lock{jwt_cache.mutex};
jwt_cache.username = username;
jwt_cache.token = token;
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
configuration/configure_profile_manager.cpp
configuration/configure_profile_manager.h
configuration/configure_profile_manager.ui
+ configuration/configure_ringcon.cpp
+ configuration/configure_ringcon.h
+ configuration/configure_ringcon.ui
configuration/configure_network.cpp
configuration/configure_network.h
configuration/configure_network.ui
@@ -293,7 +296,7 @@ if (YUZU_USE_QT_WEB_ENGINE)
endif ()
if(UNIX AND NOT APPLE)
- install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+ install(TARGETS yuzu)
endif()
if (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 @@
&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;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
-&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;
+&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;
&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;
&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>
</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 @@
#include <thread>
#include "common/assert.h"
-#include "common/param_package.h"
#include "common/string_util.h"
#include "core/core.h"
#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 @@
#include <QLineEdit>
#include <QScrollArea>
#include <QStandardItemModel>
+#include <QTreeView>
#include <QVBoxLayout>
#include "common/fs/path_util.h"
#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 @@
#include <vector>
#include <QDialog>
#include <QList>
-#include <QTreeView>
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
@@ -19,6 +18,7 @@ class QLabel;
class QScrollArea;
class QStandardItem;
class QStandardItemModel;
+class QTreeView;
class QVBoxLayout;
namespace 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 @@
#include <QWebEngineScriptCollection>
#include <QWebEngineSettings>
#include <QWebEngineUrlScheme>
+
+#include "core/hid/input_interpreter.h"
+#include "yuzu/applets/qt_web_browser_scripts.h"
#endif
#include "common/fs/path_util.h"
-#include "common/param_package.h"
#include "core/core.h"
-#include "core/hid/hid_types.h"
-#include "core/hid/input_interpreter.h"
#include "input_common/drivers/keyboard.h"
-#include "input_common/main.h"
#include "yuzu/applets/qt_web_browser.h"
-#include "yuzu/applets/qt_web_browser_scripts.h"
#include "yuzu/main.h"
#include "yuzu/util/url_request_interceptor.h"
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 @@
#pragma once
#include <atomic>
-#include <memory>
#include <thread>
#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 @@
#include <QApplication>
#include <QHBoxLayout>
-#include <QKeyEvent>
#include <QMessageBox>
#include <QPainter>
#include <QScreen>
@@ -28,7 +27,6 @@
#include "common/assert.h"
#include "common/microprofile.h"
#include "common/scm_rev.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
@@ -38,7 +36,6 @@
#include "input_common/drivers/touch_screen.h"
#include "input_common/main.h"
#include "video_core/renderer_base.h"
-#include "video_core/video_core.h"
#include "yuzu/bootmanager.h"
#include "yuzu/main.h"
@@ -936,6 +933,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
auto& renderer = system.Renderer();
const f32 res_scale = Settings::values.resolution_info.up_factor;
+ if (renderer.IsScreenshotPending()) {
+ LOG_WARNING(Render,
+ "A screenshot is already requested or in progress, ignoring the request");
+ return;
+ }
+
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
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 @@
#include <QThread>
#include <QTouchEvent>
#include <QWidget>
-#include <QWindow>
#include "common/thread.h"
#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 @@
<item row="4" column="0">
<widget class="QRadioButton" name="radioButton_Great">
<property name="text">
- <string>Great </string>
+ <string>Great</string>
</property>
</widget>
</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 = {
0,
};
+const std::array<int, 2> Config::default_ringcon_analogs{{
+ Qt::Key_A,
+ Qt::Key_D,
+}};
+
// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// 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() {
ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
}
+void Config::ReadHidbusValues() {
+ Settings::values.enable_ring_controller =
+ ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool();
+
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
+ auto& ringcon_analogs = Settings::values.ringcon_analogs;
+
+ ringcon_analogs =
+ qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (ringcon_analogs.empty()) {
+ ringcon_analogs = default_param;
+ }
+}
+
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
@@ -369,6 +391,7 @@ void Config::ReadControlValues() {
ReadMouseValues();
ReadTouchscreenValues();
ReadMotionTouchValues();
+ ReadHidbusValues();
#ifdef _WIN32
ReadBasicSetting(Settings::values.enable_raw_input);
@@ -775,6 +798,7 @@ void Config::ReadUIValues() {
ReadBasicSetting(UISettings::values.pause_when_in_background);
ReadBasicSetting(UISettings::values.mute_when_in_background);
ReadBasicSetting(UISettings::values.hide_mouse);
+ ReadBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup();
}
@@ -961,6 +985,16 @@ void Config::SaveMotionTouchValues() {
qt_config->endArray();
}
+void Config::SaveHidbusValues() {
+ WriteBasicSetting(Settings::values.enable_ring_controller);
+
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
+ WriteSetting(QStringLiteral("ring_controller"),
+ QString::fromStdString(Settings::values.ringcon_analogs),
+ QString::fromStdString(default_param));
+}
+
void Config::SaveValues() {
if (global) {
SaveControlValues();
@@ -1001,6 +1035,7 @@ void Config::SaveControlValues() {
SaveMouseValues();
SaveTouchscreenValues();
SaveMotionTouchValues();
+ SaveHidbusValues();
WriteGlobalSetting(Settings::values.use_docked_mode);
WriteGlobalSetting(Settings::values.vibration_enabled);
@@ -1155,6 +1190,8 @@ void Config::SaveCpuValues() {
WriteBasicSetting(Settings::values.cpuopt_misc_ir);
WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
WriteBasicSetting(Settings::values.cpuopt_fastmem);
+ WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
+ WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
}
qt_config->endGroup();
@@ -1306,6 +1343,7 @@ void Config::SaveUIValues() {
WriteBasicSetting(UISettings::values.pause_when_in_background);
WriteBasicSetting(UISettings::values.mute_when_in_background);
WriteBasicSetting(UISettings::values.hide_mouse);
+ WriteBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup();
}
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:
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<int, 2> default_stick_mod;
+ static const std::array<int, 2> default_ringcon_analogs;
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -66,6 +67,7 @@ private:
void ReadMouseValues();
void ReadTouchscreenValues();
void ReadMotionTouchValues();
+ void ReadHidbusValues();
// Read functions bases off the respective config section names.
void ReadAudioValues();
@@ -93,6 +95,7 @@ private:
void SaveMouseValues();
void SaveTouchscreenValues();
void SaveMotionTouchValues();
+ void SaveHidbusValues();
// Save functions based off the respective config section names.
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 @@
// Refer to the license.txt file included.
#include <QCheckBox>
-#include <QComboBox>
#include <QObject>
#include <QString>
#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 @@
#include <QCheckBox>
#include <QComboBox>
-#include <QString>
#include "common/settings.h"
namespace 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 @@
#include <memory>
-#include <QSignalBlocker>
-
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <QComboBox>
-#include <QMessageBox>
-
#include "common/common_types.h"
-#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#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 @@
#include <memory>
#include <QWidget>
-#include "common/settings.h"
namespace Core {
class 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 @@
<string>Unsafe</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>Paranoid (disables most optimizations)</string>
+ </property>
+ </item>
</widget>
</item>
</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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <QComboBox>
-
-#include "common/common_types.h"
-#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#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() {
ui->disable_loop_safety_checks->setChecked(
Settings::values.disable_shader_loop_safety_checks.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
+
+#ifdef YUZU_USE_QT_WEB_ENGINE
+ ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
+#else
+ ui->disable_web_applet->setEnabled(false);
+ ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
+#endif
}
void ConfigureDebug::ApplyConfiguration() {
@@ -80,6 +87,7 @@ void ConfigureDebug::ApplyConfiguration() {
ui->disable_loop_safety_checks->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
Settings::values.extended_logging = ui->extended_logging->isChecked();
+ UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
Debugger::ToggleConsole();
Common::Log::Filter filter;
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 @@
<property name="title">
<string>Logging</string>
</property>
- <layout class="QGridLayout" name="gridLayout_1">
- <item row="0" column="0" colspan="2">
- <layout class="QHBoxLayout" name="horizontalLayout_1">
- <item>
- <widget class="QLabel" name="label_1">
- <property name="text">
- <string>Global Log Filter</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="log_filter_edit"/>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="toggle_console">
- <property name="text">
- <string>Show Log in Console</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QPushButton" name="open_log_button">
- <property name="text">
- <string>Open Log Location</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="extended_logging">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="toolTip">
- <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
- </property>
+ <layout class="QGridLayout" name="gridLayout_1">
+ <item row="0" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_1">
+ <item>
+ <widget class="QLabel" name="label_1">
<property name="text">
- <string>Enable Extended Logging**</string>
+ <string>Global Log Filter</string>
</property>
- </widget>
- </item>
- </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="log_filter_edit"/>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="toggle_console">
+ <property name="text">
+ <string>Show Log in Console</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="open_log_button">
+ <property name="text">
+ <string>Open Log Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="extended_logging">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
+ </property>
+ <property name="text">
+ <string>Enable Extended Logging**</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
<item>
@@ -183,7 +183,7 @@
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
- <item> row="0" column="0">
+ <item row="0" column="0">
<widget class="QCheckBox" name="quest_flag">
<property name="text">
<string>Kiosk (Quest) Mode</string>
@@ -214,7 +214,14 @@
<item row="1" column="1">
<widget class="QCheckBox" name="enable_all_controllers">
<property name="text">
- <string>Enable all Controller Types</string>
+ <string>Enable All Controller Types</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="disable_web_applet">
+ <property name="text">
+ <string>Disable Web Applet</string>
</property>
</widget>
</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 @@
// Refer to the license.txt file included.
#include <memory>
-#include <QAbstractButton>
-#include <QDialogButtonBox>
-#include <QHash>
-#include <QListWidgetItem>
-#include <QPushButton>
-#include <QSignalBlocker>
-#include <QTabWidget>
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
@@ -32,6 +25,7 @@
#include "yuzu/configuration/configure_ui.h"
#include "yuzu/configuration/configure_web.h"
#include "yuzu/hotkeys.h"
+#include "yuzu/uisettings.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
InputCommon::InputSubsystem* input_subsystem,
@@ -176,6 +170,8 @@ void ConfigureDialog::PopulateSelectionList() {
void ConfigureDialog::OnLanguageChanged(const QString& locale) {
emit LanguageChanged(locale);
+ // Reloading the game list is needed to force retranslation.
+ UISettings::values.is_game_list_reload_pending = true;
// first apply the configuration, and then restore the display
ApplyConfiguration();
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 @@
#include <functional>
#include <utility>
-#include <QCheckBox>
#include <QMessageBox>
-#include <QSpinBox>
#include "common/settings.h"
#include "core/core.h"
#include "ui_configure_general.h"
-#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_general.h"
#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 @@
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include <QColorDialog>
-#include <QComboBox>
#include <QVulkanInstance>
#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
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
ui->hotkey_list->setModel(model);
- ui->hotkey_list->setColumnWidth(name_column, 200);
- ui->hotkey_list->resizeColumnToContents(hotkey_column);
+ ui->hotkey_list->header()->setStretchLastSection(false);
+ ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch);
+ ui->hotkey_list->header()->setMinimumSectionSize(150);
connect(ui->button_restore_defaults, &QPushButton::clicked, this,
&ConfigureHotkeys::RestoreDefaults);
@@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
}
ui->hotkey_list->expandAll();
- ui->hotkey_list->resizeColumnToContents(name_column);
ui->hotkey_list->resizeColumnToContents(hotkey_column);
+ ui->hotkey_list->resizeColumnToContents(controller_column);
}
void 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <memory>
#include <thread>
-#include <QSignalBlocker>
-#include <QTimer>
-
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
@@ -24,6 +20,7 @@
#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_motion_touch.h"
+#include "yuzu/configuration/configure_ringcon.h"
#include "yuzu/configuration/configure_touchscreen_advanced.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
@@ -162,6 +159,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
[this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
+ connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog,
+ [this, input_subsystem, &hid_core] {
+ CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
+ });
connect(ui->vibrationButton, &QPushButton::clicked,
[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)
&ConfigureInputAdvanced::UpdateUIEnabled);
connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
&ConfigureInputAdvanced::UpdateUIEnabled);
+ connect(ui->enable_ring_controller, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
connect(ui->debug_configure, &QPushButton::clicked, this,
[this] { CallDebugControllerDialog(); });
connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
[this] { CallTouchscreenConfigDialog(); });
connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
- &ConfigureInputAdvanced::CallMotionTouchConfigDialog);
+ [this] { CallMotionTouchConfigDialog(); });
+ connect(ui->ring_controller_configure, &QPushButton::clicked, this,
+ [this] { CallRingControllerDialog(); });
#ifndef _WIN32
ui->enable_raw_input->setVisible(false);
@@ -132,6 +136,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
+ Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
}
void ConfigureInputAdvanced::LoadConfiguration() {
@@ -164,6 +169,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
+ ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
UpdateUIEnabled();
}
@@ -185,4 +191,5 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
+ ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
}
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:
void CallMouseConfigDialog();
void CallTouchscreenConfigDialog();
void CallMotionTouchConfigDialog();
+ void CallRingControllerDialog();
private:
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 @@
</property>
</widget>
</item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="enable_ring_controller">
+ <property name="text">
+ <string>Ring Controller</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="ring_controller_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</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 @@
#include <utility>
#include <QGridLayout>
#include <QInputDialog>
-#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
+#include "common/assert.h"
#include "common/param_package.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
@@ -23,7 +23,6 @@
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_input_player_widget.h"
-#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h"
@@ -121,6 +120,23 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
}
}
+QString GetDirectionName(const std::string& direction) {
+ if (direction == "left") {
+ return QObject::tr("Left");
+ }
+ if (direction == "right") {
+ return QObject::tr("Right");
+ }
+ if (direction == "up") {
+ return QObject::tr("Up");
+ }
+ if (direction == "down") {
+ return QObject::tr("Down");
+ }
+ UNIMPLEMENTED_MSG("Unimplemented direction name={}", direction);
+ return QString::fromStdString(direction);
+}
+
void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
const std::string& button_name) {
// The poller returned a complete axis, so set all the buttons
@@ -164,7 +180,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
if (common_button_name == Common::Input::ButtonNames::Value) {
if (param.Has("hat")) {
- const QString hat = QString::fromStdString(param.Get("direction", ""));
+ const QString hat = GetDirectionName(param.Get("direction", ""));
return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
}
if (param.Has("axis")) {
@@ -473,6 +489,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
[=, this](const Common::ParamPackage& params) {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
+ // Correct axis direction for inverted sticks
+ if (input_subsystem->IsStickInverted(param)) {
+ switch (analog_id) {
+ case Settings::NativeAnalog::LStick: {
+ const bool invert_value = param.Get("invert_x", "+") == "-";
+ const std::string invert_str = invert_value ? "+" : "-";
+ param.Set("invert_x", invert_str);
+ break;
+ }
+ case Settings::NativeAnalog::RStick: {
+ const bool invert_value = param.Get("invert_y", "+") == "-";
+ const std::string invert_str = invert_value ? "+" : "-";
+ param.Set("invert_y", invert_str);
+ break;
+ }
+ default:
+ break;
+ }
+ }
emulated_controller->SetStickParam(analog_id, param);
},
InputCommon::Polling::InputType::Stick);
@@ -1409,10 +1444,10 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
- event->ignore();
if (!input_setter || !event) {
return;
}
+ event->ignore();
if (event->key() != Qt::Key_Escape) {
input_subsystem->GetKeyboard()->PressKey(event->key());
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <array>
#include <sstream>
#include <QCloseEvent>
-#include <QLabel>
#include <QMessageBox>
-#include <QPushButton>
-#include <QRegularExpression>
#include <QStringListModel>
-#include <QVBoxLayout>
#include "common/logging/log.h"
#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 @@
#include <memory>
#include <QDialog>
-#include "common/param_package.h"
class QLabel;
class 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <QGraphicsItem>
#include <QtConcurrent/QtConcurrent>
#include "common/settings.h"
#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 @@
#pragma once
#include <memory>
-#include <QFutureWatcher>
#include <QWidget>
namespace 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 @@
#include <QAbstractButton>
#include <QCheckBox>
-#include <QDialogButtonBox>
-#include <QHeaderView>
-#include <QMenu>
#include <QPushButton>
-#include <QStandardItemModel>
#include <QString>
#include <QTimer>
-#include <QTreeView>
#include "common/fs/fs_util.h"
-#include "common/fs/path_util.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#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 @@
#include <memory>
#include <string>
-#include <vector>
#include <QDialog>
#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 @@
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/uisettings.h"
-#include "yuzu/util/util.h"
ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigurePerGameAddons>()}, system{system_} {
@@ -48,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p
item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
+ tree_view->header()->setStretchLastSection(false);
+ tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
+ tree_view->header()->setMinimumSectionSize(150);
+
// We must register all custom types with the Qt Automoc system so that we are able to use it
// with signals/slots. In this case, QList falls under the umbrella of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@@ -139,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() {
item_model->appendRow(list_items.back());
}
- tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
+ tree_view->resizeColumnToContents(1);
}
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 @@
#include <algorithm>
#include <QFileDialog>
#include <QGraphicsItem>
-#include <QGraphicsScene>
#include <QHeaderView>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QTreeView>
-#include <QVBoxLayout>
#include "common/assert.h"
#include "common/fs/path_util.h"
#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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QTimer>
+
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
+#include "input_common/drivers/keyboard.h"
+#include "input_common/drivers/mouse.h"
+#include "input_common/main.h"
+#include "ui_configure_ringcon.h"
+#include "yuzu/bootmanager.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_ringcon.h"
+
+const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
+ ConfigureRingController::analog_sub_buttons{{
+ "left",
+ "right",
+ }};
+
+namespace {
+
+QString GetKeyName(int key_code) {
+ switch (key_code) {
+ case Qt::Key_Shift:
+ return QObject::tr("Shift");
+ case Qt::Key_Control:
+ return QObject::tr("Ctrl");
+ case Qt::Key_Alt:
+ return QObject::tr("Alt");
+ case Qt::Key_Meta:
+ return {};
+ default:
+ return QKeySequence(key_code).toString();
+ }
+}
+
+QString GetButtonName(Common::Input::ButtonNames button_name) {
+ switch (button_name) {
+ case Common::Input::ButtonNames::ButtonLeft:
+ return QObject::tr("Left");
+ case Common::Input::ButtonNames::ButtonRight:
+ return QObject::tr("Right");
+ case Common::Input::ButtonNames::ButtonDown:
+ return QObject::tr("Down");
+ case Common::Input::ButtonNames::ButtonUp:
+ return QObject::tr("Up");
+ case Common::Input::ButtonNames::TriggerZ:
+ return QObject::tr("Z");
+ case Common::Input::ButtonNames::TriggerR:
+ return QObject::tr("R");
+ case Common::Input::ButtonNames::TriggerL:
+ return QObject::tr("L");
+ case Common::Input::ButtonNames::ButtonA:
+ return QObject::tr("A");
+ case Common::Input::ButtonNames::ButtonB:
+ return QObject::tr("B");
+ case Common::Input::ButtonNames::ButtonX:
+ return QObject::tr("X");
+ case Common::Input::ButtonNames::ButtonY:
+ return QObject::tr("Y");
+ case Common::Input::ButtonNames::ButtonStart:
+ return QObject::tr("Start");
+ case Common::Input::ButtonNames::L1:
+ return QObject::tr("L1");
+ case Common::Input::ButtonNames::L2:
+ return QObject::tr("L2");
+ case Common::Input::ButtonNames::L3:
+ return QObject::tr("L3");
+ case Common::Input::ButtonNames::R1:
+ return QObject::tr("R1");
+ case Common::Input::ButtonNames::R2:
+ return QObject::tr("R2");
+ case Common::Input::ButtonNames::R3:
+ return QObject::tr("R3");
+ case Common::Input::ButtonNames::Circle:
+ return QObject::tr("Circle");
+ case Common::Input::ButtonNames::Cross:
+ return QObject::tr("Cross");
+ case Common::Input::ButtonNames::Square:
+ return QObject::tr("Square");
+ case Common::Input::ButtonNames::Triangle:
+ return QObject::tr("Triangle");
+ case Common::Input::ButtonNames::Share:
+ return QObject::tr("Share");
+ case Common::Input::ButtonNames::Options:
+ return QObject::tr("Options");
+ default:
+ return QObject::tr("[undefined]");
+ }
+}
+
+void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
+ const std::string& button_name) {
+ // The poller returned a complete axis, so set all the buttons
+ if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
+ analog_param = input_param;
+ return;
+ }
+ // Check if the current configuration has either no engine or an axis binding.
+ // Clears out the old binding and adds one with analog_from_button.
+ if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
+ analog_param = {
+ {"engine", "analog_from_button"},
+ };
+ }
+ analog_param.Set(button_name, input_param.Serialize());
+}
+} // namespace
+
+ConfigureRingController::ConfigureRingController(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem_,
+ Core::HID::HIDCore& hid_core_)
+ : QDialog(parent), timeout_timer(std::make_unique<QTimer>()),
+ poll_timer(std::make_unique<QTimer>()), input_subsystem{input_subsystem_},
+
+ ui(std::make_unique<Ui::ConfigureRingController>()) {
+ ui->setupUi(this);
+
+ analog_map_buttons = {
+ ui->buttonRingAnalogPull,
+ ui->buttonRingAnalogPush,
+ };
+
+ emulated_device = hid_core_.GetEmulatedDevices();
+ emulated_device->SaveCurrentConfig();
+ emulated_device->EnableConfiguration();
+
+ LoadConfiguration();
+
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
+ auto* const analog_button = analog_map_buttons[sub_button_id];
+
+ if (analog_button == nullptr) {
+ continue;
+ }
+
+ connect(analog_button, &QPushButton::clicked, [=, this] {
+ HandleClick(
+ analog_map_buttons[sub_button_id],
+ [=, this](const Common::ParamPackage& params) {
+ Common::ParamPackage param = emulated_device->GetRingParam();
+ SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
+ emulated_device->SetRingParam(param);
+ },
+ InputCommon::Polling::InputType::Stick);
+ });
+
+ analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ connect(analog_button, &QPushButton::customContextMenuRequested,
+ [=, this](const QPoint& menu_location) {
+ QMenu context_menu;
+ Common::ParamPackage param = emulated_device->GetRingParam();
+ context_menu.addAction(tr("Clear"), [&] {
+ emulated_device->SetRingParam({});
+ analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Invert axis"), [&] {
+ const bool invert_value = param.Get("invert_x", "+") == "-";
+ const std::string invert_str = invert_value ? "+" : "-";
+ param.Set("invert_x", invert_str);
+ emulated_device->SetRingParam(param);
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
+ ++sub_button_id) {
+ analog_map_buttons[sub_button_id]->setText(
+ AnalogToText(param, analog_sub_buttons[sub_button_id]));
+ }
+ });
+ context_menu.exec(
+ analog_map_buttons[sub_button_id]->mapToGlobal(menu_location));
+ });
+ }
+
+ connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
+ Common::ParamPackage param = emulated_device->GetRingParam();
+ const auto slider_value = ui->sliderRingAnalogDeadzone->value();
+ ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
+ param.Set("deadzone", slider_value / 100.0f);
+ emulated_device->SetRingParam(param);
+ });
+
+ connect(ui->restore_defaults_button, &QPushButton::clicked, this,
+ &ConfigureRingController::RestoreDefaults);
+
+ timeout_timer->setSingleShot(true);
+ connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
+
+ connect(poll_timer.get(), &QTimer::timeout, [this] {
+ const auto& params = input_subsystem->GetNextInput();
+ if (params.Has("engine") && IsInputAcceptable(params)) {
+ SetPollingResult(params, false);
+ return;
+ }
+ });
+
+ resize(0, 0);
+}
+
+ConfigureRingController::~ConfigureRingController() {
+ emulated_device->DisableConfiguration();
+};
+
+void ConfigureRingController::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureRingController::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigureRingController::UpdateUI() {
+ RetranslateUI();
+ const Common::ParamPackage param = emulated_device->GetRingParam();
+
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
+ auto* const analog_button = analog_map_buttons[sub_button_id];
+
+ if (analog_button == nullptr) {
+ continue;
+ }
+
+ analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
+ }
+
+ const auto deadzone_label = ui->labelRingAnalogDeadzone;
+ const auto deadzone_slider = ui->sliderRingAnalogDeadzone;
+
+ int slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
+ deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
+ deadzone_slider->setValue(slider_value);
+}
+
+void ConfigureRingController::ApplyConfiguration() {
+ emulated_device->DisableConfiguration();
+ emulated_device->SaveCurrentConfig();
+ emulated_device->EnableConfiguration();
+}
+
+void ConfigureRingController::LoadConfiguration() {
+ UpdateUI();
+}
+
+void ConfigureRingController::RestoreDefaults() {
+ const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
+ 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
+ emulated_device->SetRingParam(Common::ParamPackage(default_ring_string));
+ UpdateUI();
+}
+
+void ConfigureRingController::HandleClick(
+ QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::InputType type) {
+ button->setText(tr("[waiting]"));
+ button->setFocus();
+
+ input_setter = new_input_setter;
+
+ input_subsystem->BeginMapping(type);
+
+ QWidget::grabMouse();
+ QWidget::grabKeyboard();
+
+ timeout_timer->start(2500); // Cancel after 2.5 seconds
+ poll_timer->start(25); // Check for new inputs every 25ms
+}
+
+void ConfigureRingController::SetPollingResult(const Common::ParamPackage& params, bool abort) {
+ timeout_timer->stop();
+ poll_timer->stop();
+ input_subsystem->StopMapping();
+
+ QWidget::releaseMouse();
+ QWidget::releaseKeyboard();
+
+ if (!abort) {
+ (*input_setter)(params);
+ }
+
+ UpdateUI();
+
+ input_setter = std::nullopt;
+}
+
+bool ConfigureRingController::IsInputAcceptable(const Common::ParamPackage& params) const {
+ return true;
+}
+
+void ConfigureRingController::mousePressEvent(QMouseEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+
+ const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
+ input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
+}
+
+void ConfigureRingController::keyPressEvent(QKeyEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+ event->ignore();
+ if (event->key() != Qt::Key_Escape) {
+ input_subsystem->GetKeyboard()->PressKey(event->key());
+ }
+}
+
+QString ConfigureRingController::ButtonToText(const Common::ParamPackage& param) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ }
+
+ const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
+ const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
+ const auto common_button_name = input_subsystem->GetButtonName(param);
+
+ // Retrieve the names from Qt
+ if (param.Get("engine", "") == "keyboard") {
+ const QString button_str = GetKeyName(param.Get("code", 0));
+ return QObject::tr("%1%2").arg(toggle, button_str);
+ }
+
+ if (common_button_name == Common::Input::ButtonNames::Invalid) {
+ return QObject::tr("[invalid]");
+ }
+
+ if (common_button_name == Common::Input::ButtonNames::Engine) {
+ return QString::fromStdString(param.Get("engine", ""));
+ }
+
+ if (common_button_name == Common::Input::ButtonNames::Value) {
+ if (param.Has("hat")) {
+ const QString hat = QString::fromStdString(param.Get("direction", ""));
+ return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
+ }
+ if (param.Has("axis")) {
+ const QString axis = QString::fromStdString(param.Get("axis", ""));
+ return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
+ }
+ if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
+ const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
+ const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
+ const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
+ return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
+ }
+ if (param.Has("motion")) {
+ const QString motion = QString::fromStdString(param.Get("motion", ""));
+ return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
+ }
+ if (param.Has("button")) {
+ const QString button = QString::fromStdString(param.Get("button", ""));
+ return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
+ }
+ }
+
+ QString button_name = GetButtonName(common_button_name);
+ if (param.Has("hat")) {
+ return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
+ }
+ if (param.Has("axis")) {
+ return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
+ }
+ if (param.Has("motion")) {
+ return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
+ }
+ if (param.Has("button")) {
+ return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
+ }
+
+ return QObject::tr("[unknown]");
+}
+
+QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param,
+ const std::string& dir) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ }
+
+ if (param.Get("engine", "") == "analog_from_button") {
+ return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
+ }
+
+ if (!param.Has("axis_x") || !param.Has("axis_y")) {
+ return QObject::tr("[unknown]");
+ }
+
+ const auto engine_str = param.Get("engine", "");
+ const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
+ const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
+ const bool invert_x = param.Get("invert_x", "+") == "-";
+ const bool invert_y = param.Get("invert_y", "+") == "-";
+
+ if (dir == "modifier") {
+ return QObject::tr("[unused]");
+ }
+
+ if (dir == "left") {
+ const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
+ return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
+ }
+ if (dir == "right") {
+ const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
+ return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
+ }
+ if (dir == "up") {
+ const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
+ return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
+ }
+ if (dir == "down") {
+ const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
+ return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
+ }
+
+ return QObject::tr("[unknown]");
+} \ 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 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <QDialog>
+
+namespace InputCommon {
+class InputSubsystem;
+} // namespace InputCommon
+
+namespace Core::HID {
+class HIDCore;
+class EmulatedDevices;
+} // namespace Core::HID
+
+namespace Ui {
+class ConfigureRingController;
+} // namespace Ui
+
+class ConfigureRingController : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureRingController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_,
+ Core::HID::HIDCore& hid_core_);
+ ~ConfigureRingController() override;
+
+ void ApplyConfiguration();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void UpdateUI();
+
+ /// Load configuration settings.
+ void LoadConfiguration();
+
+ /// Restore all buttons to their default values.
+ void RestoreDefaults();
+
+ /// Called when the button was pressed.
+ void HandleClick(QPushButton* button,
+ std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::InputType type);
+
+ /// Finish polling and configure input using the input_setter.
+ void SetPollingResult(const Common::ParamPackage& params, bool abort);
+
+ /// Checks whether a given input can be accepted.
+ bool IsInputAcceptable(const Common::ParamPackage& params) const;
+
+ /// Handle mouse button press events.
+ void mousePressEvent(QMouseEvent* event) override;
+
+ /// Handle key press events.
+ void keyPressEvent(QKeyEvent* event) override;
+
+ QString ButtonToText(const Common::ParamPackage& param);
+
+ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
+
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 2;
+
+ // A group of four QPushButtons represent one analog input. The buttons each represent left,
+ // right, respectively.
+ std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM> analog_map_buttons;
+
+ static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
+
+ std::unique_ptr<QTimer> timeout_timer;
+ std::unique_ptr<QTimer> poll_timer;
+
+ /// This will be the the setting function when an input is awaiting configuration.
+ std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
+
+ InputCommon::InputSubsystem* input_subsystem;
+ Core::HID::EmulatedDevices* emulated_device;
+
+ std::unique_ptr<Ui::ConfigureRingController> ui;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureRingController</class>
+ <widget class="QDialog" name="ConfigureRingController">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>298</width>
+ <height>339</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Ring Controller</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="minimumSize">
+ <size>
+ <width>280</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <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>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="RingAnalog">
+ <property name="title">
+ <string>Ring Sensor Parameters</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
+ <property name="title">
+ <string>Pull</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRingAnalogPull">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Pull</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
+ <property name="title">
+ <string>Push</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRingAnalogPush">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Push</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRingAnalogDeadzone">
+ <property name="text">
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRingAnalogDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureRingController</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureRingController</receiver>
+ <slot>reject()</slot>
+ </connection>
+ </connections>
+</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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <array>
#include <chrono>
#include <optional>
#include <QFileDialog>
#include <QGraphicsItem>
#include <QMessageBox>
-#include "common/assert.h"
#include "common/settings.h"
#include "core/core.h"
#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 @@
#include <memory>
-#include <QList>
#include <QWidget>
namespace 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 @@
#include <QDialog>
+class QLineEdit;
+
namespace Ui {
class ConfigureTas;
}
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 @@
#include <QKeyEvent>
#include <QMessageBox>
#include <QMouseEvent>
-#include <QResizeEvent>
#include <QStandardItemModel>
#include <QTimer>
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-#include <unordered_map>
-
-#include <fmt/format.h>
-
-#include "common/param_package.h"
#include "common/settings.h"
#include "core/hid/emulated_controller.h"
#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 @@
#pragma once
#include <string>
-#include <string_view>
#include <unordered_map>
namespace 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 @@
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/uisettings.h"
-#include "yuzu/util/util.h"
-#include "common/assert.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#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 @@
#include <memory>
#include <vector>
-#include <QAbstractItemModel>
#include <QDockWidget>
#include <QTreeView>
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 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
-#include <QKeyEvent>
#include <QList>
#include <QMenu>
#include <QThreadPool>
+#include <QToolButton>
#include <fmt/format.h>
#include "common/common_types.h"
#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 @@
#pragma once
#include <QFileSystemWatcher>
-#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QList>
-#include <QModelIndex>
-#include <QSettings>
-#include <QStandardItem>
#include <QStandardItemModel>
#include <QString>
-#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
#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 @@
#include <QCoreApplication>
#include <QFileInfo>
-#include <QImage>
#include <QObject>
#include <QStandardItem>
#include <QString>
@@ -165,8 +164,8 @@ public:
}
const CompatStatus& status = iterator->second;
setData(compatibility, CompatNumberRole);
- setText(QObject::tr(status.text));
- setToolTip(QObject::tr(status.tooltip));
+ setText(tr(status.text));
+ setToolTip(tr(status.tooltip));
setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
}
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 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
-#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "yuzu/compatibility_list.h"
#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 @@
#pragma once
#include <atomic>
-#include <map>
#include <memory>
#include <string>
-#include <unordered_map>
#include <QList>
#include <QObject>
#include <QRunnable>
#include <QString>
-#include <QVector>
-#include "common/common_types.h"
#include "yuzu/compatibility_list.h"
namespace 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 @@
// Refer to the license.txt file included.
#include <sstream>
-#include <QKeySequence>
#include <QShortcut>
#include <QTreeWidgetItem>
#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 @@
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFileInfo>
-#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#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 @@
#include <QBuffer>
#include <QByteArray>
#include <QGraphicsOpacityEffect>
-#include <QHBoxLayout>
#include <QIODevice>
#include <QImage>
-#include <QLabel>
#include <QPainter>
-#include <QPalette>
#include <QPixmap>
-#include <QProgressBar>
#include <QPropertyAnimation>
#include <QStyleOption>
-#include <QTime>
-#include <QtConcurrent/QtConcurrentRun>
-#include "common/logging/log.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/loader/loader.h"
#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 @@
#include "configuration/configure_input.h"
#include "configuration/configure_per_game.h"
#include "configuration/configure_tas.h"
-#include "configuration/configure_vibration.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
@@ -53,8 +53,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QClipboard>
#include <QDesktopServices>
#include <QDesktopWidget>
-#include <QDialogButtonBox>
-#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
@@ -76,11 +74,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <fmt/format.h>
#include "common/detached_tasks.h"
#include "common/fs/fs.h"
-#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
#include "common/logging/backend.h"
-#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/memory_detect.h"
#include "common/microprofile.h"
@@ -156,7 +152,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
-constexpr int default_mouse_timeout = 2500;
+constexpr int default_mouse_hide_timeout = 2500;
+constexpr int default_mouse_center_timeout = 10;
/**
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
@@ -249,9 +246,9 @@ GMainWindow::GMainWindow()
#ifdef ARCHITECTURE_x86_64
const auto& caps = Common::GetCPUCaps();
std::string cpu_string = caps.cpu_string;
- if (caps.avx || caps.avx2 || caps.avx512) {
+ if (caps.avx || caps.avx2 || caps.avx512f) {
cpu_string += " | AVX";
- if (caps.avx512) {
+ if (caps.avx512f) {
cpu_string += "512";
} else if (caps.avx2) {
cpu_string += '2';
@@ -291,13 +288,14 @@ GMainWindow::GMainWindow()
ui->menubar->setCursor(QCursor());
statusBar()->setCursor(QCursor());
- mouse_hide_timer.setInterval(default_mouse_timeout);
+ mouse_hide_timer.setInterval(default_mouse_hide_timeout);
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
- MigrateConfigFiles();
+ mouse_center_timer.setInterval(default_mouse_center_timeout);
+ connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
- ui->action_Fullscreen->setChecked(false);
+ MigrateConfigFiles();
#if defined(HAVE_SDL2) && !defined(_WIN32)
SDL_InitSubSystem(SDL_INIT_VIDEO);
@@ -316,17 +314,20 @@ GMainWindow::GMainWindow()
}
QString game_path;
+ bool has_gamepath = false;
+ bool is_fullscreen = false;
for (int i = 1; i < args.size(); ++i) {
// Preserves drag/drop functionality
if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
game_path = args[1];
+ has_gamepath = true;
break;
}
// Launch game in fullscreen mode
if (args[i] == QStringLiteral("-f")) {
- ui->action_Fullscreen->setChecked(true);
+ is_fullscreen = true;
continue;
}
@@ -369,9 +370,15 @@ GMainWindow::GMainWindow()
}
game_path = args[++i];
+ has_gamepath = true;
}
}
+ // Override fullscreen setting if gamepath or argument is provided
+ if (has_gamepath || is_fullscreen) {
+ ui->action_Fullscreen->setChecked(is_fullscreen);
+ }
+
if (!game_path.isEmpty()) {
BootGame(game_path);
}
@@ -586,7 +593,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
#ifdef YUZU_USE_QT_WEB_ENGINE
// Raw input breaks with the web applet, Disable web applets if enabled
- if (disable_web_applet || Settings::values.enable_raw_input) {
+ if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed,
"http://localhost/");
return;
@@ -651,12 +658,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] {
const auto result = QMessageBox::warning(
this, tr("Disable Web Applet"),
- tr("Disabling the web applet will cause it to not be shown again for the rest of the "
- "emulated session. This can lead to undefined behavior and should only be used with "
- "Super Mario 3D All-Stars. Are you sure you want to disable the web applet?"),
+ tr("Disabling the web applet can lead to undefined behavior and should only be used "
+ "with Super Mario 3D All-Stars. Are you sure you want to disable the web "
+ "applet?\n(This can be re-enabled in the Debug settings.)"),
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::Yes) {
- disable_web_applet = true;
+ UISettings::values.disable_web_applet = true;
web_browser_view.SetFinished(true);
}
});
@@ -1284,6 +1291,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
system->SetAppletFrontendSet({
std::make_unique<QtControllerSelector>(*this), // Controller Selector
std::make_unique<QtErrorDisplay>(*this), // Error Display
+ nullptr, // Mii Editor
nullptr, // Parental Controls
nullptr, // Photo Viewer
std::make_unique<QtProfileSelector>(*this), // Profile Selector
@@ -3297,10 +3305,26 @@ void GMainWindow::ShowMouseCursor() {
}
}
+void GMainWindow::CenterMouseCursor() {
+ if (emu_thread == nullptr || !Settings::values.mouse_panning) {
+ mouse_center_timer.stop();
+ return;
+ }
+ if (!this->isActiveWindow()) {
+ mouse_center_timer.stop();
+ return;
+ }
+ const int center_x = render_window->width() / 2;
+ const int center_y = render_window->height() / 2;
+
+ QCursor::setPos(mapToGlobal({center_x, center_y}));
+}
+
void GMainWindow::OnMouseActivity() {
if (!Settings::values.mouse_panning) {
ShowMouseCursor();
}
+ mouse_center_timer.stop();
}
void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
@@ -3573,6 +3597,22 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
AcceptDropEvent(event);
}
+void GMainWindow::leaveEvent(QEvent* event) {
+ if (Settings::values.mouse_panning) {
+ const QRect& rect = geometry();
+ QPoint position = QCursor::pos();
+
+ qint32 x = qBound(rect.left(), position.x(), rect.right());
+ qint32 y = qBound(rect.top(), position.y(), rect.bottom());
+ // Only start the timer if the mouse has left the window bound.
+ // The leave event is also triggered when the window looses focus.
+ if (x != position.x() || y != position.y()) {
+ mouse_center_timer.start();
+ }
+ event->accept();
+ }
+}
+
bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
@@ -3648,6 +3688,14 @@ void GMainWindow::UpdateUITheme() {
setStyleSheet({});
}
+ QPalette new_pal(qApp->palette());
+ if (UISettings::IsDarkTheme()) {
+ new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
+ } else {
+ new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
+ }
+ qApp->setPalette(new_pal);
+
QIcon::setThemeName(current_theme);
QIcon::setThemeSearchPaths(theme_paths);
}
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 @@
#include <memory>
#include <optional>
-#include <unordered_map>
#include <QMainWindow>
#include <QTimer>
#include <QTranslator>
#include "common/common_types.h"
-#include "core/hle/service/acc/profile_manager.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
@@ -330,6 +328,7 @@ private:
void UpdateUISettings();
void HideMouseCursor();
void ShowMouseCursor();
+ void CenterMouseCursor();
void OpenURL(const QUrl& url);
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
@@ -374,6 +373,7 @@ private:
bool auto_paused = false;
bool auto_muted = false;
QTimer mouse_hide_timer;
+ QTimer mouse_center_timer;
// FS
std::shared_ptr<FileSys::VfsFilesystem> vfs;
@@ -400,9 +400,6 @@ private:
// Last game booted, used for multi-process apps
QString last_filename_booted;
- // Disables the web applet for the rest of the emulated session
- bool disable_web_applet{};
-
// Applets
QtSoftwareKeyboardDialog* software_keyboard = nullptr;
@@ -423,4 +420,5 @@ protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
+ void leaveEvent(QEvent* event) override;
};
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{{
{"Midnight Blue Colorful", "colorful_midnight_blue"},
}};
+bool IsDarkTheme() {
+ const auto& theme = UISettings::values.theme;
+ return theme == QStringLiteral("qdarkstyle") ||
+ theme == QStringLiteral("qdarkstyle_midnight_blue") ||
+ theme == QStringLiteral("colorful_dark") ||
+ theme == QStringLiteral("colorful_midnight_blue");
+}
+
Values values = {};
} // 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 @@
namespace UISettings {
+bool IsDarkTheme();
+
struct ContextualShortcut {
QString keyseq;
QString controller_keyseq;
@@ -114,6 +116,7 @@ struct Values {
bool configuration_applied;
bool reset_to_defaults;
+ Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"};
};
extern 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
}
void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
if (!Settings::values.controller_navigation) {
return;
}
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 @@
#pragma once
-#include <array>
#include <atomic>
#include <memory>
#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)
endif()
if(UNIX AND NOT APPLE)
- install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+ install(TARGETS yuzu-cmd)
endif()
if (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 @@
// Refer to the license.txt file included.
#include <memory>
+#include <optional>
#include <sstream>
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
@@ -20,7 +21,6 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
-#include "common/param_package.h"
#include "common/settings.h"
#include "core/hle/service/acc/profile_manager.h"
#include "input_common/main.h"
@@ -29,11 +29,12 @@
namespace FS = Common::FS;
-Config::Config() {
- // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- sdl2_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
- sdl2_config = std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc));
+const std::filesystem::path default_config_path =
+ FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
+Config::Config(std::optional<std::filesystem::path> config_path)
+ : sdl2_config_loc{config_path.value_or(default_config_path)},
+ sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} {
Reload();
}
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 @@
#include <filesystem>
#include <memory>
+#include <optional>
#include <string>
#include "common/settings.h"
@@ -13,14 +14,14 @@
class INIReader;
class Config {
- std::unique_ptr<INIReader> sdl2_config;
std::filesystem::path sdl2_config_loc;
+ std::unique_ptr<INIReader> sdl2_config;
bool LoadINI(const std::string& default_contents = "", bool retry = true);
void ReadValues();
public:
- Config();
+ explicit Config(std::optional<std::filesystem::path> config_path);
~Config();
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 =
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
-use_multi_core=
+use_multi_core =
+
+# Enable extended guest system memory layout (6GB DRAM)
+# 0 (default): Disabled, 1: Enabled
+use_extended_memory_layout =
[Cpu]
# Adjusts various optimizations.
@@ -338,12 +342,6 @@ fps_cap =
# null: No audio output
output_engine =
-# Whether or not to enable the audio-stretching post-processing effect.
-# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
-# at the cost of increasing audio latency.
-# 0: No, 1 (default): Yes
-enable_audio_stretching =
-
# Which audio device to use.
# auto (default): Auto-select
output_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) {
}
void EmuWindow_SDL2::Fullscreen() {
+ SDL_DisplayMode display_mode;
switch (Settings::values.fullscreen_mode.GetValue()) {
case Settings::FullscreenMode::Exclusive:
- // Set window size to render size before entering fullscreen -- SDL does not resize to
- // display dimensions in this mode.
- // TODO: Multiply the window size by resolution_factor (for both docked modes)
- if (Settings::values.use_docked_mode) {
- SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width,
- Layout::ScreenDocked::Height);
+ // Set window size to render size before entering fullscreen -- SDL2 does not resize window
+ // to display dimensions automatically in this mode.
+ if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
+ SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
+ } else {
+ LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
}
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 @@
#pragma once
-#include <memory>
#include <utility>
#include "core/frontend/emu_window.h"
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 @@
#include <fmt/format.h>
#include <glad/glad.h>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#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 @@
#include <fmt/format.h>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
-#include "common/settings.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
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 @@
#include <fmt/ostream.h>
#include "common/detached_tasks.h"
-#include "common/fs/fs.h"
-#include "common/fs/fs_paths.h"
-#include "common/fs/path_util.h"
#include "common/logging/backend.h"
-#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/nvidia_flags.h"
@@ -66,7 +62,8 @@ static void PrintHelp(const char* argv0) {
"-f, --fullscreen Start in fullscreen mode\n"
"-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n"
- "-p, --program Pass following string as arguments to executable\n";
+ "-p, --program Pass following string as arguments to executable\n"
+ "-c, --config Load the specified configuration file\n";
}
static void PrintVersion() {
@@ -77,8 +74,8 @@ static void PrintVersion() {
int main(int argc, char** argv) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
+ Common::Log::Start();
Common::DetachedTasks detached_tasks;
- Config config;
int option_index = 0;
#ifdef _WIN32
@@ -91,19 +88,24 @@ int main(int argc, char** argv) {
}
#endif
std::string filepath;
+ std::optional<std::string> config_path;
+ std::string program_args;
bool fullscreen = false;
static struct option long_options[] = {
+ // clang-format off
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"program", optional_argument, 0, 'p'},
+ {"config", required_argument, 0, 'c'},
{0, 0, 0, 0},
+ // clang-format on
};
while (optind < argc) {
- int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
+ int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
if (arg != -1) {
switch (static_cast<char>(arg)) {
case 'f':
@@ -117,9 +119,12 @@ int main(int argc, char** argv) {
PrintVersion();
return 0;
case 'p':
- Settings::values.program_args = argv[optind];
+ program_args = argv[optind];
++optind;
break;
+ case 'c':
+ config_path = optarg;
+ break;
}
} else {
#ifdef _WIN32
@@ -131,6 +136,12 @@ int main(int argc, char** argv) {
}
}
+ Config config{config_path};
+
+ if (!program_args.empty()) {
+ Settings::values.program_args = program_args;
+ }
+
#ifdef _WIN32
LocalFree(argv_w);
#endif