summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt34
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/audio_core.cpp2
-rw-r--r--src/audio_core/audio_manager.cpp17
-rw-r--r--src/audio_core/audio_manager.h19
-rw-r--r--src/audio_core/in/audio_in_system.cpp8
-rw-r--r--src/audio_core/in/audio_in_system.h2
-rw-r--r--src/audio_core/out/audio_out_system.cpp10
-rw-r--r--src/audio_core/out/audio_out_system.h4
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp4
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.h2
-rw-r--r--src/audio_core/renderer/behavior/info_updater.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp2
-rw-r--r--src/audio_core/renderer/system.cpp89
-rw-r--r--src/audio_core/renderer/system.h16
-rw-r--r--src/audio_core/renderer/system_manager.cpp2
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp4
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp31
-rw-r--r--src/audio_core/sink/cubeb_sink.h7
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp14
-rw-r--r--src/audio_core/sink/sdl2_sink.h7
-rw-r--r--src/audio_core/sink/sink_details.cpp66
-rw-r--r--src/audio_core/sink/sink_details.h2
-rw-r--r--src/audio_core/sink/sink_stream.cpp9
-rw-r--r--src/common/CMakeLists.txt46
-rw-r--r--src/common/address_space.cpp10
-rw-r--r--src/common/address_space.h150
-rw-r--r--src/common/address_space.inc366
-rw-r--r--src/common/algorithm.h8
-rw-r--r--src/common/bit_field.h15
-rw-r--r--src/common/bounded_threadsafe_queue.h9
-rw-r--r--src/common/concepts.h24
-rw-r--r--src/common/fixed_point.h274
-rw-r--r--src/common/fs/file.h12
-rw-r--r--src/common/hash.h7
-rw-r--r--src/common/input.h35
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/multi_level_page_table.cpp9
-rw-r--r--src/common/multi_level_page_table.h78
-rw-r--r--src/common/multi_level_page_table.inc84
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/CMakeLists.txt34
-rw-r--r--src/core/arm/arm_interface.cpp8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp3
-rw-r--r--src/core/core.cpp115
-rw-r--r--src/core/core.h29
-rw-r--r--src/core/core_timing.cpp40
-rw-r--r--src/core/core_timing.h14
-rw-r--r--src/core/cpu_manager.cpp4
-rw-r--r--src/core/debugger/debugger.cpp2
-rw-r--r--src/core/device_memory.h10
-rw-r--r--src/core/file_sys/card_image.cpp4
-rw-r--r--src/core/file_sys/control_metadata.cpp43
-rw-r--r--src/core/file_sys/control_metadata.h6
-rw-r--r--src/core/file_sys/program_metadata.cpp54
-rw-r--r--src/core/file_sys/program_metadata.h14
-rw-r--r--src/core/file_sys/savedata_factory.cpp58
-rw-r--r--src/core/file_sys/savedata_factory.h4
-rw-r--r--src/core/frontend/framebuffer_layout.cpp2
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/hardware_interrupt_manager.cpp32
-rw-r--r--src/core/hardware_interrupt_manager.h32
-rw-r--r--src/core/hid/emulated_controller.cpp144
-rw-r--r--src/core/hid/emulated_controller.h39
-rw-r--r--src/core/hid/input_converter.cpp22
-rw-r--r--src/core/hid/input_converter.h8
-rw-r--r--src/core/hid/irs_types.h20
-rw-r--r--src/core/hle/ipc_helpers.h17
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp22
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h8
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp105
-rw-r--r--src/core/hle/kernel/hle_ipc.h40
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp8
-rw-r--r--src/core/hle/kernel/k_class_token.cpp12
-rw-r--r--src/core/hle/kernel/k_class_token.h1
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_client_port.h3
-rw-r--r--src/core/hle/kernel/k_client_session.cpp18
-rw-r--r--src/core/hle/kernel/k_client_session.h3
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp2
-rw-r--r--src/core/hle/kernel/k_dynamic_page_manager.h136
-rw-r--r--src/core/hle/kernel/k_dynamic_resource_manager.h58
-rw-r--r--src/core/hle/kernel/k_dynamic_slab_heap.h122
-rw-r--r--src/core/hle/kernel/k_event.cpp44
-rw-r--r--src/core/hle/kernel/k_event.h31
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp29
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.h4
-rw-r--r--src/core/hle/kernel/k_linked_list.h1
-rw-r--r--src/core/hle/kernel/k_memory_block.h506
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp409
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h145
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp2
-rw-r--r--src/core/hle/kernel/k_page_buffer.cpp2
-rw-r--r--src/core/hle/kernel/k_page_buffer.h1
-rw-r--r--src/core/hle/kernel/k_page_table.cpp1302
-rw-r--r--src/core/hle/kernel/k_page_table.h319
-rw-r--r--src/core/hle/kernel/k_port.cpp6
-rw-r--r--src/core/hle/kernel/k_process.cpp112
-rw-r--r--src/core/hle/kernel/k_process.h83
-rw-r--r--src/core/hle/kernel/k_readable_event.cpp33
-rw-r--r--src/core/hle/kernel/k_readable_event.h17
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp26
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.h19
-rw-r--r--src/core/hle/kernel/k_server_session.cpp434
-rw-r--r--src/core/hle/kernel/k_server_session.h89
-rw-r--r--src/core/hle/kernel/k_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h3
-rw-r--r--src/core/hle/kernel/k_session_request.cpp61
-rw-r--r--src/core/hle/kernel/k_session_request.h306
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp2
-rw-r--r--src/core/hle/kernel/k_shared_memory.h4
-rw-r--r--src/core/hle/kernel/k_shared_memory_info.h3
-rw-r--r--src/core/hle/kernel/k_slab_heap.h27
-rw-r--r--src/core/hle/kernel/k_thread.cpp155
-rw-r--r--src/core/hle/kernel/k_thread.h12
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h2
-rw-r--r--src/core/hle/kernel/k_worker_task_manager.cpp2
-rw-r--r--src/core/hle/kernel/k_writable_event.cpp35
-rw-r--r--src/core/hle/kernel/k_writable_event.h39
-rw-r--r--src/core/hle/kernel/kernel.cpp132
-rw-r--r--src/core/hle/kernel/kernel.h33
-rw-r--r--src/core/hle/kernel/service_thread.cpp230
-rw-r--r--src/core/hle/kernel/service_thread.h6
-rw-r--r--src/core/hle/kernel/slab_helpers.h2
-rw-r--r--src/core/hle/kernel/svc.cpp206
-rw-r--r--src/core/hle/kernel/svc_common.h7
-rw-r--r--src/core/hle/kernel/svc_types.h13
-rw-r--r--src/core/hle/kernel/svc_wrap.h32
-rw-r--r--src/core/hle/result.h130
-rw-r--r--src/core/hle/service/acc/acc.cpp34
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/async_context.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp25
-rw-r--r--src/core/hle/service/acc/profile_manager.h3
-rw-r--r--src/core/hle/service/am/am.cpp25
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applets.cpp10
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/audio/audctl.cpp16
-rw-r--r--src/core/hle/service/audio/audin_u.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp30
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp14
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/friend/friend.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp40
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp229
-rw-r--r--src/core/hle/service/hid/controllers/palma.h163
-rw-r--r--src/core/hle/service/hid/errors.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp447
-rw-r--r--src/core/hle/service/hid/hid.h29
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp8
-rw-r--r--src/core/hle/service/hid/irs.cpp3
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h4
-rw-r--r--src/core/hle/service/kernel_helpers.cpp5
-rw-r--r--src/core/hle/service/ldn/lan_discovery.cpp633
-rw-r--r--src/core/hle/service/ldn/lan_discovery.h134
-rw-r--r--src/core/hle/service/ldn/ldn.cpp229
-rw-r--r--src/core/hle/service/ldn/ldn_types.h48
-rw-r--r--src/core/hle/service/ldr/ldr.cpp4
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp161
-rw-r--r--src/core/hle/service/mii/mii_manager.h4
-rw-r--r--src/core/hle/service/nfc/nfc.cpp8
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp87
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h13
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1093
-rw-r--r--src/core/hle/service/nfp/nfp.h161
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp690
-rw-r--r--src/core/hle/service/nfp/nfp_device.h100
-rw-r--r--src/core/hle/service/nfp/nfp_result.h24
-rw-r--r--src/core/hle/service/nfp/nfp_types.h (renamed from src/core/hle/service/nfp/amiibo_types.h)154
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp661
-rw-r--r--src/core/hle/service/nfp/nfp_user.h50
-rw-r--r--src/core/hle/service/nim/nim.cpp4
-rw-r--r--src/core/hle/service/ns/ns.cpp30
-rw-r--r--src/core/hle/service/ns/ns.h3
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp50
-rw-r--r--src/core/hle/service/nvdrv/core/container.h52
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp273
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.h176
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.cpp121
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h134
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp19
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp496
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h191
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp364
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h114
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp129
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h54
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp81
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h23
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp233
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h56
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h17
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp131
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h123
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp31
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h6
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp38
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h84
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp9
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp26
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h10
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp55
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h15
-rw-r--r--src/core/hle/service/ptm/psm.cpp6
-rw-r--r--src/core/hle/service/ptm/ts.cpp15
-rw-r--r--src/core/hle/service/ptm/ts.h1
-rw-r--r--src/core/hle/service/service.cpp25
-rw-r--r--src/core/hle/service/service.h6
-rw-r--r--src/core/hle/service/set/set_sys.cpp79
-rw-r--r--src/core/hle/service/set/set_sys.h2
-rw-r--r--src/core/hle/service/sm/sm.cpp43
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp40
-rw-r--r--src/core/hle/service/sockets/bsd.cpp2
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp10
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h6
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp34
-rw-r--r--src/core/hle/service/vi/display/vi_display.h27
-rw-r--r--src/core/hle/service/vi/vi.cpp49
-rw-r--r--src/core/hle/service/vi/vi_results.h15
-rw-r--r--src/core/internal_network/network.cpp12
-rw-r--r--src/core/internal_network/network_interface.cpp12
-rw-r--r--src/core/internal_network/network_interface.h1
-rw-r--r--src/core/internal_network/socket_proxy.cpp8
-rw-r--r--src/core/internal_network/sockets.h11
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/memory.cpp52
-rw-r--r--src/core/memory.h1
-rw-r--r--src/dedicated_room/CMakeLists.txt2
-rw-r--r--src/dedicated_room/yuzu_room.cpp13
-rw-r--r--src/input_common/CMakeLists.txt11
-rw-r--r--src/input_common/drivers/gc_adapter.cpp10
-rw-r--r--src/input_common/drivers/gc_adapter.h4
-rw-r--r--src/input_common/drivers/mouse.cpp2
-rw-r--r--src/input_common/drivers/sdl_driver.cpp72
-rw-r--r--src/input_common/drivers/sdl_driver.h4
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp101
-rw-r--r--src/input_common/drivers/virtual_amiibo.h61
-rw-r--r--src/input_common/input_engine.cpp37
-rw-r--r--src/input_common/input_engine.h24
-rw-r--r--src/input_common/input_poller.cpp96
-rw-r--r--src/input_common/input_poller.h10
-rw-r--r--src/input_common/main.cpp21
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/network/network.cpp2
-rw-r--r--src/network/room.cpp63
-rw-r--r--src/network/room.h1
-rw-r--r--src/network/room_member.cpp57
-rw-r--r--src/network/room_member.h35
-rw-r--r--src/shader_recompiler/CMakeLists.txt14
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp25
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp6
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp47
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp98
-rw-r--r--src/shader_recompiler/runtime_info.h2
-rw-r--r--src/shader_recompiler/shader_info.h7
-rw-r--r--src/tests/core/core_timing.cpp3
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/CMakeLists.txt60
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h176
-rw-r--r--src/video_core/cdma_pusher.cpp29
-rw-r--r--src/video_core/cdma_pusher.h18
-rw-r--r--src/video_core/command_classes/host1x.cpp29
-rw-r--r--src/video_core/control/channel_state.cpp40
-rw-r--r--src/video_core/control/channel_state.h68
-rw-r--r--src/video_core/control/channel_state_cache.cpp14
-rw-r--r--src/video_core/control/channel_state_cache.h101
-rw-r--r--src/video_core/control/channel_state_cache.inc86
-rw-r--r--src/video_core/control/scheduler.cpp32
-rw-r--r--src/video_core/control/scheduler.h37
-rw-r--r--src/video_core/dirty_flags.cpp24
-rw-r--r--src/video_core/dma_pusher.cpp26
-rw-r--r--src/video_core/dma_pusher.h39
-rw-r--r--src/video_core/engines/engine_upload.cpp46
-rw-r--r--src/video_core/engines/engine_upload.h6
-rw-r--r--src/video_core/engines/kepler_compute.cpp13
-rw-r--r--src/video_core/engines/kepler_memory.cpp13
-rw-r--r--src/video_core/engines/maxwell_3d.cpp578
-rw-r--r--src/video_core/engines/maxwell_3d.h3877
-rw-r--r--src/video_core/engines/maxwell_dma.cpp236
-rw-r--r--src/video_core/engines/maxwell_dma.h8
-rw-r--r--src/video_core/engines/puller.cpp305
-rw-r--r--src/video_core/engines/puller.h177
-rw-r--r--src/video_core/fence_manager.h104
-rw-r--r--src/video_core/gpu.cpp706
-rw-r--r--src/video_core/gpu.h143
-rw-r--r--src/video_core/gpu_thread.cpp24
-rw-r--r--src/video_core/gpu_thread.h14
-rw-r--r--src/video_core/host1x/codecs/codec.cpp (renamed from src/video_core/command_classes/codecs/codec.cpp)44
-rw-r--r--src/video_core/host1x/codecs/codec.h (renamed from src/video_core/command_classes/codecs/codec.h)21
-rw-r--r--src/video_core/host1x/codecs/h264.cpp (renamed from src/video_core/command_classes/codecs/h264.cpp)17
-rw-r--r--src/video_core/host1x/codecs/h264.h (renamed from src/video_core/command_classes/codecs/h264.h)16
-rw-r--r--src/video_core/host1x/codecs/vp8.cpp (renamed from src/video_core/command_classes/codecs/vp8.cpp)12
-rw-r--r--src/video_core/host1x/codecs/vp8.h (renamed from src/video_core/command_classes/codecs/vp8.h)15
-rw-r--r--src/video_core/host1x/codecs/vp9.cpp (renamed from src/video_core/command_classes/codecs/vp9.cpp)23
-rw-r--r--src/video_core/host1x/codecs/vp9.h (renamed from src/video_core/command_classes/codecs/vp9.h)22
-rw-r--r--src/video_core/host1x/codecs/vp9_types.h (renamed from src/video_core/command_classes/codecs/vp9_types.h)1
-rw-r--r--src/video_core/host1x/control.cpp33
-rw-r--r--src/video_core/host1x/control.h (renamed from src/video_core/command_classes/host1x.h)20
-rw-r--r--src/video_core/host1x/host1x.cpp17
-rw-r--r--src/video_core/host1x/host1x.h57
-rw-r--r--src/video_core/host1x/nvdec.cpp (renamed from src/video_core/command_classes/nvdec.cpp)11
-rw-r--r--src/video_core/host1x/nvdec.h (renamed from src/video_core/command_classes/nvdec.h)14
-rw-r--r--src/video_core/host1x/nvdec_common.h (renamed from src/video_core/command_classes/nvdec_common.h)4
-rw-r--r--src/video_core/host1x/sync_manager.cpp (renamed from src/video_core/command_classes/sync_manager.cpp)13
-rw-r--r--src/video_core/host1x/sync_manager.h (renamed from src/video_core/command_classes/sync_manager.h)12
-rw-r--r--src/video_core/host1x/syncpoint_manager.cpp106
-rw-r--r--src/video_core/host1x/syncpoint_manager.h98
-rw-r--r--src/video_core/host1x/vic.cpp (renamed from src/video_core/command_classes/vic.cpp)36
-rw-r--r--src/video_core/host1x/vic.h (renamed from src/video_core/command_classes/vic.h)13
-rw-r--r--src/video_core/host_shaders/astc_decoder.comp2
-rw-r--r--src/video_core/macro/macro.cpp1
-rw-r--r--src/video_core/macro/macro_hle.cpp118
-rw-r--r--src/video_core/macro/macro_interpreter.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp64
-rw-r--r--src/video_core/memory_manager.cpp785
-rw-r--r--src/video_core/memory_manager.h189
-rw-r--r--src/video_core/pte_kind.h264
-rw-r--r--src/video_core/query_cache.h22
-rw-r--r--src/video_core/rasterizer_interface.h22
-rw-r--r--src/video_core/renderer_base.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h16
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp58
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h20
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp353
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h26
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp86
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp70
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h83
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h291
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp303
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h15
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp341
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h2
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp32
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h28
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp104
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp266
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp68
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h27
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp47
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h16
-rw-r--r--src/video_core/shader_cache.cpp37
-rw-r--r--src/video_core/shader_cache.h15
-rw-r--r--src/video_core/shader_environment.cpp23
-rw-r--r--src/video_core/shader_environment.h2
-rw-r--r--src/video_core/surface.cpp17
-rw-r--r--src/video_core/surface.h16
-rw-r--r--src/video_core/texture_cache/descriptor_table.h2
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp8
-rw-r--r--src/video_core/texture_cache/formatter.h8
-rw-r--r--src/video_core/texture_cache/image_base.cpp13
-rw-r--r--src/video_core/texture_cache/image_base.h3
-rw-r--r--src/video_core/texture_cache/image_info.cpp37
-rw-r--r--src/video_core/texture_cache/render_targets.h1
-rw-r--r--src/video_core/texture_cache/texture_cache.cpp15
-rw-r--r--src/video_core/texture_cache/texture_cache.h229
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h85
-rw-r--r--src/video_core/texture_cache/util.cpp3
-rw-r--r--src/video_core/textures/astc.cpp8
-rw-r--r--src/video_core/textures/decoders.cpp242
-rw-r--r--src/video_core/textures/decoders.h33
-rw-r--r--src/video_core/transform_feedback.cpp118
-rw-r--r--src/video_core/transform_feedback.h3
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h20
-rw-r--r--src/web_service/web_backend.cpp3
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/applets/qt_controller.ui2
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_audio.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui10
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp7
-rw-r--r--src/yuzu/configuration/configure_graphics.ui5
-rw-r--r--src/yuzu/configuration/configure_input.cpp7
-rw-r--r--src/yuzu/configuration/configure_ui.cpp3
-rw-r--r--src/yuzu/configuration/configure_ui.ui7
-rw-r--r--src/yuzu/configuration/input_profiles.cpp2
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/main.cpp103
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui25
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp12
-rw-r--r--src/yuzu/multiplayer/client_room.cpp3
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp2
-rw-r--r--src/yuzu/multiplayer/direct_connect.h1
-rw-r--r--src/yuzu/multiplayer/host_room.cpp1
-rw-r--r--src/yuzu/multiplayer/host_room.h3
-rw-r--r--src/yuzu/multiplayer/lobby.cpp67
-rw-r--r--src/yuzu/multiplayer/lobby.h8
-rw-r--r--src/yuzu/multiplayer/lobby_p.h18
-rw-r--r--src/yuzu/multiplayer/message.cpp6
-rw-r--r--src/yuzu/multiplayer/state.cpp82
-rw-r--r--src/yuzu/multiplayer/state.h14
-rw-r--r--src/yuzu/startup_checks.cpp102
-rw-r--r--src/yuzu/startup_checks.h2
-rw-r--r--src/yuzu/uisettings.h5
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
444 files changed, 21131 insertions, 9825 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 54de1dc94..0ac3d254e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -58,13 +58,11 @@ if (MSVC)
58 58
59 # Warnings 59 # Warnings
60 /W3 60 /W3
61 /we4018 # 'expression': signed/unsigned mismatch 61 /WX
62
62 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled 63 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
63 /we4101 # 'identifier': unreferenced local variable
64 /we4189 # 'identifier': local variable is initialized but not referenced 64 /we4189 # 'identifier': local variable is initialized but not referenced
65 /we4265 # 'class': class has virtual functions, but destructor is not virtual 65 /we4265 # 'class': class has virtual functions, but destructor is not virtual
66 /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
67 /we4305 # 'context': truncation from 'type1' to 'type2'
68 /we4388 # 'expression': signed/unsigned mismatch 66 /we4388 # 'expression': signed/unsigned mismatch
69 /we4389 # 'operator': signed/unsigned mismatch 67 /we4389 # 'operator': signed/unsigned mismatch
70 /we4456 # Declaration of 'identifier' hides previous local declaration 68 /we4456 # Declaration of 'identifier' hides previous local declaration
@@ -75,10 +73,13 @@ if (MSVC)
75 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect 73 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
76 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 74 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
77 /we4555 # Expression has no effect; expected expression with side-effect 75 /we4555 # Expression has no effect; expected expression with side-effect
78 /we4715 # 'function': not all control paths return a value 76 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
79 /we4834 # Discarding return value of function with 'nodiscard' attribute
80 /we5038 # data member 'member1' will be initialized after data member 'member2' 77 /we5038 # data member 'member1' will be initialized after data member 'member2'
78 /we5233 # explicit lambda capture 'identifier' is not used
81 /we5245 # 'function': unreferenced function with internal linkage has been removed 79 /we5245 # 'function': unreferenced function with internal linkage has been removed
80
81 /wd4100 # 'identifier': unreferenced formal parameter
82 /wd4324 # 'struct_name': structure was padded due to __declspec(align())
82 ) 83 )
83 84
84 if (USE_CCACHE) 85 if (USE_CCACHE)
@@ -99,28 +100,23 @@ if (MSVC)
99 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) 100 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
100else() 101else()
101 add_compile_options( 102 add_compile_options(
102 -Wall 103 -Werror=all
103 -Werror=array-bounds 104 -Werror=extra
104 -Werror=implicit-fallthrough
105 -Werror=missing-declarations 105 -Werror=missing-declarations
106 -Werror=missing-field-initializers
107 -Werror=reorder
108 -Werror=shadow 106 -Werror=shadow
109 -Werror=sign-compare 107 -Werror=unused
110 -Werror=switch 108
111 -Werror=uninitialized
112 -Werror=unused-function
113 -Werror=unused-result
114 -Werror=unused-variable
115 -Wextra
116 -Wmissing-declarations
117 -Wno-attributes 109 -Wno-attributes
118 -Wno-invalid-offsetof 110 -Wno-invalid-offsetof
119 -Wno-unused-parameter 111 -Wno-unused-parameter
112
113 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
114 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
120 ) 115 )
121 116
122 if (ARCHITECTURE_x86_64) 117 if (ARCHITECTURE_x86_64)
123 add_compile_options("-mcx16") 118 add_compile_options("-mcx16")
119 add_compile_options("-fwrapv")
124 endif() 120 endif()
125 121
126 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) 122 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 144f1bab2..0a1f3bf18 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -206,20 +206,11 @@ if (MSVC)
206 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 206 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
207 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch 207 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
208 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 208 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
209 /we4456 # Declaration of 'identifier' hides previous local declaration 209 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
210 /we4457 # Declaration of 'identifier' hides function parameter
211 /we4458 # Declaration of 'identifier' hides class member
212 /we4459 # Declaration of 'identifier' hides global declaration
213 ) 210 )
214else() 211else()
215 target_compile_options(audio_core PRIVATE 212 target_compile_options(audio_core PRIVATE
216 -Werror=conversion 213 -Werror=conversion
217 -Werror=ignored-qualifiers
218 -Werror=shadow
219 -Werror=unused-variable
220
221 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
222 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
223 214
224 -Wno-sign-conversion 215 -Wno-sign-conversion
225 ) 216 )
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
index c845330cd..07a679c32 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -8,7 +8,7 @@
8 8
9namespace AudioCore { 9namespace AudioCore {
10 10
11AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>(system)} { 11AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
12 CreateSinks(); 12 CreateSinks();
13 // Must be created after the sinks 13 // Must be created after the sinks
14 adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink); 14 adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink);
diff --git a/src/audio_core/audio_manager.cpp b/src/audio_core/audio_manager.cpp
index 2f1bba9c3..2acde668e 100644
--- a/src/audio_core/audio_manager.cpp
+++ b/src/audio_core/audio_manager.cpp
@@ -1,14 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "audio_core/audio_in_manager.h"
5#include "audio_core/audio_manager.h" 4#include "audio_core/audio_manager.h"
6#include "audio_core/audio_out_manager.h"
7#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/service/audio/errors.h"
8 7
9namespace AudioCore { 8namespace AudioCore {
10 9
11AudioManager::AudioManager(Core::System& system_) : system{system_} { 10AudioManager::AudioManager() {
12 thread = std::jthread([this]() { ThreadFunc(); }); 11 thread = std::jthread([this]() { ThreadFunc(); });
13} 12}
14 13
@@ -27,7 +26,7 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
27 26
28 const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; 27 const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
29 if (buffer_events[index] == nullptr) { 28 if (buffer_events[index] == nullptr) {
30 buffer_events[index] = buffer_func; 29 buffer_events[index] = std::move(buffer_func);
31 needs_update = true; 30 needs_update = true;
32 events.SetAudioEvent(Event::Type::AudioOutManager, true); 31 events.SetAudioEvent(Event::Type::AudioOutManager, true);
33 } 32 }
@@ -43,7 +42,7 @@ Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
43 42
44 const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; 43 const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
45 if (buffer_events[index] == nullptr) { 44 if (buffer_events[index] == nullptr) {
46 buffer_events[index] = buffer_func; 45 buffer_events[index] = std::move(buffer_func);
47 needs_update = true; 46 needs_update = true;
48 events.SetAudioEvent(Event::Type::AudioInManager, true); 47 events.SetAudioEvent(Event::Type::AudioInManager, true);
49 } 48 }
@@ -60,19 +59,21 @@ void AudioManager::ThreadFunc() {
60 running = true; 59 running = true;
61 60
62 while (running) { 61 while (running) {
63 auto timed_out{events.Wait(l, std::chrono::seconds(2))}; 62 const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
64 63
65 if (events.CheckAudioEventSet(Event::Type::Max)) { 64 if (events.CheckAudioEventSet(Event::Type::Max)) {
66 break; 65 break;
67 } 66 }
68 67
69 for (size_t i = 0; i < buffer_events.size(); i++) { 68 for (size_t i = 0; i < buffer_events.size(); i++) {
70 if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) { 69 const auto event_type = static_cast<Event::Type>(i);
70
71 if (events.CheckAudioEventSet(event_type) || timed_out) {
71 if (buffer_events[i]) { 72 if (buffer_events[i]) {
72 buffer_events[i](); 73 buffer_events[i]();
73 } 74 }
74 } 75 }
75 events.SetAudioEvent(Event::Type(i), false); 76 events.SetAudioEvent(event_type, false);
76 } 77 }
77 } 78 }
78} 79}
diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h
index 8cbd95e22..abf077de4 100644
--- a/src/audio_core/audio_manager.h
+++ b/src/audio_core/audio_manager.h
@@ -10,22 +10,11 @@
10#include <thread> 10#include <thread>
11 11
12#include "audio_core/audio_event.h" 12#include "audio_core/audio_event.h"
13#include "core/hle/service/audio/errors.h"
14 13
15namespace Core { 14union Result;
16class System;
17}
18 15
19namespace AudioCore { 16namespace AudioCore {
20 17
21namespace AudioOut {
22class Manager;
23}
24
25namespace AudioIn {
26class Manager;
27}
28
29/** 18/**
30 * The AudioManager's main purpose is to wait for buffer events for the audio in and out managers, 19 * The AudioManager's main purpose is to wait for buffer events for the audio in and out managers,
31 * and call an associated callback to release buffers. 20 * and call an associated callback to release buffers.
@@ -43,7 +32,7 @@ class AudioManager {
43 using BufferEventFunc = std::function<void()>; 32 using BufferEventFunc = std::function<void()>;
44 33
45public: 34public:
46 explicit AudioManager(Core::System& system); 35 explicit AudioManager();
47 36
48 /** 37 /**
49 * Shutdown the audio manager. 38 * Shutdown the audio manager.
@@ -80,10 +69,6 @@ private:
80 */ 69 */
81 void ThreadFunc(); 70 void ThreadFunc();
82 71
83 /// Core system
84 Core::System& system;
85 /// Have sessions started palying?
86 bool sessions_started{};
87 /// Is the main thread running? 72 /// Is the main thread running?
88 std::atomic<bool> running{}; 73 std::atomic<bool> running{};
89 /// Unused 74 /// Unused
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index e7f918a47..4324cafd8 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -23,7 +23,7 @@ System::~System() {
23void System::Finalize() { 23void System::Finalize() {
24 Stop(); 24 Stop();
25 session->Finalize(); 25 session->Finalize();
26 buffer_event->GetWritableEvent().Signal(); 26 buffer_event->Signal();
27} 27}
28 28
29void System::StartSession() { 29void System::StartSession() {
@@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name,
56 return ResultSuccess; 56 return ResultSuccess;
57} 57}
58 58
59Result System::Initialize(std::string& device_name, const AudioInParameter& in_params, 59Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
60 const u32 handle_, const u64 applet_resource_user_id_) { 60 const u32 handle_, const u64 applet_resource_user_id_) {
61 auto result{IsConfigValid(device_name, in_params)}; 61 auto result{IsConfigValid(device_name, in_params)};
62 if (result.IsError()) { 62 if (result.IsError()) {
@@ -142,7 +142,7 @@ void System::ReleaseBuffers() {
142 142
143 if (signal) { 143 if (signal) {
144 // Signal if any buffer was released, or if none are registered, we need more. 144 // Signal if any buffer was released, or if none are registered, we need more.
145 buffer_event->GetWritableEvent().Signal(); 145 buffer_event->Signal();
146 } 146 }
147} 147}
148 148
@@ -159,7 +159,7 @@ bool System::FlushAudioInBuffers() {
159 buffers.FlushBuffers(buffers_released); 159 buffers.FlushBuffers(buffers_released);
160 160
161 if (buffers_released > 0) { 161 if (buffers_released > 0) {
162 buffer_event->GetWritableEvent().Signal(); 162 buffer_event->Signal();
163 } 163 }
164 return true; 164 return true;
165} 165}
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
index b9dc0e60f..1c5154638 100644
--- a/src/audio_core/in/audio_in_system.h
+++ b/src/audio_core/in/audio_in_system.h
@@ -97,7 +97,7 @@ public:
97 * @param applet_resource_user_id - Unused. 97 * @param applet_resource_user_id - Unused.
98 * @return Result code. 98 * @return Result code.
99 */ 99 */
100 Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle, 100 Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
101 u64 applet_resource_user_id); 101 u64 applet_resource_user_id);
102 102
103 /** 103 /**
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index 8b907590a..a66208ed9 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -24,7 +24,7 @@ System::~System() {
24void System::Finalize() { 24void System::Finalize() {
25 Stop(); 25 Stop();
26 session->Finalize(); 26 session->Finalize();
27 buffer_event->GetWritableEvent().Signal(); 27 buffer_event->Signal();
28} 28}
29 29
30std::string_view System::GetDefaultOutputDeviceName() const { 30std::string_view System::GetDefaultOutputDeviceName() const {
@@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name,
49 return Service::Audio::ERR_INVALID_CHANNEL_COUNT; 49 return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
50} 50}
51 51
52Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_, 52Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
53 u64& applet_resource_user_id_) { 53 u64 applet_resource_user_id_) {
54 auto result = IsConfigValid(device_name, in_params); 54 auto result = IsConfigValid(device_name, in_params);
55 if (result.IsError()) { 55 if (result.IsError()) {
56 return result; 56 return result;
@@ -141,7 +141,7 @@ void System::ReleaseBuffers() {
141 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; 141 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
142 if (signal) { 142 if (signal) {
143 // Signal if any buffer was released, or if none are registered, we need more. 143 // Signal if any buffer was released, or if none are registered, we need more.
144 buffer_event->GetWritableEvent().Signal(); 144 buffer_event->Signal();
145 } 145 }
146} 146}
147 147
@@ -158,7 +158,7 @@ bool System::FlushAudioOutBuffers() {
158 buffers.FlushBuffers(buffers_released); 158 buffers.FlushBuffers(buffers_released);
159 159
160 if (buffers_released > 0) { 160 if (buffers_released > 0) {
161 buffer_event->GetWritableEvent().Signal(); 161 buffer_event->Signal();
162 } 162 }
163 return true; 163 return true;
164} 164}
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
index 0817b2f37..b95cb91be 100644
--- a/src/audio_core/out/audio_out_system.h
+++ b/src/audio_core/out/audio_out_system.h
@@ -88,8 +88,8 @@ public:
88 * @param applet_resource_user_id - Unused. 88 * @param applet_resource_user_id - Unused.
89 * @return Result code. 89 * @return Result code.
90 */ 90 */
91 Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle, 91 Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
92 u64& applet_resource_user_id); 92 u64 applet_resource_user_id);
93 93
94 /** 94 /**
95 * Start this system. 95 * Start this system.
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
index bafe4822a..d982ef630 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -47,7 +47,7 @@ RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
47 return msg; 47 return msg;
48} 48}
49 49
50CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) { 50CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) {
51 return command_buffers[session_id]; 51 return command_buffers[session_id];
52} 52}
53 53
@@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() {
132} 132}
133 133
134void AudioRenderer::ThreadFunc() { 134void AudioRenderer::ThreadFunc() {
135 constexpr char name[]{"yuzu:AudioRenderer"}; 135 constexpr char name[]{"AudioRenderer"};
136 MicroProfileOnThreadCreate(name); 136 MicroProfileOnThreadCreate(name);
137 Common::SetCurrentThreadName(name); 137 Common::SetCurrentThreadName(name);
138 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); 138 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h
index 02e923c84..151f38c1b 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.h
+++ b/src/audio_core/renderer/adsp/audio_renderer.h
@@ -83,7 +83,7 @@ public:
83 * @param session_id - The session id to get (0 or 1). 83 * @param session_id - The session id to get (0 or 1).
84 * @return The command buffer. 84 * @return The command buffer.
85 */ 85 */
86 CommandBuffer& GetCommandBuffer(s32 session_id); 86 CommandBuffer& GetCommandBuffer(u32 session_id);
87 87
88 /** 88 /**
89 * Set the command buffer with the given session id (0 or 1). 89 * Set the command buffer with the given session id (0 or 1).
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp
index c0a307b89..574cf0982 100644
--- a/src/audio_core/renderer/behavior/info_updater.cpp
+++ b/src/audio_core/renderer/behavior/info_updater.cpp
@@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
91 voice_info.Initialize(); 91 voice_info.Initialize();
92 92
93 for (u32 channel = 0; channel < in_param.channel_count; channel++) { 93 for (u32 channel = 0; channel < in_param.channel_count; channel++) {
94 std::memset(voice_states[channel], 0, sizeof(VoiceState)); 94 *voice_states[channel] = {};
95 } 95 }
96 } 96 }
97 97
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index 1baae74fd..edb30ce72 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor
94void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { 94void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
95 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; 95 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
96 if (needs_init) { 96 if (needs_init) {
97 std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState)); 97 *state_ = {};
98 } 98 }
99 99
100 auto input_buffer{ 100 auto input_buffer{
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
index b3c3ba4ba..48a7cba8a 100644
--- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
@@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc
30 for (u32 i = 0; i < filter_tap_count; i++) { 30 for (u32 i = 0; i < filter_tap_count; i++) {
31 auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])}; 31 auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])};
32 if (needs_init[i]) { 32 if (needs_init[i]) {
33 std::memset(state, 0, sizeof(VoiceState::BiquadFilterState)); 33 *state = {};
34 } 34 }
35 35
36 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, 36 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index 7a217969e..4fac30c7c 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
98 : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} 98 : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
99 99
100Result System::Initialize(const AudioRendererParameterInternal& params, 100Result System::Initialize(const AudioRendererParameterInternal& params,
101 Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, 101 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
102 const u32 process_handle_, const u64 applet_resource_user_id_, 102 u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) {
103 const s32 session_id_) {
104 if (!CheckValidRevision(params.revision)) { 103 if (!CheckValidRevision(params.revision)) {
105 return Service::Audio::ERR_INVALID_REVISION; 104 return Service::Audio::ERR_INVALID_REVISION;
106 } 105 }
@@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
354 353
355 render_time_limit_percent = 100; 354 render_time_limit_percent = 100;
356 drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto; 355 drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
356 drop_voice_param = 1.0f;
357 num_voices_dropped = 0;
357 358
358 allocator.Align(0x40); 359 allocator.Align(0x40);
359 command_workbuffer_size = allocator.GetRemainingSize(); 360 command_workbuffer_size = allocator.GetRemainingSize();
@@ -534,7 +535,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
534 return result; 535 return result;
535 } 536 }
536 537
537 adsp_rendered_event->GetWritableEvent().Clear(); 538 adsp_rendered_event->Clear();
538 num_times_updated++; 539 num_times_updated++;
539 540
540 const auto end_time{core.CoreTiming().GetClockTicks()}; 541 const auto end_time{core.CoreTiming().GetClockTicks()};
@@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const {
547 return render_time_limit_percent; 548 return render_time_limit_percent;
548} 549}
549 550
550void System::SetRenderingTimeLimit(const u32 limit) { 551void System::SetRenderingTimeLimit(u32 limit) {
551 render_time_limit_percent = limit; 552 render_time_limit_percent = limit;
552} 553}
553 554
@@ -625,7 +626,7 @@ void System::SendCommandToDsp() {
625 reset_command_buffers = false; 626 reset_command_buffers = false;
626 command_buffer_size = command_size; 627 command_buffer_size = command_size;
627 if (remaining_command_count == 0) { 628 if (remaining_command_count == 0) {
628 adsp_rendered_event->GetWritableEvent().Signal(); 629 adsp_rendered_event->Signal();
629 } 630 }
630 } else { 631 } else {
631 adsp.ClearRemainCount(session_id); 632 adsp.ClearRemainCount(session_id);
@@ -635,7 +636,7 @@ void System::SendCommandToDsp() {
635} 636}
636 637
637u64 System::GenerateCommand(std::span<u8> in_command_buffer, 638u64 System::GenerateCommand(std::span<u8> in_command_buffer,
638 [[maybe_unused]] const u64 command_buffer_size_) { 639 [[maybe_unused]] u64 command_buffer_size_) {
639 PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); 640 PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
640 const auto start_time{core.CoreTiming().GetClockTicks()}; 641 const auto start_time{core.CoreTiming().GetClockTicks()};
641 642
@@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
693 694
694 voice_context.SortInfo(); 695 voice_context.SortInfo();
695 696
696 const auto start_estimated_time{command_buffer.estimated_process_time}; 697 const auto start_estimated_time{drop_voice_param *
698 static_cast<f32>(command_buffer.estimated_process_time)};
697 699
698 command_generator.GenerateVoiceCommands(); 700 command_generator.GenerateVoiceCommands();
699 command_generator.GenerateSubMixCommands(); 701 command_generator.GenerateSubMixCommands();
@@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
712 render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); 714 render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
713 time_limit_percent = 70.0f; 715 time_limit_percent = 70.0f;
714 } 716 }
717
718 const auto end_estimated_time{drop_voice_param *
719 static_cast<f32>(command_buffer.estimated_process_time)};
720 const auto estimated_time{start_estimated_time - end_estimated_time};
721
715 const auto time_limit{static_cast<u32>( 722 const auto time_limit{static_cast<u32>(
716 static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) + 723 estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) *
717 (((time_limit_percent / 100.0f) * 2'880'000.0) * 724 (static_cast<f32>(render_time_limit_percent) / 100.0f)))};
718 (static_cast<f32>(render_time_limit_percent) / 100.0f)))}; 725 num_voices_dropped =
719 num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit); 726 DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit);
720 } 727 }
721 728
722 command_list_header->buffer_size = command_buffer.size; 729 command_list_header->buffer_size = command_buffer.size;
@@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
737 return command_buffer.size; 744 return command_buffer.size;
738} 745}
739 746
740u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time, 747f32 System::GetVoiceDropParameter() const {
741 const u32 time_limit) { 748 return drop_voice_param;
749}
750
751void System::SetVoiceDropParameter(f32 voice_drop_) {
752 drop_voice_param = voice_drop_;
753}
754
755u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) {
742 u32 i{0}; 756 u32 i{0};
743 auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)}; 757 auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
744 ICommand* cmd{}; 758 ICommand* cmd{nullptr};
745 759
746 for (; i < command_buffer.count; i++) { 760 // Find a first valid voice to drop
761 while (i < command_buffer.count) {
747 cmd = reinterpret_cast<ICommand*>(command_list); 762 cmd = reinterpret_cast<ICommand*>(command_list);
748 if (cmd->type != CommandId::Performance && 763 if (cmd->type == CommandId::Performance ||
749 cmd->type != CommandId::DataSourcePcmInt16Version1 && 764 cmd->type == CommandId::DataSourcePcmInt16Version1 ||
750 cmd->type != CommandId::DataSourcePcmInt16Version2 && 765 cmd->type == CommandId::DataSourcePcmInt16Version2 ||
751 cmd->type != CommandId::DataSourcePcmFloatVersion1 && 766 cmd->type == CommandId::DataSourcePcmFloatVersion1 ||
752 cmd->type != CommandId::DataSourcePcmFloatVersion2 && 767 cmd->type == CommandId::DataSourcePcmFloatVersion2 ||
753 cmd->type != CommandId::DataSourceAdpcmVersion1 && 768 cmd->type == CommandId::DataSourceAdpcmVersion1 ||
754 cmd->type != CommandId::DataSourceAdpcmVersion2) { 769 cmd->type == CommandId::DataSourceAdpcmVersion2) {
755 break; 770 break;
756 } 771 }
757 command_list += cmd->size; 772 command_list += cmd->size;
773 i++;
758 } 774 }
759 775
760 if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) { 776 if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
@@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
767 const auto node_id_type{cmd->node_id >> 28}; 783 const auto node_id_type{cmd->node_id >> 28};
768 const auto node_id_base{cmd->node_id & 0xFFF}; 784 const auto node_id_base{cmd->node_id & 0xFFF};
769 785
786 // If the new estimated process time falls below the limit, we're done dropping.
770 if (estimated_process_time <= time_limit) { 787 if (estimated_process_time <= time_limit) {
771 break; 788 break;
772 } 789 }
@@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
775 break; 792 break;
776 } 793 }
777 794
795 // Don't drop voices marked with the highest priority.
778 auto& voice_info{voice_context.GetInfo(node_id_base)}; 796 auto& voice_info{voice_context.GetInfo(node_id_base)};
779 if (voice_info.priority == HighestVoicePriority) { 797 if (voice_info.priority == HighestVoicePriority) {
780 break; 798 break;
@@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
783 voices_dropped++; 801 voices_dropped++;
784 voice_info.voice_dropped = true; 802 voice_info.voice_dropped = true;
785 803
786 if (i < command_buffer.count) { 804 // First iteration should drop the voice, and then iterate through all of the commands tied
787 while (cmd->node_id == node_id) { 805 // to the voice. We don't need reverb on a voice which we've just removed, for example.
788 if (cmd->type == CommandId::DepopPrepare) { 806 // Depops can't be removed otherwise we'll introduce audio popping, and we don't
789 cmd->enabled = true; 807 // remove perf commands. Lower the estimated time for each command dropped.
790 } else if (cmd->type == CommandId::Performance || !cmd->enabled) { 808 while (i < command_buffer.count && cmd->node_id == node_id) {
791 cmd->enabled = false; 809 if (cmd->type == CommandId::DepopPrepare) {
792 } 810 cmd->enabled = true;
793 i++; 811 } else if (cmd->enabled && cmd->type != CommandId::Performance) {
794 command_list += cmd->size; 812 cmd->enabled = false;
795 cmd = reinterpret_cast<ICommand*>(command_list); 813 estimated_process_time -= static_cast<u32>(
814 drop_voice_param * static_cast<f32>(cmd->estimated_process_time));
796 } 815 }
816 command_list += cmd->size;
817 cmd = reinterpret_cast<ICommand*>(command_list);
818 i++;
797 } 819 }
820 i++;
798 } 821 }
799 return voices_dropped; 822 return voices_dropped;
800} 823}
diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h
index bcbe65b07..429196e41 100644
--- a/src/audio_core/renderer/system.h
+++ b/src/audio_core/renderer/system.h
@@ -196,6 +196,20 @@ public:
196 */ 196 */
197 u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit); 197 u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit);
198 198
199 /**
200 * Get the current voice drop parameter.
201 *
202 * @return The current voice drop.
203 */
204 f32 GetVoiceDropParameter() const;
205
206 /**
207 * Set the voice drop parameter.
208 *
209 * @param The new voice drop.
210 */
211 void SetVoiceDropParameter(f32 voice_drop);
212
199private: 213private:
200 /// Core system 214 /// Core system
201 Core::System& core; 215 Core::System& core;
@@ -301,6 +315,8 @@ private:
301 u32 num_voices_dropped{}; 315 u32 num_voices_dropped{};
302 /// Tick that rendering started 316 /// Tick that rendering started
303 u64 render_start_tick{}; 317 u64 render_start_tick{};
318 /// Parameter to control the threshold for dropping voices if the audio graph gets too large
319 f32 drop_voice_param{1.0f};
304}; 320};
305 321
306} // namespace AudioRenderer 322} // namespace AudioRenderer
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp
index 9c1331e19..f66b2b890 100644
--- a/src/audio_core/renderer/system_manager.cpp
+++ b/src/audio_core/renderer/system_manager.cpp
@@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) {
94} 94}
95 95
96void SystemManager::ThreadFunc() { 96void SystemManager::ThreadFunc() {
97 constexpr char name[]{"yuzu:AudioRenderSystemManager"}; 97 constexpr char name[]{"AudioRenderSystemManager"};
98 MicroProfileOnThreadCreate(name); 98 MicroProfileOnThreadCreate(name);
99 Common::SetCurrentThreadName(name); 99 Common::SetCurrentThreadName(name);
100 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 100 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
index eafb51b01..a501a677d 100644
--- a/src/audio_core/renderer/voice/voice_context.cpp
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -74,8 +74,8 @@ void VoiceContext::SortInfo() {
74 } 74 }
75 75
76 std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) { 76 std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
77 return a->priority != b->priority ? a->priority < b->priority 77 return a->priority != b->priority ? a->priority > b->priority
78 : a->sort_order < b->sort_order; 78 : a->sort_order > b->sort_order;
79 }); 79 });
80} 80}
81 81
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 36b115ad6..32c1b1cb3 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -66,10 +66,10 @@ public:
66 const auto latency_error = cubeb_get_min_latency(ctx, &params, &minimum_latency); 66 const auto latency_error = cubeb_get_min_latency(ctx, &params, &minimum_latency);
67 if (latency_error != CUBEB_OK) { 67 if (latency_error != CUBEB_OK) {
68 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); 68 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
69 minimum_latency = 256U; 69 minimum_latency = TargetSampleCount * 2;
70 } 70 }
71 71
72 minimum_latency = std::max(minimum_latency, 256u); 72 minimum_latency = std::max(minimum_latency, TargetSampleCount * 2);
73 73
74 LOG_INFO(Service_Audio, 74 LOG_INFO(Service_Audio,
75 "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " 75 "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) "
@@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
326 return device_list; 326 return device_list;
327} 327}
328 328
329u32 GetCubebLatency() {
330 cubeb* ctx;
331
332 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
333 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
334 // Return a large latency so we choose SDL instead.
335 return 10000u;
336 }
337
338 cubeb_stream_params params{};
339 params.rate = TargetSampleRate;
340 params.channels = 2;
341 params.format = CUBEB_SAMPLE_S16LE;
342 params.prefs = CUBEB_STREAM_PREF_NONE;
343 params.layout = CUBEB_LAYOUT_STEREO;
344
345 u32 latency{0};
346 const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency);
347 if (latency_error != CUBEB_OK) {
348 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
349 latency = TargetSampleCount * 2;
350 }
351 latency = std::max(latency, TargetSampleCount * 2);
352 cubeb_destroy(ctx);
353 return latency;
354}
355
329} // namespace AudioCore::Sink 356} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h
index 4b0cb160d..3302cb98d 100644
--- a/src/audio_core/sink/cubeb_sink.h
+++ b/src/audio_core/sink/cubeb_sink.h
@@ -96,4 +96,11 @@ private:
96 */ 96 */
97std::vector<std::string> ListCubebSinkDevices(bool capture); 97std::vector<std::string> ListCubebSinkDevices(bool capture);
98 98
99/**
100 * Get the reported latency for this sink.
101 *
102 * @return Minimum latency for this sink.
103 */
104u32 GetCubebLatency();
105
99} // namespace AudioCore::Sink 106} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index 1bd001b94..c138dc628 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -47,11 +47,7 @@ public:
47 spec.freq = TargetSampleRate; 47 spec.freq = TargetSampleRate;
48 spec.channels = static_cast<u8>(device_channels); 48 spec.channels = static_cast<u8>(device_channels);
49 spec.format = AUDIO_S16SYS; 49 spec.format = AUDIO_S16SYS;
50 if (type == StreamType::Render) { 50 spec.samples = TargetSampleCount * 2;
51 spec.samples = TargetSampleCount;
52 } else {
53 spec.samples = 1024;
54 }
55 spec.callback = &SDLSinkStream::DataCallback; 51 spec.callback = &SDLSinkStream::DataCallback;
56 spec.userdata = this; 52 spec.userdata = this;
57 53
@@ -234,10 +230,16 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
234 230
235 const int device_count = SDL_GetNumAudioDevices(capture); 231 const int device_count = SDL_GetNumAudioDevices(capture);
236 for (int i = 0; i < device_count; ++i) { 232 for (int i = 0; i < device_count; ++i) {
237 device_list.emplace_back(SDL_GetAudioDeviceName(i, 0)); 233 if (const char* name = SDL_GetAudioDeviceName(i, capture)) {
234 device_list.emplace_back(name);
235 }
238 } 236 }
239 237
240 return device_list; 238 return device_list;
241} 239}
242 240
241u32 GetSDLLatency() {
242 return TargetSampleCount * 2;
243}
244
243} // namespace AudioCore::Sink 245} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h
index f01eddc1b..27ed1ab94 100644
--- a/src/audio_core/sink/sdl2_sink.h
+++ b/src/audio_core/sink/sdl2_sink.h
@@ -87,4 +87,11 @@ private:
87 */ 87 */
88std::vector<std::string> ListSDLSinkDevices(bool capture); 88std::vector<std::string> ListSDLSinkDevices(bool capture);
89 89
90/**
91 * Get the reported latency for this sink.
92 *
93 * @return Minimum latency for this sink.
94 */
95u32 GetSDLLatency();
96
90} // namespace AudioCore::Sink 97} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 67bdab779..39ea6d91b 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -21,58 +21,80 @@ namespace {
21struct SinkDetails { 21struct SinkDetails {
22 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); 22 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
23 using ListDevicesFn = std::vector<std::string> (*)(bool); 23 using ListDevicesFn = std::vector<std::string> (*)(bool);
24 using LatencyFn = u32 (*)();
24 25
25 /// Name for this sink. 26 /// Name for this sink.
26 const char* id; 27 std::string_view id;
27 /// A method to call to construct an instance of this type of sink. 28 /// A method to call to construct an instance of this type of sink.
28 FactoryFn factory; 29 FactoryFn factory;
29 /// A method to call to list available devices. 30 /// A method to call to list available devices.
30 ListDevicesFn list_devices; 31 ListDevicesFn list_devices;
32 /// Method to get the latency of this backend.
33 LatencyFn latency;
31}; 34};
32 35
33// sink_details is ordered in terms of desirability, with the best choice at the top. 36// sink_details is ordered in terms of desirability, with the best choice at the top.
34constexpr SinkDetails sink_details[] = { 37constexpr SinkDetails sink_details[] = {
35#ifdef HAVE_CUBEB 38#ifdef HAVE_CUBEB
36 SinkDetails{"cubeb", 39 SinkDetails{
37 [](std::string_view device_id) -> std::unique_ptr<Sink> { 40 "cubeb",
38 return std::make_unique<CubebSink>(device_id); 41 [](std::string_view device_id) -> std::unique_ptr<Sink> {
39 }, 42 return std::make_unique<CubebSink>(device_id);
40 &ListCubebSinkDevices}, 43 },
44 &ListCubebSinkDevices,
45 &GetCubebLatency,
46 },
41#endif 47#endif
42#ifdef HAVE_SDL2 48#ifdef HAVE_SDL2
43 SinkDetails{"sdl2", 49 SinkDetails{
44 [](std::string_view device_id) -> std::unique_ptr<Sink> { 50 "sdl2",
45 return std::make_unique<SDLSink>(device_id); 51 [](std::string_view device_id) -> std::unique_ptr<Sink> {
46 }, 52 return std::make_unique<SDLSink>(device_id);
47 &ListSDLSinkDevices}, 53 },
54 &ListSDLSinkDevices,
55 &GetSDLLatency,
56 },
48#endif 57#endif
49 SinkDetails{"null", 58 SinkDetails{"null",
50 [](std::string_view device_id) -> std::unique_ptr<Sink> { 59 [](std::string_view device_id) -> std::unique_ptr<Sink> {
51 return std::make_unique<NullSink>(device_id); 60 return std::make_unique<NullSink>(device_id);
52 }, 61 },
53 [](bool capture) { return std::vector<std::string>{"null"}; }}, 62 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
54}; 63};
55 64
56const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { 65const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) {
57 auto iter = 66 const auto find_backend{[](std::string_view id) {
58 std::find_if(std::begin(sink_details), std::end(sink_details), 67 return std::find_if(std::begin(sink_details), std::end(sink_details),
59 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); 68 [&id](const auto& sink_detail) { return sink_detail.id == id; });
69 }};
60 70
61 if (sink_id == "auto" || iter == std::end(sink_details)) { 71 auto iter = find_backend(sink_id);
62 if (sink_id != "auto") { 72
63 LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); 73 if (sink_id == "auto") {
74 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
75 // causes audio issues, in that case go with SDL.
76#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
77 iter = find_backend("cubeb");
78 if (iter->latency() > TargetSampleCount * 3) {
79 iter = find_backend("sdl2");
64 } 80 }
65 // Auto-select. 81#else
66 // sink_details is ordered in terms of desirability, with the best choice at the front.
67 iter = std::begin(sink_details); 82 iter = std::begin(sink_details);
83#endif
84 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id);
85 }
86
87 if (iter == std::end(sink_details)) {
88 LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
89 iter = find_backend("null");
68 } 90 }
69 91
70 return *iter; 92 return *iter;
71} 93}
72} // Anonymous namespace 94} // Anonymous namespace
73 95
74std::vector<const char*> GetSinkIDs() { 96std::vector<std::string_view> GetSinkIDs() {
75 std::vector<const char*> sink_ids(std::size(sink_details)); 97 std::vector<std::string_view> sink_ids(std::size(sink_details));
76 98
77 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), 99 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
78 [](const auto& sink) { return sink.id; }); 100 [](const auto& sink) { return sink.id; });
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index 3ebdb1e30..e75932898 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -19,7 +19,7 @@ class Sink;
19 * 19 *
20 * @return Vector of available sink names. 20 * @return Vector of available sink names.
21 */ 21 */
22std::vector<const char*> GetSinkIDs(); 22std::vector<std::string_view> GetSinkIDs();
23 23
24/** 24/**
25 * Gets the list of devices for a particular sink identified by the given ID. 25 * Gets the list of devices for a particular sink identified by the given ID.
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 37fe725e4..849f862b0 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -214,8 +214,13 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
214 // video play out without attempting to stall. 214 // video play out without attempting to stall.
215 // Can hopefully remove this later with a more complete NVDEC implementation. 215 // Can hopefully remove this later with a more complete NVDEC implementation.
216 const auto nvdec_active{system.AudioCore().IsNVDECActive()}; 216 const auto nvdec_active{system.AudioCore().IsNVDECActive()};
217 if (!nvdec_active && queued_buffers > max_queue_size) { 217
218 // Core timing cannot be paused in single-core mode, so Stall ends up being called over and over
219 // and never recovers to a normal state, so just skip attempting to sync things on single-core.
220 if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) {
218 Stall(); 221 Stall();
222 } else if (system.IsMulticore() && queued_buffers <= max_queue_size) {
223 Unstall();
219 } 224 }
220 225
221 while (frames_written < num_frames) { 226 while (frames_written < num_frames) {
@@ -255,7 +260,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
255 std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size], 260 std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],
256 frame_size_bytes); 261 frame_size_bytes);
257 262
258 if (stalled && queued_buffers <= max_queue_size) { 263 if (system.IsMulticore() && queued_buffers <= max_queue_size) {
259 Unstall(); 264 Unstall();
260 } 265 }
261} 266}
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 68436a4bc..c0555f840 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -14,34 +14,11 @@ if (DEFINED ENV{DISPLAYVERSION})
14 set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) 14 set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
15endif () 15endif ()
16 16
17# Pass the path to git to the GenerateSCMRev.cmake as well 17include(GenerateSCMRev)
18find_package(Git QUIET)
19
20add_custom_command(OUTPUT scm_rev.cpp
21 COMMAND ${CMAKE_COMMAND}
22 -DSRC_DIR=${PROJECT_SOURCE_DIR}
23 -DBUILD_REPOSITORY=${BUILD_REPOSITORY}
24 -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
25 -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
26 -DBUILD_TAG=${BUILD_TAG}
27 -DBUILD_ID=${DISPLAY_VERSION}
28 -DGIT_REF_SPEC=${GIT_REF_SPEC}
29 -DGIT_REV=${GIT_REV}
30 -DGIT_DESC=${GIT_DESC}
31 -DGIT_BRANCH=${GIT_BRANCH}
32 -DBUILD_FULLNAME=${BUILD_FULLNAME}
33 -DGIT_EXECUTABLE=${GIT_EXECUTABLE}
34 -P ${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
35 DEPENDS
36 # Check that the scm_rev files haven't changed
37 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
38 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
39 # technically we should regenerate if the git version changed, but its not worth the effort imo
40 "${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
41 VERBATIM
42)
43 18
44add_library(common STATIC 19add_library(common STATIC
20 address_space.cpp
21 address_space.h
45 algorithm.h 22 algorithm.h
46 alignment.h 23 alignment.h
47 announce_multiplayer_room.h 24 announce_multiplayer_room.h
@@ -106,6 +83,8 @@ add_library(common STATIC
106 microprofile.cpp 83 microprofile.cpp
107 microprofile.h 84 microprofile.h
108 microprofileui.h 85 microprofileui.h
86 multi_level_page_table.cpp
87 multi_level_page_table.h
109 nvidia_flags.cpp 88 nvidia_flags.cpp
110 nvidia_flags.h 89 nvidia_flags.h
111 page_table.cpp 90 page_table.cpp
@@ -117,7 +96,7 @@ add_library(common STATIC
117 quaternion.h 96 quaternion.h
118 reader_writer_queue.h 97 reader_writer_queue.h
119 ring_buffer.h 98 ring_buffer.h
120 scm_rev.cpp 99 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
121 scm_rev.h 100 scm_rev.h
122 scope_exit.h 101 scope_exit.h
123 settings.cpp 102 settings.cpp
@@ -177,12 +156,13 @@ if (MSVC)
177 ) 156 )
178 target_compile_options(common PRIVATE 157 target_compile_options(common PRIVATE
179 /W4 158 /W4
180 /WX 159
160 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
161 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
162 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
181 ) 163 )
182else() 164else()
183 target_compile_options(common PRIVATE 165 target_compile_options(common PRIVATE
184 -Werror
185
186 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 166 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
187 ) 167 )
188endif() 168endif()
@@ -190,7 +170,11 @@ endif()
190create_target_directory_groups(common) 170create_target_directory_groups(common)
191 171
192target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 172target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
193target_link_libraries(common PRIVATE lz4::lz4) 173if (TARGET lz4::lz4)
174 target_link_libraries(common PRIVATE lz4::lz4)
175else()
176 target_link_libraries(common PRIVATE LZ4::lz4_shared)
177endif()
194if (TARGET zstd::zstd) 178if (TARGET zstd::zstd)
195 target_link_libraries(common PRIVATE zstd::zstd) 179 target_link_libraries(common PRIVATE zstd::zstd)
196else() 180else()
diff --git a/src/common/address_space.cpp b/src/common/address_space.cpp
new file mode 100644
index 000000000..866e78dbe
--- /dev/null
+++ b/src/common/address_space.cpp
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/address_space.inc"
5
6namespace Common {
7
8template class Common::FlatAllocator<u32, 0, 32>;
9
10}
diff --git a/src/common/address_space.h b/src/common/address_space.h
new file mode 100644
index 000000000..9222b2fdc
--- /dev/null
+++ b/src/common/address_space.h
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <concepts>
7#include <functional>
8#include <mutex>
9#include <vector>
10
11#include "common/common_types.h"
12
13namespace Common {
14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits;
16
17struct EmptyStruct {};
18
19/**
20 * @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector
21 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
23 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
24requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap {
26public:
27 /// The maximum VA that this AS can technically reach
28 static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) +
29 ((1ULL << (AddressSpaceBits - 1)) - 1)};
30
31 explicit FlatAddressSpaceMap(VaType va_limit,
32 std::function<void(VaType, VaType)> unmap_callback = {});
33
34 FlatAddressSpaceMap() = default;
35
36 void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info = {}) {
37 std::scoped_lock lock(block_mutex);
38 MapLocked(virt, phys, size, extra_info);
39 }
40
41 void Unmap(VaType virt, VaType size) {
42 std::scoped_lock lock(block_mutex);
43 UnmapLocked(virt, size);
44 }
45
46 VaType GetVALimit() const {
47 return va_limit;
48 }
49
50protected:
51 /**
52 * @brief Represents a block of memory in the AS, the physical mapping is contiguous until
53 * another block with a different phys address is hit
54 */
55 struct Block {
56 /// VA of the block
57 VaType virt{UnmappedVa};
58 /// PA of the block, will increase 1-1 with VA until a new block is encountered
59 PaType phys{UnmappedPa};
60 [[no_unique_address]] ExtraBlockInfo extra_info;
61
62 Block() = default;
63
64 Block(VaType virt_, PaType phys_, ExtraBlockInfo extra_info_)
65 : virt(virt_), phys(phys_), extra_info(extra_info_) {}
66
67 bool Valid() const {
68 return virt != UnmappedVa;
69 }
70
71 bool Mapped() const {
72 return phys != UnmappedPa;
73 }
74
75 bool Unmapped() const {
76 return phys == UnmappedPa;
77 }
78
79 bool operator<(const VaType& p_virt) const {
80 return virt < p_virt;
81 }
82 };
83
84 /**
85 * @brief Maps a PA range into the given AS region
86 * @note block_mutex MUST be locked when calling this
87 */
88 void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info);
89
90 /**
91 * @brief Unmaps the given range and merges it with other unmapped regions
92 * @note block_mutex MUST be locked when calling this
93 */
94 void UnmapLocked(VaType virt, VaType size);
95
96 std::mutex block_mutex;
97 std::vector<Block> blocks{Block{}};
98
99 /// a soft limit on the maximum VA of the AS
100 VaType va_limit{VaMaximum};
101
102private:
103 /// Callback called when the mappings in an region have changed
104 std::function<void(VaType, VaType)> unmap_callback{};
105};
106
107/**
108 * @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an
109 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
110 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private:
116 using Base = FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits>;
117
118public:
119 explicit FlatAllocator(VaType virt_start, VaType va_limit = Base::VaMaximum);
120
121 /**
122 * @brief Allocates a region in the AS of the given size and returns its address
123 */
124 VaType Allocate(VaType size);
125
126 /**
127 * @brief Marks the given region in the AS as allocated
128 */
129 void AllocateFixed(VaType virt, VaType size);
130
131 /**
132 * @brief Frees an AS region so it can be used again
133 */
134 void Free(VaType virt, VaType size);
135
136 VaType GetVAStart() const {
137 return virt_start;
138 }
139
140private:
141 /// The base VA of the allocator, no allocations will be below this
142 VaType virt_start;
143
144 /**
145 * The end address for the initial linear allocation pass
146 * Once this reaches the AS limit the slower allocation path will be used
147 */
148 VaType current_linear_alloc_end;
149};
150} // namespace Common
diff --git a/src/common/address_space.inc b/src/common/address_space.inc
new file mode 100644
index 000000000..2195dabd5
--- /dev/null
+++ b/src/common/address_space.inc
@@ -0,0 +1,366 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/address_space.h"
5#include "common/assert.h"
6
7#define MAP_MEMBER(returnType) \
8 template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
9 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
10 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAddressSpaceMap< \
11 VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
12#define MAP_MEMBER_CONST() \
13 template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
14 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
15 requires AddressSpaceValid<VaType, AddressSpaceBits> FlatAddressSpaceMap< \
16 VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
17
18#define MM_MEMBER(returnType) \
19 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
20 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
21 FlatMemoryManager<VaType, UnmappedVa, AddressSpaceBits>
22
23#define ALLOC_MEMBER(returnType) \
24 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
25 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
26 FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
27#define ALLOC_MEMBER_CONST() \
28 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
29 requires AddressSpaceValid<VaType, AddressSpaceBits> \
30 FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
31
32namespace Common {
33MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_,
34 std::function<void(VaType, VaType)> unmap_callback_)
35 : va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} {
36 if (va_limit > VaMaximum) {
37 ASSERT_MSG(false, "Invalid VA limit!");
38 }
39}
40
41MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info) {
42 VaType virt_end{virt + size};
43
44 if (virt_end > va_limit) {
45 ASSERT_MSG(false,
46 "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
47 virt_end, va_limit);
48 }
49
50 auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
51 if (block_end_successor == blocks.begin()) {
52 ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end);
53 }
54
55 auto block_end_predecessor{std::prev(block_end_successor)};
56
57 if (block_end_successor != blocks.end()) {
58 // We have blocks in front of us, if one is directly in front then we don't have to add a
59 // tail
60 if (block_end_successor->virt != virt_end) {
61 PaType tailPhys{[&]() -> PaType {
62 if constexpr (!PaContigSplit) {
63 // Always propagate unmapped regions rather than calculating offset
64 return block_end_predecessor->phys;
65 } else {
66 if (block_end_predecessor->Unmapped()) {
67 // Always propagate unmapped regions rather than calculating offset
68 return block_end_predecessor->phys;
69 } else {
70 return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
71 }
72 }
73 }()};
74
75 if (block_end_predecessor->virt >= virt) {
76 // If this block's start would be overlapped by the map then reuse it as a tail
77 // block
78 block_end_predecessor->virt = virt_end;
79 block_end_predecessor->phys = tailPhys;
80 block_end_predecessor->extra_info = block_end_predecessor->extra_info;
81
82 // No longer predecessor anymore
83 block_end_successor = block_end_predecessor--;
84 } else {
85 // Else insert a new one and we're done
86 blocks.insert(block_end_successor,
87 {Block(virt, phys, extra_info),
88 Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
89 if (unmap_callback) {
90 unmap_callback(virt, size);
91 }
92
93 return;
94 }
95 }
96 } else {
97 // block_end_predecessor will always be unmapped as blocks has to be terminated by an
98 // unmapped chunk
99 if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
100 // Move the unmapped block start backwards
101 block_end_predecessor->virt = virt_end;
102
103 // No longer predecessor anymore
104 block_end_successor = block_end_predecessor--;
105 } else {
106 // Else insert a new one and we're done
107 blocks.insert(block_end_successor,
108 {Block(virt, phys, extra_info), Block(virt_end, UnmappedPa, {})});
109 if (unmap_callback) {
110 unmap_callback(virt, size);
111 }
112
113 return;
114 }
115 }
116
117 auto block_start_successor{block_end_successor};
118
119 // Walk the block vector to find the start successor as this is more efficient than another
120 // binary search in most scenarios
121 while (std::prev(block_start_successor)->virt >= virt) {
122 block_start_successor--;
123 }
124
125 // Check that the start successor is either the end block or something in between
126 if (block_start_successor->virt > virt_end) {
127 ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
128 } else if (block_start_successor->virt == virt_end) {
129 // We need to create a new block as there are none spare that we would overwrite
130 blocks.insert(block_start_successor, Block(virt, phys, extra_info));
131 } else {
132 // Erase overwritten blocks
133 if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
134 blocks.erase(eraseStart, block_end_successor);
135 }
136
137 // Reuse a block that would otherwise be overwritten as a start block
138 block_start_successor->virt = virt;
139 block_start_successor->phys = phys;
140 block_start_successor->extra_info = extra_info;
141 }
142
143 if (unmap_callback) {
144 unmap_callback(virt, size);
145 }
146}
147
148MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
149 VaType virt_end{virt + size};
150
151 if (virt_end > va_limit) {
152 ASSERT_MSG(false,
153 "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
154 virt_end, va_limit);
155 }
156
157 auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
158 if (block_end_successor == blocks.begin()) {
159 ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}",
160 virt_end);
161 }
162
163 auto block_end_predecessor{std::prev(block_end_successor)};
164
165 auto walk_back_to_predecessor{[&](auto iter) {
166 while (iter->virt >= virt) {
167 iter--;
168 }
169
170 return iter;
171 }};
172
173 auto erase_blocks_with_end_unmapped{[&](auto unmappedEnd) {
174 auto block_start_predecessor{walk_back_to_predecessor(unmappedEnd)};
175 auto block_start_successor{std::next(block_start_predecessor)};
176
177 auto eraseEnd{[&]() {
178 if (block_start_predecessor->Unmapped()) {
179 // If the start predecessor is unmapped then we can erase everything in our region
180 // and be done
181 return std::next(unmappedEnd);
182 } else {
183 // Else reuse the end predecessor as the start of our unmapped region then erase all
184 // up to it
185 unmappedEnd->virt = virt;
186 return unmappedEnd;
187 }
188 }()};
189
190 // We can't have two unmapped regions after each other
191 if (eraseEnd != blocks.end() &&
192 (eraseEnd == block_start_successor ||
193 (block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) {
194 ASSERT_MSG(false, "Multiple contiguous unmapped regions are unsupported!");
195 }
196
197 blocks.erase(block_start_successor, eraseEnd);
198 }};
199
200 // We can avoid any splitting logic if these are the case
201 if (block_end_predecessor->Unmapped()) {
202 if (block_end_predecessor->virt > virt) {
203 erase_blocks_with_end_unmapped(block_end_predecessor);
204 }
205
206 if (unmap_callback) {
207 unmap_callback(virt, size);
208 }
209
210 return; // The region is unmapped, bail out early
211 } else if (block_end_successor->virt == virt_end && block_end_successor->Unmapped()) {
212 erase_blocks_with_end_unmapped(block_end_successor);
213
214 if (unmap_callback) {
215 unmap_callback(virt, size);
216 }
217
218 return; // The region is unmapped here and doesn't need splitting, bail out early
219 } else if (block_end_successor == blocks.end()) {
220 // This should never happen as the end should always follow an unmapped block
221 ASSERT_MSG(false, "Unexpected Memory Manager state!");
222 } else if (block_end_successor->virt != virt_end) {
223 // If one block is directly in front then we don't have to add a tail
224
225 // The previous block is mapped so we will need to add a tail with an offset
226 PaType tailPhys{[&]() {
227 if constexpr (PaContigSplit) {
228 return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
229 } else {
230 return block_end_predecessor->phys;
231 }
232 }()};
233
234 if (block_end_predecessor->virt >= virt) {
235 // If this block's start would be overlapped by the unmap then reuse it as a tail block
236 block_end_predecessor->virt = virt_end;
237 block_end_predecessor->phys = tailPhys;
238
239 // No longer predecessor anymore
240 block_end_successor = block_end_predecessor--;
241 } else {
242 blocks.insert(block_end_successor,
243 {Block(virt, UnmappedPa, {}),
244 Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
245 if (unmap_callback) {
246 unmap_callback(virt, size);
247 }
248
249 // The previous block is mapped and ends before
250 return;
251 }
252 }
253
254 // Walk the block vector to find the start predecessor as this is more efficient than another
255 // binary search in most scenarios
256 auto block_start_predecessor{walk_back_to_predecessor(block_end_successor)};
257 auto block_start_successor{std::next(block_start_predecessor)};
258
259 if (block_start_successor->virt > virt_end) {
260 ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
261 } else if (block_start_successor->virt == virt_end) {
262 // There are no blocks between the start and the end that would let us skip inserting a new
263 // one for head
264
265 // The previous block is may be unmapped, if so we don't need to insert any unmaps after it
266 if (block_start_predecessor->Mapped()) {
267 blocks.insert(block_start_successor, Block(virt, UnmappedPa, {}));
268 }
269 } else if (block_start_predecessor->Unmapped()) {
270 // If the previous block is unmapped
271 blocks.erase(block_start_successor, block_end_predecessor);
272 } else {
273 // Erase overwritten blocks, skipping the first one as we have written the unmapped start
274 // block there
275 if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
276 blocks.erase(eraseStart, block_end_successor);
277 }
278
279 // Add in the unmapped block header
280 block_start_successor->virt = virt;
281 block_start_successor->phys = UnmappedPa;
282 }
283
284 if (unmap_callback)
285 unmap_callback(virt, size);
286}
287
288ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_)
289 : Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {}
290
291ALLOC_MEMBER(VaType)::Allocate(VaType size) {
292 std::scoped_lock lock(this->block_mutex);
293
294 VaType alloc_start{UnmappedVa};
295 VaType alloc_end{current_linear_alloc_end + size};
296
297 // Avoid searching backwards in the address space if possible
298 if (alloc_end >= current_linear_alloc_end && alloc_end <= this->va_limit) {
299 auto alloc_end_successor{
300 std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)};
301 if (alloc_end_successor == this->blocks.begin()) {
302 ASSERT_MSG(false, "First block in AS map is invalid!");
303 }
304
305 auto alloc_end_predecessor{std::prev(alloc_end_successor)};
306 if (alloc_end_predecessor->virt <= current_linear_alloc_end) {
307 alloc_start = current_linear_alloc_end;
308 } else {
309 // Skip over fixed any mappings in front of us
310 while (alloc_end_successor != this->blocks.end()) {
311 if (alloc_end_successor->virt - alloc_end_predecessor->virt < size ||
312 alloc_end_predecessor->Mapped()) {
313 alloc_start = alloc_end_predecessor->virt;
314 break;
315 }
316
317 alloc_end_predecessor = alloc_end_successor++;
318
319 // Use the VA limit to calculate if we can fit in the final block since it has no
320 // successor
321 if (alloc_end_successor == this->blocks.end()) {
322 alloc_end = alloc_end_predecessor->virt + size;
323
324 if (alloc_end >= alloc_end_predecessor->virt && alloc_end <= this->va_limit) {
325 alloc_start = alloc_end_predecessor->virt;
326 }
327 }
328 }
329 }
330 }
331
332 if (alloc_start != UnmappedVa) {
333 current_linear_alloc_end = alloc_start + size;
334 } else { // If linear allocation overflows the AS then find a gap
335 if (this->blocks.size() <= 2) {
336 ASSERT_MSG(false, "Unexpected allocator state!");
337 }
338
339 auto search_predecessor{this->blocks.begin()};
340 auto search_successor{std::next(search_predecessor)};
341
342 while (search_successor != this->blocks.end() &&
343 (search_successor->virt - search_predecessor->virt < size ||
344 search_predecessor->Mapped())) {
345 search_predecessor = search_successor++;
346 }
347
348 if (search_successor != this->blocks.end()) {
349 alloc_start = search_predecessor->virt;
350 } else {
351 return {}; // AS is full
352 }
353 }
354
355 this->MapLocked(alloc_start, true, size, {});
356 return alloc_start;
357}
358
359ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
360 this->Map(virt, true, size);
361}
362
363ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
364 this->Unmap(virt, size);
365}
366} // namespace Common
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index 9ddfd637b..c27c9241d 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -24,4 +24,12 @@ template <class ForwardIt, class T, class Compare = std::less<>>
24 return first != last && !comp(value, *first) ? first : last; 24 return first != last && !comp(value, *first) ? first : last;
25} 25}
26 26
27template <typename T, typename Func, typename... Args>
28T FoldRight(T initial_value, Func&& func, Args&&... args) {
29 T value{initial_value};
30 const auto high_func = [&value, &func]<typename U>(U x) { value = func(value, x); };
31 (std::invoke(high_func, std::forward<Args>(args)), ...);
32 return value;
33}
34
27} // namespace Common 35} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7e1df62b1..e4e58ea45 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -141,10 +141,6 @@ public:
141 constexpr BitField(BitField&&) noexcept = default; 141 constexpr BitField(BitField&&) noexcept = default;
142 constexpr BitField& operator=(BitField&&) noexcept = default; 142 constexpr BitField& operator=(BitField&&) noexcept = default;
143 143
144 [[nodiscard]] constexpr operator T() const {
145 return Value();
146 }
147
148 constexpr void Assign(const T& value) { 144 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER 145#ifdef _MSC_VER
150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 146 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
@@ -162,6 +158,17 @@ public:
162 return ExtractValue(storage); 158 return ExtractValue(storage);
163 } 159 }
164 160
161 template <typename ConvertedToType>
162 [[nodiscard]] constexpr ConvertedToType As() const {
163 static_assert(!std::is_same_v<T, ConvertedToType>,
164 "Unnecessary cast. Use Value() instead.");
165 return static_cast<ConvertedToType>(Value());
166 }
167
168 [[nodiscard]] constexpr operator T() const {
169 return Value();
170 }
171
165 [[nodiscard]] constexpr explicit operator bool() const { 172 [[nodiscard]] constexpr explicit operator bool() const {
166 return Value() != 0; 173 return Value() != 0;
167 } 174 }
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
index 7e465549b..21217801e 100644
--- a/src/common/bounded_threadsafe_queue.h
+++ b/src/common/bounded_threadsafe_queue.h
@@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer
21constexpr size_t hardware_interference_size = 64; 21constexpr size_t hardware_interference_size = 64;
22#endif 22#endif
23 23
24#ifdef _MSC_VER
25#pragma warning(push)
26#pragma warning(disable : 4324)
27#endif
28
29template <typename T, size_t capacity = 0x400> 24template <typename T, size_t capacity = 0x400>
30class MPSCQueue { 25class MPSCQueue {
31public: 26public:
@@ -160,8 +155,4 @@ private:
160 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); 155 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
161}; 156};
162 157
163#ifdef _MSC_VER
164#pragma warning(pop)
165#endif
166
167} // namespace Common 158} // namespace Common
diff --git a/src/common/concepts.h b/src/common/concepts.h
index a97555f6a..a9acff3e7 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -3,24 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <iterator>
6#include <type_traits> 7#include <type_traits>
7 8
8namespace Common { 9namespace Common {
9 10
10// Check if type is like an STL container 11// Check if type satisfies the ContiguousContainer named requirement.
11template <typename T> 12template <typename T>
12concept IsSTLContainer = requires(T t) { 13concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
13 typename T::value_type;
14 typename T::iterator;
15 typename T::const_iterator;
16 // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
17 t.begin();
18 t.end();
19 t.cbegin();
20 t.cend();
21 t.data();
22 t.size();
23};
24 14
25// TODO: Replace with std::derived_from when the <concepts> header 15// TODO: Replace with std::derived_from when the <concepts> header
26// is available on all supported platforms. 16// is available on all supported platforms.
@@ -34,4 +24,12 @@ concept DerivedFrom = requires {
34template <typename From, typename To> 24template <typename From, typename To>
35concept ConvertibleTo = std::is_convertible_v<From, To>; 25concept ConvertibleTo = std::is_convertible_v<From, To>;
36 26
27// No equivalents in the stdlib
28
29template <typename T>
30concept IsArithmetic = std::is_arithmetic_v<T>;
31
32template <typename T>
33concept IsIntegral = std::is_integral_v<T>;
34
37} // namespace Common 35} // namespace Common
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h
index 4a0f72cc9..f899b0d54 100644
--- a/src/common/fixed_point.h
+++ b/src/common/fixed_point.h
@@ -4,14 +4,7 @@
4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h 4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math 5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
6 6
7#ifndef FIXED_H_ 7#pragma once
8#define FIXED_H_
9
10#if __cplusplus >= 201402L
11#define CONSTEXPR14 constexpr
12#else
13#define CONSTEXPR14
14#endif
15 8
16#include <cstddef> // for size_t 9#include <cstddef> // for size_t
17#include <cstdint> 10#include <cstdint>
@@ -19,6 +12,8 @@
19#include <ostream> 12#include <ostream>
20#include <type_traits> 13#include <type_traits>
21 14
15#include <common/concepts.h>
16
22namespace Common { 17namespace Common {
23 18
24template <size_t I, size_t F> 19template <size_t I, size_t F>
@@ -57,8 +52,8 @@ struct type_from_size<64> {
57 static constexpr size_t size = 64; 52 static constexpr size_t size = 64;
58 53
59 using value_type = int64_t; 54 using value_type = int64_t;
60 using unsigned_type = std::make_unsigned<value_type>::type; 55 using unsigned_type = std::make_unsigned_t<value_type>;
61 using signed_type = std::make_signed<value_type>::type; 56 using signed_type = std::make_signed_t<value_type>;
62 using next_size = type_from_size<128>; 57 using next_size = type_from_size<128>;
63}; 58};
64 59
@@ -68,8 +63,8 @@ struct type_from_size<32> {
68 static constexpr size_t size = 32; 63 static constexpr size_t size = 32;
69 64
70 using value_type = int32_t; 65 using value_type = int32_t;
71 using unsigned_type = std::make_unsigned<value_type>::type; 66 using unsigned_type = std::make_unsigned_t<value_type>;
72 using signed_type = std::make_signed<value_type>::type; 67 using signed_type = std::make_signed_t<value_type>;
73 using next_size = type_from_size<64>; 68 using next_size = type_from_size<64>;
74}; 69};
75 70
@@ -79,8 +74,8 @@ struct type_from_size<16> {
79 static constexpr size_t size = 16; 74 static constexpr size_t size = 16;
80 75
81 using value_type = int16_t; 76 using value_type = int16_t;
82 using unsigned_type = std::make_unsigned<value_type>::type; 77 using unsigned_type = std::make_unsigned_t<value_type>;
83 using signed_type = std::make_signed<value_type>::type; 78 using signed_type = std::make_signed_t<value_type>;
84 using next_size = type_from_size<32>; 79 using next_size = type_from_size<32>;
85}; 80};
86 81
@@ -90,8 +85,8 @@ struct type_from_size<8> {
90 static constexpr size_t size = 8; 85 static constexpr size_t size = 8;
91 86
92 using value_type = int8_t; 87 using value_type = int8_t;
93 using unsigned_type = std::make_unsigned<value_type>::type; 88 using unsigned_type = std::make_unsigned_t<value_type>;
94 using signed_type = std::make_signed<value_type>::type; 89 using signed_type = std::make_signed_t<value_type>;
95 using next_size = type_from_size<16>; 90 using next_size = type_from_size<16>;
96}; 91};
97 92
@@ -106,9 +101,9 @@ constexpr B next_to_base(N rhs) {
106struct divide_by_zero : std::exception {}; 101struct divide_by_zero : std::exception {};
107 102
108template <size_t I, size_t F> 103template <size_t I, size_t F>
109CONSTEXPR14 FixedPoint<I, F> divide( 104constexpr FixedPoint<I, F> divide(
110 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, 105 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
111 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 106 std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
112 107
113 using next_type = typename FixedPoint<I, F>::next_type; 108 using next_type = typename FixedPoint<I, F>::next_type;
114 using base_type = typename FixedPoint<I, F>::base_type; 109 using base_type = typename FixedPoint<I, F>::base_type;
@@ -126,9 +121,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
126} 121}
127 122
128template <size_t I, size_t F> 123template <size_t I, size_t F>
129CONSTEXPR14 FixedPoint<I, F> divide( 124constexpr FixedPoint<I, F> divide(
130 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, 125 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
131 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 126 std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
132 127
133 using unsigned_type = typename FixedPoint<I, F>::unsigned_type; 128 using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
134 129
@@ -196,9 +191,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
196 191
197// this is the usual implementation of multiplication 192// this is the usual implementation of multiplication
198template <size_t I, size_t F> 193template <size_t I, size_t F>
199CONSTEXPR14 FixedPoint<I, F> multiply( 194constexpr FixedPoint<I, F> multiply(
200 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, 195 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
201 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 196 std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
202 197
203 using next_type = typename FixedPoint<I, F>::next_type; 198 using next_type = typename FixedPoint<I, F>::next_type;
204 using base_type = typename FixedPoint<I, F>::base_type; 199 using base_type = typename FixedPoint<I, F>::base_type;
@@ -215,9 +210,9 @@ CONSTEXPR14 FixedPoint<I, F> multiply(
215// it is slightly slower, but is more robust since it doesn't 210// it is slightly slower, but is more robust since it doesn't
216// require and upgraded type 211// require and upgraded type
217template <size_t I, size_t F> 212template <size_t I, size_t F>
218CONSTEXPR14 FixedPoint<I, F> multiply( 213constexpr FixedPoint<I, F> multiply(
219 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, 214 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
220 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 215 std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
221 216
222 using base_type = typename FixedPoint<I, F>::base_type; 217 using base_type = typename FixedPoint<I, F>::base_type;
223 218
@@ -272,19 +267,20 @@ public:
272 static constexpr base_type one = base_type(1) << fractional_bits; 267 static constexpr base_type one = base_type(1) << fractional_bits;
273 268
274public: // constructors 269public: // constructors
275 FixedPoint() = default; 270 constexpr FixedPoint() = default;
276 FixedPoint(const FixedPoint&) = default; 271
277 FixedPoint(FixedPoint&&) = default; 272 constexpr FixedPoint(const FixedPoint&) = default;
278 FixedPoint& operator=(const FixedPoint&) = default; 273 constexpr FixedPoint& operator=(const FixedPoint&) = default;
274
275 constexpr FixedPoint(FixedPoint&&) noexcept = default;
276 constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
279 277
280 template <class Number> 278 template <IsArithmetic Number>
281 constexpr FixedPoint( 279 constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
282 Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
283 : data_(static_cast<base_type>(n * one)) {}
284 280
285public: // conversion 281public: // conversion
286 template <size_t I2, size_t F2> 282 template <size_t I2, size_t F2>
287 CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) { 283 constexpr explicit FixedPoint(FixedPoint<I2, F2> other) {
288 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); 284 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
289 using T = FixedPoint<I2, F2>; 285 using T = FixedPoint<I2, F2>;
290 286
@@ -308,36 +304,14 @@ public:
308 } 304 }
309 305
310public: // comparison operators 306public: // comparison operators
311 constexpr bool operator==(FixedPoint rhs) const { 307 friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
312 return data_ == rhs.data_;
313 }
314
315 constexpr bool operator!=(FixedPoint rhs) const {
316 return data_ != rhs.data_;
317 }
318
319 constexpr bool operator<(FixedPoint rhs) const {
320 return data_ < rhs.data_;
321 }
322
323 constexpr bool operator>(FixedPoint rhs) const {
324 return data_ > rhs.data_;
325 }
326
327 constexpr bool operator<=(FixedPoint rhs) const {
328 return data_ <= rhs.data_;
329 }
330
331 constexpr bool operator>=(FixedPoint rhs) const {
332 return data_ >= rhs.data_;
333 }
334 308
335public: // unary operators 309public: // unary operators
336 constexpr bool operator!() const { 310 [[nodiscard]] constexpr bool operator!() const {
337 return !data_; 311 return !data_;
338 } 312 }
339 313
340 constexpr FixedPoint operator~() const { 314 [[nodiscard]] constexpr FixedPoint operator~() const {
341 // NOTE(eteran): this will often appear to "just negate" the value 315 // NOTE(eteran): this will often appear to "just negate" the value
342 // that is not an error, it is because -x == (~x+1) 316 // that is not an error, it is because -x == (~x+1)
343 // and that "+1" is adding an infinitesimally small fraction to the 317 // and that "+1" is adding an infinitesimally small fraction to the
@@ -345,89 +319,87 @@ public: // unary operators
345 return FixedPoint::from_base(~data_); 319 return FixedPoint::from_base(~data_);
346 } 320 }
347 321
348 constexpr FixedPoint operator-() const { 322 [[nodiscard]] constexpr FixedPoint operator-() const {
349 return FixedPoint::from_base(-data_); 323 return FixedPoint::from_base(-data_);
350 } 324 }
351 325
352 constexpr FixedPoint operator+() const { 326 [[nodiscard]] constexpr FixedPoint operator+() const {
353 return FixedPoint::from_base(+data_); 327 return FixedPoint::from_base(+data_);
354 } 328 }
355 329
356 CONSTEXPR14 FixedPoint& operator++() { 330 constexpr FixedPoint& operator++() {
357 data_ += one; 331 data_ += one;
358 return *this; 332 return *this;
359 } 333 }
360 334
361 CONSTEXPR14 FixedPoint& operator--() { 335 constexpr FixedPoint& operator--() {
362 data_ -= one; 336 data_ -= one;
363 return *this; 337 return *this;
364 } 338 }
365 339
366 CONSTEXPR14 FixedPoint operator++(int) { 340 constexpr FixedPoint operator++(int) {
367 FixedPoint tmp(*this); 341 FixedPoint tmp(*this);
368 data_ += one; 342 data_ += one;
369 return tmp; 343 return tmp;
370 } 344 }
371 345
372 CONSTEXPR14 FixedPoint operator--(int) { 346 constexpr FixedPoint operator--(int) {
373 FixedPoint tmp(*this); 347 FixedPoint tmp(*this);
374 data_ -= one; 348 data_ -= one;
375 return tmp; 349 return tmp;
376 } 350 }
377 351
378public: // basic math operators 352public: // basic math operators
379 CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) { 353 constexpr FixedPoint& operator+=(FixedPoint n) {
380 data_ += n.data_; 354 data_ += n.data_;
381 return *this; 355 return *this;
382 } 356 }
383 357
384 CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) { 358 constexpr FixedPoint& operator-=(FixedPoint n) {
385 data_ -= n.data_; 359 data_ -= n.data_;
386 return *this; 360 return *this;
387 } 361 }
388 362
389 CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) { 363 constexpr FixedPoint& operator*=(FixedPoint n) {
390 return assign(detail::multiply(*this, n)); 364 return assign(detail::multiply(*this, n));
391 } 365 }
392 366
393 CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) { 367 constexpr FixedPoint& operator/=(FixedPoint n) {
394 FixedPoint temp; 368 FixedPoint temp;
395 return assign(detail::divide(*this, n, temp)); 369 return assign(detail::divide(*this, n, temp));
396 } 370 }
397 371
398private: 372private:
399 CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) { 373 constexpr FixedPoint& assign(FixedPoint rhs) {
400 data_ = rhs.data_; 374 data_ = rhs.data_;
401 return *this; 375 return *this;
402 } 376 }
403 377
404public: // binary math operators, effects underlying bit pattern since these 378public: // binary math operators, effects underlying bit pattern since these
405 // don't really typically make sense for non-integer values 379 // don't really typically make sense for non-integer values
406 CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) { 380 constexpr FixedPoint& operator&=(FixedPoint n) {
407 data_ &= n.data_; 381 data_ &= n.data_;
408 return *this; 382 return *this;
409 } 383 }
410 384
411 CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) { 385 constexpr FixedPoint& operator|=(FixedPoint n) {
412 data_ |= n.data_; 386 data_ |= n.data_;
413 return *this; 387 return *this;
414 } 388 }
415 389
416 CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) { 390 constexpr FixedPoint& operator^=(FixedPoint n) {
417 data_ ^= n.data_; 391 data_ ^= n.data_;
418 return *this; 392 return *this;
419 } 393 }
420 394
421 template <class Integer, 395 template <IsIntegral Integer>
422 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 396 constexpr FixedPoint& operator>>=(Integer n) {
423 CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
424 data_ >>= n; 397 data_ >>= n;
425 return *this; 398 return *this;
426 } 399 }
427 400
428 template <class Integer, 401 template <IsIntegral Integer>
429 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 402 constexpr FixedPoint& operator<<=(Integer n) {
430 CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
431 data_ <<= n; 403 data_ <<= n;
432 return *this; 404 return *this;
433 } 405 }
@@ -437,42 +409,42 @@ public: // conversion to basic types
437 data_ += (data_ & fractional_mask) >> 1; 409 data_ += (data_ & fractional_mask) >> 1;
438 } 410 }
439 411
440 constexpr int to_int() { 412 [[nodiscard]] constexpr int to_int() {
441 round_up(); 413 round_up();
442 return static_cast<int>((data_ & integer_mask) >> fractional_bits); 414 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
443 } 415 }
444 416
445 constexpr unsigned int to_uint() const { 417 [[nodiscard]] constexpr unsigned int to_uint() {
446 round_up(); 418 round_up();
447 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); 419 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
448 } 420 }
449 421
450 constexpr int64_t to_long() { 422 [[nodiscard]] constexpr int64_t to_long() {
451 round_up(); 423 round_up();
452 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); 424 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
453 } 425 }
454 426
455 constexpr int to_int_floor() const { 427 [[nodiscard]] constexpr int to_int_floor() const {
456 return static_cast<int>((data_ & integer_mask) >> fractional_bits); 428 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
457 } 429 }
458 430
459 constexpr int64_t to_long_floor() { 431 [[nodiscard]] constexpr int64_t to_long_floor() const {
460 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); 432 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
461 } 433 }
462 434
463 constexpr unsigned int to_uint_floor() const { 435 [[nodiscard]] constexpr unsigned int to_uint_floor() const {
464 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); 436 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
465 } 437 }
466 438
467 constexpr float to_float() const { 439 [[nodiscard]] constexpr float to_float() const {
468 return static_cast<float>(data_) / FixedPoint::one; 440 return static_cast<float>(data_) / FixedPoint::one;
469 } 441 }
470 442
471 constexpr double to_double() const { 443 [[nodiscard]] constexpr double to_double() const {
472 return static_cast<double>(data_) / FixedPoint::one; 444 return static_cast<double>(data_) / FixedPoint::one;
473 } 445 }
474 446
475 constexpr base_type to_raw() const { 447 [[nodiscard]] constexpr base_type to_raw() const {
476 return data_; 448 return data_;
477 } 449 }
478 450
@@ -480,27 +452,27 @@ public: // conversion to basic types
480 data_ &= fractional_mask; 452 data_ &= fractional_mask;
481 } 453 }
482 454
483 constexpr base_type get_frac() const { 455 [[nodiscard]] constexpr base_type get_frac() const {
484 return data_ & fractional_mask; 456 return data_ & fractional_mask;
485 } 457 }
486 458
487public: 459public:
488 CONSTEXPR14 void swap(FixedPoint& rhs) { 460 constexpr void swap(FixedPoint& rhs) noexcept {
489 using std::swap; 461 using std::swap;
490 swap(data_, rhs.data_); 462 swap(data_, rhs.data_);
491 } 463 }
492 464
493public: 465public:
494 base_type data_; 466 base_type data_{};
495}; 467};
496 468
497// if we have the same fractional portion, but differing integer portions, we trivially upgrade the 469// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
498// smaller type 470// smaller type
499template <size_t I1, size_t I2, size_t F> 471template <size_t I1, size_t I2, size_t F>
500CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 472constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
501operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 473 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
502 474
503 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 475 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
504 476
505 const T l = T::from_base(lhs.to_raw()); 477 const T l = T::from_base(lhs.to_raw());
506 const T r = T::from_base(rhs.to_raw()); 478 const T r = T::from_base(rhs.to_raw());
@@ -508,10 +480,10 @@ operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
508} 480}
509 481
510template <size_t I1, size_t I2, size_t F> 482template <size_t I1, size_t I2, size_t F>
511CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 483constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
512operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 484 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
513 485
514 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 486 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
515 487
516 const T l = T::from_base(lhs.to_raw()); 488 const T l = T::from_base(lhs.to_raw());
517 const T r = T::from_base(rhs.to_raw()); 489 const T r = T::from_base(rhs.to_raw());
@@ -519,10 +491,10 @@ operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
519} 491}
520 492
521template <size_t I1, size_t I2, size_t F> 493template <size_t I1, size_t I2, size_t F>
522CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 494constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
523operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 495 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
524 496
525 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 497 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
526 498
527 const T l = T::from_base(lhs.to_raw()); 499 const T l = T::from_base(lhs.to_raw());
528 const T r = T::from_base(rhs.to_raw()); 500 const T r = T::from_base(rhs.to_raw());
@@ -530,10 +502,10 @@ operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
530} 502}
531 503
532template <size_t I1, size_t I2, size_t F> 504template <size_t I1, size_t I2, size_t F>
533CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 505constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
534operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 506 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
535 507
536 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 508 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
537 509
538 const T l = T::from_base(lhs.to_raw()); 510 const T l = T::from_base(lhs.to_raw());
539 const T r = T::from_base(rhs.to_raw()); 511 const T r = T::from_base(rhs.to_raw());
@@ -548,159 +520,133 @@ std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
548 520
549// basic math operators 521// basic math operators
550template <size_t I, size_t F> 522template <size_t I, size_t F>
551CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 523constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
552 lhs += rhs; 524 lhs += rhs;
553 return lhs; 525 return lhs;
554} 526}
555template <size_t I, size_t F> 527template <size_t I, size_t F>
556CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 528constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
557 lhs -= rhs; 529 lhs -= rhs;
558 return lhs; 530 return lhs;
559} 531}
560template <size_t I, size_t F> 532template <size_t I, size_t F>
561CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 533constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
562 lhs *= rhs; 534 lhs *= rhs;
563 return lhs; 535 return lhs;
564} 536}
565template <size_t I, size_t F> 537template <size_t I, size_t F>
566CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 538constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
567 lhs /= rhs; 539 lhs /= rhs;
568 return lhs; 540 return lhs;
569} 541}
570 542
571template <size_t I, size_t F, class Number, 543template <size_t I, size_t F, IsArithmetic Number>
572 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 544constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
573CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
574 lhs += FixedPoint<I, F>(rhs); 545 lhs += FixedPoint<I, F>(rhs);
575 return lhs; 546 return lhs;
576} 547}
577template <size_t I, size_t F, class Number, 548template <size_t I, size_t F, IsArithmetic Number>
578 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 549constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
579CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
580 lhs -= FixedPoint<I, F>(rhs); 550 lhs -= FixedPoint<I, F>(rhs);
581 return lhs; 551 return lhs;
582} 552}
583template <size_t I, size_t F, class Number, 553template <size_t I, size_t F, IsArithmetic Number>
584 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 554constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
585CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
586 lhs *= FixedPoint<I, F>(rhs); 555 lhs *= FixedPoint<I, F>(rhs);
587 return lhs; 556 return lhs;
588} 557}
589template <size_t I, size_t F, class Number, 558template <size_t I, size_t F, IsArithmetic Number>
590 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 559constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
591CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
592 lhs /= FixedPoint<I, F>(rhs); 560 lhs /= FixedPoint<I, F>(rhs);
593 return lhs; 561 return lhs;
594} 562}
595 563
596template <size_t I, size_t F, class Number, 564template <size_t I, size_t F, IsArithmetic Number>
597 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 565constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
598CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
599 FixedPoint<I, F> tmp(lhs); 566 FixedPoint<I, F> tmp(lhs);
600 tmp += rhs; 567 tmp += rhs;
601 return tmp; 568 return tmp;
602} 569}
603template <size_t I, size_t F, class Number, 570template <size_t I, size_t F, IsArithmetic Number>
604 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 571constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
605CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
606 FixedPoint<I, F> tmp(lhs); 572 FixedPoint<I, F> tmp(lhs);
607 tmp -= rhs; 573 tmp -= rhs;
608 return tmp; 574 return tmp;
609} 575}
610template <size_t I, size_t F, class Number, 576template <size_t I, size_t F, IsArithmetic Number>
611 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 577constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
612CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
613 FixedPoint<I, F> tmp(lhs); 578 FixedPoint<I, F> tmp(lhs);
614 tmp *= rhs; 579 tmp *= rhs;
615 return tmp; 580 return tmp;
616} 581}
617template <size_t I, size_t F, class Number, 582template <size_t I, size_t F, IsArithmetic Number>
618 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 583constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
619CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
620 FixedPoint<I, F> tmp(lhs); 584 FixedPoint<I, F> tmp(lhs);
621 tmp /= rhs; 585 tmp /= rhs;
622 return tmp; 586 return tmp;
623} 587}
624 588
625// shift operators 589// shift operators
626template <size_t I, size_t F, class Integer, 590template <size_t I, size_t F, IsIntegral Integer>
627 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 591constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
628CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
629 lhs <<= rhs; 592 lhs <<= rhs;
630 return lhs; 593 return lhs;
631} 594}
632template <size_t I, size_t F, class Integer, 595template <size_t I, size_t F, IsIntegral Integer>
633 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 596constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
634CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
635 lhs >>= rhs; 597 lhs >>= rhs;
636 return lhs; 598 return lhs;
637} 599}
638 600
639// comparison operators 601// comparison operators
640template <size_t I, size_t F, class Number, 602template <size_t I, size_t F, IsArithmetic Number>
641 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
642constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { 603constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
643 return lhs > FixedPoint<I, F>(rhs); 604 return lhs > FixedPoint<I, F>(rhs);
644} 605}
645template <size_t I, size_t F, class Number, 606template <size_t I, size_t F, IsArithmetic Number>
646 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
647constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { 607constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
648 return lhs < FixedPoint<I, F>(rhs); 608 return lhs < FixedPoint<I, F>(rhs);
649} 609}
650template <size_t I, size_t F, class Number, 610template <size_t I, size_t F, IsArithmetic Number>
651 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
652constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { 611constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
653 return lhs >= FixedPoint<I, F>(rhs); 612 return lhs >= FixedPoint<I, F>(rhs);
654} 613}
655template <size_t I, size_t F, class Number, 614template <size_t I, size_t F, IsArithmetic Number>
656 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
657constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { 615constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
658 return lhs <= FixedPoint<I, F>(rhs); 616 return lhs <= FixedPoint<I, F>(rhs);
659} 617}
660template <size_t I, size_t F, class Number, 618template <size_t I, size_t F, IsArithmetic Number>
661 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
662constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { 619constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
663 return lhs == FixedPoint<I, F>(rhs); 620 return lhs == FixedPoint<I, F>(rhs);
664} 621}
665template <size_t I, size_t F, class Number, 622template <size_t I, size_t F, IsArithmetic Number>
666 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
667constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { 623constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
668 return lhs != FixedPoint<I, F>(rhs); 624 return lhs != FixedPoint<I, F>(rhs);
669} 625}
670 626
671template <size_t I, size_t F, class Number, 627template <size_t I, size_t F, IsArithmetic Number>
672 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
673constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { 628constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
674 return FixedPoint<I, F>(lhs) > rhs; 629 return FixedPoint<I, F>(lhs) > rhs;
675} 630}
676template <size_t I, size_t F, class Number, 631template <size_t I, size_t F, IsArithmetic Number>
677 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
678constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { 632constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
679 return FixedPoint<I, F>(lhs) < rhs; 633 return FixedPoint<I, F>(lhs) < rhs;
680} 634}
681template <size_t I, size_t F, class Number, 635template <size_t I, size_t F, IsArithmetic Number>
682 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
683constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { 636constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
684 return FixedPoint<I, F>(lhs) >= rhs; 637 return FixedPoint<I, F>(lhs) >= rhs;
685} 638}
686template <size_t I, size_t F, class Number, 639template <size_t I, size_t F, IsArithmetic Number>
687 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
688constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { 640constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
689 return FixedPoint<I, F>(lhs) <= rhs; 641 return FixedPoint<I, F>(lhs) <= rhs;
690} 642}
691template <size_t I, size_t F, class Number, 643template <size_t I, size_t F, IsArithmetic Number>
692 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
693constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { 644constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
694 return FixedPoint<I, F>(lhs) == rhs; 645 return FixedPoint<I, F>(lhs) == rhs;
695} 646}
696template <size_t I, size_t F, class Number, 647template <size_t I, size_t F, IsArithmetic Number>
697 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
698constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { 648constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
699 return FixedPoint<I, F>(lhs) != rhs; 649 return FixedPoint<I, F>(lhs) != rhs;
700} 650}
701 651
702} // namespace Common 652} // namespace Common
703
704#undef CONSTEXPR14
705
706#endif
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 69b53384c..167c4d826 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -209,8 +209,8 @@ public:
209 209
210 /** 210 /**
211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. 211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
212 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 212 * If T is not a contiguous container as defined by the concept IsContiguousContainer, this
213 * ReadObject and T must be a trivially copyable object. 213 * calls ReadObject and T must be a trivially copyable object.
214 * 214 *
215 * See ReadSpan for more details if T is a contiguous container. 215 * See ReadSpan for more details if T is a contiguous container.
216 * See ReadObject for more details if T is a trivially copyable object. 216 * See ReadObject for more details if T is a trivially copyable object.
@@ -223,7 +223,7 @@ public:
223 */ 223 */
224 template <typename T> 224 template <typename T>
225 [[nodiscard]] size_t Read(T& data) const { 225 [[nodiscard]] size_t Read(T& data) const {
226 if constexpr (IsSTLContainer<T>) { 226 if constexpr (IsContiguousContainer<T>) {
227 using ContiguousType = typename T::value_type; 227 using ContiguousType = typename T::value_type;
228 static_assert(std::is_trivially_copyable_v<ContiguousType>, 228 static_assert(std::is_trivially_copyable_v<ContiguousType>,
229 "Data type must be trivially copyable."); 229 "Data type must be trivially copyable.");
@@ -235,8 +235,8 @@ public:
235 235
236 /** 236 /**
237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. 237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
238 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 238 * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
239 * WriteObject and T must be a trivially copyable object. 239 * calls WriteObject and T must be a trivially copyable object.
240 * 240 *
241 * See WriteSpan for more details if T is a contiguous container. 241 * See WriteSpan for more details if T is a contiguous container.
242 * See WriteObject for more details if T is a trivially copyable object. 242 * See WriteObject for more details if T is a trivially copyable object.
@@ -249,7 +249,7 @@ public:
249 */ 249 */
250 template <typename T> 250 template <typename T>
251 [[nodiscard]] size_t Write(const T& data) const { 251 [[nodiscard]] size_t Write(const T& data) const {
252 if constexpr (IsSTLContainer<T>) { 252 if constexpr (IsContiguousContainer<T>) {
253 using ContiguousType = typename T::value_type; 253 using ContiguousType = typename T::value_type;
254 static_assert(std::is_trivially_copyable_v<ContiguousType>, 254 static_assert(std::is_trivially_copyable_v<ContiguousType>,
255 "Data type must be trivially copyable."); 255 "Data type must be trivially copyable.");
diff --git a/src/common/hash.h b/src/common/hash.h
index b6f3e6d6f..e8fe78b07 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -18,4 +18,11 @@ struct PairHash {
18 } 18 }
19}; 19};
20 20
21template <typename T>
22struct IdentityHash {
23 [[nodiscard]] size_t operator()(T value) const noexcept {
24 return static_cast<size_t>(value);
25 }
26};
27
21} // namespace Common 28} // namespace Common
diff --git a/src/common/input.h b/src/common/input.h
index 825b0d650..cb30b7254 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -76,6 +76,19 @@ enum class PollingError {
76 Unknown, 76 Unknown,
77}; 77};
78 78
79// Nfc reply from the controller
80enum class NfcState {
81 Success,
82 NewAmiibo,
83 WaitingForAmiibo,
84 AmiiboRemoved,
85 NotAnAmiibo,
86 NotSupported,
87 WrongDeviceState,
88 WriteFailed,
89 Unknown,
90};
91
79// Ir camera reply from the controller 92// Ir camera reply from the controller
80enum class CameraError { 93enum class CameraError {
81 None, 94 None,
@@ -87,7 +100,6 @@ enum class CameraError {
87enum class VibrationAmplificationType { 100enum class VibrationAmplificationType {
88 Linear, 101 Linear,
89 Exponential, 102 Exponential,
90 Test,
91}; 103};
92 104
93// Analog properties for calibration 105// Analog properties for calibration
@@ -202,6 +214,11 @@ struct CameraStatus {
202 std::vector<u8> data{}; 214 std::vector<u8> data{};
203}; 215};
204 216
217struct NfcStatus {
218 NfcState state{};
219 std::vector<u8> data{};
220};
221
205// List of buttons to be passed to Qt that can be translated 222// List of buttons to be passed to Qt that can be translated
206enum class ButtonNames { 223enum class ButtonNames {
207 Undefined, 224 Undefined,
@@ -259,7 +276,9 @@ struct CallbackStatus {
259 BodyColorStatus color_status{}; 276 BodyColorStatus color_status{};
260 BatteryStatus battery_status{}; 277 BatteryStatus battery_status{};
261 VibrationStatus vibration_status{}; 278 VibrationStatus vibration_status{};
262 CameraStatus camera_status{}; 279 CameraFormat camera_status{CameraFormat::None};
280 NfcState nfc_status{NfcState::Unknown};
281 std::vector<u8> raw_data{};
263}; 282};
264 283
265// Triggered once every input change 284// Triggered once every input change
@@ -305,6 +324,10 @@ public:
305 return VibrationError::NotSupported; 324 return VibrationError::NotSupported;
306 } 325 }
307 326
327 virtual bool IsVibrationEnabled() {
328 return false;
329 }
330
308 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 331 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
309 return PollingError::NotSupported; 332 return PollingError::NotSupported;
310 } 333 }
@@ -312,6 +335,14 @@ public:
312 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 335 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
313 return CameraError::NotSupported; 336 return CameraError::NotSupported;
314 } 337 }
338
339 virtual NfcState SupportsNfc() const {
340 return NfcState::NotSupported;
341 }
342
343 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
344 return NfcState::NotSupported;
345 }
315}; 346};
316 347
317/// An abstract class template for a factory that can create input devices. 348/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 8ce1c2fd1..15d92505e 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -219,7 +219,7 @@ private:
219 219
220 void StartBackendThread() { 220 void StartBackendThread() {
221 backend_thread = std::jthread([this](std::stop_token stop_token) { 221 backend_thread = std::jthread([this](std::stop_token stop_token) {
222 Common::SetCurrentThreadName("yuzu:Log"); 222 Common::SetCurrentThreadName("Logger");
223 Entry entry; 223 Entry entry;
224 const auto write_logs = [this, &entry]() { 224 const auto write_logs = [this, &entry]() {
225 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); 225 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp
new file mode 100644
index 000000000..46e362f3b
--- /dev/null
+++ b/src/common/multi_level_page_table.cpp
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/multi_level_page_table.inc"
5
6namespace Common {
7template class Common::MultiLevelPageTable<u64>;
8template class Common::MultiLevelPageTable<u32>;
9} // namespace Common
diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h
new file mode 100644
index 000000000..31f6676a0
--- /dev/null
+++ b/src/common/multi_level_page_table.h
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <type_traits>
7#include <utility>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Common {
13
14template <typename BaseAddr>
15class MultiLevelPageTable final {
16public:
17 constexpr MultiLevelPageTable() = default;
18 explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits,
19 std::size_t page_bits);
20
21 ~MultiLevelPageTable() noexcept;
22
23 MultiLevelPageTable(const MultiLevelPageTable&) = delete;
24 MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete;
25
26 MultiLevelPageTable(MultiLevelPageTable&& other) noexcept
27 : address_space_bits{std::exchange(other.address_space_bits, 0)},
28 first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange(
29 other.page_bits, 0)},
30 first_level_shift{std::exchange(other.first_level_shift, 0)},
31 first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)},
32 first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr,
33 nullptr)} {}
34
35 MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept {
36 address_space_bits = std::exchange(other.address_space_bits, 0);
37 first_level_bits = std::exchange(other.first_level_bits, 0);
38 page_bits = std::exchange(other.page_bits, 0);
39 first_level_shift = std::exchange(other.first_level_shift, 0);
40 first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0);
41 alloc_size = std::exchange(other.alloc_size, 0);
42 first_level_map = std::move(other.first_level_map);
43 base_ptr = std::exchange(other.base_ptr, nullptr);
44 return *this;
45 }
46
47 void ReserveRange(u64 start, std::size_t size);
48
49 [[nodiscard]] const BaseAddr& operator[](std::size_t index) const {
50 return base_ptr[index];
51 }
52
53 [[nodiscard]] BaseAddr& operator[](std::size_t index) {
54 return base_ptr[index];
55 }
56
57 [[nodiscard]] BaseAddr* data() {
58 return base_ptr;
59 }
60
61 [[nodiscard]] const BaseAddr* data() const {
62 return base_ptr;
63 }
64
65private:
66 void AllocateLevel(u64 level);
67
68 std::size_t address_space_bits{};
69 std::size_t first_level_bits{};
70 std::size_t page_bits{};
71 std::size_t first_level_shift{};
72 std::size_t first_level_chunk_size{};
73 std::size_t alloc_size{};
74 std::vector<void*> first_level_map{};
75 BaseAddr* base_ptr{};
76};
77
78} // namespace Common
diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc
new file mode 100644
index 000000000..8ac506fa0
--- /dev/null
+++ b/src/common/multi_level_page_table.inc
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifdef _WIN32
5#include <windows.h>
6#else
7#include <sys/mman.h>
8#endif
9
10#include "common/assert.h"
11#include "common/multi_level_page_table.h"
12
13namespace Common {
14
15template <typename BaseAddr>
16MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
17 std::size_t first_level_bits_,
18 std::size_t page_bits_)
19 : address_space_bits{address_space_bits_},
20 first_level_bits{first_level_bits_}, page_bits{page_bits_} {
21 if (page_bits == 0) {
22 return;
23 }
24 first_level_shift = address_space_bits - first_level_bits;
25 first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr);
26 alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr);
27 std::size_t first_level_size = 1ULL << first_level_bits;
28 first_level_map.resize(first_level_size, nullptr);
29#ifdef _WIN32
30 void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
31#else
32 void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
33
34 if (base == MAP_FAILED) {
35 base = nullptr;
36 }
37#endif
38
39 ASSERT(base);
40 base_ptr = reinterpret_cast<BaseAddr*>(base);
41}
42
43template <typename BaseAddr>
44MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept {
45 if (!base_ptr) {
46 return;
47 }
48#ifdef _WIN32
49 ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE));
50#else
51 ASSERT(munmap(base_ptr, alloc_size) == 0);
52#endif
53}
54
55template <typename BaseAddr>
56void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
57 const u64 new_start = start >> first_level_shift;
58 const u64 new_end = (start + size) >> first_level_shift;
59 for (u64 i = new_start; i <= new_end; i++) {
60 if (!first_level_map[i]) {
61 AllocateLevel(i);
62 }
63 }
64}
65
66template <typename BaseAddr>
67void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
68 void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
69#ifdef _WIN32
70 void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
71#else
72 void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
73 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
74
75 if (base == MAP_FAILED) {
76 base = nullptr;
77 }
78#endif
79 ASSERT(base);
80
81 first_level_map[level] = base;
82}
83
84} // namespace Common
diff --git a/src/common/settings.h b/src/common/settings.h
index 851812f28..0eb98939c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -431,7 +431,7 @@ struct Values {
431 FullscreenMode::Exclusive, 431 FullscreenMode::Exclusive,
432#endif 432#endif
433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
434 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"}; 434 SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"};
435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; 435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; 436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; 437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
@@ -531,6 +531,7 @@ struct Values {
531 Setting<bool> use_auto_stub{false, "use_auto_stub"}; 531 Setting<bool> use_auto_stub{false, "use_auto_stub"};
532 Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; 532 Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
533 Setting<bool> create_crash_dumps{false, "create_crash_dumps"}; 533 Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
534 Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"};
534 535
535 // Miscellaneous 536 // Miscellaneous
536 Setting<std::string> log_filter{"*:Info", "log_filter"}; 537 Setting<std::string> log_filter{"*:Info", "log_filter"};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 33cf470d5..113e663b5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -138,8 +138,6 @@ add_library(core STATIC
138 frontend/emu_window.h 138 frontend/emu_window.h
139 frontend/framebuffer_layout.cpp 139 frontend/framebuffer_layout.cpp
140 frontend/framebuffer_layout.h 140 frontend/framebuffer_layout.h
141 hardware_interrupt_manager.cpp
142 hardware_interrupt_manager.h
143 hid/emulated_console.cpp 141 hid/emulated_console.cpp
144 hid/emulated_console.h 142 hid/emulated_console.h
145 hid/emulated_controller.cpp 143 hid/emulated_controller.cpp
@@ -192,6 +190,9 @@ add_library(core STATIC
192 hle/kernel/k_code_memory.h 190 hle/kernel/k_code_memory.h
193 hle/kernel/k_condition_variable.cpp 191 hle/kernel/k_condition_variable.cpp
194 hle/kernel/k_condition_variable.h 192 hle/kernel/k_condition_variable.h
193 hle/kernel/k_dynamic_page_manager.h
194 hle/kernel/k_dynamic_resource_manager.h
195 hle/kernel/k_dynamic_slab_heap.h
195 hle/kernel/k_event.cpp 196 hle/kernel/k_event.cpp
196 hle/kernel/k_event.h 197 hle/kernel/k_event.h
197 hle/kernel/k_handle_table.cpp 198 hle/kernel/k_handle_table.cpp
@@ -242,6 +243,8 @@ add_library(core STATIC
242 hle/kernel/k_server_session.h 243 hle/kernel/k_server_session.h
243 hle/kernel/k_session.cpp 244 hle/kernel/k_session.cpp
244 hle/kernel/k_session.h 245 hle/kernel/k_session.h
246 hle/kernel/k_session_request.cpp
247 hle/kernel/k_session_request.h
245 hle/kernel/k_shared_memory.cpp 248 hle/kernel/k_shared_memory.cpp
246 hle/kernel/k_shared_memory.h 249 hle/kernel/k_shared_memory.h
247 hle/kernel/k_shared_memory_info.h 250 hle/kernel/k_shared_memory_info.h
@@ -263,8 +266,6 @@ add_library(core STATIC
263 hle/kernel/k_worker_task.h 266 hle/kernel/k_worker_task.h
264 hle/kernel/k_worker_task_manager.cpp 267 hle/kernel/k_worker_task_manager.cpp
265 hle/kernel/k_worker_task_manager.h 268 hle/kernel/k_worker_task_manager.h
266 hle/kernel/k_writable_event.cpp
267 hle/kernel/k_writable_event.h
268 hle/kernel/kernel.cpp 269 hle/kernel/kernel.cpp
269 hle/kernel/kernel.h 270 hle/kernel/kernel.h
270 hle/kernel/memory_types.h 271 hle/kernel/memory_types.h
@@ -460,6 +461,8 @@ add_library(core STATIC
460 hle/service/hid/controllers/mouse.h 461 hle/service/hid/controllers/mouse.h
461 hle/service/hid/controllers/npad.cpp 462 hle/service/hid/controllers/npad.cpp
462 hle/service/hid/controllers/npad.h 463 hle/service/hid/controllers/npad.h
464 hle/service/hid/controllers/palma.cpp
465 hle/service/hid/controllers/palma.h
463 hle/service/hid/controllers/stubbed.cpp 466 hle/service/hid/controllers/stubbed.cpp
464 hle/service/hid/controllers/stubbed.h 467 hle/service/hid/controllers/stubbed.h
465 hle/service/hid/controllers/touchscreen.cpp 468 hle/service/hid/controllers/touchscreen.cpp
@@ -494,6 +497,8 @@ add_library(core STATIC
494 hle/service/jit/jit.h 497 hle/service/jit/jit.h
495 hle/service/lbl/lbl.cpp 498 hle/service/lbl/lbl.cpp
496 hle/service/lbl/lbl.h 499 hle/service/lbl/lbl.h
500 hle/service/ldn/lan_discovery.cpp
501 hle/service/ldn/lan_discovery.h
497 hle/service/ldn/ldn_results.h 502 hle/service/ldn/ldn_results.h
498 hle/service/ldn/ldn.cpp 503 hle/service/ldn/ldn.cpp
499 hle/service/ldn/ldn.h 504 hle/service/ldn/ldn.h
@@ -521,9 +526,12 @@ add_library(core STATIC
521 hle/service/nfc/nfc.h 526 hle/service/nfc/nfc.h
522 hle/service/nfp/amiibo_crypto.cpp 527 hle/service/nfp/amiibo_crypto.cpp
523 hle/service/nfp/amiibo_crypto.h 528 hle/service/nfp/amiibo_crypto.h
524 hle/service/nfp/amiibo_types.h
525 hle/service/nfp/nfp.cpp 529 hle/service/nfp/nfp.cpp
526 hle/service/nfp/nfp.h 530 hle/service/nfp/nfp.h
531 hle/service/nfp/nfp_device.cpp
532 hle/service/nfp/nfp_device.h
533 hle/service/nfp/nfp_result.h
534 hle/service/nfp/nfp_types.h
527 hle/service/nfp/nfp_user.cpp 535 hle/service/nfp/nfp_user.cpp
528 hle/service/nfp/nfp_user.h 536 hle/service/nfp/nfp_user.h
529 hle/service/ngct/ngct.cpp 537 hle/service/ngct/ngct.cpp
@@ -543,6 +551,12 @@ add_library(core STATIC
543 hle/service/ns/ns.h 551 hle/service/ns/ns.h
544 hle/service/ns/pdm_qry.cpp 552 hle/service/ns/pdm_qry.cpp
545 hle/service/ns/pdm_qry.h 553 hle/service/ns/pdm_qry.h
554 hle/service/nvdrv/core/container.cpp
555 hle/service/nvdrv/core/container.h
556 hle/service/nvdrv/core/nvmap.cpp
557 hle/service/nvdrv/core/nvmap.h
558 hle/service/nvdrv/core/syncpoint_manager.cpp
559 hle/service/nvdrv/core/syncpoint_manager.h
546 hle/service/nvdrv/devices/nvdevice.h 560 hle/service/nvdrv/devices/nvdevice.h
547 hle/service/nvdrv/devices/nvdisp_disp0.cpp 561 hle/service/nvdrv/devices/nvdisp_disp0.cpp
548 hle/service/nvdrv/devices/nvdisp_disp0.h 562 hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -571,8 +585,6 @@ add_library(core STATIC
571 hle/service/nvdrv/nvdrv_interface.h 585 hle/service/nvdrv/nvdrv_interface.h
572 hle/service/nvdrv/nvmemp.cpp 586 hle/service/nvdrv/nvmemp.cpp
573 hle/service/nvdrv/nvmemp.h 587 hle/service/nvdrv/nvmemp.h
574 hle/service/nvdrv/syncpoint_manager.cpp
575 hle/service/nvdrv/syncpoint_manager.h
576 hle/service/nvflinger/binder.h 588 hle/service/nvflinger/binder.h
577 hle/service/nvflinger/buffer_item.h 589 hle/service/nvflinger/buffer_item.h
578 hle/service/nvflinger/buffer_item_consumer.cpp 590 hle/service/nvflinger/buffer_item_consumer.cpp
@@ -762,19 +774,15 @@ if (MSVC)
762 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 774 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
763 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch 775 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
764 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 776 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
777 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
765 ) 778 )
766else() 779else()
767 target_compile_options(core PRIVATE 780 target_compile_options(core PRIVATE
768 -Werror=conversion 781 -Werror=conversion
769 -Werror=ignored-qualifiers
770 782
771 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 783 -Wno-sign-conversion
772 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
773 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
774 784
775 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 785 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
776
777 -Wno-sign-conversion
778 ) 786 )
779endif() 787endif()
780 788
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 953d96439..29ba562dc 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -134,6 +134,14 @@ void ARM_Interface::Run() {
134 } 134 }
135 system.ExitDynarmicProfile(); 135 system.ExitDynarmicProfile();
136 136
137 // If the thread is scheduled for termination, exit the thread.
138 if (current_thread->HasDpc()) {
139 if (current_thread->IsTerminationRequested()) {
140 current_thread->Exit();
141 UNREACHABLE();
142 }
143 }
144
137 // Notify the debugger and go to sleep if a breakpoint was hit, 145 // Notify the debugger and go to sleep if a breakpoint was hit,
138 // or if the thread is unable to continue for any reason. 146 // or if the thread is unable to continue for any reason.
139 if (Has(hr, breakpoint) || Has(hr, no_execute)) { 147 if (Has(hr, breakpoint) || Has(hr, no_execute)) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index d1e70f19d..287ba102e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -450,7 +450,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::S
450 // Frame records are two words long: 450 // Frame records are two words long:
451 // fp+0 : pointer to previous frame record 451 // fp+0 : pointer to previous frame record
452 // fp+4 : value of lr for frame 452 // fp+4 : value of lr for frame
453 while (true) { 453 for (size_t i = 0; i < 256; i++) {
454 out.push_back({"", 0, lr, 0, ""}); 454 out.push_back({"", 0, lr, 0, ""});
455 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { 455 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
456 break; 456 break;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1d46f6d40..afb7fb3a0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -111,6 +111,7 @@ public:
111 LOG_ERROR(Core_ARM, 111 LOG_ERROR(Core_ARM,
112 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 112 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
113 num_instructions, memory.Read32(pc)); 113 num_instructions, memory.Read32(pc));
114 ReturnException(pc, ARM_Interface::no_execute);
114 } 115 }
115 116
116 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, 117 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -516,7 +517,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
516 // Frame records are two words long: 517 // Frame records are two words long:
517 // fp+0 : pointer to previous frame record 518 // fp+0 : pointer to previous frame record
518 // fp+8 : value of lr for frame 519 // fp+8 : value of lr for frame
519 while (true) { 520 for (size_t i = 0; i < 256; i++) {
520 out.push_back({"", 0, lr, 0, ""}); 521 out.push_back({"", 0, lr, 0, ""});
521 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { 522 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
522 break; 523 break;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 121092868..d8934be52 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,7 +27,6 @@
27#include "core/file_sys/savedata_factory.h" 27#include "core/file_sys/savedata_factory.h"
28#include "core/file_sys/vfs_concat.h" 28#include "core/file_sys/vfs_concat.h"
29#include "core/file_sys/vfs_real.h" 29#include "core/file_sys/vfs_real.h"
30#include "core/hardware_interrupt_manager.h"
31#include "core/hid/hid_core.h" 30#include "core/hid/hid_core.h"
32#include "core/hle/kernel/k_memory_manager.h" 31#include "core/hle/kernel/k_memory_manager.h"
33#include "core/hle/kernel/k_process.h" 32#include "core/hle/kernel/k_process.h"
@@ -51,6 +50,7 @@
51#include "core/telemetry_session.h" 50#include "core/telemetry_session.h"
52#include "core/tools/freezer.h" 51#include "core/tools/freezer.h"
53#include "network/network.h" 52#include "network/network.h"
53#include "video_core/host1x/host1x.h"
54#include "video_core/renderer_base.h" 54#include "video_core/renderer_base.h"
55#include "video_core/video_core.h" 55#include "video_core/video_core.h"
56 56
@@ -133,6 +133,56 @@ struct System::Impl {
133 : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, 133 : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
134 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} 134 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
135 135
136 void Initialize(System& system) {
137 device_memory = std::make_unique<Core::DeviceMemory>();
138
139 is_multicore = Settings::values.use_multi_core.GetValue();
140 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
141
142 core_timing.SetMulticore(is_multicore);
143 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
144
145 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
146 const auto current_time =
147 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
148 Settings::values.custom_rtc_differential =
149 Settings::values.custom_rtc.value_or(current_time) - current_time;
150
151 // Create a default fs if one doesn't already exist.
152 if (virtual_filesystem == nullptr) {
153 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
154 }
155 if (content_provider == nullptr) {
156 content_provider = std::make_unique<FileSys::ContentProviderUnion>();
157 }
158
159 // Create default implementations of applets if one is not provided.
160 applet_manager.SetDefaultAppletsIfMissing();
161
162 is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
163
164 kernel.SetMulticore(is_multicore);
165 cpu_manager.SetMulticore(is_multicore);
166 cpu_manager.SetAsyncGpu(is_async_gpu);
167 }
168
169 void ReinitializeIfNecessary(System& system) {
170 const bool must_reinitialize =
171 is_multicore != Settings::values.use_multi_core.GetValue() ||
172 extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue();
173
174 if (!must_reinitialize) {
175 return;
176 }
177
178 LOG_DEBUG(Kernel, "Re-initializing");
179
180 is_multicore = Settings::values.use_multi_core.GetValue();
181 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
182
183 Initialize(system);
184 }
185
136 SystemResultStatus Run() { 186 SystemResultStatus Run() {
137 std::unique_lock<std::mutex> lk(suspend_guard); 187 std::unique_lock<std::mutex> lk(suspend_guard);
138 status = SystemResultStatus::Success; 188 status = SystemResultStatus::Success;
@@ -178,43 +228,21 @@ struct System::Impl {
178 debugger = std::make_unique<Debugger>(system, port); 228 debugger = std::make_unique<Debugger>(system, port);
179 } 229 }
180 230
181 SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 231 SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) {
182 LOG_DEBUG(Core, "initialized OK"); 232 LOG_DEBUG(Core, "initialized OK");
183 233
184 device_memory = std::make_unique<Core::DeviceMemory>(); 234 // Setting changes may require a full system reinitialization (e.g., disabling multicore).
185 235 ReinitializeIfNecessary(system);
186 is_multicore = Settings::values.use_multi_core.GetValue();
187 is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
188
189 kernel.SetMulticore(is_multicore);
190 cpu_manager.SetMulticore(is_multicore);
191 cpu_manager.SetAsyncGpu(is_async_gpu);
192 core_timing.SetMulticore(is_multicore);
193 236
194 kernel.Initialize(); 237 kernel.Initialize();
195 cpu_manager.Initialize(); 238 cpu_manager.Initialize();
196 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
197
198 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
199 const auto current_time =
200 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
201 Settings::values.custom_rtc_differential =
202 Settings::values.custom_rtc.value_or(current_time) - current_time;
203
204 // Create a default fs if one doesn't already exist.
205 if (virtual_filesystem == nullptr)
206 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
207 if (content_provider == nullptr)
208 content_provider = std::make_unique<FileSys::ContentProviderUnion>();
209
210 /// Create default implementations of applets if one is not provided.
211 applet_manager.SetDefaultAppletsIfMissing();
212 239
213 /// Reset all glue registrations 240 /// Reset all glue registrations
214 arp_manager.ResetAll(); 241 arp_manager.ResetAll();
215 242
216 telemetry_session = std::make_unique<Core::TelemetrySession>(); 243 telemetry_session = std::make_unique<Core::TelemetrySession>();
217 244
245 host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
218 gpu_core = VideoCore::CreateGPU(emu_window, system); 246 gpu_core = VideoCore::CreateGPU(emu_window, system);
219 if (!gpu_core) { 247 if (!gpu_core) {
220 return SystemResultStatus::ErrorVideoCore; 248 return SystemResultStatus::ErrorVideoCore;
@@ -224,7 +252,6 @@ struct System::Impl {
224 252
225 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 253 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
226 services = std::make_unique<Service::Services>(service_manager, system); 254 services = std::make_unique<Service::Services>(service_manager, system);
227 interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
228 255
229 // Initialize time manager, which must happen after kernel is created 256 // Initialize time manager, which must happen after kernel is created
230 time_manager.Initialize(); 257 time_manager.Initialize();
@@ -253,11 +280,11 @@ struct System::Impl {
253 return SystemResultStatus::ErrorGetLoader; 280 return SystemResultStatus::ErrorGetLoader;
254 } 281 }
255 282
256 SystemResultStatus init_result{Init(system, emu_window)}; 283 SystemResultStatus init_result{SetupForMainProcess(system, emu_window)};
257 if (init_result != SystemResultStatus::Success) { 284 if (init_result != SystemResultStatus::Success) {
258 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 285 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
259 static_cast<int>(init_result)); 286 static_cast<int>(init_result));
260 Shutdown(); 287 ShutdownMainProcess();
261 return init_result; 288 return init_result;
262 } 289 }
263 290
@@ -276,7 +303,7 @@ struct System::Impl {
276 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 303 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
277 if (load_result != Loader::ResultStatus::Success) { 304 if (load_result != Loader::ResultStatus::Success) {
278 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); 305 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
279 Shutdown(); 306 ShutdownMainProcess();
280 307
281 return static_cast<SystemResultStatus>( 308 return static_cast<SystemResultStatus>(
282 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); 309 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
@@ -335,7 +362,7 @@ struct System::Impl {
335 return status; 362 return status;
336 } 363 }
337 364
338 void Shutdown() { 365 void ShutdownMainProcess() {
339 SetShuttingDown(true); 366 SetShuttingDown(true);
340 367
341 // Log last frame performance stats if game was loded 368 // Log last frame performance stats if game was loded
@@ -363,20 +390,21 @@ struct System::Impl {
363 kernel.ShutdownCores(); 390 kernel.ShutdownCores();
364 cpu_manager.Shutdown(); 391 cpu_manager.Shutdown();
365 debugger.reset(); 392 debugger.reset();
393 services->KillNVNFlinger();
366 kernel.CloseServices(); 394 kernel.CloseServices();
367 services.reset(); 395 services.reset();
368 service_manager.reset(); 396 service_manager.reset();
369 cheat_engine.reset(); 397 cheat_engine.reset();
370 telemetry_session.reset(); 398 telemetry_session.reset();
371 time_manager.Shutdown(); 399 time_manager.Shutdown();
372 core_timing.Shutdown(); 400 core_timing.ClearPendingEvents();
373 app_loader.reset(); 401 app_loader.reset();
374 audio_core.reset(); 402 audio_core.reset();
375 gpu_core.reset(); 403 gpu_core.reset();
404 host1x_core.reset();
376 perf_stats.reset(); 405 perf_stats.reset();
377 kernel.Shutdown(); 406 kernel.Shutdown();
378 memory.Reset(); 407 memory.Reset();
379 applet_manager.ClearAll();
380 408
381 if (auto room_member = room_network.GetRoomMember().lock()) { 409 if (auto room_member = room_network.GetRoomMember().lock()) {
382 Network::GameInfo game_info{}; 410 Network::GameInfo game_info{};
@@ -450,7 +478,7 @@ struct System::Impl {
450 /// AppLoader used to load the current executing application 478 /// AppLoader used to load the current executing application
451 std::unique_ptr<Loader::AppLoader> app_loader; 479 std::unique_ptr<Loader::AppLoader> app_loader;
452 std::unique_ptr<Tegra::GPU> gpu_core; 480 std::unique_ptr<Tegra::GPU> gpu_core;
453 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 481 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
454 std::unique_ptr<Core::DeviceMemory> device_memory; 482 std::unique_ptr<Core::DeviceMemory> device_memory;
455 std::unique_ptr<AudioCore::AudioCore> audio_core; 483 std::unique_ptr<AudioCore::AudioCore> audio_core;
456 Core::Memory::Memory memory; 484 Core::Memory::Memory memory;
@@ -499,6 +527,7 @@ struct System::Impl {
499 527
500 bool is_multicore{}; 528 bool is_multicore{};
501 bool is_async_gpu{}; 529 bool is_async_gpu{};
530 bool extended_memory_layout{};
502 531
503 ExecuteProgramCallback execute_program_callback; 532 ExecuteProgramCallback execute_program_callback;
504 ExitCallback exit_callback; 533 ExitCallback exit_callback;
@@ -519,6 +548,10 @@ const CpuManager& System::GetCpuManager() const {
519 return impl->cpu_manager; 548 return impl->cpu_manager;
520} 549}
521 550
551void System::Initialize() {
552 impl->Initialize(*this);
553}
554
522SystemResultStatus System::Run() { 555SystemResultStatus System::Run() {
523 return impl->Run(); 556 return impl->Run();
524} 557}
@@ -539,8 +572,8 @@ void System::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
539 impl->kernel.InvalidateCpuInstructionCacheRange(addr, size); 572 impl->kernel.InvalidateCpuInstructionCacheRange(addr, size);
540} 573}
541 574
542void System::Shutdown() { 575void System::ShutdownMainProcess() {
543 impl->Shutdown(); 576 impl->ShutdownMainProcess();
544} 577}
545 578
546bool System::IsShuttingDown() const { 579bool System::IsShuttingDown() const {
@@ -668,12 +701,12 @@ const Tegra::GPU& System::GPU() const {
668 return *impl->gpu_core; 701 return *impl->gpu_core;
669} 702}
670 703
671Core::Hardware::InterruptManager& System::InterruptManager() { 704Tegra::Host1x::Host1x& System::Host1x() {
672 return *impl->interrupt_manager; 705 return *impl->host1x_core;
673} 706}
674 707
675const Core::Hardware::InterruptManager& System::InterruptManager() const { 708const Tegra::Host1x::Host1x& System::Host1x() const {
676 return *impl->interrupt_manager; 709 return *impl->host1x_core;
677} 710}
678 711
679VideoCore::RendererBase& System::Renderer() { 712VideoCore::RendererBase& System::Renderer() {
diff --git a/src/core/core.h b/src/core/core.h
index 0ce3b1d60..4ebedffd9 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -74,6 +74,9 @@ class TimeManager;
74namespace Tegra { 74namespace Tegra {
75class DebugContext; 75class DebugContext;
76class GPU; 76class GPU;
77namespace Host1x {
78class Host1x;
79} // namespace Host1x
77} // namespace Tegra 80} // namespace Tegra
78 81
79namespace VideoCore { 82namespace VideoCore {
@@ -88,10 +91,6 @@ namespace Core::Timing {
88class CoreTiming; 91class CoreTiming;
89} 92}
90 93
91namespace Core::Hardware {
92class InterruptManager;
93}
94
95namespace Core::HID { 94namespace Core::HID {
96class HIDCore; 95class HIDCore;
97} 96}
@@ -144,6 +143,12 @@ public:
144 System& operator=(System&&) = delete; 143 System& operator=(System&&) = delete;
145 144
146 /** 145 /**
146 * Initializes the system
147 * This function will initialize core functionaility used for system emulation
148 */
149 void Initialize();
150
151 /**
147 * Run the OS and Application 152 * Run the OS and Application
148 * This function will start emulation and run the relevant devices 153 * This function will start emulation and run the relevant devices
149 */ 154 */
@@ -167,8 +172,8 @@ public:
167 172
168 void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); 173 void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
169 174
170 /// Shutdown the emulated system. 175 /// Shutdown the main emulated process.
171 void Shutdown(); 176 void ShutdownMainProcess();
172 177
173 /// Check if the core is shutting down. 178 /// Check if the core is shutting down.
174 [[nodiscard]] bool IsShuttingDown() const; 179 [[nodiscard]] bool IsShuttingDown() const;
@@ -260,6 +265,12 @@ public:
260 /// Gets an immutable reference to the GPU interface. 265 /// Gets an immutable reference to the GPU interface.
261 [[nodiscard]] const Tegra::GPU& GPU() const; 266 [[nodiscard]] const Tegra::GPU& GPU() const;
262 267
268 /// Gets a mutable reference to the Host1x interface
269 [[nodiscard]] Tegra::Host1x::Host1x& Host1x();
270
271 /// Gets an immutable reference to the Host1x interface.
272 [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const;
273
263 /// Gets a mutable reference to the renderer. 274 /// Gets a mutable reference to the renderer.
264 [[nodiscard]] VideoCore::RendererBase& Renderer(); 275 [[nodiscard]] VideoCore::RendererBase& Renderer();
265 276
@@ -296,12 +307,6 @@ public:
296 /// Provides a constant reference to the core timing instance. 307 /// Provides a constant reference to the core timing instance.
297 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; 308 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
298 309
299 /// Provides a reference to the interrupt manager instance.
300 [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
301
302 /// Provides a constant reference to the interrupt manager instance.
303 [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
304
305 /// Provides a reference to the kernel instance. 310 /// Provides a reference to the kernel instance.
306 [[nodiscard]] Kernel::KernelCore& Kernel(); 311 [[nodiscard]] Kernel::KernelCore& Kernel();
307 312
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index f6c4567ba..0e7b5f943 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -40,10 +40,12 @@ struct CoreTiming::Event {
40CoreTiming::CoreTiming() 40CoreTiming::CoreTiming()
41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} 41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
42 42
43CoreTiming::~CoreTiming() = default; 43CoreTiming::~CoreTiming() {
44 Reset();
45}
44 46
45void CoreTiming::ThreadEntry(CoreTiming& instance) { 47void CoreTiming::ThreadEntry(CoreTiming& instance) {
46 constexpr char name[] = "yuzu:HostTiming"; 48 constexpr char name[] = "HostTiming";
47 MicroProfileOnThreadCreate(name); 49 MicroProfileOnThreadCreate(name);
48 Common::SetCurrentThreadName(name); 50 Common::SetCurrentThreadName(name);
49 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); 51 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
@@ -53,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
53} 55}
54 56
55void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { 57void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
58 Reset();
56 on_thread_init = std::move(on_thread_init_); 59 on_thread_init = std::move(on_thread_init_);
57 event_fifo_id = 0; 60 event_fifo_id = 0;
58 shutting_down = false; 61 shutting_down = false;
@@ -65,17 +68,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
65 } 68 }
66} 69}
67 70
68void CoreTiming::Shutdown() { 71void CoreTiming::ClearPendingEvents() {
69 paused = true; 72 event_queue.clear();
70 shutting_down = true;
71 pause_event.Set();
72 event.Set();
73 if (timer_thread) {
74 timer_thread->join();
75 }
76 ClearPendingEvents();
77 timer_thread.reset();
78 has_started = false;
79} 73}
80 74
81void CoreTiming::Pause(bool is_paused) { 75void CoreTiming::Pause(bool is_paused) {
@@ -196,10 +190,6 @@ u64 CoreTiming::GetClockTicks() const {
196 return CpuCyclesToClockCycles(ticks); 190 return CpuCyclesToClockCycles(ticks);
197} 191}
198 192
199void CoreTiming::ClearPendingEvents() {
200 event_queue.clear();
201}
202
203void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { 193void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
204 std::scoped_lock lock{basic_lock}; 194 std::scoped_lock lock{basic_lock};
205 195
@@ -270,6 +260,7 @@ void CoreTiming::ThreadLoop() {
270 // There are more events left in the queue, wait until the next event. 260 // There are more events left in the queue, wait until the next event.
271 const auto wait_time = *next_time - GetGlobalTimeNs().count(); 261 const auto wait_time = *next_time - GetGlobalTimeNs().count();
272 if (wait_time > 0) { 262 if (wait_time > 0) {
263#ifdef _WIN32
273 // Assume a timer resolution of 1ms. 264 // Assume a timer resolution of 1ms.
274 static constexpr s64 TimerResolutionNS = 1000000; 265 static constexpr s64 TimerResolutionNS = 1000000;
275 266
@@ -287,6 +278,9 @@ void CoreTiming::ThreadLoop() {
287 if (event.IsSet()) { 278 if (event.IsSet()) {
288 event.Reset(); 279 event.Reset();
289 } 280 }
281#else
282 event.WaitFor(std::chrono::nanoseconds(wait_time));
283#endif
290 } 284 }
291 } else { 285 } else {
292 // Queue is empty, wait until another event is scheduled and signals us to continue. 286 // Queue is empty, wait until another event is scheduled and signals us to continue.
@@ -303,6 +297,18 @@ void CoreTiming::ThreadLoop() {
303 } 297 }
304} 298}
305 299
300void CoreTiming::Reset() {
301 paused = true;
302 shutting_down = true;
303 pause_event.Set();
304 event.Set();
305 if (timer_thread) {
306 timer_thread->join();
307 }
308 timer_thread.reset();
309 has_started = false;
310}
311
306std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { 312std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
307 if (is_multicore) { 313 if (is_multicore) {
308 return clock->GetTimeNS(); 314 return clock->GetTimeNS();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 3259397b2..b5925193c 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -61,19 +61,14 @@ public:
61 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. 61 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
62 void Initialize(std::function<void()>&& on_thread_init_); 62 void Initialize(std::function<void()>&& on_thread_init_);
63 63
64 /// Tears down all timing related functionality. 64 /// Clear all pending events. This should ONLY be done on exit.
65 void Shutdown(); 65 void ClearPendingEvents();
66 66
67 /// Sets if emulation is multicore or single core, must be set before Initialize 67 /// Sets if emulation is multicore or single core, must be set before Initialize
68 void SetMulticore(bool is_multicore_) { 68 void SetMulticore(bool is_multicore_) {
69 is_multicore = is_multicore_; 69 is_multicore = is_multicore_;
70 } 70 }
71 71
72 /// Check if it's using host timing.
73 bool IsHostTiming() const {
74 return is_multicore;
75 }
76
77 /// Pauses/Unpauses the execution of the timer thread. 72 /// Pauses/Unpauses the execution of the timer thread.
78 void Pause(bool is_paused); 73 void Pause(bool is_paused);
79 74
@@ -136,12 +131,11 @@ public:
136private: 131private:
137 struct Event; 132 struct Event;
138 133
139 /// Clear all pending events. This should ONLY be done on exit.
140 void ClearPendingEvents();
141
142 static void ThreadEntry(CoreTiming& instance); 134 static void ThreadEntry(CoreTiming& instance);
143 void ThreadLoop(); 135 void ThreadLoop();
144 136
137 void Reset();
138
145 std::unique_ptr<Common::WallClock> clock; 139 std::unique_ptr<Common::WallClock> clock;
146 140
147 s64 global_timer = 0; 141 s64 global_timer = 0;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 9b1565ae1..0dd4c2196 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -189,9 +189,9 @@ void CpuManager::RunThread(std::size_t core) {
189 system.RegisterCoreThread(core); 189 system.RegisterCoreThread(core);
190 std::string name; 190 std::string name;
191 if (is_multicore) { 191 if (is_multicore) {
192 name = "yuzu:CPUCore_" + std::to_string(core); 192 name = "CPUCore_" + std::to_string(core);
193 } else { 193 } else {
194 name = "yuzu:CPUThread"; 194 name = "CPUThread";
195 } 195 }
196 MicroProfileOnThreadCreate(name.c_str()); 196 MicroProfileOnThreadCreate(name.c_str());
197 Common::SetCurrentThreadName(name.c_str()); 197 Common::SetCurrentThreadName(name.c_str());
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index e42bdd17d..339f971e6 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -140,7 +140,7 @@ private:
140 } 140 }
141 141
142 void ThreadLoop(std::stop_token stop_token) { 142 void ThreadLoop(std::stop_token stop_token) {
143 Common::SetCurrentThreadName("yuzu:Debugger"); 143 Common::SetCurrentThreadName("Debugger");
144 144
145 // Set up the client signals for new data. 145 // Set up the client signals for new data.
146 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); 146 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
diff --git a/src/core/device_memory.h b/src/core/device_memory.h
index df61b0c0b..90510733c 100644
--- a/src/core/device_memory.h
+++ b/src/core/device_memory.h
@@ -31,12 +31,14 @@ public:
31 DramMemoryMap::Base; 31 DramMemoryMap::Base;
32 } 32 }
33 33
34 u8* GetPointer(PAddr addr) { 34 template <typename T>
35 return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); 35 T* GetPointer(PAddr addr) {
36 return reinterpret_cast<T*>(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base));
36 } 37 }
37 38
38 const u8* GetPointer(PAddr addr) const { 39 template <typename T>
39 return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); 40 const T* GetPointer(PAddr addr) const {
41 return reinterpret_cast<T*>(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base));
40 } 42 }
41 43
42 Common::HostMemory buffer; 44 Common::HostMemory buffer;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index f23d9373b..5d02865f4 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -232,8 +232,8 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
232 232
233std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { 233std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
234 const auto program_id = secure_partition->GetProgramTitleID(); 234 const auto program_id = secure_partition->GetProgramTitleID();
235 const auto iter = std::find_if( 235 const auto iter =
236 ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) { 236 std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr<NCA>& nca) {
237 return nca->GetType() == type && nca->GetTitleId() == program_id; 237 return nca->GetType() == type && nca->GetTitleId() == program_id;
238 }); 238 });
239 return iter == ncas.end() ? nullptr : *iter; 239 return iter == ncas.end() ? nullptr : *iter;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index be25da2f6..50f44f598 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/settings.h"
4#include "common/string_util.h" 5#include "common/string_util.h"
5#include "common/swap.h" 6#include "common/swap.h"
6#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
@@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const {
37 developer_name.size()); 38 developer_name.size());
38} 39}
39 40
41constexpr std::array<Language, 18> language_to_codes = {{
42 Language::Japanese,
43 Language::AmericanEnglish,
44 Language::French,
45 Language::German,
46 Language::Italian,
47 Language::Spanish,
48 Language::Chinese,
49 Language::Korean,
50 Language::Dutch,
51 Language::Portuguese,
52 Language::Russian,
53 Language::Taiwanese,
54 Language::BritishEnglish,
55 Language::CanadianFrench,
56 Language::LatinAmericanSpanish,
57 Language::Chinese,
58 Language::Taiwanese,
59 Language::BrazilianPortuguese,
60}};
61
40NACP::NACP() = default; 62NACP::NACP() = default;
41 63
42NACP::NACP(VirtualFile file) { 64NACP::NACP(VirtualFile file) {
@@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) {
45 67
46NACP::~NACP() = default; 68NACP::~NACP() = default;
47 69
48const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 70const LanguageEntry& NACP::GetLanguageEntry() const {
49 if (language != Language::Default) { 71 Language language = language_to_codes[Settings::values.language_index.GetValue()];
50 return raw.language_entries.at(static_cast<u8>(language)); 72
73 {
74 const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));
75 if (!language_entry.GetApplicationName().empty())
76 return language_entry;
51 } 77 }
52 78
53 for (const auto& language_entry : raw.language_entries) { 79 for (const auto& language_entry : raw.language_entries) {
@@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
55 return language_entry; 81 return language_entry;
56 } 82 }
57 83
58 // Fallback to English 84 return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish));
59 return GetLanguageEntry(Language::AmericanEnglish);
60} 85}
61 86
62std::string NACP::GetApplicationName(Language language) const { 87std::string NACP::GetApplicationName() const {
63 return GetLanguageEntry(language).GetApplicationName(); 88 return GetLanguageEntry().GetApplicationName();
64} 89}
65 90
66std::string NACP::GetDeveloperName(Language language) const { 91std::string NACP::GetDeveloperName() const {
67 return GetLanguageEntry(language).GetDeveloperName(); 92 return GetLanguageEntry().GetDeveloperName();
68} 93}
69 94
70u64 NACP::GetTitleId() const { 95u64 NACP::GetTitleId() const {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 75295519c..6a81873b1 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -101,9 +101,9 @@ public:
101 explicit NACP(VirtualFile file); 101 explicit NACP(VirtualFile file);
102 ~NACP(); 102 ~NACP();
103 103
104 const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; 104 const LanguageEntry& GetLanguageEntry() const;
105 std::string GetApplicationName(Language language = Language::Default) const; 105 std::string GetApplicationName() const;
106 std::string GetDeveloperName(Language language = Language::Default) const; 106 std::string GetDeveloperName() const;
107 u64 GetTitleId() const; 107 u64 GetTitleId() const;
108 u64 GetDLCBaseTitleId() const; 108 u64 GetDLCBaseTitleId() const;
109 std::string GetVersionString() const; 109 std::string GetVersionString() const;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index e0cdf3520..f00479bd3 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -33,11 +33,55 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
33 return Loader::ResultStatus::ErrorBadACIHeader; 33 return Loader::ResultStatus::ErrorBadACIHeader;
34 } 34 }
35 35
36 if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) { 36 // Load acid_file_access per-component instead of the entire struct, since this struct does not
37 // reflect the layout of the real data.
38 std::size_t current_offset = acid_header.fac_offset;
39 if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version,
40 sizeof(FileAccessControl::version),
41 current_offset)) {
42 return Loader::ResultStatus::ErrorBadFileAccessControl;
43 }
44 if (sizeof(FileAccessControl::permissions) !=
45 file->ReadBytes(&acid_file_access.permissions, sizeof(FileAccessControl::permissions),
46 current_offset += sizeof(FileAccessControl::version) + 3)) {
47 return Loader::ResultStatus::ErrorBadFileAccessControl;
48 }
49 if (sizeof(FileAccessControl::unknown) !=
50 file->ReadBytes(&acid_file_access.unknown, sizeof(FileAccessControl::unknown),
51 current_offset + sizeof(FileAccessControl::permissions))) {
37 return Loader::ResultStatus::ErrorBadFileAccessControl; 52 return Loader::ResultStatus::ErrorBadFileAccessControl;
38 } 53 }
39 54
40 if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) { 55 // Load aci_file_access per-component instead of the entire struct, same as acid_file_access
56 current_offset = aci_header.fah_offset;
57 if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version,
58 sizeof(FileAccessHeader::version),
59 current_offset)) {
60 return Loader::ResultStatus::ErrorBadFileAccessHeader;
61 }
62 if (sizeof(FileAccessHeader::permissions) !=
63 file->ReadBytes(&aci_file_access.permissions, sizeof(FileAccessHeader::permissions),
64 current_offset += sizeof(FileAccessHeader::version) + 3)) {
65 return Loader::ResultStatus::ErrorBadFileAccessHeader;
66 }
67 if (sizeof(FileAccessHeader::unk_offset) !=
68 file->ReadBytes(&aci_file_access.unk_offset, sizeof(FileAccessHeader::unk_offset),
69 current_offset += sizeof(FileAccessHeader::permissions))) {
70 return Loader::ResultStatus::ErrorBadFileAccessHeader;
71 }
72 if (sizeof(FileAccessHeader::unk_size) !=
73 file->ReadBytes(&aci_file_access.unk_size, sizeof(FileAccessHeader::unk_size),
74 current_offset += sizeof(FileAccessHeader::unk_offset))) {
75 return Loader::ResultStatus::ErrorBadFileAccessHeader;
76 }
77 if (sizeof(FileAccessHeader::unk_offset_2) !=
78 file->ReadBytes(&aci_file_access.unk_offset_2, sizeof(FileAccessHeader::unk_offset_2),
79 current_offset += sizeof(FileAccessHeader::unk_size))) {
80 return Loader::ResultStatus::ErrorBadFileAccessHeader;
81 }
82 if (sizeof(FileAccessHeader::unk_size_2) !=
83 file->ReadBytes(&aci_file_access.unk_size_2, sizeof(FileAccessHeader::unk_size_2),
84 current_offset + sizeof(FileAccessHeader::unk_offset_2))) {
41 return Loader::ResultStatus::ErrorBadFileAccessHeader; 85 return Loader::ResultStatus::ErrorBadFileAccessHeader;
42 } 86 }
43 87
@@ -83,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
83} 127}
84 128
85bool ProgramMetadata::Is64BitProgram() const { 129bool ProgramMetadata::Is64BitProgram() const {
86 return npdm_header.has_64_bit_instructions; 130 return npdm_header.has_64_bit_instructions.As<bool>();
87} 131}
88 132
89ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { 133ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
@@ -152,9 +196,7 @@ void ProgramMetadata::Print() const {
152 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); 196 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
153 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); 197 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
154 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); 198 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
155 u64_le permissions_l; // local copy to fix alignment error 199 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
156 std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l));
157 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l);
158 200
159 // Begin ACI0 printing (actual perms, unsigned) 201 // Begin ACI0 printing (actual perms, unsigned)
160 LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); 202 LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index e8fb4e27f..2e8960b07 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -144,20 +144,18 @@ private:
144 144
145 static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong"); 145 static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
146 146
147#pragma pack(push, 1) 147 // FileAccessControl and FileAccessHeader need loaded per-component: this layout does not
148 148 // reflect the real layout to avoid reference binding to misaligned addresses
149 struct FileAccessControl { 149 struct FileAccessControl {
150 u8 version; 150 u8 version;
151 INSERT_PADDING_BYTES(3); 151 // 3 padding bytes
152 u64_le permissions; 152 u64_le permissions;
153 std::array<u8, 0x20> unknown; 153 std::array<u8, 0x20> unknown;
154 }; 154 };
155 155
156 static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
157
158 struct FileAccessHeader { 156 struct FileAccessHeader {
159 u8 version; 157 u8 version;
160 INSERT_PADDING_BYTES(3); 158 // 3 padding bytes
161 u64_le permissions; 159 u64_le permissions;
162 u32_le unk_offset; 160 u32_le unk_offset;
163 u32_le unk_size; 161 u32_le unk_size;
@@ -165,10 +163,6 @@ private:
165 u32_le unk_size_2; 163 u32_le unk_size_2;
166 }; 164 };
167 165
168 static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
169
170#pragma pack(pop)
171
172 Header npdm_header; 166 Header npdm_header;
173 AciHeader aci_header; 167 AciHeader aci_header;
174 AcidHeader acid_header; 168 AcidHeader acid_header;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 8c1b2523c..1567da231 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -5,6 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/uuid.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/file_sys/savedata_factory.h" 10#include "core/file_sys/savedata_factory.h"
10#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
@@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA
59 attr.title_id == 0 && attr.save_id == 0); 60 attr.title_id == 0 && attr.save_id == 0);
60} 61}
61 62
63std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
64 u128 user_id) {
65 // Only detect nand user saves.
66 const auto space_id_path = [space_id]() -> std::string_view {
67 switch (space_id) {
68 case SaveDataSpaceId::NandUser:
69 return "/user/save";
70 default:
71 return "";
72 }
73 }();
74
75 if (space_id_path.empty()) {
76 return "";
77 }
78
79 Common::UUID uuid;
80 std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
81
82 // Only detect account/device saves from the future location.
83 switch (type) {
84 case SaveDataType::SaveData:
85 return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
86 case SaveDataType::DeviceSaveData:
87 return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
88 default:
89 return "";
90 }
91}
92
62} // Anonymous namespace 93} // Anonymous namespace
63 94
64std::string SaveDataAttribute::DebugInfo() const { 95std::string SaveDataAttribute::DebugInfo() const {
@@ -82,7 +113,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
82 PrintSaveDataAttributeWarnings(meta); 113 PrintSaveDataAttributeWarnings(meta);
83 114
84 const auto save_directory = 115 const auto save_directory =
85 GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 116 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
86 117
87 auto out = dir->CreateDirectoryRelative(save_directory); 118 auto out = dir->CreateDirectoryRelative(save_directory);
88 119
@@ -99,7 +130,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
99 const SaveDataAttribute& meta) const { 130 const SaveDataAttribute& meta) const {
100 131
101 const auto save_directory = 132 const auto save_directory =
102 GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 133 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
103 134
104 auto out = dir->GetDirectoryRelative(save_directory); 135 auto out = dir->GetDirectoryRelative(save_directory);
105 136
@@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
134 } 165 }
135} 166}
136 167
137std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space, 168std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
138 SaveDataType type, u64 title_id, u128 user_id, 169 SaveDataSpaceId space, SaveDataType type, u64 title_id,
139 u64 save_id) { 170 u128 user_id, u64 save_id) {
140 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 171 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
141 // be interpreted as the title id of the current process. 172 // be interpreted as the title id of the current process.
142 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { 173 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
@@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
145 } 176 }
146 } 177 }
147 178
179 // For compat with a future impl.
180 if (std::string future_path =
181 GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id);
182 !future_path.empty()) {
183 // Check if this location exists, and prefer it over the old.
184 if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) {
185 LOG_INFO(Service_FS, "Using save at new location: {}", future_path);
186 return future_path;
187 }
188 }
189
148 std::string out = GetSaveDataSpaceIdPath(space); 190 std::string out = GetSaveDataSpaceIdPath(space);
149 191
150 switch (type) { 192 switch (type) {
@@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
167 209
168SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, 210SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
169 u128 user_id) const { 211 u128 user_id) const {
170 const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 212 const auto path =
213 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
171 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 214 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
172 215
173 const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME); 216 const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
@@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
185 228
186void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 229void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
187 SaveDataSize new_value) const { 230 SaveDataSize new_value) const {
188 const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 231 const auto path =
232 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
189 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 233 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
190 234
191 const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME); 235 const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index a763b94c8..d3633ef03 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -95,8 +95,8 @@ public:
95 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 95 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
96 96
97 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); 97 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
98 static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type, 98 static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
99 u64 title_id, u128 user_id, u64 save_id); 99 SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
100 100
101 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; 101 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
102 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 102 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 90dd68ff1..b4081fc39 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) {
67 return 3.0f / 4.0f; 67 return 3.0f / 4.0f;
68 case AspectRatio::R21_9: 68 case AspectRatio::R21_9:
69 return 9.0f / 21.0f; 69 return 9.0f / 21.0f;
70 case AspectRatio::R16_10:
71 return 10.0f / 16.0f;
70 case AspectRatio::StretchToWindow: 72 case AspectRatio::StretchToWindow:
71 return window_aspect_ratio; 73 return window_aspect_ratio;
72 default: 74 default:
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 1561d994e..94683b30f 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -27,6 +27,7 @@ enum class AspectRatio {
27 Default, 27 Default,
28 R4_3, 28 R4_3,
29 R21_9, 29 R21_9,
30 R16_10,
30 StretchToWindow, 31 StretchToWindow,
31}; 32};
32 33
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
deleted file mode 100644
index d08cc3315..000000000
--- a/src/core/hardware_interrupt_manager.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_interrupt_manager.h"
7#include "core/hle/service/nvdrv/nvdrv_interface.h"
8#include "core/hle/service/sm/sm.h"
9
10namespace Core::Hardware {
11
12InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
13 gpu_interrupt_event = Core::Timing::CreateEvent(
14 "GPUInterrupt",
15 [this](std::uintptr_t message, u64 time,
16 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
17 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
18 const u32 syncpt = static_cast<u32>(message >> 32);
19 const u32 value = static_cast<u32>(message);
20 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
21 return std::nullopt;
22 });
23}
24
25InterruptManager::~InterruptManager() = default;
26
27void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
28 const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
29 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
30}
31
32} // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
deleted file mode 100644
index 5665c5918..000000000
--- a/src/core/hardware_interrupt_manager.h
+++ /dev/null
@@ -1,32 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Core::Timing {
15struct EventType;
16}
17
18namespace Core::Hardware {
19
20class InterruptManager {
21public:
22 explicit InterruptManager(Core::System& system);
23 ~InterruptManager();
24
25 void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
26
27private:
28 Core::System& system;
29 std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event;
30};
31
32} // namespace Core::Hardware
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 01c43be93..ec1364452 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -93,7 +93,7 @@ void EmulatedController::ReloadFromSettings() {
93 .body = GetNpadColor(player.body_color_left), 93 .body = GetNpadColor(player.body_color_left),
94 .button = GetNpadColor(player.button_color_left), 94 .button = GetNpadColor(player.button_color_left),
95 }; 95 };
96 controller.colors_state.left = { 96 controller.colors_state.right = {
97 .body = GetNpadColor(player.body_color_right), 97 .body = GetNpadColor(player.body_color_right),
98 .button = GetNpadColor(player.button_color_right), 98 .button = GetNpadColor(player.button_color_right),
99 }; 99 };
@@ -131,13 +131,16 @@ void EmulatedController::LoadDevices() {
131 battery_params[RightIndex].Set("battery", true); 131 battery_params[RightIndex].Set("battery", true);
132 132
133 camera_params = Common::ParamPackage{"engine:camera,camera:1"}; 133 camera_params = Common::ParamPackage{"engine:camera,camera:1"};
134 nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
134 135
135 output_params[LeftIndex] = left_joycon; 136 output_params[LeftIndex] = left_joycon;
136 output_params[RightIndex] = right_joycon; 137 output_params[RightIndex] = right_joycon;
137 output_params[2] = camera_params; 138 output_params[2] = camera_params;
139 output_params[3] = nfc_params;
138 output_params[LeftIndex].Set("output", true); 140 output_params[LeftIndex].Set("output", true);
139 output_params[RightIndex].Set("output", true); 141 output_params[RightIndex].Set("output", true);
140 output_params[2].Set("output", true); 142 output_params[2].Set("output", true);
143 output_params[3].Set("output", true);
141 144
142 LoadTASParams(); 145 LoadTASParams();
143 146
@@ -155,6 +158,7 @@ void EmulatedController::LoadDevices() {
155 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), 158 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
156 Common::Input::CreateDevice<Common::Input::InputDevice>); 159 Common::Input::CreateDevice<Common::Input::InputDevice>);
157 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); 160 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
161 nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
158 std::transform(output_params.begin(), output_params.end(), output_devices.begin(), 162 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
159 Common::Input::CreateDevice<Common::Input::OutputDevice>); 163 Common::Input::CreateDevice<Common::Input::OutputDevice>);
160 164
@@ -284,6 +288,16 @@ void EmulatedController::ReloadInput() {
284 camera_devices->ForceUpdate(); 288 camera_devices->ForceUpdate();
285 } 289 }
286 290
291 if (nfc_devices) {
292 if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
293 nfc_devices->SetCallback({
294 .on_change =
295 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
296 });
297 nfc_devices->ForceUpdate();
298 }
299 }
300
287 // Use a common UUID for TAS 301 // Use a common UUID for TAS
288 static constexpr Common::UUID TAS_UUID = Common::UUID{ 302 static constexpr Common::UUID TAS_UUID = Common::UUID{
289 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 303 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -339,6 +353,8 @@ void EmulatedController::UnloadInput() {
339 for (auto& stick : tas_stick_devices) { 353 for (auto& stick : tas_stick_devices) {
340 stick.reset(); 354 stick.reset();
341 } 355 }
356 camera_devices.reset();
357 nfc_devices.reset();
342} 358}
343 359
344void EmulatedController::EnableConfiguration() { 360void EmulatedController::EnableConfiguration() {
@@ -903,6 +919,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
903 TriggerOnChange(ControllerTriggerType::IrSensor, true); 919 TriggerOnChange(ControllerTriggerType::IrSensor, true);
904} 920}
905 921
922void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
923 std::unique_lock lock{mutex};
924 controller.nfc_values = TransformToNfc(callback);
925
926 if (is_configuring) {
927 lock.unlock();
928 TriggerOnChange(ControllerTriggerType::Nfc, false);
929 return;
930 }
931
932 controller.nfc_state = {
933 controller.nfc_values.state,
934 controller.nfc_values.data,
935 };
936
937 lock.unlock();
938 TriggerOnChange(ControllerTriggerType::Nfc, true);
939}
940
906bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 941bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
907 if (device_index >= output_devices.size()) { 942 if (device_index >= output_devices.size()) {
908 return false; 943 return false;
@@ -935,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
935 Common::Input::VibrationError::None; 970 Common::Input::VibrationError::None;
936} 971}
937 972
938bool EmulatedController::TestVibration(std::size_t device_index) { 973bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
939 if (device_index >= output_devices.size()) {
940 return false;
941 }
942 if (!output_devices[device_index]) {
943 return false;
944 }
945
946 const auto player_index = NpadIdTypeToIndex(npad_id_type); 974 const auto player_index = NpadIdTypeToIndex(npad_id_type);
947 const auto& player = Settings::values.players.GetValue()[player_index]; 975 const auto& player = Settings::values.players.GetValue()[player_index];
948 976
@@ -950,37 +978,27 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
950 return false; 978 return false;
951 } 979 }
952 980
953 const Common::Input::VibrationStatus test_vibration = { 981 if (device_index >= output_devices.size()) {
954 .low_amplitude = 0.001f, 982 return false;
955 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, 983 }
956 .high_amplitude = 0.001f,
957 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
958 .type = Common::Input::VibrationAmplificationType::Test,
959 };
960
961 const Common::Input::VibrationStatus zero_vibration = {
962 .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
963 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
964 .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
965 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
966 .type = Common::Input::VibrationAmplificationType::Test,
967 };
968
969 // Send a slight vibration to test for rumble support
970 output_devices[device_index]->SetVibration(test_vibration);
971 984
972 // Wait for about 15ms to ensure the controller is ready for the stop command 985 if (!output_devices[device_index]) {
973 std::this_thread::sleep_for(std::chrono::milliseconds(15)); 986 return false;
987 }
974 988
975 // Stop any vibration and return the result 989 return output_devices[device_index]->IsVibrationEnabled();
976 return output_devices[device_index]->SetVibration(zero_vibration) ==
977 Common::Input::VibrationError::None;
978} 990}
979 991
980bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 992bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
981 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); 993 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
982 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 994 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
983 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; 995 auto& nfc_output_device = output_devices[3];
996
997 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
998 const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
999
1000 return virtual_nfc_result == Common::Input::PollingError::None ||
1001 mapped_nfc_result == Common::Input::PollingError::None;
984} 1002}
985 1003
986bool EmulatedController::SetCameraFormat( 1004bool EmulatedController::SetCameraFormat(
@@ -1000,6 +1018,33 @@ bool EmulatedController::SetCameraFormat(
1000 camera_format)) == Common::Input::CameraError::None; 1018 camera_format)) == Common::Input::CameraError::None;
1001} 1019}
1002 1020
1021bool EmulatedController::HasNfc() const {
1022 const auto& nfc_output_device = output_devices[3];
1023
1024 switch (npad_type) {
1025 case NpadStyleIndex::JoyconRight:
1026 case NpadStyleIndex::JoyconDual:
1027 case NpadStyleIndex::ProController:
1028 case NpadStyleIndex::Handheld:
1029 break;
1030 default:
1031 return false;
1032 }
1033
1034 const bool has_virtual_nfc =
1035 npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld;
1036 const bool is_virtual_nfc_supported =
1037 nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported;
1038
1039 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1040}
1041
1042bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1043 auto& nfc_output_device = output_devices[3];
1044
1045 return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1046}
1047
1003void EmulatedController::SetLedPattern() { 1048void EmulatedController::SetLedPattern() {
1004 for (auto& device : output_devices) { 1049 for (auto& device : output_devices) {
1005 if (!device) { 1050 if (!device) {
@@ -1091,27 +1136,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
1091 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 1136 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1092 switch (type) { 1137 switch (type) {
1093 case NpadStyleIndex::ProController: 1138 case NpadStyleIndex::ProController:
1094 return supported_style_tag.fullkey; 1139 return supported_style_tag.fullkey.As<bool>();
1095 case NpadStyleIndex::Handheld: 1140 case NpadStyleIndex::Handheld:
1096 return supported_style_tag.handheld; 1141 return supported_style_tag.handheld.As<bool>();
1097 case NpadStyleIndex::JoyconDual: 1142 case NpadStyleIndex::JoyconDual:
1098 return supported_style_tag.joycon_dual; 1143 return supported_style_tag.joycon_dual.As<bool>();
1099 case NpadStyleIndex::JoyconLeft: 1144 case NpadStyleIndex::JoyconLeft:
1100 return supported_style_tag.joycon_left; 1145 return supported_style_tag.joycon_left.As<bool>();
1101 case NpadStyleIndex::JoyconRight: 1146 case NpadStyleIndex::JoyconRight:
1102 return supported_style_tag.joycon_right; 1147 return supported_style_tag.joycon_right.As<bool>();
1103 case NpadStyleIndex::GameCube: 1148 case NpadStyleIndex::GameCube:
1104 return supported_style_tag.gamecube; 1149 return supported_style_tag.gamecube.As<bool>();
1105 case NpadStyleIndex::Pokeball: 1150 case NpadStyleIndex::Pokeball:
1106 return supported_style_tag.palma; 1151 return supported_style_tag.palma.As<bool>();
1107 case NpadStyleIndex::NES: 1152 case NpadStyleIndex::NES:
1108 return supported_style_tag.lark; 1153 return supported_style_tag.lark.As<bool>();
1109 case NpadStyleIndex::SNES: 1154 case NpadStyleIndex::SNES:
1110 return supported_style_tag.lucia; 1155 return supported_style_tag.lucia.As<bool>();
1111 case NpadStyleIndex::N64: 1156 case NpadStyleIndex::N64:
1112 return supported_style_tag.lagoon; 1157 return supported_style_tag.lagoon.As<bool>();
1113 case NpadStyleIndex::SegaGenesis: 1158 case NpadStyleIndex::SegaGenesis:
1114 return supported_style_tag.lager; 1159 return supported_style_tag.lager.As<bool>();
1115 default: 1160 default:
1116 return false; 1161 return false;
1117 } 1162 }
@@ -1167,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const {
1167 return is_connected; 1212 return is_connected;
1168} 1213}
1169 1214
1170bool EmulatedController::IsVibrationEnabled() const {
1171 const auto player_index = NpadIdTypeToIndex(npad_id_type);
1172 const auto& player = Settings::values.players.GetValue()[player_index];
1173 return player.vibration_enabled;
1174}
1175
1176NpadIdType EmulatedController::GetNpadIdType() const { 1215NpadIdType EmulatedController::GetNpadIdType() const {
1177 std::scoped_lock lock{mutex}; 1216 std::scoped_lock lock{mutex};
1178 return npad_id_type; 1217 return npad_id_type;
@@ -1363,6 +1402,11 @@ const CameraState& EmulatedController::GetCamera() const {
1363 return controller.camera_state; 1402 return controller.camera_state;
1364} 1403}
1365 1404
1405const NfcState& EmulatedController::GetNfc() const {
1406 std::scoped_lock lock{mutex};
1407 return controller.nfc_state;
1408}
1409
1366NpadColor EmulatedController::GetNpadColor(u32 color) { 1410NpadColor EmulatedController::GetNpadColor(u32 color) {
1367 return { 1411 return {
1368 .r = static_cast<u8>((color >> 16) & 0xFF), 1412 .r = static_cast<u8>((color >> 16) & 0xFF),
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index c3aa8f9d3..d004ca56a 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -20,7 +20,7 @@
20 20
21namespace Core::HID { 21namespace Core::HID {
22const std::size_t max_emulated_controllers = 2; 22const std::size_t max_emulated_controllers = 2;
23const std::size_t output_devices = 3; 23const std::size_t output_devices_size = 4;
24struct ControllerMotionInfo { 24struct ControllerMotionInfo {
25 Common::Input::MotionStatus raw_status{}; 25 Common::Input::MotionStatus raw_status{};
26 MotionInput emulated{}; 26 MotionInput emulated{};
@@ -37,7 +37,8 @@ using TriggerDevices =
37using BatteryDevices = 37using BatteryDevices =
38 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 38 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
39using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; 39using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
40using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; 40using NfcDevices = std::unique_ptr<Common::Input::InputDevice>;
41using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
41 42
42using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 43using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
43using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 44using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
@@ -45,7 +46,8 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native
45using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 46using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
46using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 47using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
47using CameraParams = Common::ParamPackage; 48using CameraParams = Common::ParamPackage;
48using OutputParams = std::array<Common::ParamPackage, output_devices>; 49using NfcParams = Common::ParamPackage;
50using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
49 51
50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 52using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; 53using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -55,6 +57,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 57using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 58using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using CameraValues = Common::Input::CameraStatus; 59using CameraValues = Common::Input::CameraStatus;
60using NfcValues = Common::Input::NfcStatus;
58using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 61using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
59 62
60struct AnalogSticks { 63struct AnalogSticks {
@@ -80,6 +83,11 @@ struct CameraState {
80 std::size_t sample{}; 83 std::size_t sample{};
81}; 84};
82 85
86struct NfcState {
87 Common::Input::NfcState state{};
88 std::vector<u8> data{};
89};
90
83struct ControllerMotion { 91struct ControllerMotion {
84 Common::Vec3f accel{}; 92 Common::Vec3f accel{};
85 Common::Vec3f gyro{}; 93 Common::Vec3f gyro{};
@@ -107,6 +115,7 @@ struct ControllerStatus {
107 BatteryValues battery_values{}; 115 BatteryValues battery_values{};
108 VibrationValues vibration_values{}; 116 VibrationValues vibration_values{};
109 CameraValues camera_values{}; 117 CameraValues camera_values{};
118 NfcValues nfc_values{};
110 119
111 // Data for HID serices 120 // Data for HID serices
112 HomeButtonState home_button_state{}; 121 HomeButtonState home_button_state{};
@@ -119,6 +128,7 @@ struct ControllerStatus {
119 ControllerColors colors_state{}; 128 ControllerColors colors_state{};
120 BatteryLevelState battery_state{}; 129 BatteryLevelState battery_state{};
121 CameraState camera_state{}; 130 CameraState camera_state{};
131 NfcState nfc_state{};
122}; 132};
123 133
124enum class ControllerTriggerType { 134enum class ControllerTriggerType {
@@ -130,6 +140,7 @@ enum class ControllerTriggerType {
130 Battery, 140 Battery,
131 Vibration, 141 Vibration,
132 IrSensor, 142 IrSensor,
143 Nfc,
133 Connected, 144 Connected,
134 Disconnected, 145 Disconnected,
135 Type, 146 Type,
@@ -195,9 +206,6 @@ public:
195 */ 206 */
196 bool IsConnected(bool get_temporary_value = false) const; 207 bool IsConnected(bool get_temporary_value = false) const;
197 208
198 /// Returns true if vibration is enabled
199 bool IsVibrationEnabled() const;
200
201 /// Removes all callbacks created from input devices 209 /// Removes all callbacks created from input devices
202 void UnloadInput(); 210 void UnloadInput();
203 211
@@ -315,6 +323,9 @@ public:
315 /// Returns the latest camera status from the controller 323 /// Returns the latest camera status from the controller
316 const CameraState& GetCamera() const; 324 const CameraState& GetCamera() const;
317 325
326 /// Returns the latest ntag status from the controller
327 const NfcState& GetNfc() const;
328
318 /** 329 /**
319 * Sends a specific vibration to the output device 330 * Sends a specific vibration to the output device
320 * @return true if vibration had no errors 331 * @return true if vibration had no errors
@@ -325,7 +336,7 @@ public:
325 * Sends a small vibration to the output device 336 * Sends a small vibration to the output device
326 * @return true if SetVibration was successfull 337 * @return true if SetVibration was successfull
327 */ 338 */
328 bool TestVibration(std::size_t device_index); 339 bool IsVibrationEnabled(std::size_t device_index);
329 340
330 /** 341 /**
331 * Sets the desired data to be polled from a controller 342 * Sets the desired data to be polled from a controller
@@ -341,6 +352,12 @@ public:
341 */ 352 */
342 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); 353 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
343 354
355 /// Returns true if the device has nfc support
356 bool HasNfc() const;
357
358 /// Returns true if the nfc tag was written
359 bool WriteNfc(const std::vector<u8>& data);
360
344 /// Returns the led pattern corresponding to this emulated controller 361 /// Returns the led pattern corresponding to this emulated controller
345 LedPattern GetLedPattern() const; 362 LedPattern GetLedPattern() const;
346 363
@@ -425,6 +442,12 @@ private:
425 void SetCamera(const Common::Input::CallbackStatus& callback); 442 void SetCamera(const Common::Input::CallbackStatus& callback);
426 443
427 /** 444 /**
445 * Updates the nfc status of the controller
446 * @param callback A CallbackStatus containing the nfc status
447 */
448 void SetNfc(const Common::Input::CallbackStatus& callback);
449
450 /**
428 * Converts a color format from bgra to rgba 451 * Converts a color format from bgra to rgba
429 * @param color in bgra format 452 * @param color in bgra format
430 * @return NpadColor in rgba format 453 * @return NpadColor in rgba format
@@ -458,6 +481,7 @@ private:
458 TriggerParams trigger_params; 481 TriggerParams trigger_params;
459 BatteryParams battery_params; 482 BatteryParams battery_params;
460 CameraParams camera_params; 483 CameraParams camera_params;
484 NfcParams nfc_params;
461 OutputParams output_params; 485 OutputParams output_params;
462 486
463 ButtonDevices button_devices; 487 ButtonDevices button_devices;
@@ -466,6 +490,7 @@ private:
466 TriggerDevices trigger_devices; 490 TriggerDevices trigger_devices;
467 BatteryDevices battery_devices; 491 BatteryDevices battery_devices;
468 CameraDevices camera_devices; 492 CameraDevices camera_devices;
493 NfcDevices nfc_devices;
469 OutputDevices output_devices; 494 OutputDevices output_devices;
470 495
471 // TAS related variables 496 // TAS related variables
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 52fb69e9c..5d8b75b50 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -277,7 +277,10 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
277 Common::Input::CameraStatus camera{}; 277 Common::Input::CameraStatus camera{};
278 switch (callback.type) { 278 switch (callback.type) {
279 case Common::Input::InputType::IrSensor: 279 case Common::Input::InputType::IrSensor:
280 camera = callback.camera_status; 280 camera = {
281 .format = callback.camera_status,
282 .data = callback.raw_data,
283 };
281 break; 284 break;
282 default: 285 default:
283 LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); 286 LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
@@ -287,6 +290,23 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
287 return camera; 290 return camera;
288} 291}
289 292
293Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
294 Common::Input::NfcStatus nfc{};
295 switch (callback.type) {
296 case Common::Input::InputType::Nfc:
297 nfc = {
298 .state = callback.nfc_status,
299 .data = callback.raw_data,
300 };
301 break;
302 default:
303 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
304 break;
305 }
306
307 return nfc;
308}
309
290void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 310void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
291 const auto& properties = analog.properties; 311 const auto& properties = analog.properties;
292 float& raw_value = analog.raw_value; 312 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 143c50cc0..b7eb6e660 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -85,6 +85,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); 85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
86 86
87/** 87/**
88 * Converts raw input data into a valid nfc status.
89 *
90 * @param callback Supported callbacks: Nfc.
91 * @return A valid CameraObject object.
92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94
95/**
88 * Converts raw analog data into a valid analog value 96 * Converts raw analog data into a valid analog value
89 * @param analog An analog object containing raw data and properties 97 * @param analog An analog object containing raw data and properties
90 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 98 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
index 88c5b016d..0d1bfe53f 100644
--- a/src/core/hid/irs_types.h
+++ b/src/core/hid/irs_types.h
@@ -14,7 +14,7 @@ enum class CameraAmbientNoiseLevel : u32 {
14 Low, 14 Low,
15 Medium, 15 Medium,
16 High, 16 High,
17 Unkown3, // This level can't be reached 17 Unknown3, // This level can't be reached
18}; 18};
19 19
20// This is nn::irsensor::CameraLightTarget 20// This is nn::irsensor::CameraLightTarget
@@ -75,9 +75,9 @@ enum class IrCameraStatus : u32 {
75enum class IrCameraInternalStatus : u32 { 75enum class IrCameraInternalStatus : u32 {
76 Stopped, 76 Stopped,
77 FirmwareUpdateNeeded, 77 FirmwareUpdateNeeded,
78 Unkown2, 78 Unknown2,
79 Unkown3, 79 Unknown3,
80 Unkown4, 80 Unknown4,
81 FirmwareVersionRequested, 81 FirmwareVersionRequested,
82 FirmwareVersionIsInvalid, 82 FirmwareVersionIsInvalid,
83 Ready, 83 Ready,
@@ -121,20 +121,20 @@ enum class IrSensorFunctionLevel : u8 {
121 121
122// This is nn::irsensor::MomentProcessorPreprocess 122// This is nn::irsensor::MomentProcessorPreprocess
123enum class MomentProcessorPreprocess : u32 { 123enum class MomentProcessorPreprocess : u32 {
124 Unkown0, 124 Unknown0,
125 Unkown1, 125 Unknown1,
126}; 126};
127 127
128// This is nn::irsensor::PackedMomentProcessorPreprocess 128// This is nn::irsensor::PackedMomentProcessorPreprocess
129enum class PackedMomentProcessorPreprocess : u8 { 129enum class PackedMomentProcessorPreprocess : u8 {
130 Unkown0, 130 Unknown0,
131 Unkown1, 131 Unknown1,
132}; 132};
133 133
134// This is nn::irsensor::PointingStatus 134// This is nn::irsensor::PointingStatus
135enum class PointingStatus : u32 { 135enum class PointingStatus : u32 {
136 Unkown0, 136 Unknown0,
137 Unkown1, 137 Unknown1,
138}; 138};
139 139
140struct IrsRect { 140struct IrsRect {
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index d631c0357..3bb111748 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -86,13 +86,13 @@ public:
86 u32 num_domain_objects{}; 86 u32 num_domain_objects{};
87 const bool always_move_handles{ 87 const bool always_move_handles{
88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; 88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
89 if (!ctx.Session()->IsDomain() || always_move_handles) { 89 if (!ctx.GetManager()->IsDomain() || always_move_handles) {
90 num_handles_to_move = num_objects_to_move; 90 num_handles_to_move = num_objects_to_move;
91 } else { 91 } else {
92 num_domain_objects = num_objects_to_move; 92 num_domain_objects = num_objects_to_move;
93 } 93 }
94 94
95 if (ctx.Session()->IsDomain()) { 95 if (ctx.GetManager()->IsDomain()) {
96 raw_data_size += 96 raw_data_size +=
97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); 97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
98 ctx.write_size += num_domain_objects; 98 ctx.write_size += num_domain_objects;
@@ -125,7 +125,7 @@ public:
125 if (!ctx.IsTipc()) { 125 if (!ctx.IsTipc()) {
126 AlignWithPadding(); 126 AlignWithPadding();
127 127
128 if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) { 128 if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
129 IPC::DomainMessageHeader domain_header{}; 129 IPC::DomainMessageHeader domain_header{};
130 domain_header.num_objects = num_domain_objects; 130 domain_header.num_objects = num_domain_objects;
131 PushRaw(domain_header); 131 PushRaw(domain_header);
@@ -145,7 +145,7 @@ public:
145 145
146 template <class T> 146 template <class T>
147 void PushIpcInterface(std::shared_ptr<T> iface) { 147 void PushIpcInterface(std::shared_ptr<T> iface) {
148 if (context->Session()->IsDomain()) { 148 if (context->GetManager()->IsDomain()) {
149 context->AddDomainObject(std::move(iface)); 149 context->AddDomainObject(std::move(iface));
150 } else { 150 } else {
151 kernel.CurrentProcess()->GetResourceLimit()->Reserve( 151 kernel.CurrentProcess()->GetResourceLimit()->Reserve(
@@ -153,9 +153,10 @@ public:
153 153
154 auto* session = Kernel::KSession::Create(kernel); 154 auto* session = Kernel::KSession::Create(kernel);
155 session->Initialize(nullptr, iface->GetServiceName()); 155 session->Initialize(nullptr, iface->GetServiceName());
156 iface->RegisterSession(&session->GetServerSession(),
157 std::make_shared<Kernel::SessionRequestManager>(kernel));
156 158
157 context->AddMoveObject(&session->GetClientSession()); 159 context->AddMoveObject(&session->GetClientSession());
158 iface->ClientConnected(&session->GetServerSession());
159 } 160 }
160 } 161 }
161 162
@@ -385,7 +386,7 @@ public:
385 386
386 template <class T> 387 template <class T>
387 std::weak_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
388 ASSERT(context->Session()->IsDomain()); 389 ASSERT(context->GetManager()->IsDomain());
389 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
390 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
391 } 392 }
@@ -404,7 +405,7 @@ inline s32 RequestParser::Pop() {
404} 405}
405 406
406// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. 407// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
407#if defined(__GNUC__) 408#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
408#pragma GCC diagnostic push 409#pragma GCC diagnostic push
409#pragma GCC diagnostic ignored "-Wclass-memaccess" 410#pragma GCC diagnostic ignored "-Wclass-memaccess"
410#endif 411#endif
@@ -415,7 +416,7 @@ void RequestParser::PopRaw(T& value) {
415 std::memcpy(&value, cmdbuf + index, sizeof(T)); 416 std::memcpy(&value, cmdbuf + index, sizeof(T));
416 index += (sizeof(T) + 3) / 4; // round up to word length 417 index += (sizeof(T) + 3) / 4; // round up to word length
417} 418}
418#if defined(__GNUC__) 419#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
419#pragma GCC diagnostic pop 420#pragma GCC diagnostic pop
420#endif 421#endif
421 422
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 65576b8c4..fd911a3a5 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const {
49 return scheduler_lock.IsLockedByCurrentThread(); 49 return scheduler_lock.IsLockedByCurrentThread();
50} 50}
51 51
52void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
53 ASSERT(IsLocked());
54
55 woken_dummy_threads.insert(thread);
56}
57
58void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
59 ASSERT(IsLocked());
60
61 woken_dummy_threads.erase(thread);
62}
63
64void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
65 ASSERT(IsLocked());
66
67 for (auto* thread : woken_dummy_threads) {
68 thread->DummyThreadEndWait();
69 }
70
71 woken_dummy_threads.clear();
72}
73
52} // namespace Kernel 74} // namespace Kernel
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 67bb9852d..220ed6192 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <atomic> 6#include <atomic>
7#include <set>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -58,6 +59,10 @@ public:
58 /// Returns true if the global scheduler lock is acquired 59 /// Returns true if the global scheduler lock is acquired
59 bool IsLocked() const; 60 bool IsLocked() const;
60 61
62 void UnregisterDummyThreadForWakeup(KThread* thread);
63 void RegisterDummyThreadForWakeup(KThread* thread);
64 void WakeupWaitingDummyThreads();
65
61 [[nodiscard]] LockType& SchedulerLock() { 66 [[nodiscard]] LockType& SchedulerLock() {
62 return scheduler_lock; 67 return scheduler_lock;
63 } 68 }
@@ -76,6 +81,9 @@ private:
76 KSchedulerPriorityQueue priority_queue; 81 KSchedulerPriorityQueue priority_queue;
77 LockType scheduler_lock; 82 LockType scheduler_lock;
78 83
84 /// Lists dummy threads pending wakeup on lock release
85 std::set<KThread*> woken_dummy_threads;
86
79 /// Lists all thread ids that aren't deleted/etc. 87 /// Lists all thread ids that aren't deleted/etc.
80 std::vector<KThread*> thread_list; 88 std::vector<KThread*> thread_list;
81 std::mutex global_list_guard; 89 std::mutex global_list_guard;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5b3feec66..fd354d484 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -16,9 +16,11 @@
16#include "core/hle/kernel/k_auto_object.h" 16#include "core/hle/kernel/k_auto_object.h"
17#include "core/hle/kernel/k_handle_table.h" 17#include "core/hle/kernel/k_handle_table.h"
18#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
19#include "core/hle/kernel/k_server_port.h"
19#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
20#include "core/hle/kernel/k_thread.h" 21#include "core/hle/kernel/k_thread.h"
21#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/service_thread.h"
22#include "core/memory.h" 24#include "core/memory.h"
23 25
24namespace Kernel { 26namespace Kernel {
@@ -34,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se
34} 36}
35 37
36SessionRequestHandler::~SessionRequestHandler() { 38SessionRequestHandler::~SessionRequestHandler() {
37 kernel.ReleaseServiceThread(service_thread); 39 kernel.ReleaseServiceThread(service_thread.lock());
40}
41
42void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
43 auto* server_session = server_port->AcceptSession();
44 ASSERT(server_session != nullptr);
45
46 RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
47}
48
49void SessionRequestHandler::RegisterSession(KServerSession* server_session,
50 std::shared_ptr<SessionRequestManager> manager) {
51 manager->SetSessionHandler(shared_from_this());
52 service_thread.lock()->RegisterServerSession(server_session, manager);
53 server_session->Close();
38} 54}
39 55
40SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} 56SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
@@ -56,15 +72,77 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
56 } 72 }
57} 73}
58 74
59void SessionRequestHandler::ClientConnected(KServerSession* session) { 75Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session,
60 session->ClientConnected(shared_from_this()); 76 HLERequestContext& context) {
77 Result result = ResultSuccess;
78
79 // If the session has been converted to a domain, handle the domain request
80 if (this->HasSessionRequestHandler(context)) {
81 if (IsDomain() && context.HasDomainMessageHeader()) {
82 result = HandleDomainSyncRequest(server_session, context);
83 // If there is no domain header, the regular session handler is used
84 } else if (this->HasSessionHandler()) {
85 // If this manager has an associated HLE handler, forward the request to it.
86 result = this->SessionHandler().HandleSyncRequest(*server_session, context);
87 }
88 } else {
89 ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
90 IPC::ResponseBuilder rb(context, 2);
91 rb.Push(ResultSuccess);
92 }
93
94 if (convert_to_domain) {
95 ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
96 this->ConvertToDomain();
97 convert_to_domain = false;
98 }
61 99
62 // Ensure our server session is tracked globally. 100 return result;
63 kernel.RegisterServerObject(session);
64} 101}
65 102
66void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 103Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session,
67 session->ClientDisconnected(); 104 HLERequestContext& context) {
105 if (!context.HasDomainMessageHeader()) {
106 return ResultSuccess;
107 }
108
109 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
110 ASSERT(context.GetManager().get() == this);
111
112 // If there is a DomainMessageHeader, then this is CommandType "Request"
113 const auto& domain_message_header = context.GetDomainMessageHeader();
114 const u32 object_id{domain_message_header.object_id};
115 switch (domain_message_header.command) {
116 case IPC::DomainMessageHeader::CommandType::SendMessage:
117 if (object_id > this->DomainHandlerCount()) {
118 LOG_CRITICAL(IPC,
119 "object_id {} is too big! This probably means a recent service call "
120 "needed to return a new interface!",
121 object_id);
122 ASSERT(false);
123 return ResultSuccess; // Ignore error if asserts are off
124 }
125 if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) {
126 return strong_ptr->HandleSyncRequest(*server_session, context);
127 } else {
128 ASSERT(false);
129 return ResultSuccess;
130 }
131
132 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
133 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
134
135 this->CloseDomainHandler(object_id - 1);
136
137 IPC::ResponseBuilder rb{context, 2};
138 rb.Push(ResultSuccess);
139 return ResultSuccess;
140 }
141 }
142
143 LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
144 ASSERT(false);
145 return ResultSuccess;
68} 146}
69 147
70HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, 148HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
@@ -126,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
126 // Padding to align to 16 bytes 204 // Padding to align to 16 bytes
127 rp.AlignWithPadding(); 205 rp.AlignWithPadding();
128 206
129 if (Session()->IsDomain() && 207 if (GetManager()->IsDomain() &&
130 ((command_header->type == IPC::CommandType::Request || 208 ((command_header->type == IPC::CommandType::Request ||
131 command_header->type == IPC::CommandType::RequestWithContext) || 209 command_header->type == IPC::CommandType::RequestWithContext) ||
132 !incoming)) { 210 !incoming)) {
@@ -135,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
135 if (incoming || domain_message_header) { 213 if (incoming || domain_message_header) {
136 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); 214 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
137 } else { 215 } else {
138 if (Session()->IsDomain()) { 216 if (GetManager()->IsDomain()) {
139 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 217 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
140 } 218 }
141 } 219 }
@@ -228,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
228 // Write the domain objects to the command buffer, these go after the raw untranslated data. 306 // Write the domain objects to the command buffer, these go after the raw untranslated data.
229 // TODO(Subv): This completely ignores C buffers. 307 // TODO(Subv): This completely ignores C buffers.
230 308
231 if (Session()->IsDomain()) { 309 if (GetManager()->IsDomain()) {
232 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); 310 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
233 for (const auto& object : outgoing_domain_objects) { 311 for (auto& object : outgoing_domain_objects) {
234 server_session->AppendDomainHandler(object); 312 GetManager()->AppendDomainHandler(std::move(object));
235 cmd_buf[current_offset++] = 313 cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
236 static_cast<u32_le>(server_session->NumDomainRequestHandlers());
237 } 314 }
238 } 315 }
239 316
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 99265ce90..67da8e7e1 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -43,13 +43,15 @@ class Domain;
43class HLERequestContext; 43class HLERequestContext;
44class KAutoObject; 44class KAutoObject;
45class KernelCore; 45class KernelCore;
46class KEvent;
46class KHandleTable; 47class KHandleTable;
48class KServerPort;
47class KProcess; 49class KProcess;
48class KServerSession; 50class KServerSession;
49class KThread; 51class KThread;
50class KReadableEvent; 52class KReadableEvent;
51class KSession; 53class KSession;
52class KWritableEvent; 54class SessionRequestManager;
53class ServiceThread; 55class ServiceThread;
54 56
55enum class ThreadWakeupReason; 57enum class ThreadWakeupReason;
@@ -76,19 +78,9 @@ public:
76 virtual Result HandleSyncRequest(Kernel::KServerSession& session, 78 virtual Result HandleSyncRequest(Kernel::KServerSession& session,
77 Kernel::HLERequestContext& context) = 0; 79 Kernel::HLERequestContext& context) = 0;
78 80
79 /** 81 void AcceptSession(KServerPort* server_port);
80 * Signals that a client has just connected to this HLE handler and keeps the 82 void RegisterSession(KServerSession* server_session,
81 * associated ServerSession alive for the duration of the connection. 83 std::shared_ptr<SessionRequestManager> manager);
82 * @param server_session Owning pointer to the ServerSession associated with the connection.
83 */
84 void ClientConnected(KServerSession* session);
85
86 /**
87 * Signals that a client has just disconnected from this HLE handler and releases the
88 * associated ServerSession.
89 * @param server_session ServerSession associated with the connection.
90 */
91 void ClientDisconnected(KServerSession* session);
92 84
93 std::weak_ptr<ServiceThread> GetServiceThread() const { 85 std::weak_ptr<ServiceThread> GetServiceThread() const {
94 return service_thread; 86 return service_thread;
@@ -121,6 +113,10 @@ public:
121 is_domain = true; 113 is_domain = true;
122 } 114 }
123 115
116 void ConvertToDomainOnRequestEnd() {
117 convert_to_domain = true;
118 }
119
124 std::size_t DomainHandlerCount() const { 120 std::size_t DomainHandlerCount() const {
125 return domain_handlers.size(); 121 return domain_handlers.size();
126 } 122 }
@@ -164,7 +160,11 @@ public:
164 160
165 bool HasSessionRequestHandler(const HLERequestContext& context) const; 161 bool HasSessionRequestHandler(const HLERequestContext& context) const;
166 162
163 Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
164 Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
165
167private: 166private:
167 bool convert_to_domain{};
168 bool is_domain{}; 168 bool is_domain{};
169 SessionRequestHandlerPtr session_handler; 169 SessionRequestHandlerPtr session_handler;
170 std::vector<SessionRequestHandlerPtr> domain_handlers; 170 std::vector<SessionRequestHandlerPtr> domain_handlers;
@@ -295,7 +295,7 @@ public:
295 */ 295 */
296 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> 296 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
297 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { 297 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
298 if constexpr (Common::IsSTLContainer<T>) { 298 if constexpr (Common::IsContiguousContainer<T>) {
299 using ContiguousType = typename T::value_type; 299 using ContiguousType = typename T::value_type;
300 static_assert(std::is_trivially_copyable_v<ContiguousType>, 300 static_assert(std::is_trivially_copyable_v<ContiguousType>,
301 "Container to WriteBuffer must contain trivially copyable objects"); 301 "Container to WriteBuffer must contain trivially copyable objects");
@@ -341,11 +341,11 @@ public:
341 341
342 template <typename T> 342 template <typename T>
343 std::shared_ptr<T> GetDomainHandler(std::size_t index) const { 343 std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
344 return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); 344 return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
345 } 345 }
346 346
347 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { 347 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
348 manager = std::move(manager_); 348 manager = manager_;
349 } 349 }
350 350
351 std::string Description() const; 351 std::string Description() const;
@@ -354,6 +354,10 @@ public:
354 return *thread; 354 return *thread;
355 } 355 }
356 356
357 std::shared_ptr<SessionRequestManager> GetManager() const {
358 return manager.lock();
359 }
360
357private: 361private:
358 friend class IPC::ResponseBuilder; 362 friend class IPC::ResponseBuilder;
359 363
@@ -387,7 +391,7 @@ private:
387 u32 handles_offset{}; 391 u32 handles_offset{};
388 u32 domain_offset{}; 392 u32 domain_offset{};
389 393
390 std::weak_ptr<SessionRequestManager> manager; 394 std::weak_ptr<SessionRequestManager> manager{};
391 395
392 KernelCore& kernel; 396 KernelCore& kernel;
393 Core::Memory::Memory& memory; 397 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 9b6b284d0..477e4e407 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
19#include "core/hle/kernel/k_resource_limit.h" 19#include "core/hle/kernel/k_resource_limit.h"
20#include "core/hle/kernel/k_session.h" 20#include "core/hle/kernel/k_session.h"
21#include "core/hle/kernel/k_session_request.h"
21#include "core/hle/kernel/k_shared_memory.h" 22#include "core/hle/kernel/k_shared_memory.h"
22#include "core/hle/kernel/k_shared_memory_info.h" 23#include "core/hle/kernel/k_shared_memory_info.h"
23#include "core/hle/kernel/k_system_control.h" 24#include "core/hle/kernel/k_system_control.h"
@@ -34,6 +35,7 @@ namespace Kernel::Init {
34 HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ 35 HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
35 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ 36 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
36 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ 37 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
38 HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
37 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ 39 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
38 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ 40 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
39 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 41 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
@@ -94,8 +96,8 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
94 // TODO(bunnei): Fix this once we support the kernel virtual memory layout. 96 // TODO(bunnei): Fix this once we support the kernel virtual memory layout.
95 97
96 if (size > 0) { 98 if (size > 0) {
97 void* backing_kernel_memory{ 99 void* backing_kernel_memory{system.DeviceMemory().GetPointer<void>(
98 system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; 100 TranslateSlabAddrToPhysical(memory_layout, start))};
99 101
100 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); 102 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
101 ASSERT(region != nullptr); 103 ASSERT(region != nullptr);
@@ -181,7 +183,7 @@ void InitializeKPageBufferSlabHeap(Core::System& system) {
181 ASSERT(slab_address != 0); 183 ASSERT(slab_address != 0);
182 184
183 // Initialize the slabheap. 185 // Initialize the slabheap.
184 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), 186 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
185 slab_size); 187 slab_size);
186} 188}
187 189
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index cc2a0f7ca..10265c23c 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -18,7 +18,6 @@
18#include "core/hle/kernel/k_synchronization_object.h" 18#include "core/hle/kernel/k_synchronization_object.h"
19#include "core/hle/kernel/k_thread.h" 19#include "core/hle/kernel/k_thread.h"
20#include "core/hle/kernel/k_transfer_memory.h" 20#include "core/hle/kernel/k_transfer_memory.h"
21#include "core/hle/kernel/k_writable_event.h"
22 21
23namespace Kernel { 22namespace Kernel {
24 23
@@ -42,13 +41,12 @@ static_assert(ClassToken<KPort> == 0b10000101'00000000);
42static_assert(ClassToken<KSession> == 0b00011001'00000000); 41static_assert(ClassToken<KSession> == 0b00011001'00000000);
43static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000); 42static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
44static_assert(ClassToken<KEvent> == 0b01001001'00000000); 43static_assert(ClassToken<KEvent> == 0b01001001'00000000);
45static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
46// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000); 44// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
47// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000); 45// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
48static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); 46static_assert(ClassToken<KTransferMemory> == 0b01010001'00000000);
49// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); 47// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
50// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); 48// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
51static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); 49static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);
52 50
53// Ensure that the token hierarchy is correct. 51// Ensure that the token hierarchy is correct.
54 52
@@ -73,13 +71,12 @@ static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>)
73static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>)); 71static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
74static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>)); 72static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
75static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>)); 73static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
76static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAutoObject>));
77// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>)); 74// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
78// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>)); 75// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
79static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); 76static_assert(ClassToken<KTransferMemory> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
80// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); 77// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
81// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); 78// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
82static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); 79static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
83 80
84// Ensure that the token hierarchy reflects the class hierarchy. 81// Ensure that the token hierarchy reflects the class hierarchy.
85 82
@@ -110,7 +107,6 @@ static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
110static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>); 107static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
111static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>); 108static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
112static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>); 109static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
113static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>);
114// static_assert(std::is_final_v<KLightClientSession> && 110// static_assert(std::is_final_v<KLightClientSession> &&
115// std::is_base_of_v<KAutoObject, KLightClientSession>); 111// std::is_base_of_v<KAutoObject, KLightClientSession>);
116// static_assert(std::is_final_v<KLightServerSession> && 112// static_assert(std::is_final_v<KLightServerSession> &&
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index c9001ae3d..ab20e00ff 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -101,7 +101,6 @@ public:
101 KSession, 101 KSession,
102 KSharedMemory, 102 KSharedMemory,
103 KEvent, 103 KEvent,
104 KWritableEvent,
105 KLightClientSession, 104 KLightClientSession,
106 KLightServerSession, 105 KLightServerSession,
107 KTransferMemory, 106 KTransferMemory,
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 3cb22ff4d..eaa2e094c 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const {
58 return num_sessions < max_sessions; 58 return num_sessions < max_sessions;
59} 59}
60 60
61Result KClientPort::CreateSession(KClientSession** out, 61Result KClientPort::CreateSession(KClientSession** out) {
62 std::shared_ptr<SessionRequestManager> session_manager) {
63 // Reserve a new session from the resource limit. 62 // Reserve a new session from the resource limit.
64 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), 63 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
65 LimitableResource::Sessions); 64 LimitableResource::Sessions);
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
104 } 103 }
105 104
106 // Initialize the session. 105 // Initialize the session.
107 session->Initialize(this, parent->GetName(), session_manager); 106 session->Initialize(this, parent->GetName());
108 107
109 // Commit the session reservation. 108 // Commit the session reservation.
110 session_reservation.Commit(); 109 session_reservation.Commit();
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index e17eff28f..81046fb86 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -52,8 +52,7 @@ public:
52 void Destroy() override; 52 void Destroy() override;
53 bool IsSignaled() const override; 53 bool IsSignaled() const override;
54 54
55 Result CreateSession(KClientSession** out, 55 Result CreateSession(KClientSession** out);
56 std::shared_ptr<SessionRequestManager> session_manager = nullptr);
57 56
58private: 57private:
59 std::atomic<s32> num_sessions{}; 58 std::atomic<s32> num_sessions{};
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index b2a887b14..b4197a8d5 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/scope_exit.h"
4#include "core/hle/kernel/hle_ipc.h" 5#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/kernel/k_client_session.h" 6#include "core/hle/kernel/k_client_session.h"
6#include "core/hle/kernel/k_server_session.h" 7#include "core/hle/kernel/k_server_session.h"
@@ -10,6 +11,8 @@
10 11
11namespace Kernel { 12namespace Kernel {
12 13
14static constexpr u32 MessageBufferSize = 0x100;
15
13KClientSession::KClientSession(KernelCore& kernel_) 16KClientSession::KClientSession(KernelCore& kernel_)
14 : KAutoObjectWithSlabHeapAndContainer{kernel_} {} 17 : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
15KClientSession::~KClientSession() = default; 18KClientSession::~KClientSession() = default;
@@ -21,10 +24,17 @@ void KClientSession::Destroy() {
21 24
22void KClientSession::OnServerClosed() {} 25void KClientSession::OnServerClosed() {}
23 26
24Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, 27Result KClientSession::SendSyncRequest() {
25 Core::Timing::CoreTiming& core_timing) { 28 // Create a session request.
26 // Signal the server session that new data is available 29 KSessionRequest* request = KSessionRequest::Create(kernel);
27 return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); 30 R_UNLESS(request != nullptr, ResultOutOfResource);
31 SCOPE_EXIT({ request->Close(); });
32
33 // Initialize the request.
34 request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
35
36 // Send the request.
37 return parent->GetServerSession().OnRequest(request);
28} 38}
29 39
30} // namespace Kernel 40} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 0c750d756..b4a19c546 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -46,8 +46,7 @@ public:
46 return parent; 46 return parent;
47 } 47 }
48 48
49 Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, 49 Result SendSyncRequest();
50 Core::Timing::CoreTiming& core_timing);
51 50
52 void OnServerClosed(); 51 void OnServerClosed();
53 52
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index da57ceb21..4b1c134d4 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -34,7 +34,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
34 34
35 // Clear the memory. 35 // Clear the memory.
36 for (const auto& block : m_page_group.Nodes()) { 36 for (const auto& block : m_page_group.Nodes()) {
37 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); 37 std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
38 } 38 }
39 39
40 // Set remaining tracking members. 40 // Set remaining tracking members.
diff --git a/src/core/hle/kernel/k_dynamic_page_manager.h b/src/core/hle/kernel/k_dynamic_page_manager.h
new file mode 100644
index 000000000..9076c8fa3
--- /dev/null
+++ b/src/core/hle/kernel/k_dynamic_page_manager.h
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/alignment.h"
7#include "common/common_types.h"
8#include "core/hle/kernel/k_page_bitmap.h"
9#include "core/hle/kernel/k_spin_lock.h"
10#include "core/hle/kernel/memory_types.h"
11#include "core/hle/kernel/svc_results.h"
12
13namespace Kernel {
14
15class KDynamicPageManager {
16public:
17 class PageBuffer {
18 private:
19 u8 m_buffer[PageSize];
20 };
21 static_assert(sizeof(PageBuffer) == PageSize);
22
23public:
24 KDynamicPageManager() = default;
25
26 template <typename T>
27 T* GetPointer(VAddr addr) {
28 return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
29 }
30
31 template <typename T>
32 const T* GetPointer(VAddr addr) const {
33 return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
34 }
35
36 Result Initialize(VAddr addr, size_t sz) {
37 // We need to have positive size.
38 R_UNLESS(sz > 0, ResultOutOfMemory);
39 m_backing_memory.resize(sz);
40
41 // Calculate management overhead.
42 const size_t management_size =
43 KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
44 const size_t allocatable_size = sz - management_size;
45
46 // Set tracking fields.
47 m_address = addr;
48 m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
49 m_count = allocatable_size / sizeof(PageBuffer);
50 R_UNLESS(m_count > 0, ResultOutOfMemory);
51
52 // Clear the management region.
53 u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
54 std::memset(management_ptr, 0, management_size);
55
56 // Initialize the bitmap.
57 m_page_bitmap.Initialize(management_ptr, m_count);
58
59 // Free the pages to the bitmap.
60 for (size_t i = 0; i < m_count; i++) {
61 // Ensure the freed page is all-zero.
62 std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
63
64 // Set the bit for the free page.
65 m_page_bitmap.SetBit(i);
66 }
67
68 R_SUCCEED();
69 }
70
71 VAddr GetAddress() const {
72 return m_address;
73 }
74 size_t GetSize() const {
75 return m_size;
76 }
77 size_t GetUsed() const {
78 return m_used;
79 }
80 size_t GetPeak() const {
81 return m_peak;
82 }
83 size_t GetCount() const {
84 return m_count;
85 }
86
87 PageBuffer* Allocate() {
88 // Take the lock.
89 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
90 KScopedSpinLock lk(m_lock);
91
92 // Find a random free block.
93 s64 soffset = m_page_bitmap.FindFreeBlock(true);
94 if (soffset < 0) [[unlikely]] {
95 return nullptr;
96 }
97
98 const size_t offset = static_cast<size_t>(soffset);
99
100 // Update our tracking.
101 m_page_bitmap.ClearBit(offset);
102 m_peak = std::max(m_peak, (++m_used));
103
104 return GetPointer<PageBuffer>(m_address) + offset;
105 }
106
107 void Free(PageBuffer* pb) {
108 // Ensure all pages in the heap are zero.
109 std::memset(pb, 0, PageSize);
110
111 // Take the lock.
112 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
113 KScopedSpinLock lk(m_lock);
114
115 // Set the bit for the free page.
116 size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
117 m_page_bitmap.SetBit(offset);
118
119 // Decrement our used count.
120 --m_used;
121 }
122
123private:
124 KSpinLock m_lock;
125 KPageBitmap m_page_bitmap;
126 size_t m_used{};
127 size_t m_peak{};
128 size_t m_count{};
129 VAddr m_address{};
130 size_t m_size{};
131
132 // TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
133 std::vector<u8> m_backing_memory;
134};
135
136} // namespace Kernel
diff --git a/src/core/hle/kernel/k_dynamic_resource_manager.h b/src/core/hle/kernel/k_dynamic_resource_manager.h
new file mode 100644
index 000000000..1ce517e8e
--- /dev/null
+++ b/src/core/hle/kernel/k_dynamic_resource_manager.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "core/hle/kernel/k_dynamic_slab_heap.h"
8#include "core/hle/kernel/k_memory_block.h"
9
10namespace Kernel {
11
12template <typename T, bool ClearNode = false>
13class KDynamicResourceManager {
14 YUZU_NON_COPYABLE(KDynamicResourceManager);
15 YUZU_NON_MOVEABLE(KDynamicResourceManager);
16
17public:
18 using DynamicSlabType = KDynamicSlabHeap<T, ClearNode>;
19
20public:
21 constexpr KDynamicResourceManager() = default;
22
23 constexpr size_t GetSize() const {
24 return m_slab_heap->GetSize();
25 }
26 constexpr size_t GetUsed() const {
27 return m_slab_heap->GetUsed();
28 }
29 constexpr size_t GetPeak() const {
30 return m_slab_heap->GetPeak();
31 }
32 constexpr size_t GetCount() const {
33 return m_slab_heap->GetCount();
34 }
35
36 void Initialize(KDynamicPageManager* page_allocator, DynamicSlabType* slab_heap) {
37 m_page_allocator = page_allocator;
38 m_slab_heap = slab_heap;
39 }
40
41 T* Allocate() const {
42 return m_slab_heap->Allocate(m_page_allocator);
43 }
44
45 void Free(T* t) const {
46 m_slab_heap->Free(t);
47 }
48
49private:
50 KDynamicPageManager* m_page_allocator{};
51 DynamicSlabType* m_slab_heap{};
52};
53
54class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
55
56using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
57
58} // namespace Kernel
diff --git a/src/core/hle/kernel/k_dynamic_slab_heap.h b/src/core/hle/kernel/k_dynamic_slab_heap.h
new file mode 100644
index 000000000..3a0ddd050
--- /dev/null
+++ b/src/core/hle/kernel/k_dynamic_slab_heap.h
@@ -0,0 +1,122 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7
8#include "common/common_funcs.h"
9#include "core/hle/kernel/k_dynamic_page_manager.h"
10#include "core/hle/kernel/k_slab_heap.h"
11
12namespace Kernel {
13
14template <typename T, bool ClearNode = false>
15class KDynamicSlabHeap : protected impl::KSlabHeapImpl {
16 YUZU_NON_COPYABLE(KDynamicSlabHeap);
17 YUZU_NON_MOVEABLE(KDynamicSlabHeap);
18
19public:
20 constexpr KDynamicSlabHeap() = default;
21
22 constexpr VAddr GetAddress() const {
23 return m_address;
24 }
25 constexpr size_t GetSize() const {
26 return m_size;
27 }
28 constexpr size_t GetUsed() const {
29 return m_used.load();
30 }
31 constexpr size_t GetPeak() const {
32 return m_peak.load();
33 }
34 constexpr size_t GetCount() const {
35 return m_count.load();
36 }
37
38 constexpr bool IsInRange(VAddr addr) const {
39 return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1;
40 }
41
42 void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) {
43 ASSERT(page_allocator != nullptr);
44
45 // Initialize members.
46 m_address = page_allocator->GetAddress();
47 m_size = page_allocator->GetSize();
48
49 // Initialize the base allocator.
50 KSlabHeapImpl::Initialize();
51
52 // Allocate until we have the correct number of objects.
53 while (m_count.load() < num_objects) {
54 auto* allocated = reinterpret_cast<T*>(page_allocator->Allocate());
55 ASSERT(allocated != nullptr);
56
57 for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) {
58 KSlabHeapImpl::Free(allocated + i);
59 }
60
61 m_count += sizeof(PageBuffer) / sizeof(T);
62 }
63 }
64
65 T* Allocate(KDynamicPageManager* page_allocator) {
66 T* allocated = static_cast<T*>(KSlabHeapImpl::Allocate());
67
68 // If we successfully allocated and we should clear the node, do so.
69 if constexpr (ClearNode) {
70 if (allocated != nullptr) [[likely]] {
71 reinterpret_cast<KSlabHeapImpl::Node*>(allocated)->next = nullptr;
72 }
73 }
74
75 // If we fail to allocate, try to get a new page from our next allocator.
76 if (allocated == nullptr) [[unlikely]] {
77 if (page_allocator != nullptr) {
78 allocated = reinterpret_cast<T*>(page_allocator->Allocate());
79 if (allocated != nullptr) {
80 // If we succeeded in getting a page, free the rest to our slab.
81 for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
82 KSlabHeapImpl::Free(allocated + i);
83 }
84 m_count += sizeof(PageBuffer) / sizeof(T);
85 }
86 }
87 }
88
89 if (allocated != nullptr) [[likely]] {
90 // Construct the object.
91 std::construct_at(allocated);
92
93 // Update our tracking.
94 const size_t used = ++m_used;
95 size_t peak = m_peak.load();
96 while (peak < used) {
97 if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) {
98 break;
99 }
100 }
101 }
102
103 return allocated;
104 }
105
106 void Free(T* t) {
107 KSlabHeapImpl::Free(t);
108 --m_used;
109 }
110
111private:
112 using PageBuffer = KDynamicPageManager::PageBuffer;
113
114private:
115 std::atomic<size_t> m_used{};
116 std::atomic<size_t> m_peak{};
117 std::atomic<size_t> m_count{};
118 VAddr m_address{};
119 size_t m_size{};
120};
121
122} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index e52fafbc7..78ca59463 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -8,39 +8,45 @@
8namespace Kernel { 8namespace Kernel {
9 9
10KEvent::KEvent(KernelCore& kernel_) 10KEvent::KEvent(KernelCore& kernel_)
11 : KAutoObjectWithSlabHeapAndContainer{kernel_}, readable_event{kernel_}, writable_event{ 11 : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {}
12 kernel_} {}
13 12
14KEvent::~KEvent() = default; 13KEvent::~KEvent() = default;
15 14
16void KEvent::Initialize(std::string&& name_, KProcess* owner_) { 15void KEvent::Initialize(KProcess* owner) {
17 // Increment reference count. 16 // Create our readable event.
18 // Because reference count is one on creation, this will result 17 KAutoObject::Create(std::addressof(m_readable_event));
19 // in a reference count of two. Thus, when both readable and
20 // writable events are closed this object will be destroyed.
21 Open();
22 18
23 // Create our sub events. 19 // Initialize our readable event.
24 KAutoObject::Create(std::addressof(readable_event)); 20 m_readable_event.Initialize(this);
25 KAutoObject::Create(std::addressof(writable_event));
26
27 // Initialize our sub sessions.
28 readable_event.Initialize(this, name_ + ":Readable");
29 writable_event.Initialize(this, name_ + ":Writable");
30 21
31 // Set our owner process. 22 // Set our owner process.
32 owner = owner_; 23 m_owner = owner;
33 owner->Open(); 24 m_owner->Open();
34 25
35 // Mark initialized. 26 // Mark initialized.
36 name = std::move(name_); 27 m_initialized = true;
37 initialized = true;
38} 28}
39 29
40void KEvent::Finalize() { 30void KEvent::Finalize() {
41 KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize(); 31 KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
42} 32}
43 33
34Result KEvent::Signal() {
35 KScopedSchedulerLock sl{kernel};
36
37 R_SUCCEED_IF(m_readable_event_destroyed);
38
39 return m_readable_event.Signal();
40}
41
42Result KEvent::Clear() {
43 KScopedSchedulerLock sl{kernel};
44
45 R_SUCCEED_IF(m_readable_event_destroyed);
46
47 return m_readable_event.Clear();
48}
49
44void KEvent::PostDestroy(uintptr_t arg) { 50void KEvent::PostDestroy(uintptr_t arg) {
45 // Release the event count resource the owner process holds. 51 // Release the event count resource the owner process holds.
46 KProcess* owner = reinterpret_cast<KProcess*>(arg); 52 KProcess* owner = reinterpret_cast<KProcess*>(arg);
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 2ff828feb..48ce7d9a0 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/src/core/hle/kernel/k_event.h
@@ -4,14 +4,12 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/kernel/k_readable_event.h" 6#include "core/hle/kernel/k_readable_event.h"
7#include "core/hle/kernel/k_writable_event.h"
8#include "core/hle/kernel/slab_helpers.h" 7#include "core/hle/kernel/slab_helpers.h"
9 8
10namespace Kernel { 9namespace Kernel {
11 10
12class KernelCore; 11class KernelCore;
13class KReadableEvent; 12class KReadableEvent;
14class KWritableEvent;
15class KProcess; 13class KProcess;
16 14
17class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> { 15class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
@@ -21,37 +19,40 @@ public:
21 explicit KEvent(KernelCore& kernel_); 19 explicit KEvent(KernelCore& kernel_);
22 ~KEvent() override; 20 ~KEvent() override;
23 21
24 void Initialize(std::string&& name, KProcess* owner_); 22 void Initialize(KProcess* owner);
25 23
26 void Finalize() override; 24 void Finalize() override;
27 25
28 bool IsInitialized() const override { 26 bool IsInitialized() const override {
29 return initialized; 27 return m_initialized;
30 } 28 }
31 29
32 uintptr_t GetPostDestroyArgument() const override { 30 uintptr_t GetPostDestroyArgument() const override {
33 return reinterpret_cast<uintptr_t>(owner); 31 return reinterpret_cast<uintptr_t>(m_owner);
34 } 32 }
35 33
36 KProcess* GetOwner() const override { 34 KProcess* GetOwner() const override {
37 return owner; 35 return m_owner;
38 } 36 }
39 37
40 KReadableEvent& GetReadableEvent() { 38 KReadableEvent& GetReadableEvent() {
41 return readable_event; 39 return m_readable_event;
42 }
43
44 KWritableEvent& GetWritableEvent() {
45 return writable_event;
46 } 40 }
47 41
48 static void PostDestroy(uintptr_t arg); 42 static void PostDestroy(uintptr_t arg);
49 43
44 Result Signal();
45 Result Clear();
46
47 void OnReadableEventDestroyed() {
48 m_readable_event_destroyed = true;
49 }
50
50private: 51private:
51 KReadableEvent readable_event; 52 KReadableEvent m_readable_event;
52 KWritableEvent writable_event; 53 KProcess* m_owner{};
53 KProcess* owner{}; 54 bool m_initialized{};
54 bool initialized{}; 55 bool m_readable_event_destroyed{};
55}; 56};
56 57
57} // namespace Kernel 58} // namespace Kernel
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index 1b577a5b3..4a6b60d26 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -11,29 +11,34 @@
11namespace Kernel::KInterruptManager { 11namespace Kernel::KInterruptManager {
12 12
13void HandleInterrupt(KernelCore& kernel, s32 core_id) { 13void HandleInterrupt(KernelCore& kernel, s32 core_id) {
14 auto* process = kernel.CurrentProcess();
15 if (!process) {
16 return;
17 }
18
19 // Acknowledge the interrupt. 14 // Acknowledge the interrupt.
20 kernel.PhysicalCore(core_id).ClearInterrupt(); 15 kernel.PhysicalCore(core_id).ClearInterrupt();
21 16
22 auto& current_thread = GetCurrentThread(kernel); 17 auto& current_thread = GetCurrentThread(kernel);
23 18
24 // If the user disable count is set, we may need to pin the current thread. 19 if (auto* process = kernel.CurrentProcess(); process) {
25 if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { 20 // If the user disable count is set, we may need to pin the current thread.
26 KScopedSchedulerLock sl{kernel}; 21 if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
22 KScopedSchedulerLock sl{kernel};
27 23
28 // Pin the current thread. 24 // Pin the current thread.
29 process->PinCurrentThread(core_id); 25 process->PinCurrentThread(core_id);
30 26
31 // Set the interrupt flag for the thread. 27 // Set the interrupt flag for the thread.
32 GetCurrentThread(kernel).SetInterruptFlag(); 28 GetCurrentThread(kernel).SetInterruptFlag();
29 }
33 } 30 }
34 31
35 // Request interrupt scheduling. 32 // Request interrupt scheduling.
36 kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); 33 kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
37} 34}
38 35
36void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) {
37 for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
38 if (core_mask & (1ULL << core_id)) {
39 kernel.PhysicalCore(core_id).Interrupt();
40 }
41 }
42}
43
39} // namespace Kernel::KInterruptManager 44} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h
index f103dfe3f..803dc9211 100644
--- a/src/core/hle/kernel/k_interrupt_manager.h
+++ b/src/core/hle/kernel/k_interrupt_manager.h
@@ -11,6 +11,8 @@ class KernelCore;
11 11
12namespace KInterruptManager { 12namespace KInterruptManager {
13void HandleInterrupt(KernelCore& kernel, s32 core_id); 13void HandleInterrupt(KernelCore& kernel, s32 core_id);
14} 14void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask);
15
16} // namespace KInterruptManager
15 17
16} // namespace Kernel 18} // namespace Kernel
diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h
index 78859ced3..29ebd16b7 100644
--- a/src/core/hle/kernel/k_linked_list.h
+++ b/src/core/hle/kernel/k_linked_list.h
@@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>,
16 public KSlabAllocated<KLinkedListNode> { 16 public KSlabAllocated<KLinkedListNode> {
17 17
18public: 18public:
19 explicit KLinkedListNode(KernelCore&) {}
19 KLinkedListNode() = default; 20 KLinkedListNode() = default;
20 21
21 void Initialize(void* it) { 22 void Initialize(void* it) {
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 18df1f836..9444f6bd2 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -6,6 +6,7 @@
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/intrusive_red_black_tree.h"
9#include "core/hle/kernel/memory_types.h" 10#include "core/hle/kernel/memory_types.h"
10#include "core/hle/kernel/svc_types.h" 11#include "core/hle/kernel/svc_types.h"
11 12
@@ -168,9 +169,8 @@ constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission per
168 169
169enum class KMemoryAttribute : u8 { 170enum class KMemoryAttribute : u8 {
170 None = 0x00, 171 None = 0x00,
171 Mask = 0x7F, 172 All = 0xFF,
172 All = Mask, 173 UserMask = All,
173 DontCareMask = 0x80,
174 174
175 Locked = static_cast<u8>(Svc::MemoryAttribute::Locked), 175 Locked = static_cast<u8>(Svc::MemoryAttribute::Locked),
176 IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked), 176 IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
@@ -178,76 +178,112 @@ enum class KMemoryAttribute : u8 {
178 Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), 178 Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
179 179
180 SetMask = Uncached, 180 SetMask = Uncached,
181
182 IpcAndDeviceMapped = IpcLocked | DeviceShared,
183 LockedAndIpcLocked = Locked | IpcLocked,
184 DeviceSharedAndUncached = DeviceShared | Uncached
185}; 181};
186DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); 182DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
187 183
188static_assert((static_cast<u8>(KMemoryAttribute::Mask) & 184enum class KMemoryBlockDisableMergeAttribute : u8 {
189 static_cast<u8>(KMemoryAttribute::DontCareMask)) == 0); 185 None = 0,
186 Normal = (1u << 0),
187 DeviceLeft = (1u << 1),
188 IpcLeft = (1u << 2),
189 Locked = (1u << 3),
190 DeviceRight = (1u << 4),
191
192 AllLeft = Normal | DeviceLeft | IpcLeft | Locked,
193 AllRight = DeviceRight,
194};
195DECLARE_ENUM_FLAG_OPERATORS(KMemoryBlockDisableMergeAttribute);
190 196
191struct KMemoryInfo { 197struct KMemoryInfo {
192 VAddr addr{}; 198 uintptr_t m_address;
193 std::size_t size{}; 199 size_t m_size;
194 KMemoryState state{}; 200 KMemoryState m_state;
195 KMemoryPermission perm{}; 201 u16 m_device_disable_merge_left_count;
196 KMemoryAttribute attribute{}; 202 u16 m_device_disable_merge_right_count;
197 KMemoryPermission original_perm{}; 203 u16 m_ipc_lock_count;
198 u16 ipc_lock_count{}; 204 u16 m_device_use_count;
199 u16 device_use_count{}; 205 u16 m_ipc_disable_merge_count;
206 KMemoryPermission m_permission;
207 KMemoryAttribute m_attribute;
208 KMemoryPermission m_original_permission;
209 KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
200 210
201 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { 211 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
202 return { 212 return {
203 addr, 213 .addr = m_address,
204 size, 214 .size = m_size,
205 static_cast<Svc::MemoryState>(state & KMemoryState::Mask), 215 .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
206 static_cast<Svc::MemoryAttribute>(attribute & KMemoryAttribute::Mask), 216 .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
207 static_cast<Svc::MemoryPermission>(perm & KMemoryPermission::UserMask), 217 .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
208 ipc_lock_count, 218 .ipc_refcount = m_ipc_lock_count,
209 device_use_count, 219 .device_refcount = m_device_use_count,
220 .padding = {},
210 }; 221 };
211 } 222 }
212 223
213 constexpr VAddr GetAddress() const { 224 constexpr uintptr_t GetAddress() const {
214 return addr; 225 return m_address;
226 }
227
228 constexpr size_t GetSize() const {
229 return m_size;
215 } 230 }
216 constexpr std::size_t GetSize() const { 231
217 return size; 232 constexpr size_t GetNumPages() const {
233 return this->GetSize() / PageSize;
218 } 234 }
219 constexpr std::size_t GetNumPages() const { 235
220 return GetSize() / PageSize; 236 constexpr uintptr_t GetEndAddress() const {
237 return this->GetAddress() + this->GetSize();
221 } 238 }
222 constexpr VAddr GetEndAddress() const { 239
223 return GetAddress() + GetSize(); 240 constexpr uintptr_t GetLastAddress() const {
241 return this->GetEndAddress() - 1;
224 } 242 }
225 constexpr VAddr GetLastAddress() const { 243
226 return GetEndAddress() - 1; 244 constexpr u16 GetIpcLockCount() const {
245 return m_ipc_lock_count;
227 } 246 }
247
248 constexpr u16 GetIpcDisableMergeCount() const {
249 return m_ipc_disable_merge_count;
250 }
251
228 constexpr KMemoryState GetState() const { 252 constexpr KMemoryState GetState() const {
229 return state; 253 return m_state;
254 }
255
256 constexpr KMemoryPermission GetPermission() const {
257 return m_permission;
230 } 258 }
259
260 constexpr KMemoryPermission GetOriginalPermission() const {
261 return m_original_permission;
262 }
263
231 constexpr KMemoryAttribute GetAttribute() const { 264 constexpr KMemoryAttribute GetAttribute() const {
232 return attribute; 265 return m_attribute;
233 } 266 }
234 constexpr KMemoryPermission GetPermission() const { 267
235 return perm; 268 constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
269 return m_disable_merge_attribute;
236 } 270 }
237}; 271};
238 272
239class KMemoryBlock final { 273class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
240 friend class KMemoryBlockManager;
241
242private: 274private:
243 VAddr addr{}; 275 u16 m_device_disable_merge_left_count;
244 std::size_t num_pages{}; 276 u16 m_device_disable_merge_right_count;
245 KMemoryState state{KMemoryState::None}; 277 VAddr m_address;
246 u16 ipc_lock_count{}; 278 size_t m_num_pages;
247 u16 device_use_count{}; 279 KMemoryState m_memory_state;
248 KMemoryPermission perm{KMemoryPermission::None}; 280 u16 m_ipc_lock_count;
249 KMemoryPermission original_perm{KMemoryPermission::None}; 281 u16 m_device_use_count;
250 KMemoryAttribute attribute{KMemoryAttribute::None}; 282 u16 m_ipc_disable_merge_count;
283 KMemoryPermission m_permission;
284 KMemoryPermission m_original_permission;
285 KMemoryAttribute m_attribute;
286 KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
251 287
252public: 288public:
253 static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) { 289 static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
@@ -261,113 +297,349 @@ public:
261 } 297 }
262 298
263public: 299public:
264 constexpr KMemoryBlock() = default;
265 constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_,
266 KMemoryPermission perm_, KMemoryAttribute attribute_)
267 : addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {}
268
269 constexpr VAddr GetAddress() const { 300 constexpr VAddr GetAddress() const {
270 return addr; 301 return m_address;
271 } 302 }
272 303
273 constexpr std::size_t GetNumPages() const { 304 constexpr size_t GetNumPages() const {
274 return num_pages; 305 return m_num_pages;
275 } 306 }
276 307
277 constexpr std::size_t GetSize() const { 308 constexpr size_t GetSize() const {
278 return GetNumPages() * PageSize; 309 return this->GetNumPages() * PageSize;
279 } 310 }
280 311
281 constexpr VAddr GetEndAddress() const { 312 constexpr VAddr GetEndAddress() const {
282 return GetAddress() + GetSize(); 313 return this->GetAddress() + this->GetSize();
283 } 314 }
284 315
285 constexpr VAddr GetLastAddress() const { 316 constexpr VAddr GetLastAddress() const {
286 return GetEndAddress() - 1; 317 return this->GetEndAddress() - 1;
318 }
319
320 constexpr u16 GetIpcLockCount() const {
321 return m_ipc_lock_count;
322 }
323
324 constexpr u16 GetIpcDisableMergeCount() const {
325 return m_ipc_disable_merge_count;
326 }
327
328 constexpr KMemoryPermission GetPermission() const {
329 return m_permission;
330 }
331
332 constexpr KMemoryPermission GetOriginalPermission() const {
333 return m_original_permission;
334 }
335
336 constexpr KMemoryAttribute GetAttribute() const {
337 return m_attribute;
287 } 338 }
288 339
289 constexpr KMemoryInfo GetMemoryInfo() const { 340 constexpr KMemoryInfo GetMemoryInfo() const {
290 return { 341 return {
291 GetAddress(), GetSize(), state, perm, 342 .m_address = this->GetAddress(),
292 attribute, original_perm, ipc_lock_count, device_use_count, 343 .m_size = this->GetSize(),
344 .m_state = m_memory_state,
345 .m_device_disable_merge_left_count = m_device_disable_merge_left_count,
346 .m_device_disable_merge_right_count = m_device_disable_merge_right_count,
347 .m_ipc_lock_count = m_ipc_lock_count,
348 .m_device_use_count = m_device_use_count,
349 .m_ipc_disable_merge_count = m_ipc_disable_merge_count,
350 .m_permission = m_permission,
351 .m_attribute = m_attribute,
352 .m_original_permission = m_original_permission,
353 .m_disable_merge_attribute = m_disable_merge_attribute,
293 }; 354 };
294 } 355 }
295 356
296 void ShareToDevice(KMemoryPermission /*new_perm*/) { 357public:
297 ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || 358 explicit KMemoryBlock() = default;
298 device_use_count == 0); 359
299 attribute |= KMemoryAttribute::DeviceShared; 360 constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
300 const u16 new_use_count{++device_use_count}; 361 KMemoryAttribute attr)
301 ASSERT(new_use_count > 0); 362 : Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(),
363 m_device_disable_merge_left_count(), m_device_disable_merge_right_count(),
364 m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0),
365 m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p),
366 m_original_permission(KMemoryPermission::None), m_attribute(attr),
367 m_disable_merge_attribute() {}
368
369 constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
370 KMemoryAttribute attr) {
371 m_device_disable_merge_left_count = 0;
372 m_device_disable_merge_right_count = 0;
373 m_address = addr;
374 m_num_pages = np;
375 m_memory_state = ms;
376 m_ipc_lock_count = 0;
377 m_device_use_count = 0;
378 m_permission = p;
379 m_original_permission = KMemoryPermission::None;
380 m_attribute = attr;
381 m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None;
382 }
383
384 constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
385 constexpr auto AttributeIgnoreMask =
386 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
387 return m_memory_state == s && m_permission == p &&
388 (m_attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
389 }
390
391 constexpr bool HasSameProperties(const KMemoryBlock& rhs) const {
392 return m_memory_state == rhs.m_memory_state && m_permission == rhs.m_permission &&
393 m_original_permission == rhs.m_original_permission &&
394 m_attribute == rhs.m_attribute && m_ipc_lock_count == rhs.m_ipc_lock_count &&
395 m_device_use_count == rhs.m_device_use_count;
396 }
397
398 constexpr bool CanMergeWith(const KMemoryBlock& rhs) const {
399 return this->HasSameProperties(rhs) &&
400 (m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight) ==
401 KMemoryBlockDisableMergeAttribute::None &&
402 (rhs.m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft) ==
403 KMemoryBlockDisableMergeAttribute::None;
302 } 404 }
303 405
304 void UnshareToDevice(KMemoryPermission /*new_perm*/) { 406 constexpr bool Contains(VAddr addr) const {
305 ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); 407 return this->GetAddress() <= addr && addr <= this->GetEndAddress();
306 const u16 prev_use_count{device_use_count--}; 408 }
307 ASSERT(prev_use_count > 0); 409
308 if (prev_use_count == 1) { 410 constexpr void Add(const KMemoryBlock& added_block) {
309 attribute &= ~KMemoryAttribute::DeviceShared; 411 ASSERT(added_block.GetNumPages() > 0);
412 ASSERT(this->GetAddress() + added_block.GetSize() - 1 <
413 this->GetEndAddress() + added_block.GetSize() - 1);
414
415 m_num_pages += added_block.GetNumPages();
416 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
417 m_disable_merge_attribute | added_block.m_disable_merge_attribute);
418 m_device_disable_merge_right_count = added_block.m_device_disable_merge_right_count;
419 }
420
421 constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a,
422 bool set_disable_merge_attr, u8 set_mask, u8 clear_mask) {
423 ASSERT(m_original_permission == KMemoryPermission::None);
424 ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None);
425
426 m_memory_state = s;
427 m_permission = p;
428 m_attribute = static_cast<KMemoryAttribute>(
429 a | (m_attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
430
431 if (set_disable_merge_attr && set_mask != 0) {
432 m_disable_merge_attribute = m_disable_merge_attribute |
433 static_cast<KMemoryBlockDisableMergeAttribute>(set_mask);
434 }
435 if (clear_mask != 0) {
436 m_disable_merge_attribute = m_disable_merge_attribute &
437 static_cast<KMemoryBlockDisableMergeAttribute>(~clear_mask);
310 } 438 }
311 } 439 }
312 440
313private: 441 constexpr void Split(KMemoryBlock* block, VAddr addr) {
314 constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const { 442 ASSERT(this->GetAddress() < addr);
315 constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask | 443 ASSERT(this->Contains(addr));
316 KMemoryAttribute::IpcLocked | 444 ASSERT(Common::IsAligned(addr, PageSize));
317 KMemoryAttribute::DeviceShared}; 445
318 return state == s && perm == p && 446 block->m_address = m_address;
319 (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); 447 block->m_num_pages = (addr - this->GetAddress()) / PageSize;
448 block->m_memory_state = m_memory_state;
449 block->m_ipc_lock_count = m_ipc_lock_count;
450 block->m_device_use_count = m_device_use_count;
451 block->m_permission = m_permission;
452 block->m_original_permission = m_original_permission;
453 block->m_attribute = m_attribute;
454 block->m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
455 m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft);
456 block->m_ipc_disable_merge_count = m_ipc_disable_merge_count;
457 block->m_device_disable_merge_left_count = m_device_disable_merge_left_count;
458 block->m_device_disable_merge_right_count = 0;
459
460 m_address = addr;
461 m_num_pages -= block->m_num_pages;
462
463 m_ipc_disable_merge_count = 0;
464 m_device_disable_merge_left_count = 0;
465 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
466 m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight);
320 } 467 }
321 468
322 constexpr bool HasSameProperties(const KMemoryBlock& rhs) const { 469 constexpr void UpdateDeviceDisableMergeStateForShareLeft(
323 return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && 470 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
324 attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && 471 if (left) {
325 device_use_count == rhs.device_use_count; 472 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
473 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft);
474 const u16 new_device_disable_merge_left_count = ++m_device_disable_merge_left_count;
475 ASSERT(new_device_disable_merge_left_count > 0);
476 }
326 } 477 }
327 478
328 constexpr bool Contains(VAddr start) const { 479 constexpr void UpdateDeviceDisableMergeStateForShareRight(
329 return GetAddress() <= start && start <= GetEndAddress(); 480 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
481 if (right) {
482 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
483 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight);
484 const u16 new_device_disable_merge_right_count = ++m_device_disable_merge_right_count;
485 ASSERT(new_device_disable_merge_right_count > 0);
486 }
487 }
488
489 constexpr void UpdateDeviceDisableMergeStateForShare(KMemoryPermission new_perm, bool left,
490 bool right) {
491 this->UpdateDeviceDisableMergeStateForShareLeft(new_perm, left, right);
492 this->UpdateDeviceDisableMergeStateForShareRight(new_perm, left, right);
330 } 493 }
331 494
332 constexpr void Add(std::size_t count) { 495 constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
333 ASSERT(count > 0); 496 bool right) {
334 ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); 497 // We must either be shared or have a zero lock count.
498 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
499 m_device_use_count == 0);
335 500
336 num_pages += count; 501 // Share.
502 const u16 new_count = ++m_device_use_count;
503 ASSERT(new_count > 0);
504
505 m_attribute = static_cast<KMemoryAttribute>(m_attribute | KMemoryAttribute::DeviceShared);
506
507 this->UpdateDeviceDisableMergeStateForShare(new_perm, left, right);
337 } 508 }
338 509
339 constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm, 510 constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(
340 KMemoryAttribute new_attribute) { 511 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
341 ASSERT(original_perm == KMemoryPermission::None);
342 ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None);
343 512
344 state = new_state; 513 if (left) {
345 perm = new_perm; 514 if (!m_device_disable_merge_left_count) {
515 return;
516 }
517 --m_device_disable_merge_left_count;
518 }
346 519
347 attribute = static_cast<KMemoryAttribute>( 520 m_device_disable_merge_left_count =
348 new_attribute | 521 std::min(m_device_disable_merge_left_count, m_device_use_count);
349 (attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); 522
523 if (m_device_disable_merge_left_count == 0) {
524 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
525 m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceLeft);
526 }
350 } 527 }
351 528
352 constexpr KMemoryBlock Split(VAddr split_addr) { 529 constexpr void UpdateDeviceDisableMergeStateForUnshareRight(
353 ASSERT(GetAddress() < split_addr); 530 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
354 ASSERT(Contains(split_addr)); 531 if (right) {
355 ASSERT(Common::IsAligned(split_addr, PageSize)); 532 const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
533 ASSERT(old_device_disable_merge_right_count > 0);
534 if (old_device_disable_merge_right_count == 1) {
535 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
536 m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceRight);
537 }
538 }
539 }
356 540
357 KMemoryBlock block; 541 constexpr void UpdateDeviceDisableMergeStateForUnshare(KMemoryPermission new_perm, bool left,
358 block.addr = addr; 542 bool right) {
359 block.num_pages = (split_addr - GetAddress()) / PageSize; 543 this->UpdateDeviceDisableMergeStateForUnshareLeft(new_perm, left, right);
360 block.state = state; 544 this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
361 block.ipc_lock_count = ipc_lock_count; 545 }
362 block.device_use_count = device_use_count;
363 block.perm = perm;
364 block.original_perm = original_perm;
365 block.attribute = attribute;
366 546
367 addr = split_addr; 547 constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
368 num_pages -= block.num_pages; 548 bool right) {
549 // We must be shared.
550 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
551
552 // Unhare.
553 const u16 old_count = m_device_use_count--;
554 ASSERT(old_count > 0);
555
556 if (old_count == 1) {
557 m_attribute =
558 static_cast<KMemoryAttribute>(m_attribute & ~KMemoryAttribute::DeviceShared);
559 }
560
561 this->UpdateDeviceDisableMergeStateForUnshare(new_perm, left, right);
562 }
563
564 constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left,
565 bool right) {
566
567 // We must be shared.
568 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
569
570 // Unhare.
571 const u16 old_count = m_device_use_count--;
572 ASSERT(old_count > 0);
573
574 if (old_count == 1) {
575 m_attribute =
576 static_cast<KMemoryAttribute>(m_attribute & ~KMemoryAttribute::DeviceShared);
577 }
578
579 this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
580 }
581
582 constexpr void LockForIpc(KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
583 // We must either be locked or have a zero lock count.
584 ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked ||
585 m_ipc_lock_count == 0);
586
587 // Lock.
588 const u16 new_lock_count = ++m_ipc_lock_count;
589 ASSERT(new_lock_count > 0);
590
591 // If this is our first lock, update our permissions.
592 if (new_lock_count == 1) {
593 ASSERT(m_original_permission == KMemoryPermission::None);
594 ASSERT((m_permission | new_perm | KMemoryPermission::NotMapped) ==
595 (m_permission | KMemoryPermission::NotMapped));
596 ASSERT((m_permission & KMemoryPermission::UserExecute) !=
597 KMemoryPermission::UserExecute ||
598 (new_perm == KMemoryPermission::UserRead));
599 m_original_permission = m_permission;
600 m_permission = static_cast<KMemoryPermission>(
601 (new_perm & KMemoryPermission::IpcLockChangeMask) |
602 (m_original_permission & ~KMemoryPermission::IpcLockChangeMask));
603 }
604 m_attribute = static_cast<KMemoryAttribute>(m_attribute | KMemoryAttribute::IpcLocked);
605
606 if (left) {
607 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
608 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::IpcLeft);
609 const u16 new_ipc_disable_merge_count = ++m_ipc_disable_merge_count;
610 ASSERT(new_ipc_disable_merge_count > 0);
611 }
612 }
613
614 constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left,
615 [[maybe_unused]] bool right) {
616 // We must be locked.
617 ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked);
618
619 // Unlock.
620 const u16 old_lock_count = m_ipc_lock_count--;
621 ASSERT(old_lock_count > 0);
622
623 // If this is our last unlock, update our permissions.
624 if (old_lock_count == 1) {
625 ASSERT(m_original_permission != KMemoryPermission::None);
626 m_permission = m_original_permission;
627 m_original_permission = KMemoryPermission::None;
628 m_attribute = static_cast<KMemoryAttribute>(m_attribute & ~KMemoryAttribute::IpcLocked);
629 }
630
631 if (left) {
632 const u16 old_ipc_disable_merge_count = m_ipc_disable_merge_count--;
633 ASSERT(old_ipc_disable_merge_count > 0);
634 if (old_ipc_disable_merge_count == 1) {
635 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
636 m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::IpcLeft);
637 }
638 }
639 }
369 640
370 return block; 641 constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
642 return m_disable_merge_attribute;
371 } 643 }
372}; 644};
373static_assert(std::is_trivially_destructible<KMemoryBlock>::value); 645static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index 3ddb9984f..cf4c1e371 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -2,221 +2,336 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/kernel/k_memory_block_manager.h" 4#include "core/hle/kernel/k_memory_block_manager.h"
5#include "core/hle/kernel/memory_types.h"
6 5
7namespace Kernel { 6namespace Kernel {
8 7
9KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_) 8KMemoryBlockManager::KMemoryBlockManager() = default;
10 : start_addr{start_addr_}, end_addr{end_addr_} {
11 const u64 num_pages{(end_addr - start_addr) / PageSize};
12 memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free,
13 KMemoryPermission::None, KMemoryAttribute::None);
14}
15 9
16KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) { 10Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager) {
17 auto node{memory_block_tree.begin()}; 11 // Allocate a block to encapsulate the address space, insert it into the tree.
18 while (node != end()) { 12 KMemoryBlock* start_block = slab_manager->Allocate();
19 const VAddr node_end_addr{node->GetNumPages() * PageSize + node->GetAddress()}; 13 R_UNLESS(start_block != nullptr, ResultOutOfResource);
20 if (node->GetAddress() <= addr && node_end_addr - 1 >= addr) { 14
21 return node; 15 // Set our start and end.
22 } 16 m_start_address = st;
23 node = std::next(node); 17 m_end_address = nd;
24 } 18 ASSERT(Common::IsAligned(m_start_address, PageSize));
25 return end(); 19 ASSERT(Common::IsAligned(m_end_address, PageSize));
20
21 // Initialize and insert the block.
22 start_block->Initialize(m_start_address, (m_end_address - m_start_address) / PageSize,
23 KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None);
24 m_memory_block_tree.insert(*start_block);
25
26 R_SUCCEED();
26} 27}
27 28
28VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages, 29void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
29 std::size_t num_pages, std::size_t align, 30 HostUnmapCallback&& host_unmap_callback) {
30 std::size_t offset, std::size_t guard_pages) { 31 // Erase every block until we have none left.
31 if (num_pages == 0) { 32 auto it = m_memory_block_tree.begin();
32 return {}; 33 while (it != m_memory_block_tree.end()) {
34 KMemoryBlock* block = std::addressof(*it);
35 it = m_memory_block_tree.erase(it);
36 slab_manager->Free(block);
37 host_unmap_callback(block->GetAddress(), block->GetSize());
33 } 38 }
34 39
35 const VAddr region_end{region_start + region_num_pages * PageSize}; 40 ASSERT(m_memory_block_tree.empty());
36 const VAddr region_last{region_end - 1}; 41}
37 for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) {
38 const auto info{it->GetMemoryInfo()};
39 if (region_last < info.GetAddress()) {
40 break;
41 }
42 42
43 if (info.state != KMemoryState::Free) { 43VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, size_t region_num_pages,
44 continue; 44 size_t num_pages, size_t alignment, size_t offset,
45 } 45 size_t guard_pages) const {
46 if (num_pages > 0) {
47 const VAddr region_end = region_start + region_num_pages * PageSize;
48 const VAddr region_last = region_end - 1;
49 for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend();
50 it++) {
51 const KMemoryInfo info = it->GetMemoryInfo();
52 if (region_last < info.GetAddress()) {
53 break;
54 }
55 if (info.m_state != KMemoryState::Free) {
56 continue;
57 }
46 58
47 VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()}; 59 VAddr area = (info.GetAddress() <= region_start) ? region_start : info.GetAddress();
48 area += guard_pages * PageSize; 60 area += guard_pages * PageSize;
49 61
50 const VAddr offset_area{Common::AlignDown(area, align) + offset}; 62 const VAddr offset_area = Common::AlignDown(area, alignment) + offset;
51 area = (area <= offset_area) ? offset_area : offset_area + align; 63 area = (area <= offset_area) ? offset_area : offset_area + alignment;
52 64
53 const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize}; 65 const VAddr area_end = area + num_pages * PageSize + guard_pages * PageSize;
54 const VAddr area_last{area_end - 1}; 66 const VAddr area_last = area_end - 1;
55 67
56 if (info.GetAddress() <= area && area < area_last && area_last <= region_last && 68 if (info.GetAddress() <= area && area < area_last && area_last <= region_last &&
57 area_last <= info.GetLastAddress()) { 69 area_last <= info.GetLastAddress()) {
58 return area; 70 return area;
71 }
59 } 72 }
60 } 73 }
61 74
62 return {}; 75 return {};
63} 76}
64 77
65void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, 78void KMemoryBlockManager::CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator,
66 KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, 79 VAddr address, size_t num_pages) {
67 KMemoryState state, KMemoryPermission perm, 80 // Find the iterator now that we've updated.
68 KMemoryAttribute attribute) { 81 iterator it = this->FindIterator(address);
69 const VAddr update_end_addr{addr + num_pages * PageSize}; 82 if (address != m_start_address) {
70 iterator node{memory_block_tree.begin()}; 83 it--;
84 }
71 85
72 prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped; 86 // Coalesce blocks that we can.
87 while (true) {
88 iterator prev = it++;
89 if (it == m_memory_block_tree.end()) {
90 break;
91 }
73 92
74 while (node != memory_block_tree.end()) { 93 if (prev->CanMergeWith(*it)) {
75 KMemoryBlock* block{&(*node)}; 94 KMemoryBlock* block = std::addressof(*it);
76 iterator next_node{std::next(node)}; 95 m_memory_block_tree.erase(it);
77 const VAddr cur_addr{block->GetAddress()}; 96 prev->Add(*block);
78 const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; 97 allocator->Free(block);
98 it = prev;
99 }
79 100
80 if (addr < cur_end_addr && cur_addr < update_end_addr) { 101 if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) {
81 if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) { 102 break;
82 node = next_node; 103 }
83 continue; 104 }
84 } 105}
85 106
86 iterator new_node{node}; 107void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
87 if (addr > cur_addr) { 108 size_t num_pages, KMemoryState state, KMemoryPermission perm,
88 memory_block_tree.insert(node, block->Split(addr)); 109 KMemoryAttribute attr,
110 KMemoryBlockDisableMergeAttribute set_disable_attr,
111 KMemoryBlockDisableMergeAttribute clear_disable_attr) {
112 // Ensure for auditing that we never end up with an invalid tree.
113 KScopedMemoryBlockManagerAuditor auditor(this);
114 ASSERT(Common::IsAligned(address, PageSize));
115 ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
116 KMemoryAttribute::None);
117
118 VAddr cur_address = address;
119 size_t remaining_pages = num_pages;
120 iterator it = this->FindIterator(address);
121
122 while (remaining_pages > 0) {
123 const size_t remaining_size = remaining_pages * PageSize;
124 KMemoryInfo cur_info = it->GetMemoryInfo();
125 if (it->HasProperties(state, perm, attr)) {
126 // If we already have the right properties, just advance.
127 if (cur_address + remaining_size < cur_info.GetEndAddress()) {
128 remaining_pages = 0;
129 cur_address += remaining_size;
130 } else {
131 remaining_pages =
132 (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
133 cur_address = cur_info.GetEndAddress();
89 } 134 }
135 } else {
136 // If we need to, create a new block before and insert it.
137 if (cur_info.GetAddress() != cur_address) {
138 KMemoryBlock* new_block = allocator->Allocate();
139
140 it->Split(new_block, cur_address);
141 it = m_memory_block_tree.insert(*new_block);
142 it++;
90 143
91 if (update_end_addr < cur_end_addr) { 144 cur_info = it->GetMemoryInfo();
92 new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); 145 cur_address = cur_info.GetAddress();
93 } 146 }
94 147
95 new_node->Update(state, perm, attribute); 148 // If we need to, create a new block after and insert it.
149 if (cur_info.GetSize() > remaining_size) {
150 KMemoryBlock* new_block = allocator->Allocate();
96 151
97 MergeAdjacent(new_node, next_node); 152 it->Split(new_block, cur_address + remaining_size);
98 } 153 it = m_memory_block_tree.insert(*new_block);
99 154
100 if (cur_end_addr - 1 >= update_end_addr - 1) { 155 cur_info = it->GetMemoryInfo();
101 break; 156 }
102 }
103 157
104 node = next_node; 158 // Update block state.
159 it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
160 static_cast<u8>(clear_disable_attr));
161 cur_address += cur_info.GetSize();
162 remaining_pages -= cur_info.GetNumPages();
163 }
164 it++;
105 } 165 }
166
167 this->CoalesceForUpdate(allocator, address, num_pages);
106} 168}
107 169
108void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state, 170void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator,
109 KMemoryPermission perm, KMemoryAttribute attribute) { 171 VAddr address, size_t num_pages, KMemoryState test_state,
110 const VAddr update_end_addr{addr + num_pages * PageSize}; 172 KMemoryPermission test_perm, KMemoryAttribute test_attr,
111 iterator node{memory_block_tree.begin()}; 173 KMemoryState state, KMemoryPermission perm,
174 KMemoryAttribute attr) {
175 // Ensure for auditing that we never end up with an invalid tree.
176 KScopedMemoryBlockManagerAuditor auditor(this);
177 ASSERT(Common::IsAligned(address, PageSize));
178 ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
179 KMemoryAttribute::None);
180
181 VAddr cur_address = address;
182 size_t remaining_pages = num_pages;
183 iterator it = this->FindIterator(address);
184
185 while (remaining_pages > 0) {
186 const size_t remaining_size = remaining_pages * PageSize;
187 KMemoryInfo cur_info = it->GetMemoryInfo();
188 if (it->HasProperties(test_state, test_perm, test_attr) &&
189 !it->HasProperties(state, perm, attr)) {
190 // If we need to, create a new block before and insert it.
191 if (cur_info.GetAddress() != cur_address) {
192 KMemoryBlock* new_block = allocator->Allocate();
193
194 it->Split(new_block, cur_address);
195 it = m_memory_block_tree.insert(*new_block);
196 it++;
197
198 cur_info = it->GetMemoryInfo();
199 cur_address = cur_info.GetAddress();
200 }
112 201
113 while (node != memory_block_tree.end()) { 202 // If we need to, create a new block after and insert it.
114 KMemoryBlock* block{&(*node)}; 203 if (cur_info.GetSize() > remaining_size) {
115 iterator next_node{std::next(node)}; 204 KMemoryBlock* new_block = allocator->Allocate();
116 const VAddr cur_addr{block->GetAddress()};
117 const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
118 205
119 if (addr < cur_end_addr && cur_addr < update_end_addr) { 206 it->Split(new_block, cur_address + remaining_size);
120 iterator new_node{node}; 207 it = m_memory_block_tree.insert(*new_block);
121 208
122 if (addr > cur_addr) { 209 cur_info = it->GetMemoryInfo();
123 memory_block_tree.insert(node, block->Split(addr));
124 } 210 }
125 211
126 if (update_end_addr < cur_end_addr) { 212 // Update block state.
127 new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); 213 it->Update(state, perm, attr, false, 0, 0);
214 cur_address += cur_info.GetSize();
215 remaining_pages -= cur_info.GetNumPages();
216 } else {
217 // If we already have the right properties, just advance.
218 if (cur_address + remaining_size < cur_info.GetEndAddress()) {
219 remaining_pages = 0;
220 cur_address += remaining_size;
221 } else {
222 remaining_pages =
223 (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
224 cur_address = cur_info.GetEndAddress();
128 } 225 }
129
130 new_node->Update(state, perm, attribute);
131
132 MergeAdjacent(new_node, next_node);
133 }
134
135 if (cur_end_addr - 1 >= update_end_addr - 1) {
136 break;
137 } 226 }
138 227 it++;
139 node = next_node;
140 } 228 }
229
230 this->CoalesceForUpdate(allocator, address, num_pages);
141} 231}
142 232
143void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, 233void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
234 size_t num_pages, MemoryBlockLockFunction lock_func,
144 KMemoryPermission perm) { 235 KMemoryPermission perm) {
145 const VAddr update_end_addr{addr + num_pages * PageSize}; 236 // Ensure for auditing that we never end up with an invalid tree.
146 iterator node{memory_block_tree.begin()}; 237 KScopedMemoryBlockManagerAuditor auditor(this);
238 ASSERT(Common::IsAligned(address, PageSize));
147 239
148 while (node != memory_block_tree.end()) { 240 VAddr cur_address = address;
149 KMemoryBlock* block{&(*node)}; 241 size_t remaining_pages = num_pages;
150 iterator next_node{std::next(node)}; 242 iterator it = this->FindIterator(address);
151 const VAddr cur_addr{block->GetAddress()};
152 const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
153 243
154 if (addr < cur_end_addr && cur_addr < update_end_addr) { 244 const VAddr end_address = address + (num_pages * PageSize);
155 iterator new_node{node};
156 245
157 if (addr > cur_addr) { 246 while (remaining_pages > 0) {
158 memory_block_tree.insert(node, block->Split(addr)); 247 const size_t remaining_size = remaining_pages * PageSize;
159 } 248 KMemoryInfo cur_info = it->GetMemoryInfo();
160 249
161 if (update_end_addr < cur_end_addr) { 250 // If we need to, create a new block before and insert it.
162 new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); 251 if (cur_info.m_address != cur_address) {
163 } 252 KMemoryBlock* new_block = allocator->Allocate();
164 253
165 lock_func(new_node, perm); 254 it->Split(new_block, cur_address);
255 it = m_memory_block_tree.insert(*new_block);
256 it++;
166 257
167 MergeAdjacent(new_node, next_node); 258 cur_info = it->GetMemoryInfo();
259 cur_address = cur_info.GetAddress();
168 } 260 }
169 261
170 if (cur_end_addr - 1 >= update_end_addr - 1) { 262 if (cur_info.GetSize() > remaining_size) {
171 break; 263 // If we need to, create a new block after and insert it.
264 KMemoryBlock* new_block = allocator->Allocate();
265
266 it->Split(new_block, cur_address + remaining_size);
267 it = m_memory_block_tree.insert(*new_block);
268
269 cur_info = it->GetMemoryInfo();
172 } 270 }
173 271
174 node = next_node; 272 // Call the locked update function.
273 (std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address,
274 cur_info.GetEndAddress() == end_address);
275 cur_address += cur_info.GetSize();
276 remaining_pages -= cur_info.GetNumPages();
277 it++;
175 } 278 }
176}
177 279
178void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { 280 this->CoalesceForUpdate(allocator, address, num_pages);
179 const_iterator it{FindIterator(start)};
180 KMemoryInfo info{};
181 do {
182 info = it->GetMemoryInfo();
183 func(info);
184 it = std::next(it);
185 } while (info.addr + info.size - 1 < end - 1 && it != cend());
186} 281}
187 282
188void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) { 283// Debug.
189 KMemoryBlock* block{&(*it)}; 284bool KMemoryBlockManager::CheckState() const {
190 285 // Loop over every block, ensuring that we are sorted and coalesced.
191 auto EraseIt = [&](const iterator it_to_erase) { 286 auto it = m_memory_block_tree.cbegin();
192 if (next_it == it_to_erase) { 287 auto prev = it++;
193 next_it = std::next(next_it); 288 while (it != m_memory_block_tree.cend()) {
289 const KMemoryInfo prev_info = prev->GetMemoryInfo();
290 const KMemoryInfo cur_info = it->GetMemoryInfo();
291
292 // Sequential blocks which can be merged should be merged.
293 if (prev->CanMergeWith(*it)) {
294 return false;
194 } 295 }
195 memory_block_tree.erase(it_to_erase);
196 };
197 296
198 if (it != memory_block_tree.begin()) { 297 // Sequential blocks should be sequential.
199 KMemoryBlock* prev{&(*std::prev(it))}; 298 if (prev_info.GetEndAddress() != cur_info.GetAddress()) {
200 299 return false;
201 if (block->HasSameProperties(*prev)) { 300 }
202 const iterator prev_it{std::prev(it)};
203 301
204 prev->Add(block->GetNumPages()); 302 // If the block is ipc locked, it must have a count.
205 EraseIt(it); 303 if ((cur_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
304 cur_info.m_ipc_lock_count == 0) {
305 return false;
306 }
206 307
207 it = prev_it; 308 // If the block is device shared, it must have a count.
208 block = prev; 309 if ((cur_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
310 cur_info.m_device_use_count == 0) {
311 return false;
209 } 312 }
313
314 // Advance the iterator.
315 prev = it++;
210 } 316 }
211 317
212 if (it != cend()) { 318 // Our loop will miss checking the last block, potentially, so check it.
213 const KMemoryBlock* const next{&(*std::next(it))}; 319 if (prev != m_memory_block_tree.cend()) {
320 const KMemoryInfo prev_info = prev->GetMemoryInfo();
321 // If the block is ipc locked, it must have a count.
322 if ((prev_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
323 prev_info.m_ipc_lock_count == 0) {
324 return false;
325 }
214 326
215 if (block->HasSameProperties(*next)) { 327 // If the block is device shared, it must have a count.
216 block->Add(next->GetNumPages()); 328 if ((prev_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
217 EraseIt(std::next(it)); 329 prev_info.m_device_use_count == 0) {
330 return false;
218 } 331 }
219 } 332 }
333
334 return true;
220} 335}
221 336
222} // namespace Kernel 337} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index e14741b89..9b5873883 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -4,63 +4,154 @@
4#pragma once 4#pragma once
5 5
6#include <functional> 6#include <functional>
7#include <list>
8 7
8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/k_dynamic_resource_manager.h"
10#include "core/hle/kernel/k_memory_block.h" 11#include "core/hle/kernel/k_memory_block.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
15class KMemoryBlockManagerUpdateAllocator {
16public:
17 static constexpr size_t MaxBlocks = 2;
18
19private:
20 KMemoryBlock* m_blocks[MaxBlocks];
21 size_t m_index;
22 KMemoryBlockSlabManager* m_slab_manager;
23
24private:
25 Result Initialize(size_t num_blocks) {
26 // Check num blocks.
27 ASSERT(num_blocks <= MaxBlocks);
28
29 // Set index.
30 m_index = MaxBlocks - num_blocks;
31
32 // Allocate the blocks.
33 for (size_t i = 0; i < num_blocks && i < MaxBlocks; ++i) {
34 m_blocks[m_index + i] = m_slab_manager->Allocate();
35 R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource);
36 }
37
38 R_SUCCEED();
39 }
40
41public:
42 KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm,
43 size_t num_blocks = MaxBlocks)
44 : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) {
45 *out_result = this->Initialize(num_blocks);
46 }
47
48 ~KMemoryBlockManagerUpdateAllocator() {
49 for (const auto& block : m_blocks) {
50 if (block != nullptr) {
51 m_slab_manager->Free(block);
52 }
53 }
54 }
55
56 KMemoryBlock* Allocate() {
57 ASSERT(m_index < MaxBlocks);
58 ASSERT(m_blocks[m_index] != nullptr);
59 KMemoryBlock* block = nullptr;
60 std::swap(block, m_blocks[m_index++]);
61 return block;
62 }
63
64 void Free(KMemoryBlock* block) {
65 ASSERT(m_index <= MaxBlocks);
66 ASSERT(block != nullptr);
67 if (m_index == 0) {
68 m_slab_manager->Free(block);
69 } else {
70 m_blocks[--m_index] = block;
71 }
72 }
73};
74
14class KMemoryBlockManager final { 75class KMemoryBlockManager final {
15public: 76public:
16 using MemoryBlockTree = std::list<KMemoryBlock>; 77 using MemoryBlockTree =
78 Common::IntrusiveRedBlackTreeBaseTraits<KMemoryBlock>::TreeType<KMemoryBlock>;
79 using MemoryBlockLockFunction = void (KMemoryBlock::*)(KMemoryPermission new_perm, bool left,
80 bool right);
17 using iterator = MemoryBlockTree::iterator; 81 using iterator = MemoryBlockTree::iterator;
18 using const_iterator = MemoryBlockTree::const_iterator; 82 using const_iterator = MemoryBlockTree::const_iterator;
19 83
20public: 84public:
21 KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_); 85 KMemoryBlockManager();
86
87 using HostUnmapCallback = std::function<void(VAddr, u64)>;
88
89 Result Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager);
90 void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback);
22 91
23 iterator end() { 92 iterator end() {
24 return memory_block_tree.end(); 93 return m_memory_block_tree.end();
25 } 94 }
26 const_iterator end() const { 95 const_iterator end() const {
27 return memory_block_tree.end(); 96 return m_memory_block_tree.end();
28 } 97 }
29 const_iterator cend() const { 98 const_iterator cend() const {
30 return memory_block_tree.cend(); 99 return m_memory_block_tree.cend();
31 } 100 }
32 101
33 iterator FindIterator(VAddr addr); 102 VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
103 size_t alignment, size_t offset, size_t guard_pages) const;
34 104
35 VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, 105 void Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
36 std::size_t align, std::size_t offset, std::size_t guard_pages); 106 KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr,
107 KMemoryBlockDisableMergeAttribute set_disable_attr,
108 KMemoryBlockDisableMergeAttribute clear_disable_attr);
109 void UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
110 MemoryBlockLockFunction lock_func, KMemoryPermission perm);
37 111
38 void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, 112 void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
39 KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state, 113 size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
40 KMemoryPermission perm, KMemoryAttribute attribute); 114 KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
115 KMemoryAttribute attr);
41 116
42 void Update(VAddr addr, std::size_t num_pages, KMemoryState state, 117 iterator FindIterator(VAddr address) const {
43 KMemoryPermission perm = KMemoryPermission::None, 118 return m_memory_block_tree.find(KMemoryBlock(
44 KMemoryAttribute attribute = KMemoryAttribute::None); 119 address, 1, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None));
45 120 }
46 using LockFunc = std::function<void(iterator, KMemoryPermission)>;
47 void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
48 KMemoryPermission perm);
49 121
50 using IterateFunc = std::function<void(const KMemoryInfo&)>; 122 const KMemoryBlock* FindBlock(VAddr address) const {
51 void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); 123 if (const_iterator it = this->FindIterator(address); it != m_memory_block_tree.end()) {
124 return std::addressof(*it);
125 }
52 126
53 KMemoryBlock& FindBlock(VAddr addr) { 127 return nullptr;
54 return *FindIterator(addr);
55 } 128 }
56 129
130 // Debug.
131 bool CheckState() const;
132
57private: 133private:
58 void MergeAdjacent(iterator it, iterator& next_it); 134 void CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
135 size_t num_pages);
59 136
60 [[maybe_unused]] const VAddr start_addr; 137 MemoryBlockTree m_memory_block_tree;
61 [[maybe_unused]] const VAddr end_addr; 138 VAddr m_start_address{};
139 VAddr m_end_address{};
140};
62 141
63 MemoryBlockTree memory_block_tree; 142class KScopedMemoryBlockManagerAuditor {
143public:
144 explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) {
145 ASSERT(m_manager->CheckState());
146 }
147 explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m)
148 : KScopedMemoryBlockManagerAuditor(std::addressof(m)) {}
149 ~KScopedMemoryBlockManagerAuditor() {
150 ASSERT(m_manager->CheckState());
151 }
152
153private:
154 KMemoryBlockManager* m_manager;
64}; 155};
65 156
66} // namespace Kernel 157} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 5b0a9963a..646711505 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -331,7 +331,7 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag
331 331
332 // Set all the allocated memory. 332 // Set all the allocated memory.
333 for (const auto& block : out->Nodes()) { 333 for (const auto& block : out->Nodes()) {
334 std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, 334 std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
335 block.GetSize()); 335 block.GetSize());
336 } 336 }
337 337
diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp
index 1a0bf4439..0c16dded4 100644
--- a/src/core/hle/kernel/k_page_buffer.cpp
+++ b/src/core/hle/kernel/k_page_buffer.cpp
@@ -12,7 +12,7 @@ namespace Kernel {
12 12
13KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { 13KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
14 ASSERT(Common::IsAligned(phys_addr, PageSize)); 14 ASSERT(Common::IsAligned(phys_addr, PageSize));
15 return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr)); 15 return system.DeviceMemory().GetPointer<KPageBuffer>(phys_addr);
16} 16}
17 17
18} // namespace Kernel 18} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h
index 7e50dc1d1..aef06e213 100644
--- a/src/core/hle/kernel/k_page_buffer.h
+++ b/src/core/hle/kernel/k_page_buffer.h
@@ -13,6 +13,7 @@ namespace Kernel {
13 13
14class KPageBuffer final : public KSlabAllocated<KPageBuffer> { 14class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
15public: 15public:
16 explicit KPageBuffer(KernelCore&) {}
16 KPageBuffer() = default; 17 KPageBuffer() = default;
17 18
18 static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); 19 static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index d975de844..307e491cb 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -25,7 +25,7 @@ namespace {
25 25
26using namespace Common::Literals; 26using namespace Common::Literals;
27 27
28constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { 28constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
29 switch (as_type) { 29 switch (as_type) {
30 case FileSys::ProgramAddressSpaceType::Is32Bit: 30 case FileSys::ProgramAddressSpaceType::Is32Bit:
31 case FileSys::ProgramAddressSpaceType::Is32BitNoMap: 31 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
@@ -43,27 +43,29 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
43} // namespace 43} // namespace
44 44
45KPageTable::KPageTable(Core::System& system_) 45KPageTable::KPageTable(Core::System& system_)
46 : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {} 46 : m_general_lock{system_.Kernel()},
47 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {}
47 48
48KPageTable::~KPageTable() = default; 49KPageTable::~KPageTable() = default;
49 50
50Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, 51Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
51 VAddr code_addr, std::size_t code_size, 52 VAddr code_addr, size_t code_size,
53 KMemoryBlockSlabManager* mem_block_slab_manager,
52 KMemoryManager::Pool pool) { 54 KMemoryManager::Pool pool) {
53 55
54 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { 56 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
55 return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); 57 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
56 }; 58 };
57 const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) { 59 const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) {
58 return KAddressSpaceInfo::GetAddressSpaceSize(address_space_width, type); 60 return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type);
59 }; 61 };
60 62
61 // Set our width and heap/alias sizes 63 // Set our width and heap/alias sizes
62 address_space_width = GetAddressSpaceWidthFromType(as_type); 64 m_address_space_width = GetAddressSpaceWidthFromType(as_type);
63 const VAddr start = 0; 65 const VAddr start = 0;
64 const VAddr end{1ULL << address_space_width}; 66 const VAddr end{1ULL << m_address_space_width};
65 std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; 67 size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
66 std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; 68 size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
67 69
68 ASSERT(code_addr < code_addr + code_size); 70 ASSERT(code_addr < code_addr + code_size);
69 ASSERT(code_addr + code_size - 1 <= end - 1); 71 ASSERT(code_addr + code_size - 1 <= end - 1);
@@ -75,66 +77,65 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
75 } 77 }
76 78
77 // Set code regions and determine remaining 79 // Set code regions and determine remaining
78 constexpr std::size_t RegionAlignment{2_MiB}; 80 constexpr size_t RegionAlignment{2_MiB};
79 VAddr process_code_start{}; 81 VAddr process_code_start{};
80 VAddr process_code_end{}; 82 VAddr process_code_end{};
81 std::size_t stack_region_size{}; 83 size_t stack_region_size{};
82 std::size_t kernel_map_region_size{}; 84 size_t kernel_map_region_size{};
83 85
84 if (address_space_width == 39) { 86 if (m_address_space_width == 39) {
85 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); 87 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
86 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); 88 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
87 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); 89 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
88 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); 90 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
89 code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); 91 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
90 code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); 92 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
91 alias_code_region_start = code_region_start; 93 m_alias_code_region_start = m_code_region_start;
92 alias_code_region_end = code_region_end; 94 m_alias_code_region_end = m_code_region_end;
93 process_code_start = Common::AlignDown(code_addr, RegionAlignment); 95 process_code_start = Common::AlignDown(code_addr, RegionAlignment);
94 process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment); 96 process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment);
95 } else { 97 } else {
96 stack_region_size = 0; 98 stack_region_size = 0;
97 kernel_map_region_size = 0; 99 kernel_map_region_size = 0;
98 code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); 100 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
99 code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); 101 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
100 stack_region_start = code_region_start; 102 m_stack_region_start = m_code_region_start;
101 alias_code_region_start = code_region_start; 103 m_alias_code_region_start = m_code_region_start;
102 alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + 104 m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
103 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); 105 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
104 stack_region_end = code_region_end; 106 m_stack_region_end = m_code_region_end;
105 kernel_map_region_start = code_region_start; 107 m_kernel_map_region_start = m_code_region_start;
106 kernel_map_region_end = code_region_end; 108 m_kernel_map_region_end = m_code_region_end;
107 process_code_start = code_region_start; 109 process_code_start = m_code_region_start;
108 process_code_end = code_region_end; 110 process_code_end = m_code_region_end;
109 } 111 }
110 112
111 // Set other basic fields 113 // Set other basic fields
112 is_aslr_enabled = enable_aslr; 114 m_enable_aslr = enable_aslr;
113 address_space_start = start; 115 m_enable_device_address_space_merge = false;
114 address_space_end = end; 116 m_address_space_start = start;
115 is_kernel = false; 117 m_address_space_end = end;
118 m_is_kernel = false;
119 m_memory_block_slab_manager = mem_block_slab_manager;
116 120
117 // Determine the region we can place our undetermineds in 121 // Determine the region we can place our undetermineds in
118 VAddr alloc_start{}; 122 VAddr alloc_start{};
119 std::size_t alloc_size{}; 123 size_t alloc_size{};
120 if ((process_code_start - code_region_start) >= (end - process_code_end)) { 124 if ((process_code_start - m_code_region_start) >= (end - process_code_end)) {
121 alloc_start = code_region_start; 125 alloc_start = m_code_region_start;
122 alloc_size = process_code_start - code_region_start; 126 alloc_size = process_code_start - m_code_region_start;
123 } else { 127 } else {
124 alloc_start = process_code_end; 128 alloc_start = process_code_end;
125 alloc_size = end - process_code_end; 129 alloc_size = end - process_code_end;
126 } 130 }
127 const std::size_t needed_size{ 131 const size_t needed_size =
128 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; 132 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
129 if (alloc_size < needed_size) { 133 R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory);
130 ASSERT(false);
131 return ResultOutOfMemory;
132 }
133 134
134 const std::size_t remaining_size{alloc_size - needed_size}; 135 const size_t remaining_size{alloc_size - needed_size};
135 136
136 // Determine random placements for each region 137 // Determine random placements for each region
137 std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; 138 size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
138 if (enable_aslr) { 139 if (enable_aslr) {
139 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * 140 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
140 RegionAlignment; 141 RegionAlignment;
@@ -147,117 +148,130 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
147 } 148 }
148 149
149 // Setup heap and alias regions 150 // Setup heap and alias regions
150 alias_region_start = alloc_start + alias_rnd; 151 m_alias_region_start = alloc_start + alias_rnd;
151 alias_region_end = alias_region_start + alias_region_size; 152 m_alias_region_end = m_alias_region_start + alias_region_size;
152 heap_region_start = alloc_start + heap_rnd; 153 m_heap_region_start = alloc_start + heap_rnd;
153 heap_region_end = heap_region_start + heap_region_size; 154 m_heap_region_end = m_heap_region_start + heap_region_size;
154 155
155 if (alias_rnd <= heap_rnd) { 156 if (alias_rnd <= heap_rnd) {
156 heap_region_start += alias_region_size; 157 m_heap_region_start += alias_region_size;
157 heap_region_end += alias_region_size; 158 m_heap_region_end += alias_region_size;
158 } else { 159 } else {
159 alias_region_start += heap_region_size; 160 m_alias_region_start += heap_region_size;
160 alias_region_end += heap_region_size; 161 m_alias_region_end += heap_region_size;
161 } 162 }
162 163
163 // Setup stack region 164 // Setup stack region
164 if (stack_region_size) { 165 if (stack_region_size) {
165 stack_region_start = alloc_start + stack_rnd; 166 m_stack_region_start = alloc_start + stack_rnd;
166 stack_region_end = stack_region_start + stack_region_size; 167 m_stack_region_end = m_stack_region_start + stack_region_size;
167 168
168 if (alias_rnd < stack_rnd) { 169 if (alias_rnd < stack_rnd) {
169 stack_region_start += alias_region_size; 170 m_stack_region_start += alias_region_size;
170 stack_region_end += alias_region_size; 171 m_stack_region_end += alias_region_size;
171 } else { 172 } else {
172 alias_region_start += stack_region_size; 173 m_alias_region_start += stack_region_size;
173 alias_region_end += stack_region_size; 174 m_alias_region_end += stack_region_size;
174 } 175 }
175 176
176 if (heap_rnd < stack_rnd) { 177 if (heap_rnd < stack_rnd) {
177 stack_region_start += heap_region_size; 178 m_stack_region_start += heap_region_size;
178 stack_region_end += heap_region_size; 179 m_stack_region_end += heap_region_size;
179 } else { 180 } else {
180 heap_region_start += stack_region_size; 181 m_heap_region_start += stack_region_size;
181 heap_region_end += stack_region_size; 182 m_heap_region_end += stack_region_size;
182 } 183 }
183 } 184 }
184 185
185 // Setup kernel map region 186 // Setup kernel map region
186 if (kernel_map_region_size) { 187 if (kernel_map_region_size) {
187 kernel_map_region_start = alloc_start + kmap_rnd; 188 m_kernel_map_region_start = alloc_start + kmap_rnd;
188 kernel_map_region_end = kernel_map_region_start + kernel_map_region_size; 189 m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size;
189 190
190 if (alias_rnd < kmap_rnd) { 191 if (alias_rnd < kmap_rnd) {
191 kernel_map_region_start += alias_region_size; 192 m_kernel_map_region_start += alias_region_size;
192 kernel_map_region_end += alias_region_size; 193 m_kernel_map_region_end += alias_region_size;
193 } else { 194 } else {
194 alias_region_start += kernel_map_region_size; 195 m_alias_region_start += kernel_map_region_size;
195 alias_region_end += kernel_map_region_size; 196 m_alias_region_end += kernel_map_region_size;
196 } 197 }
197 198
198 if (heap_rnd < kmap_rnd) { 199 if (heap_rnd < kmap_rnd) {
199 kernel_map_region_start += heap_region_size; 200 m_kernel_map_region_start += heap_region_size;
200 kernel_map_region_end += heap_region_size; 201 m_kernel_map_region_end += heap_region_size;
201 } else { 202 } else {
202 heap_region_start += kernel_map_region_size; 203 m_heap_region_start += kernel_map_region_size;
203 heap_region_end += kernel_map_region_size; 204 m_heap_region_end += kernel_map_region_size;
204 } 205 }
205 206
206 if (stack_region_size) { 207 if (stack_region_size) {
207 if (stack_rnd < kmap_rnd) { 208 if (stack_rnd < kmap_rnd) {
208 kernel_map_region_start += stack_region_size; 209 m_kernel_map_region_start += stack_region_size;
209 kernel_map_region_end += stack_region_size; 210 m_kernel_map_region_end += stack_region_size;
210 } else { 211 } else {
211 stack_region_start += kernel_map_region_size; 212 m_stack_region_start += kernel_map_region_size;
212 stack_region_end += kernel_map_region_size; 213 m_stack_region_end += kernel_map_region_size;
213 } 214 }
214 } 215 }
215 } 216 }
216 217
217 // Set heap members 218 // Set heap members
218 current_heap_end = heap_region_start; 219 m_current_heap_end = m_heap_region_start;
219 max_heap_size = 0; 220 m_max_heap_size = 0;
220 max_physical_memory_size = 0; 221 m_max_physical_memory_size = 0;
221 222
222 // Ensure that we regions inside our address space 223 // Ensure that we regions inside our address space
223 auto IsInAddressSpace = [&](VAddr addr) { 224 auto IsInAddressSpace = [&](VAddr addr) {
224 return address_space_start <= addr && addr <= address_space_end; 225 return m_address_space_start <= addr && addr <= m_address_space_end;
225 }; 226 };
226 ASSERT(IsInAddressSpace(alias_region_start)); 227 ASSERT(IsInAddressSpace(m_alias_region_start));
227 ASSERT(IsInAddressSpace(alias_region_end)); 228 ASSERT(IsInAddressSpace(m_alias_region_end));
228 ASSERT(IsInAddressSpace(heap_region_start)); 229 ASSERT(IsInAddressSpace(m_heap_region_start));
229 ASSERT(IsInAddressSpace(heap_region_end)); 230 ASSERT(IsInAddressSpace(m_heap_region_end));
230 ASSERT(IsInAddressSpace(stack_region_start)); 231 ASSERT(IsInAddressSpace(m_stack_region_start));
231 ASSERT(IsInAddressSpace(stack_region_end)); 232 ASSERT(IsInAddressSpace(m_stack_region_end));
232 ASSERT(IsInAddressSpace(kernel_map_region_start)); 233 ASSERT(IsInAddressSpace(m_kernel_map_region_start));
233 ASSERT(IsInAddressSpace(kernel_map_region_end)); 234 ASSERT(IsInAddressSpace(m_kernel_map_region_end));
234 235
235 // Ensure that we selected regions that don't overlap 236 // Ensure that we selected regions that don't overlap
236 const VAddr alias_start{alias_region_start}; 237 const VAddr alias_start{m_alias_region_start};
237 const VAddr alias_last{alias_region_end - 1}; 238 const VAddr alias_last{m_alias_region_end - 1};
238 const VAddr heap_start{heap_region_start}; 239 const VAddr heap_start{m_heap_region_start};
239 const VAddr heap_last{heap_region_end - 1}; 240 const VAddr heap_last{m_heap_region_end - 1};
240 const VAddr stack_start{stack_region_start}; 241 const VAddr stack_start{m_stack_region_start};
241 const VAddr stack_last{stack_region_end - 1}; 242 const VAddr stack_last{m_stack_region_end - 1};
242 const VAddr kmap_start{kernel_map_region_start}; 243 const VAddr kmap_start{m_kernel_map_region_start};
243 const VAddr kmap_last{kernel_map_region_end - 1}; 244 const VAddr kmap_last{m_kernel_map_region_end - 1};
244 ASSERT(alias_last < heap_start || heap_last < alias_start); 245 ASSERT(alias_last < heap_start || heap_last < alias_start);
245 ASSERT(alias_last < stack_start || stack_last < alias_start); 246 ASSERT(alias_last < stack_start || stack_last < alias_start);
246 ASSERT(alias_last < kmap_start || kmap_last < alias_start); 247 ASSERT(alias_last < kmap_start || kmap_last < alias_start);
247 ASSERT(heap_last < stack_start || stack_last < heap_start); 248 ASSERT(heap_last < stack_start || stack_last < heap_start);
248 ASSERT(heap_last < kmap_start || kmap_last < heap_start); 249 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
249 250
250 current_heap_end = heap_region_start; 251 m_current_heap_end = m_heap_region_start;
251 max_heap_size = 0; 252 m_max_heap_size = 0;
252 mapped_physical_memory_size = 0; 253 m_mapped_physical_memory_size = 0;
253 memory_pool = pool; 254 m_memory_pool = pool;
255
256 m_page_table_impl = std::make_unique<Common::PageTable>();
257 m_page_table_impl->Resize(m_address_space_width, PageBits);
258
259 // Initialize our memory block manager.
260 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
261 m_memory_block_slab_manager));
262}
254 263
255 page_table_impl.Resize(address_space_width, PageBits); 264void KPageTable::Finalize() {
265 // Finalize memory blocks.
266 m_memory_block_manager.Finalize(m_memory_block_slab_manager, [&](VAddr addr, u64 size) {
267 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size);
268 });
256 269
257 return InitializeMemoryLayout(start, end); 270 // Close the backing page table, as the destructor is not called for guest objects.
271 m_page_table_impl.reset();
258} 272}
259 273
260Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, 274Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState state,
261 KMemoryPermission perm) { 275 KMemoryPermission perm) {
262 const u64 size{num_pages * PageSize}; 276 const u64 size{num_pages * PageSize};
263 277
@@ -265,52 +279,76 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat
265 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); 279 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
266 280
267 // Lock the table. 281 // Lock the table.
268 KScopedLightLock lk(general_lock); 282 KScopedLightLock lk(m_general_lock);
269 283
270 // Verify that the destination memory is unmapped. 284 // Verify that the destination memory is unmapped.
271 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, 285 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
272 KMemoryPermission::None, KMemoryPermission::None, 286 KMemoryPermission::None, KMemoryPermission::None,
273 KMemoryAttribute::None, KMemoryAttribute::None)); 287 KMemoryAttribute::None, KMemoryAttribute::None));
288
289 // Create an update allocator.
290 Result allocator_result{ResultSuccess};
291 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
292 m_memory_block_slab_manager);
293
294 // Allocate and open.
274 KPageGroup pg; 295 KPageGroup pg;
275 R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( 296 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
276 &pg, num_pages, 297 &pg, num_pages,
277 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); 298 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
278 299
279 R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); 300 R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
280 301
281 block_manager->Update(addr, num_pages, state, perm); 302 // Update the blocks.
303 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
304 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
305 KMemoryBlockDisableMergeAttribute::None);
282 306
283 return ResultSuccess; 307 R_SUCCEED();
284} 308}
285 309
286Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { 310Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size) {
287 // Validate the mapping request. 311 // Validate the mapping request.
288 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), 312 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
289 ResultInvalidMemoryRegion); 313 ResultInvalidMemoryRegion);
290 314
291 // Lock the table. 315 // Lock the table.
292 KScopedLightLock lk(general_lock); 316 KScopedLightLock lk(m_general_lock);
293 317
294 // Verify that the source memory is normal heap. 318 // Verify that the source memory is normal heap.
295 KMemoryState src_state{}; 319 KMemoryState src_state{};
296 KMemoryPermission src_perm{}; 320 KMemoryPermission src_perm{};
297 std::size_t num_src_allocator_blocks{}; 321 size_t num_src_allocator_blocks{};
298 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, 322 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
299 src_address, size, KMemoryState::All, KMemoryState::Normal, 323 src_address, size, KMemoryState::All, KMemoryState::Normal,
300 KMemoryPermission::All, KMemoryPermission::UserReadWrite, 324 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
301 KMemoryAttribute::All, KMemoryAttribute::None)); 325 KMemoryAttribute::All, KMemoryAttribute::None));
302 326
303 // Verify that the destination memory is unmapped. 327 // Verify that the destination memory is unmapped.
304 std::size_t num_dst_allocator_blocks{}; 328 size_t num_dst_allocator_blocks{};
305 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, 329 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
306 KMemoryState::Free, KMemoryPermission::None, 330 KMemoryState::Free, KMemoryPermission::None,
307 KMemoryPermission::None, KMemoryAttribute::None, 331 KMemoryPermission::None, KMemoryAttribute::None,
308 KMemoryAttribute::None)); 332 KMemoryAttribute::None));
309 333
334 // Create an update allocator for the source.
335 Result src_allocator_result{ResultSuccess};
336 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
337 m_memory_block_slab_manager,
338 num_src_allocator_blocks);
339 R_TRY(src_allocator_result);
340
341 // Create an update allocator for the destination.
342 Result dst_allocator_result{ResultSuccess};
343 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
344 m_memory_block_slab_manager,
345 num_dst_allocator_blocks);
346 R_TRY(dst_allocator_result);
347
310 // Map the code memory. 348 // Map the code memory.
311 { 349 {
312 // Determine the number of pages being operated on. 350 // Determine the number of pages being operated on.
313 const std::size_t num_pages = size / PageSize; 351 const size_t num_pages = size / PageSize;
314 352
315 // Create page groups for the memory being mapped. 353 // Create page groups for the memory being mapped.
316 KPageGroup pg; 354 KPageGroup pg;
@@ -335,33 +373,37 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size
335 unprot_guard.Cancel(); 373 unprot_guard.Cancel();
336 374
337 // Apply the memory block updates. 375 // Apply the memory block updates.
338 block_manager->Update(src_address, num_pages, src_state, new_perm, 376 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
339 KMemoryAttribute::Locked); 377 src_state, new_perm, KMemoryAttribute::Locked,
340 block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, 378 KMemoryBlockDisableMergeAttribute::Locked,
341 KMemoryAttribute::None); 379 KMemoryBlockDisableMergeAttribute::None);
380 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
381 KMemoryState::AliasCode, new_perm, KMemoryAttribute::None,
382 KMemoryBlockDisableMergeAttribute::Normal,
383 KMemoryBlockDisableMergeAttribute::None);
342 } 384 }
343 385
344 return ResultSuccess; 386 R_SUCCEED();
345} 387}
346 388
347Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, 389Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size,
348 ICacheInvalidationStrategy icache_invalidation_strategy) { 390 ICacheInvalidationStrategy icache_invalidation_strategy) {
349 // Validate the mapping request. 391 // Validate the mapping request.
350 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), 392 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
351 ResultInvalidMemoryRegion); 393 ResultInvalidMemoryRegion);
352 394
353 // Lock the table. 395 // Lock the table.
354 KScopedLightLock lk(general_lock); 396 KScopedLightLock lk(m_general_lock);
355 397
356 // Verify that the source memory is locked normal heap. 398 // Verify that the source memory is locked normal heap.
357 std::size_t num_src_allocator_blocks{}; 399 size_t num_src_allocator_blocks{};
358 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, 400 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
359 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, 401 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
360 KMemoryPermission::None, KMemoryAttribute::All, 402 KMemoryPermission::None, KMemoryAttribute::All,
361 KMemoryAttribute::Locked)); 403 KMemoryAttribute::Locked));
362 404
363 // Verify that the destination memory is aliasable code. 405 // Verify that the destination memory is aliasable code.
364 std::size_t num_dst_allocator_blocks{}; 406 size_t num_dst_allocator_blocks{};
365 R_TRY(this->CheckMemoryStateContiguous( 407 R_TRY(this->CheckMemoryStateContiguous(
366 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, 408 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
367 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, 409 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
@@ -370,7 +412,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si
370 // Determine whether any pages being unmapped are code. 412 // Determine whether any pages being unmapped are code.
371 bool any_code_pages = false; 413 bool any_code_pages = false;
372 { 414 {
373 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); 415 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
374 while (true) { 416 while (true) {
375 // Get the memory info. 417 // Get the memory info.
376 const KMemoryInfo info = it->GetMemoryInfo(); 418 const KMemoryInfo info = it->GetMemoryInfo();
@@ -396,9 +438,9 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si
396 SCOPE_EXIT({ 438 SCOPE_EXIT({
397 if (reprotected_pages && any_code_pages) { 439 if (reprotected_pages && any_code_pages) {
398 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { 440 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
399 system.InvalidateCpuInstructionCacheRange(dst_address, size); 441 m_system.InvalidateCpuInstructionCacheRange(dst_address, size);
400 } else { 442 } else {
401 system.InvalidateCpuInstructionCaches(); 443 m_system.InvalidateCpuInstructionCaches();
402 } 444 }
403 } 445 }
404 }); 446 });
@@ -406,7 +448,21 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si
406 // Unmap. 448 // Unmap.
407 { 449 {
408 // Determine the number of pages being operated on. 450 // Determine the number of pages being operated on.
409 const std::size_t num_pages = size / PageSize; 451 const size_t num_pages = size / PageSize;
452
453 // Create an update allocator for the source.
454 Result src_allocator_result{ResultSuccess};
455 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
456 m_memory_block_slab_manager,
457 num_src_allocator_blocks);
458 R_TRY(src_allocator_result);
459
460 // Create an update allocator for the destination.
461 Result dst_allocator_result{ResultSuccess};
462 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
463 m_memory_block_slab_manager,
464 num_dst_allocator_blocks);
465 R_TRY(dst_allocator_result);
410 466
411 // Unmap the aliased copy of the pages. 467 // Unmap the aliased copy of the pages.
412 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 468 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
@@ -416,73 +472,34 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si
416 OperationType::ChangePermissions)); 472 OperationType::ChangePermissions));
417 473
418 // Apply the memory block updates. 474 // Apply the memory block updates.
419 block_manager->Update(dst_address, num_pages, KMemoryState::None); 475 m_memory_block_manager.Update(
420 block_manager->Update(src_address, num_pages, KMemoryState::Normal, 476 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
421 KMemoryPermission::UserReadWrite); 477 KMemoryPermission::None, KMemoryAttribute::None,
478 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
479 m_memory_block_manager.Update(
480 std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal,
481 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
482 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
422 483
423 // Note that we reprotected pages. 484 // Note that we reprotected pages.
424 reprotected_pages = true; 485 reprotected_pages = true;
425 } 486 }
426 487
427 return ResultSuccess; 488 R_SUCCEED();
428} 489}
429 490
430VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, 491VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
431 std::size_t num_pages, std::size_t alignment, std::size_t offset, 492 size_t alignment, size_t offset, size_t guard_pages) {
432 std::size_t guard_pages) {
433 VAddr address = 0; 493 VAddr address = 0;
434 494
435 if (num_pages <= region_num_pages) { 495 if (num_pages <= region_num_pages) {
436 if (this->IsAslrEnabled()) { 496 if (this->IsAslrEnabled()) {
437 // Try to directly find a free area up to 8 times. 497 UNIMPLEMENTED();
438 for (std::size_t i = 0; i < 8; i++) {
439 const std::size_t random_offset =
440 KSystemControl::GenerateRandomRange(
441 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
442 alignment;
443 const VAddr candidate =
444 Common::AlignDown((region_start + random_offset), alignment) + offset;
445
446 KMemoryInfo info = this->QueryInfoImpl(candidate);
447
448 if (info.state != KMemoryState::Free) {
449 continue;
450 }
451 if (region_start > candidate) {
452 continue;
453 }
454 if (info.GetAddress() + guard_pages * PageSize > candidate) {
455 continue;
456 }
457
458 const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
459 if (candidate_end > info.GetLastAddress()) {
460 continue;
461 }
462 if (candidate_end > region_start + region_num_pages * PageSize - 1) {
463 continue;
464 }
465
466 address = candidate;
467 break;
468 }
469 // Fall back to finding the first free area with a random offset.
470 if (address == 0) {
471 // NOTE: Nintendo does not account for guard pages here.
472 // This may theoretically cause an offset to be chosen that cannot be mapped. We
473 // will account for guard pages.
474 const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
475 0, region_num_pages - num_pages - guard_pages);
476 address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
477 region_num_pages - offset_pages, num_pages,
478 alignment, offset, guard_pages);
479 }
480 } 498 }
481
482 // Find the first free area. 499 // Find the first free area.
483 if (address == 0) { 500 if (address == 0) {
484 address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, 501 address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages,
485 alignment, offset, guard_pages); 502 alignment, offset, guard_pages);
486 } 503 }
487 } 504 }
488 505
@@ -500,7 +517,8 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
500 // Begin traversal. 517 // Begin traversal.
501 Common::PageTable::TraversalContext context; 518 Common::PageTable::TraversalContext context;
502 Common::PageTable::TraversalEntry next_entry; 519 Common::PageTable::TraversalEntry next_entry;
503 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); 520 R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, addr),
521 ResultInvalidCurrentMemory);
504 522
505 // Prepare tracking variables. 523 // Prepare tracking variables.
506 PAddr cur_addr = next_entry.phys_addr; 524 PAddr cur_addr = next_entry.phys_addr;
@@ -508,9 +526,9 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
508 size_t tot_size = cur_size; 526 size_t tot_size = cur_size;
509 527
510 // Iterate, adding to group as we go. 528 // Iterate, adding to group as we go.
511 const auto& memory_layout = system.Kernel().MemoryLayout(); 529 const auto& memory_layout = m_system.Kernel().MemoryLayout();
512 while (tot_size < size) { 530 while (tot_size < size) {
513 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), 531 R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context),
514 ResultInvalidCurrentMemory); 532 ResultInvalidCurrentMemory);
515 533
516 if (next_entry.phys_addr != (cur_addr + cur_size)) { 534 if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -538,7 +556,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
538 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); 556 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
539 R_TRY(pg.AddBlock(cur_addr, cur_pages)); 557 R_TRY(pg.AddBlock(cur_addr, cur_pages));
540 558
541 return ResultSuccess; 559 R_SUCCEED();
542} 560}
543 561
544bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { 562bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) {
@@ -546,7 +564,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu
546 564
547 const size_t size = num_pages * PageSize; 565 const size_t size = num_pages * PageSize;
548 const auto& pg = pg_ll.Nodes(); 566 const auto& pg = pg_ll.Nodes();
549 const auto& memory_layout = system.Kernel().MemoryLayout(); 567 const auto& memory_layout = m_system.Kernel().MemoryLayout();
550 568
551 // Empty groups are necessarily invalid. 569 // Empty groups are necessarily invalid.
552 if (pg.empty()) { 570 if (pg.empty()) {
@@ -573,7 +591,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu
573 // Begin traversal. 591 // Begin traversal.
574 Common::PageTable::TraversalContext context; 592 Common::PageTable::TraversalContext context;
575 Common::PageTable::TraversalEntry next_entry; 593 Common::PageTable::TraversalEntry next_entry;
576 if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { 594 if (!m_page_table_impl->BeginTraversal(next_entry, context, addr)) {
577 return false; 595 return false;
578 } 596 }
579 597
@@ -584,7 +602,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu
584 602
585 // Iterate, comparing expected to actual. 603 // Iterate, comparing expected to actual.
586 while (tot_size < size) { 604 while (tot_size < size) {
587 if (!page_table_impl.ContinueTraversal(next_entry, context)) { 605 if (!m_page_table_impl->ContinueTraversal(next_entry, context)) {
588 return false; 606 return false;
589 } 607 }
590 608
@@ -630,11 +648,11 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu
630 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); 648 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
631} 649}
632 650
633Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 651Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table,
634 VAddr src_addr) { 652 VAddr src_addr) {
635 KScopedLightLock lk(general_lock); 653 KScopedLightLock lk(m_general_lock);
636 654
637 const std::size_t num_pages{size / PageSize}; 655 const size_t num_pages{size / PageSize};
638 656
639 // Check that the memory is mapped in the destination process. 657 // Check that the memory is mapped in the destination process.
640 size_t num_allocator_blocks; 658 size_t num_allocator_blocks;
@@ -649,43 +667,51 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTab
649 KMemoryPermission::None, KMemoryAttribute::All, 667 KMemoryPermission::None, KMemoryAttribute::All,
650 KMemoryAttribute::None)); 668 KMemoryAttribute::None));
651 669
670 // Create an update allocator.
671 Result allocator_result{ResultSuccess};
672 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
673 m_memory_block_slab_manager, num_allocator_blocks);
674 R_TRY(allocator_result);
675
652 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); 676 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
653 677
654 // Apply the memory block update. 678 // Apply the memory block update.
655 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 679 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages,
656 KMemoryAttribute::None); 680 KMemoryState::Free, KMemoryPermission::None,
681 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
682 KMemoryBlockDisableMergeAttribute::Normal);
657 683
658 system.InvalidateCpuInstructionCaches(); 684 m_system.InvalidateCpuInstructionCaches();
659 685
660 return ResultSuccess; 686 R_SUCCEED();
661} 687}
662 688
663Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { 689Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
664 // Lock the physical memory lock. 690 // Lock the physical memory lock.
665 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); 691 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
666 692
667 // Calculate the last address for convenience. 693 // Calculate the last address for convenience.
668 const VAddr last_address = address + size - 1; 694 const VAddr last_address = address + size - 1;
669 695
670 // Define iteration variables. 696 // Define iteration variables.
671 VAddr cur_address; 697 VAddr cur_address;
672 std::size_t mapped_size; 698 size_t mapped_size;
673 699
674 // The entire mapping process can be retried. 700 // The entire mapping process can be retried.
675 while (true) { 701 while (true) {
676 // Check if the memory is already mapped. 702 // Check if the memory is already mapped.
677 { 703 {
678 // Lock the table. 704 // Lock the table.
679 KScopedLightLock lk(general_lock); 705 KScopedLightLock lk(m_general_lock);
680 706
681 // Iterate over the memory. 707 // Iterate over the memory.
682 cur_address = address; 708 cur_address = address;
683 mapped_size = 0; 709 mapped_size = 0;
684 710
685 auto it = block_manager->FindIterator(cur_address); 711 auto it = m_memory_block_manager.FindIterator(cur_address);
686 while (true) { 712 while (true) {
687 // Check that the iterator is valid. 713 // Check that the iterator is valid.
688 ASSERT(it != block_manager->end()); 714 ASSERT(it != m_memory_block_manager.end());
689 715
690 // Get the memory info. 716 // Get the memory info.
691 const KMemoryInfo info = it->GetMemoryInfo(); 717 const KMemoryInfo info = it->GetMemoryInfo();
@@ -716,20 +742,20 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
716 { 742 {
717 // Reserve the memory from the process resource limit. 743 // Reserve the memory from the process resource limit.
718 KScopedResourceReservation memory_reservation( 744 KScopedResourceReservation memory_reservation(
719 system.Kernel().CurrentProcess()->GetResourceLimit(), 745 m_system.Kernel().CurrentProcess()->GetResourceLimit(),
720 LimitableResource::PhysicalMemory, size - mapped_size); 746 LimitableResource::PhysicalMemory, size - mapped_size);
721 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 747 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
722 748
723 // Allocate pages for the new memory. 749 // Allocate pages for the new memory.
724 KPageGroup pg; 750 KPageGroup pg;
725 R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( 751 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess(
726 &pg, (size - mapped_size) / PageSize, 752 &pg, (size - mapped_size) / PageSize,
727 KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); 753 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
728 754
729 // Map the memory. 755 // Map the memory.
730 { 756 {
731 // Lock the table. 757 // Lock the table.
732 KScopedLightLock lk(general_lock); 758 KScopedLightLock lk(m_general_lock);
733 759
734 size_t num_allocator_blocks = 0; 760 size_t num_allocator_blocks = 0;
735 761
@@ -739,10 +765,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
739 size_t checked_mapped_size = 0; 765 size_t checked_mapped_size = 0;
740 cur_address = address; 766 cur_address = address;
741 767
742 auto it = block_manager->FindIterator(cur_address); 768 auto it = m_memory_block_manager.FindIterator(cur_address);
743 while (true) { 769 while (true) {
744 // Check that the iterator is valid. 770 // Check that the iterator is valid.
745 ASSERT(it != block_manager->end()); 771 ASSERT(it != m_memory_block_manager.end());
746 772
747 // Get the memory info. 773 // Get the memory info.
748 const KMemoryInfo info = it->GetMemoryInfo(); 774 const KMemoryInfo info = it->GetMemoryInfo();
@@ -782,6 +808,14 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
782 } 808 }
783 } 809 }
784 810
811 // Create an update allocator.
812 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
813 Result allocator_result{ResultSuccess};
814 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
815 m_memory_block_slab_manager,
816 num_allocator_blocks);
817 R_TRY(allocator_result);
818
785 // Reset the current tracking address, and make sure we clean up on failure. 819 // Reset the current tracking address, and make sure we clean up on failure.
786 cur_address = address; 820 cur_address = address;
787 auto unmap_guard = detail::ScopeExit([&] { 821 auto unmap_guard = detail::ScopeExit([&] {
@@ -791,10 +825,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
791 // Iterate, unmapping the pages. 825 // Iterate, unmapping the pages.
792 cur_address = address; 826 cur_address = address;
793 827
794 auto it = block_manager->FindIterator(cur_address); 828 auto it = m_memory_block_manager.FindIterator(cur_address);
795 while (true) { 829 while (true) {
796 // Check that the iterator is valid. 830 // Check that the iterator is valid.
797 ASSERT(it != block_manager->end()); 831 ASSERT(it != m_memory_block_manager.end());
798 832
799 // Get the memory info. 833 // Get the memory info.
800 const KMemoryInfo info = it->GetMemoryInfo(); 834 const KMemoryInfo info = it->GetMemoryInfo();
@@ -830,10 +864,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
830 PAddr pg_phys_addr = pg_it->GetAddress(); 864 PAddr pg_phys_addr = pg_it->GetAddress();
831 size_t pg_pages = pg_it->GetNumPages(); 865 size_t pg_pages = pg_it->GetNumPages();
832 866
833 auto it = block_manager->FindIterator(cur_address); 867 auto it = m_memory_block_manager.FindIterator(cur_address);
834 while (true) { 868 while (true) {
835 // Check that the iterator is valid. 869 // Check that the iterator is valid.
836 ASSERT(it != block_manager->end()); 870 ASSERT(it != m_memory_block_manager.end());
837 871
838 // Get the memory info. 872 // Get the memory info.
839 const KMemoryInfo info = it->GetMemoryInfo(); 873 const KMemoryInfo info = it->GetMemoryInfo();
@@ -886,37 +920,37 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
886 memory_reservation.Commit(); 920 memory_reservation.Commit();
887 921
888 // Increase our tracked mapped size. 922 // Increase our tracked mapped size.
889 mapped_physical_memory_size += (size - mapped_size); 923 m_mapped_physical_memory_size += (size - mapped_size);
890 924
891 // Update the relevant memory blocks. 925 // Update the relevant memory blocks.
892 block_manager->Update(address, size / PageSize, KMemoryState::Free, 926 m_memory_block_manager.UpdateIfMatch(
893 KMemoryPermission::None, KMemoryAttribute::None, 927 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
894 KMemoryState::Normal, KMemoryPermission::UserReadWrite, 928 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
895 KMemoryAttribute::None); 929 KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
896 930
897 // Cancel our guard. 931 // Cancel our guard.
898 unmap_guard.Cancel(); 932 unmap_guard.Cancel();
899 933
900 return ResultSuccess; 934 R_SUCCEED();
901 } 935 }
902 } 936 }
903 } 937 }
904} 938}
905 939
906Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { 940Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
907 // Lock the physical memory lock. 941 // Lock the physical memory lock.
908 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); 942 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
909 943
910 // Lock the table. 944 // Lock the table.
911 KScopedLightLock lk(general_lock); 945 KScopedLightLock lk(m_general_lock);
912 946
913 // Calculate the last address for convenience. 947 // Calculate the last address for convenience.
914 const VAddr last_address = address + size - 1; 948 const VAddr last_address = address + size - 1;
915 949
916 // Define iteration variables. 950 // Define iteration variables.
917 VAddr cur_address = 0; 951 VAddr cur_address = 0;
918 std::size_t mapped_size = 0; 952 size_t mapped_size = 0;
919 std::size_t num_allocator_blocks = 0; 953 size_t num_allocator_blocks = 0;
920 954
921 // Check if the memory is mapped. 955 // Check if the memory is mapped.
922 { 956 {
@@ -924,10 +958,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
924 cur_address = address; 958 cur_address = address;
925 mapped_size = 0; 959 mapped_size = 0;
926 960
927 auto it = block_manager->FindIterator(cur_address); 961 auto it = m_memory_block_manager.FindIterator(cur_address);
928 while (true) { 962 while (true) {
929 // Check that the iterator is valid. 963 // Check that the iterator is valid.
930 ASSERT(it != block_manager->end()); 964 ASSERT(it != m_memory_block_manager.end());
931 965
932 // Get the memory info. 966 // Get the memory info.
933 const KMemoryInfo info = it->GetMemoryInfo(); 967 const KMemoryInfo info = it->GetMemoryInfo();
@@ -1022,6 +1056,13 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
1022 } 1056 }
1023 ASSERT(pg.GetNumPages() == mapped_size / PageSize); 1057 ASSERT(pg.GetNumPages() == mapped_size / PageSize);
1024 1058
1059 // Create an update allocator.
1060 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1061 Result allocator_result{ResultSuccess};
1062 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1063 m_memory_block_slab_manager, num_allocator_blocks);
1064 R_TRY(allocator_result);
1065
1025 // Reset the current tracking address, and make sure we clean up on failure. 1066 // Reset the current tracking address, and make sure we clean up on failure.
1026 cur_address = address; 1067 cur_address = address;
1027 auto remap_guard = detail::ScopeExit([&] { 1068 auto remap_guard = detail::ScopeExit([&] {
@@ -1030,7 +1071,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
1030 cur_address = address; 1071 cur_address = address;
1031 1072
1032 // Iterate over the memory we unmapped. 1073 // Iterate over the memory we unmapped.
1033 auto it = block_manager->FindIterator(cur_address); 1074 auto it = m_memory_block_manager.FindIterator(cur_address);
1034 auto pg_it = pg.Nodes().begin(); 1075 auto pg_it = pg.Nodes().begin();
1035 PAddr pg_phys_addr = pg_it->GetAddress(); 1076 PAddr pg_phys_addr = pg_it->GetAddress();
1036 size_t pg_pages = pg_it->GetNumPages(); 1077 size_t pg_pages = pg_it->GetNumPages();
@@ -1085,10 +1126,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
1085 }); 1126 });
1086 1127
1087 // Iterate over the memory, unmapping as we go. 1128 // Iterate over the memory, unmapping as we go.
1088 auto it = block_manager->FindIterator(cur_address); 1129 auto it = m_memory_block_manager.FindIterator(cur_address);
1089 while (true) { 1130 while (true) {
1090 // Check that the iterator is valid. 1131 // Check that the iterator is valid.
1091 ASSERT(it != block_manager->end()); 1132 ASSERT(it != m_memory_block_manager.end());
1092 1133
1093 // Get the memory info. 1134 // Get the memory info.
1094 const KMemoryInfo info = it->GetMemoryInfo(); 1135 const KMemoryInfo info = it->GetMemoryInfo();
@@ -1115,104 +1156,159 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
1115 } 1156 }
1116 1157
1117 // Release the memory resource. 1158 // Release the memory resource.
1118 mapped_physical_memory_size -= mapped_size; 1159 m_mapped_physical_memory_size -= mapped_size;
1119 auto process{system.Kernel().CurrentProcess()}; 1160 auto process{m_system.Kernel().CurrentProcess()};
1120 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); 1161 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
1121 1162
1122 // Update memory blocks. 1163 // Update memory blocks.
1123 block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, 1164 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
1124 KMemoryAttribute::None); 1165 KMemoryState::Free, KMemoryPermission::None,
1166 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1167 KMemoryBlockDisableMergeAttribute::None);
1125 1168
1126 // TODO(bunnei): This is a workaround until the next set of changes, where we add reference 1169 // TODO(bunnei): This is a workaround until the next set of changes, where we add reference
1127 // counting for mapped pages. Until then, we must manually close the reference to the page 1170 // counting for mapped pages. Until then, we must manually close the reference to the page
1128 // group. 1171 // group.
1129 system.Kernel().MemoryManager().Close(pg); 1172 m_system.Kernel().MemoryManager().Close(pg);
1130 1173
1131 // We succeeded. 1174 // We succeeded.
1132 remap_guard.Cancel(); 1175 remap_guard.Cancel();
1133 1176
1134 return ResultSuccess; 1177 R_SUCCEED();
1135} 1178}
1136 1179
1137Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 1180Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) {
1138 KScopedLightLock lk(general_lock); 1181 // Lock the table.
1139 1182 KScopedLightLock lk(m_general_lock);
1140 KMemoryState src_state{}; 1183
1141 CASCADE_CODE(CheckMemoryState( 1184 // Validate that the source address's state is valid.
1142 &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, 1185 KMemoryState src_state;
1143 KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite, 1186 size_t num_src_allocator_blocks;
1144 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 1187 R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr,
1188 std::addressof(num_src_allocator_blocks), src_address, size,
1189 KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1190 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1191 KMemoryAttribute::All, KMemoryAttribute::None));
1145 1192
1146 if (IsRegionMapped(dst_addr, size)) { 1193 // Validate that the dst address's state is valid.
1147 return ResultInvalidCurrentMemory; 1194 size_t num_dst_allocator_blocks;
1148 } 1195 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
1196 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1197 KMemoryPermission::None, KMemoryAttribute::None,
1198 KMemoryAttribute::None));
1149 1199
1200 // Create an update allocator for the source.
1201 Result src_allocator_result{ResultSuccess};
1202 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1203 m_memory_block_slab_manager,
1204 num_src_allocator_blocks);
1205 R_TRY(src_allocator_result);
1206
1207 // Create an update allocator for the destination.
1208 Result dst_allocator_result{ResultSuccess};
1209 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1210 m_memory_block_slab_manager,
1211 num_dst_allocator_blocks);
1212 R_TRY(dst_allocator_result);
1213
1214 // Map the memory.
1150 KPageGroup page_linked_list; 1215 KPageGroup page_linked_list;
1151 const std::size_t num_pages{size / PageSize}; 1216 const size_t num_pages{size / PageSize};
1152 1217 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1153 AddRegionToPages(src_addr, num_pages, page_linked_list); 1218 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1219 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1154 1220
1221 AddRegionToPages(src_address, num_pages, page_linked_list);
1155 { 1222 {
1223 // Reprotect the source as kernel-read/not mapped.
1156 auto block_guard = detail::ScopeExit([&] { 1224 auto block_guard = detail::ScopeExit([&] {
1157 Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, 1225 Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
1158 OperationType::ChangePermissions); 1226 OperationType::ChangePermissions);
1159 }); 1227 });
1160 1228 R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions));
1161 CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, 1229 R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite));
1162 OperationType::ChangePermissions));
1163 CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite));
1164 1230
1165 block_guard.Cancel(); 1231 block_guard.Cancel();
1166 } 1232 }
1167 1233
1168 block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None, 1234 // Apply the memory block updates.
1169 KMemoryAttribute::Locked); 1235 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state,
1170 block_manager->Update(dst_addr, num_pages, KMemoryState::Stack, 1236 new_src_perm, new_src_attr,
1171 KMemoryPermission::UserReadWrite); 1237 KMemoryBlockDisableMergeAttribute::Locked,
1172 1238 KMemoryBlockDisableMergeAttribute::None);
1173 return ResultSuccess; 1239 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
1240 KMemoryState::Stack, KMemoryPermission::UserReadWrite,
1241 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1242 KMemoryBlockDisableMergeAttribute::None);
1243
1244 R_SUCCEED();
1174} 1245}
1175 1246
1176Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 1247Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) {
1177 KScopedLightLock lk(general_lock); 1248 // Lock the table.
1249 KScopedLightLock lk(m_general_lock);
1250
1251 // Validate that the source address's state is valid.
1252 KMemoryState src_state;
1253 size_t num_src_allocator_blocks;
1254 R_TRY(this->CheckMemoryState(
1255 std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks),
1256 src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1257 KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
1258 KMemoryAttribute::All, KMemoryAttribute::Locked));
1259
1260 // Validate that the dst address's state is valid.
1261 KMemoryPermission dst_perm;
1262 size_t num_dst_allocator_blocks;
1263 R_TRY(this->CheckMemoryState(
1264 nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks),
1265 dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
1266 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1178 1267
1179 KMemoryState src_state{}; 1268 // Create an update allocator for the source.
1180 CASCADE_CODE(CheckMemoryState( 1269 Result src_allocator_result{ResultSuccess};
1181 &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, 1270 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1182 KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, 1271 m_memory_block_slab_manager,
1183 KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); 1272 num_src_allocator_blocks);
1273 R_TRY(src_allocator_result);
1184 1274
1185 KMemoryPermission dst_perm{}; 1275 // Create an update allocator for the destination.
1186 CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size, 1276 Result dst_allocator_result{ResultSuccess};
1187 KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, 1277 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1188 KMemoryPermission::None, KMemoryAttribute::Mask, 1278 m_memory_block_slab_manager,
1189 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 1279 num_dst_allocator_blocks);
1280 R_TRY(dst_allocator_result);
1190 1281
1191 KPageGroup src_pages; 1282 KPageGroup src_pages;
1192 KPageGroup dst_pages; 1283 KPageGroup dst_pages;
1193 const std::size_t num_pages{size / PageSize}; 1284 const size_t num_pages{size / PageSize};
1194 1285
1195 AddRegionToPages(src_addr, num_pages, src_pages); 1286 AddRegionToPages(src_address, num_pages, src_pages);
1196 AddRegionToPages(dst_addr, num_pages, dst_pages); 1287 AddRegionToPages(dst_address, num_pages, dst_pages);
1197 1288
1198 if (!dst_pages.IsEqual(src_pages)) { 1289 R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion);
1199 return ResultInvalidMemoryRegion;
1200 }
1201 1290
1202 { 1291 {
1203 auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); 1292 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); });
1204 1293
1205 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); 1294 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
1206 CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, 1295 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
1207 OperationType::ChangePermissions)); 1296 OperationType::ChangePermissions));
1208 1297
1209 block_guard.Cancel(); 1298 block_guard.Cancel();
1210 } 1299 }
1211 1300
1212 block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite); 1301 // Apply the memory block updates.
1213 block_manager->Update(dst_addr, num_pages, KMemoryState::Free); 1302 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state,
1214 1303 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1215 return ResultSuccess; 1304 KMemoryBlockDisableMergeAttribute::None,
1305 KMemoryBlockDisableMergeAttribute::Locked);
1306 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
1307 KMemoryState::None, KMemoryPermission::None,
1308 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1309 KMemoryBlockDisableMergeAttribute::Normal);
1310
1311 R_SUCCEED();
1216} 1312}
1217 1313
1218Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, 1314Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
@@ -1225,48 +1321,54 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
1225 if (const auto result{ 1321 if (const auto result{
1226 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 1322 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
1227 result.IsError()) { 1323 result.IsError()) {
1228 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 1324 const size_t num_pages{(addr - cur_addr) / PageSize};
1229 1325
1230 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) 1326 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)
1231 .IsSuccess()); 1327 .IsSuccess());
1232 1328
1233 return result; 1329 R_RETURN(result);
1234 } 1330 }
1235 1331
1236 cur_addr += node.GetNumPages() * PageSize; 1332 cur_addr += node.GetNumPages() * PageSize;
1237 } 1333 }
1238 1334
1239 return ResultSuccess; 1335 R_SUCCEED();
1240} 1336}
1241 1337
1242Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, 1338Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state,
1243 KMemoryPermission perm) { 1339 KMemoryPermission perm) {
1244 // Check that the map is in range. 1340 // Check that the map is in range.
1245 const std::size_t num_pages{page_linked_list.GetNumPages()}; 1341 const size_t num_pages{page_linked_list.GetNumPages()};
1246 const std::size_t size{num_pages * PageSize}; 1342 const size_t size{num_pages * PageSize};
1247 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); 1343 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
1248 1344
1249 // Lock the table. 1345 // Lock the table.
1250 KScopedLightLock lk(general_lock); 1346 KScopedLightLock lk(m_general_lock);
1251 1347
1252 // Check the memory state. 1348 // Check the memory state.
1253 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 1349 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
1254 KMemoryPermission::None, KMemoryPermission::None, 1350 KMemoryPermission::None, KMemoryPermission::None,
1255 KMemoryAttribute::None, KMemoryAttribute::None)); 1351 KMemoryAttribute::None, KMemoryAttribute::None));
1256 1352
1353 // Create an update allocator.
1354 Result allocator_result{ResultSuccess};
1355 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1356 m_memory_block_slab_manager);
1357
1257 // Map the pages. 1358 // Map the pages.
1258 R_TRY(MapPages(address, page_linked_list, perm)); 1359 R_TRY(MapPages(address, page_linked_list, perm));
1259 1360
1260 // Update the blocks. 1361 // Update the blocks.
1261 block_manager->Update(address, num_pages, state, perm); 1362 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
1363 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1364 KMemoryBlockDisableMergeAttribute::None);
1262 1365
1263 return ResultSuccess; 1366 R_SUCCEED();
1264} 1367}
1265 1368
1266Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, 1369Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
1267 PAddr phys_addr, bool is_pa_valid, VAddr region_start, 1370 bool is_pa_valid, VAddr region_start, size_t region_num_pages,
1268 std::size_t region_num_pages, KMemoryState state, 1371 KMemoryState state, KMemoryPermission perm) {
1269 KMemoryPermission perm) {
1270 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); 1372 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
1271 1373
1272 // Ensure this is a valid map request. 1374 // Ensure this is a valid map request.
@@ -1275,7 +1377,7 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t
1275 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); 1377 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
1276 1378
1277 // Lock the table. 1379 // Lock the table.
1278 KScopedLightLock lk(general_lock); 1380 KScopedLightLock lk(m_general_lock);
1279 1381
1280 // Find a random address to map at. 1382 // Find a random address to map at.
1281 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, 1383 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
@@ -1288,6 +1390,11 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t
1288 KMemoryAttribute::None, KMemoryAttribute::None) 1390 KMemoryAttribute::None, KMemoryAttribute::None)
1289 .IsSuccess()); 1391 .IsSuccess());
1290 1392
1393 // Create an update allocator.
1394 Result allocator_result{ResultSuccess};
1395 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1396 m_memory_block_slab_manager);
1397
1291 // Perform mapping operation. 1398 // Perform mapping operation.
1292 if (is_pa_valid) { 1399 if (is_pa_valid) {
1293 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); 1400 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
@@ -1296,11 +1403,13 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t
1296 } 1403 }
1297 1404
1298 // Update the blocks. 1405 // Update the blocks.
1299 block_manager->Update(addr, num_pages, state, perm); 1406 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
1407 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1408 KMemoryBlockDisableMergeAttribute::None);
1300 1409
1301 // We successfully mapped the pages. 1410 // We successfully mapped the pages.
1302 *out_addr = addr; 1411 *out_addr = addr;
1303 return ResultSuccess; 1412 R_SUCCEED();
1304} 1413}
1305 1414
1306Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { 1415Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
@@ -1312,60 +1421,80 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
1312 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 1421 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
1313 OperationType::Unmap)}; 1422 OperationType::Unmap)};
1314 result.IsError()) { 1423 result.IsError()) {
1315 return result; 1424 R_RETURN(result);
1316 } 1425 }
1317 1426
1318 cur_addr += node.GetNumPages() * PageSize; 1427 cur_addr += node.GetNumPages() * PageSize;
1319 } 1428 }
1320 1429
1321 return ResultSuccess; 1430 R_SUCCEED();
1322} 1431}
1323 1432
1324Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) { 1433Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) {
1325 // Check that the unmap is in range. 1434 // Check that the unmap is in range.
1326 const std::size_t num_pages{page_linked_list.GetNumPages()}; 1435 const size_t num_pages{page_linked_list.GetNumPages()};
1327 const std::size_t size{num_pages * PageSize}; 1436 const size_t size{num_pages * PageSize};
1328 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); 1437 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1329 1438
1330 // Lock the table. 1439 // Lock the table.
1331 KScopedLightLock lk(general_lock); 1440 KScopedLightLock lk(m_general_lock);
1332 1441
1333 // Check the memory state. 1442 // Check the memory state.
1334 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, 1443 size_t num_allocator_blocks;
1444 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1445 KMemoryState::All, state, KMemoryPermission::None,
1335 KMemoryPermission::None, KMemoryAttribute::All, 1446 KMemoryPermission::None, KMemoryAttribute::All,
1336 KMemoryAttribute::None)); 1447 KMemoryAttribute::None));
1337 1448
1449 // Create an update allocator.
1450 Result allocator_result{ResultSuccess};
1451 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1452 m_memory_block_slab_manager, num_allocator_blocks);
1453 R_TRY(allocator_result);
1454
1338 // Perform the unmap. 1455 // Perform the unmap.
1339 R_TRY(UnmapPages(addr, page_linked_list)); 1456 R_TRY(UnmapPages(address, page_linked_list));
1340 1457
1341 // Update the blocks. 1458 // Update the blocks.
1342 block_manager->Update(addr, num_pages, state, KMemoryPermission::None); 1459 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
1460 KMemoryPermission::None, KMemoryAttribute::None,
1461 KMemoryBlockDisableMergeAttribute::None,
1462 KMemoryBlockDisableMergeAttribute::Normal);
1343 1463
1344 return ResultSuccess; 1464 R_SUCCEED();
1345} 1465}
1346 1466
1347Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { 1467Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) {
1348 // Check that the unmap is in range. 1468 // Check that the unmap is in range.
1349 const std::size_t size = num_pages * PageSize; 1469 const size_t size = num_pages * PageSize;
1350 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 1470 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1351 1471
1352 // Lock the table. 1472 // Lock the table.
1353 KScopedLightLock lk(general_lock); 1473 KScopedLightLock lk(m_general_lock);
1354 1474
1355 // Check the memory state. 1475 // Check the memory state.
1356 std::size_t num_allocator_blocks{}; 1476 size_t num_allocator_blocks{};
1357 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, 1477 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1358 KMemoryState::All, state, KMemoryPermission::None, 1478 KMemoryState::All, state, KMemoryPermission::None,
1359 KMemoryPermission::None, KMemoryAttribute::All, 1479 KMemoryPermission::None, KMemoryAttribute::All,
1360 KMemoryAttribute::None)); 1480 KMemoryAttribute::None));
1361 1481
1482 // Create an update allocator.
1483 Result allocator_result{ResultSuccess};
1484 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1485 m_memory_block_slab_manager, num_allocator_blocks);
1486 R_TRY(allocator_result);
1487
1362 // Perform the unmap. 1488 // Perform the unmap.
1363 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 1489 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
1364 1490
1365 // Update the blocks. 1491 // Update the blocks.
1366 block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); 1492 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
1493 KMemoryPermission::None, KMemoryAttribute::None,
1494 KMemoryBlockDisableMergeAttribute::None,
1495 KMemoryBlockDisableMergeAttribute::Normal);
1367 1496
1368 return ResultSuccess; 1497 R_SUCCEED();
1369} 1498}
1370 1499
1371Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, 1500Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
@@ -1380,7 +1509,7 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n
1380 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 1509 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1381 1510
1382 // Lock the table. 1511 // Lock the table.
1383 KScopedLightLock lk(general_lock); 1512 KScopedLightLock lk(m_general_lock);
1384 1513
1385 // Check if state allows us to create the group. 1514 // Check if state allows us to create the group.
1386 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, 1515 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
@@ -1390,15 +1519,15 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n
1390 // Create a new page group for the region. 1519 // Create a new page group for the region.
1391 R_TRY(this->MakePageGroup(*out, address, num_pages)); 1520 R_TRY(this->MakePageGroup(*out, address, num_pages));
1392 1521
1393 return ResultSuccess; 1522 R_SUCCEED();
1394} 1523}
1395 1524
1396Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1525Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
1397 Svc::MemoryPermission svc_perm) { 1526 Svc::MemoryPermission svc_perm) {
1398 const size_t num_pages = size / PageSize; 1527 const size_t num_pages = size / PageSize;
1399 1528
1400 // Lock the table. 1529 // Lock the table.
1401 KScopedLightLock lk(general_lock); 1530 KScopedLightLock lk(m_general_lock);
1402 1531
1403 // Verify we can change the memory permission. 1532 // Verify we can change the memory permission.
1404 KMemoryState old_state; 1533 KMemoryState old_state;
@@ -1435,105 +1564,101 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1435 // Succeed if there's nothing to do. 1564 // Succeed if there's nothing to do.
1436 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); 1565 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
1437 1566
1567 // Create an update allocator.
1568 Result allocator_result{ResultSuccess};
1569 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1570 m_memory_block_slab_manager, num_allocator_blocks);
1571 R_TRY(allocator_result);
1572
1438 // Perform mapping operation. 1573 // Perform mapping operation.
1439 const auto operation = 1574 const auto operation =
1440 was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions; 1575 was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions;
1441 R_TRY(Operate(addr, num_pages, new_perm, operation)); 1576 R_TRY(Operate(addr, num_pages, new_perm, operation));
1442 1577
1443 // Update the blocks. 1578 // Update the blocks.
1444 block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None); 1579 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm,
1580 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1581 KMemoryBlockDisableMergeAttribute::None);
1445 1582
1446 // Ensure cache coherency, if we're setting pages as executable. 1583 // Ensure cache coherency, if we're setting pages as executable.
1447 if (is_x) { 1584 if (is_x) {
1448 system.InvalidateCpuInstructionCacheRange(addr, size); 1585 m_system.InvalidateCpuInstructionCacheRange(addr, size);
1449 } 1586 }
1450 1587
1451 return ResultSuccess; 1588 R_SUCCEED();
1452} 1589}
1453 1590
1454KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { 1591KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
1455 KScopedLightLock lk(general_lock); 1592 KScopedLightLock lk(m_general_lock);
1456 1593
1457 return block_manager->FindBlock(addr).GetMemoryInfo(); 1594 return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo();
1458} 1595}
1459 1596
1460KMemoryInfo KPageTable::QueryInfo(VAddr addr) { 1597KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
1461 if (!Contains(addr, 1)) { 1598 if (!Contains(addr, 1)) {
1462 return {address_space_end, 0 - address_space_end, KMemoryState::Inaccessible, 1599 return {
1463 KMemoryPermission::None, KMemoryAttribute::None, KMemoryPermission::None}; 1600 .m_address = m_address_space_end,
1601 .m_size = 0 - m_address_space_end,
1602 .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible),
1603 .m_device_disable_merge_left_count = 0,
1604 .m_device_disable_merge_right_count = 0,
1605 .m_ipc_lock_count = 0,
1606 .m_device_use_count = 0,
1607 .m_ipc_disable_merge_count = 0,
1608 .m_permission = KMemoryPermission::None,
1609 .m_attribute = KMemoryAttribute::None,
1610 .m_original_permission = KMemoryPermission::None,
1611 .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None,
1612 };
1464 } 1613 }
1465 1614
1466 return QueryInfoImpl(addr); 1615 return QueryInfoImpl(addr);
1467} 1616}
1468 1617
1469Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { 1618Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) {
1470 KScopedLightLock lk(general_lock);
1471
1472 KMemoryState state{};
1473 KMemoryAttribute attribute{};
1474
1475 R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size,
1476 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
1477 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
1478 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1479 KMemoryAttribute::Mask, KMemoryAttribute::None,
1480 KMemoryAttribute::IpcAndDeviceMapped));
1481
1482 block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
1483
1484 return ResultSuccess;
1485}
1486
1487Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
1488 KScopedLightLock lk(general_lock);
1489
1490 KMemoryState state{};
1491
1492 R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
1493 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
1494 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
1495 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
1496 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
1497
1498 block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite);
1499 return ResultSuccess;
1500}
1501
1502Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
1503 Svc::MemoryPermission svc_perm) {
1504 const size_t num_pages = size / PageSize; 1619 const size_t num_pages = size / PageSize;
1505 1620
1506 // Lock the table. 1621 // Lock the table.
1507 KScopedLightLock lk(general_lock); 1622 KScopedLightLock lk(m_general_lock);
1508 1623
1509 // Verify we can change the memory permission. 1624 // Verify we can change the memory permission.
1510 KMemoryState old_state; 1625 KMemoryState old_state;
1511 KMemoryPermission old_perm; 1626 KMemoryPermission old_perm;
1512 R_TRY(this->CheckMemoryState( 1627 size_t num_allocator_blocks;
1513 std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size, 1628 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
1514 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, 1629 std::addressof(num_allocator_blocks), addr, size,
1515 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); 1630 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect,
1631 KMemoryPermission::None, KMemoryPermission::None,
1632 KMemoryAttribute::All, KMemoryAttribute::None));
1516 1633
1517 // Determine new perm. 1634 // Determine new perm.
1518 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); 1635 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
1519 R_SUCCEED_IF(old_perm == new_perm); 1636 R_SUCCEED_IF(old_perm == new_perm);
1520 1637
1638 // Create an update allocator.
1639 Result allocator_result{ResultSuccess};
1640 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1641 m_memory_block_slab_manager, num_allocator_blocks);
1642 R_TRY(allocator_result);
1643
1521 // Perform mapping operation. 1644 // Perform mapping operation.
1522 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); 1645 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
1523 1646
1524 // Update the blocks. 1647 // Update the blocks.
1525 block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None); 1648 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
1649 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1650 KMemoryBlockDisableMergeAttribute::None);
1526 1651
1527 return ResultSuccess; 1652 R_SUCCEED();
1528} 1653}
1529 1654
1530Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { 1655Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr) {
1531 const size_t num_pages = size / PageSize; 1656 const size_t num_pages = size / PageSize;
1532 ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == 1657 ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
1533 KMemoryAttribute::SetMask); 1658 KMemoryAttribute::SetMask);
1534 1659
1535 // Lock the table. 1660 // Lock the table.
1536 KScopedLightLock lk(general_lock); 1661 KScopedLightLock lk(m_general_lock);
1537 1662
1538 // Verify we can change the memory attribute. 1663 // Verify we can change the memory attribute.
1539 KMemoryState old_state; 1664 KMemoryState old_state;
@@ -1548,6 +1673,12 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3
1548 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, 1673 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
1549 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); 1674 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
1550 1675
1676 // Create an update allocator.
1677 Result allocator_result{ResultSuccess};
1678 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1679 m_memory_block_slab_manager, num_allocator_blocks);
1680 R_TRY(allocator_result);
1681
1551 // Determine the new attribute. 1682 // Determine the new attribute.
1552 const KMemoryAttribute new_attr = 1683 const KMemoryAttribute new_attr =
1553 static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) | 1684 static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
@@ -1557,123 +1688,142 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3
1557 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); 1688 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
1558 1689
1559 // Update the blocks. 1690 // Update the blocks.
1560 block_manager->Update(addr, num_pages, old_state, old_perm, new_attr); 1691 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm,
1692 new_attr, KMemoryBlockDisableMergeAttribute::None,
1693 KMemoryBlockDisableMergeAttribute::None);
1561 1694
1562 return ResultSuccess; 1695 R_SUCCEED();
1563} 1696}
1564 1697
1565Result KPageTable::SetMaxHeapSize(std::size_t size) { 1698Result KPageTable::SetMaxHeapSize(size_t size) {
1566 // Lock the table. 1699 // Lock the table.
1567 KScopedLightLock lk(general_lock); 1700 KScopedLightLock lk(m_general_lock);
1568 1701
1569 // Only process page tables are allowed to set heap size. 1702 // Only process page tables are allowed to set heap size.
1570 ASSERT(!this->IsKernel()); 1703 ASSERT(!this->IsKernel());
1571 1704
1572 max_heap_size = size; 1705 m_max_heap_size = size;
1573 1706
1574 return ResultSuccess; 1707 R_SUCCEED();
1575} 1708}
1576 1709
1577Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { 1710Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
1578 // Lock the physical memory mutex. 1711 // Lock the physical memory mutex.
1579 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); 1712 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
1580 1713
1581 // Try to perform a reduction in heap, instead of an extension. 1714 // Try to perform a reduction in heap, instead of an extension.
1582 VAddr cur_address{}; 1715 VAddr cur_address{};
1583 std::size_t allocation_size{}; 1716 size_t allocation_size{};
1584 { 1717 {
1585 // Lock the table. 1718 // Lock the table.
1586 KScopedLightLock lk(general_lock); 1719 KScopedLightLock lk(m_general_lock);
1587 1720
1588 // Validate that setting heap size is possible at all. 1721 // Validate that setting heap size is possible at all.
1589 R_UNLESS(!is_kernel, ResultOutOfMemory); 1722 R_UNLESS(!m_is_kernel, ResultOutOfMemory);
1590 R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start), 1723 R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start),
1591 ResultOutOfMemory); 1724 ResultOutOfMemory);
1592 R_UNLESS(size <= max_heap_size, ResultOutOfMemory); 1725 R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory);
1593 1726
1594 if (size < GetHeapSize()) { 1727 if (size < GetHeapSize()) {
1595 // The size being requested is less than the current size, so we need to free the end of 1728 // The size being requested is less than the current size, so we need to free the end of
1596 // the heap. 1729 // the heap.
1597 1730
1598 // Validate memory state. 1731 // Validate memory state.
1599 std::size_t num_allocator_blocks; 1732 size_t num_allocator_blocks;
1600 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), 1733 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
1601 heap_region_start + size, GetHeapSize() - size, 1734 m_heap_region_start + size, GetHeapSize() - size,
1602 KMemoryState::All, KMemoryState::Normal, 1735 KMemoryState::All, KMemoryState::Normal,
1603 KMemoryPermission::All, KMemoryPermission::UserReadWrite, 1736 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1604 KMemoryAttribute::All, KMemoryAttribute::None)); 1737 KMemoryAttribute::All, KMemoryAttribute::None));
1605 1738
1739 // Create an update allocator.
1740 Result allocator_result{ResultSuccess};
1741 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1742 m_memory_block_slab_manager,
1743 num_allocator_blocks);
1744 R_TRY(allocator_result);
1745
1606 // Unmap the end of the heap. 1746 // Unmap the end of the heap.
1607 const auto num_pages = (GetHeapSize() - size) / PageSize; 1747 const auto num_pages = (GetHeapSize() - size) / PageSize;
1608 R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None, 1748 R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None,
1609 OperationType::Unmap)); 1749 OperationType::Unmap));
1610 1750
1611 // Release the memory from the resource limit. 1751 // Release the memory from the resource limit.
1612 system.Kernel().CurrentProcess()->GetResourceLimit()->Release( 1752 m_system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
1613 LimitableResource::PhysicalMemory, num_pages * PageSize); 1753 LimitableResource::PhysicalMemory, num_pages * PageSize);
1614 1754
1615 // Apply the memory block update. 1755 // Apply the memory block update.
1616 block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free, 1756 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
1617 KMemoryPermission::None, KMemoryAttribute::None); 1757 num_pages, KMemoryState::Free, KMemoryPermission::None,
1758 KMemoryAttribute::None,
1759 KMemoryBlockDisableMergeAttribute::None,
1760 size == 0 ? KMemoryBlockDisableMergeAttribute::Normal
1761 : KMemoryBlockDisableMergeAttribute::None);
1618 1762
1619 // Update the current heap end. 1763 // Update the current heap end.
1620 current_heap_end = heap_region_start + size; 1764 m_current_heap_end = m_heap_region_start + size;
1621 1765
1622 // Set the output. 1766 // Set the output.
1623 *out = heap_region_start; 1767 *out = m_heap_region_start;
1624 return ResultSuccess; 1768 R_SUCCEED();
1625 } else if (size == GetHeapSize()) { 1769 } else if (size == GetHeapSize()) {
1626 // The size requested is exactly the current size. 1770 // The size requested is exactly the current size.
1627 *out = heap_region_start; 1771 *out = m_heap_region_start;
1628 return ResultSuccess; 1772 R_SUCCEED();
1629 } else { 1773 } else {
1630 // We have to allocate memory. Determine how much to allocate and where while the table 1774 // We have to allocate memory. Determine how much to allocate and where while the table
1631 // is locked. 1775 // is locked.
1632 cur_address = current_heap_end; 1776 cur_address = m_current_heap_end;
1633 allocation_size = size - GetHeapSize(); 1777 allocation_size = size - GetHeapSize();
1634 } 1778 }
1635 } 1779 }
1636 1780
1637 // Reserve memory for the heap extension. 1781 // Reserve memory for the heap extension.
1638 KScopedResourceReservation memory_reservation( 1782 KScopedResourceReservation memory_reservation(
1639 system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, 1783 m_system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
1640 allocation_size); 1784 allocation_size);
1641 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1785 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1642 1786
1643 // Allocate pages for the heap extension. 1787 // Allocate pages for the heap extension.
1644 KPageGroup pg; 1788 KPageGroup pg;
1645 R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( 1789 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
1646 &pg, allocation_size / PageSize, 1790 &pg, allocation_size / PageSize,
1647 KMemoryManager::EncodeOption(memory_pool, allocation_option))); 1791 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
1648 1792
1649 // Clear all the newly allocated pages. 1793 // Clear all the newly allocated pages.
1650 for (const auto& it : pg.Nodes()) { 1794 for (const auto& it : pg.Nodes()) {
1651 std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, 1795 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
1652 it.GetSize()); 1796 it.GetSize());
1653 } 1797 }
1654 1798
1655 // Map the pages. 1799 // Map the pages.
1656 { 1800 {
1657 // Lock the table. 1801 // Lock the table.
1658 KScopedLightLock lk(general_lock); 1802 KScopedLightLock lk(m_general_lock);
1659 1803
1660 // Ensure that the heap hasn't changed since we began executing. 1804 // Ensure that the heap hasn't changed since we began executing.
1661 ASSERT(cur_address == current_heap_end); 1805 ASSERT(cur_address == m_current_heap_end);
1662 1806
1663 // Check the memory state. 1807 // Check the memory state.
1664 std::size_t num_allocator_blocks{}; 1808 size_t num_allocator_blocks{};
1665 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end, 1809 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end,
1666 allocation_size, KMemoryState::All, KMemoryState::Free, 1810 allocation_size, KMemoryState::All, KMemoryState::Free,
1667 KMemoryPermission::None, KMemoryPermission::None, 1811 KMemoryPermission::None, KMemoryPermission::None,
1668 KMemoryAttribute::None, KMemoryAttribute::None)); 1812 KMemoryAttribute::None, KMemoryAttribute::None));
1669 1813
1814 // Create an update allocator.
1815 Result allocator_result{ResultSuccess};
1816 KMemoryBlockManagerUpdateAllocator allocator(
1817 std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
1818 R_TRY(allocator_result);
1819
1670 // Map the pages. 1820 // Map the pages.
1671 const auto num_pages = allocation_size / PageSize; 1821 const auto num_pages = allocation_size / PageSize;
1672 R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); 1822 R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup));
1673 1823
1674 // Clear all the newly allocated pages. 1824 // Clear all the newly allocated pages.
1675 for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { 1825 for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
1676 std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0, 1826 std::memset(m_system.Memory().GetPointer(m_current_heap_end + (cur_page * PageSize)), 0,
1677 PageSize); 1827 PageSize);
1678 } 1828 }
1679 1829
@@ -1681,133 +1831,172 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
1681 memory_reservation.Commit(); 1831 memory_reservation.Commit();
1682 1832
1683 // Apply the memory block update. 1833 // Apply the memory block update.
1684 block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal, 1834 m_memory_block_manager.Update(
1685 KMemoryPermission::UserReadWrite, KMemoryAttribute::None); 1835 std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal,
1836 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1837 m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal
1838 : KMemoryBlockDisableMergeAttribute::None,
1839 KMemoryBlockDisableMergeAttribute::None);
1686 1840
1687 // Update the current heap end. 1841 // Update the current heap end.
1688 current_heap_end = heap_region_start + size; 1842 m_current_heap_end = m_heap_region_start + size;
1689 1843
1690 // Set the output. 1844 // Set the output.
1691 *out = heap_region_start; 1845 *out = m_heap_region_start;
1692 return ResultSuccess; 1846 R_SUCCEED();
1693 } 1847 }
1694} 1848}
1695 1849
1696ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, 1850ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
1697 bool is_map_only, VAddr region_start, 1851 bool is_map_only, VAddr region_start,
1698 std::size_t region_num_pages, KMemoryState state, 1852 size_t region_num_pages, KMemoryState state,
1699 KMemoryPermission perm, PAddr map_addr) { 1853 KMemoryPermission perm, PAddr map_addr) {
1700 KScopedLightLock lk(general_lock); 1854 KScopedLightLock lk(m_general_lock);
1701
1702 if (!CanContain(region_start, region_num_pages * PageSize, state)) {
1703 return ResultInvalidCurrentMemory;
1704 }
1705
1706 if (region_num_pages <= needed_num_pages) {
1707 return ResultOutOfMemory;
1708 }
1709 1855
1856 R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
1857 ResultInvalidCurrentMemory);
1858 R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
1710 const VAddr addr{ 1859 const VAddr addr{
1711 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; 1860 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
1712 if (!addr) { 1861 R_UNLESS(addr, ResultOutOfMemory);
1713 return ResultOutOfMemory; 1862
1714 } 1863 // Create an update allocator.
1864 Result allocator_result{ResultSuccess};
1865 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1866 m_memory_block_slab_manager);
1715 1867
1716 if (is_map_only) { 1868 if (is_map_only) {
1717 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); 1869 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
1718 } else { 1870 } else {
1719 KPageGroup page_group; 1871 KPageGroup page_group;
1720 R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( 1872 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess(
1721 &page_group, needed_num_pages, 1873 &page_group, needed_num_pages,
1722 KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); 1874 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
1723 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); 1875 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
1724 } 1876 }
1725 1877
1726 block_manager->Update(addr, needed_num_pages, state, perm); 1878 // Update the blocks.
1879 m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
1880 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1881 KMemoryBlockDisableMergeAttribute::None);
1727 1882
1728 return addr; 1883 return addr;
1729} 1884}
1730 1885
1731Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1886Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
1732 KScopedLightLock lk(general_lock); 1887 bool is_aligned) {
1733 1888 // Lightly validate the range before doing anything else.
1734 KMemoryPermission perm{}; 1889 const size_t num_pages = size / PageSize;
1735 if (const Result result{CheckMemoryState( 1890 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1736 nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
1737 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
1738 KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
1739 KMemoryAttribute::DeviceSharedAndUncached)};
1740 result.IsError()) {
1741 return result;
1742 }
1743 1891
1744 block_manager->UpdateLock( 1892 // Lock the table.
1745 addr, size / PageSize, 1893 KScopedLightLock lk(m_general_lock);
1746 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1747 block->ShareToDevice(permission);
1748 },
1749 perm);
1750 1894
1751 return ResultSuccess; 1895 // Check the memory state.
1896 const auto test_state =
1897 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap);
1898 size_t num_allocator_blocks;
1899 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, test_state,
1900 test_state, perm, perm,
1901 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
1902 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
1903
1904 // Create an update allocator.
1905 Result allocator_result{ResultSuccess};
1906 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1907 m_memory_block_slab_manager, num_allocator_blocks);
1908 R_TRY(allocator_result);
1909
1910 // Update the memory blocks.
1911 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
1912 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
1913
1914 R_SUCCEED();
1752} 1915}
1753 1916
1754Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1917Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) {
1755 KScopedLightLock lk(general_lock); 1918 // Lightly validate the range before doing anything else.
1756 1919 const size_t num_pages = size / PageSize;
1757 KMemoryPermission perm{}; 1920 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1758 if (const Result result{CheckMemoryState(
1759 nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
1760 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
1761 KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
1762 KMemoryAttribute::DeviceSharedAndUncached)};
1763 result.IsError()) {
1764 return result;
1765 }
1766 1921
1767 block_manager->UpdateLock( 1922 // Lock the table.
1768 addr, size / PageSize, 1923 KScopedLightLock lk(m_general_lock);
1769 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1770 block->UnshareToDevice(permission);
1771 },
1772 perm);
1773 1924
1774 return ResultSuccess; 1925 // Check the memory state.
1926 size_t num_allocator_blocks;
1927 R_TRY(this->CheckMemoryStateContiguous(
1928 std::addressof(num_allocator_blocks), address, size,
1929 KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap,
1930 KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap,
1931 KMemoryPermission::None, KMemoryPermission::None,
1932 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
1933
1934 // Create an update allocator.
1935 Result allocator_result{ResultSuccess};
1936 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1937 m_memory_block_slab_manager, num_allocator_blocks);
1938 R_TRY(allocator_result);
1939
1940 // Update the memory blocks.
1941 const KMemoryBlockManager::MemoryBlockLockFunction lock_func =
1942 m_enable_device_address_space_merge
1943 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare
1944 : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight;
1945 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func,
1946 KMemoryPermission::None);
1947
1948 R_SUCCEED();
1775} 1949}
1776 1950
1777Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) { 1951Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) {
1778 return this->LockMemoryAndOpen( 1952 // Lightly validate the range before doing anything else.
1953 const size_t num_pages = size / PageSize;
1954 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1955
1956 // Lock the table.
1957 KScopedLightLock lk(m_general_lock);
1958
1959 // Check the memory state.
1960 size_t num_allocator_blocks;
1961 R_TRY(this->CheckMemoryStateContiguous(
1962 std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap,
1963 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
1964 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
1965
1966 // Create an update allocator.
1967 Result allocator_result{ResultSuccess};
1968 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1969 m_memory_block_slab_manager, num_allocator_blocks);
1970 R_TRY(allocator_result);
1971
1972 // Update the memory blocks.
1973 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
1974 &KMemoryBlock::UnshareToDevice, KMemoryPermission::None);
1975
1976 R_SUCCEED();
1977}
1978
1979Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) {
1980 R_RETURN(this->LockMemoryAndOpen(
1779 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, 1981 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1780 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, 1982 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1781 KMemoryAttribute::None, 1983 KMemoryAttribute::None,
1782 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | 1984 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1783 KMemoryPermission::KernelReadWrite), 1985 KMemoryPermission::KernelReadWrite),
1784 KMemoryAttribute::Locked); 1986 KMemoryAttribute::Locked));
1785} 1987}
1786 1988
1787Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) { 1989Result KPageTable::UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg) {
1788 return this->UnlockMemory( 1990 R_RETURN(this->UnlockMemory(
1789 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, 1991 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1790 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, 1992 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
1791 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); 1993 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg));
1792}
1793
1794Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
1795 block_manager = std::make_unique<KMemoryBlockManager>(start, end);
1796
1797 return ResultSuccess;
1798}
1799
1800bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
1801 return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
1802 KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
1803 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
1804 .IsError();
1805} 1994}
1806 1995
1807bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { 1996bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
1808 auto start_ptr = system.Memory().GetPointer(addr); 1997 auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(addr);
1809 for (u64 offset{}; offset < size; offset += PageSize) { 1998 for (u64 offset{}; offset < size; offset += PageSize) {
1810 if (start_ptr != system.Memory().GetPointer(addr + offset)) { 1999 if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(addr + offset)) {
1811 return false; 2000 return false;
1812 } 2001 }
1813 start_ptr += PageSize; 2002 start_ptr += PageSize;
@@ -1815,8 +2004,7 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
1815 return true; 2004 return true;
1816} 2005}
1817 2006
1818void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, 2007void KPageTable::AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list) {
1819 KPageGroup& page_linked_list) {
1820 VAddr addr{start}; 2008 VAddr addr{start};
1821 while (addr < start + (num_pages * PageSize)) { 2009 while (addr < start + (num_pages * PageSize)) {
1822 const PAddr paddr{GetPhysicalAddr(addr)}; 2010 const PAddr paddr{GetPhysicalAddr(addr)};
@@ -1826,16 +2014,16 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
1826 } 2014 }
1827} 2015}
1828 2016
1829VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, 2017VAddr KPageTable::AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages,
1830 u64 needed_num_pages, std::size_t align) { 2018 size_t align) {
1831 if (is_aslr_enabled) { 2019 if (m_enable_aslr) {
1832 UNIMPLEMENTED(); 2020 UNIMPLEMENTED();
1833 } 2021 }
1834 return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, 2022 return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
1835 IsKernel() ? 1 : 4); 2023 IsKernel() ? 1 : 4);
1836} 2024}
1837 2025
1838Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, 2026Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group,
1839 OperationType operation) { 2027 OperationType operation) {
1840 ASSERT(this->IsLockedByCurrentThread()); 2028 ASSERT(this->IsLockedByCurrentThread());
1841 2029
@@ -1844,11 +2032,11 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup&
1844 ASSERT(num_pages == page_group.GetNumPages()); 2032 ASSERT(num_pages == page_group.GetNumPages());
1845 2033
1846 for (const auto& node : page_group.Nodes()) { 2034 for (const auto& node : page_group.Nodes()) {
1847 const std::size_t size{node.GetNumPages() * PageSize}; 2035 const size_t size{node.GetNumPages() * PageSize};
1848 2036
1849 switch (operation) { 2037 switch (operation) {
1850 case OperationType::MapGroup: 2038 case OperationType::MapGroup:
1851 system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); 2039 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
1852 break; 2040 break;
1853 default: 2041 default:
1854 ASSERT(false); 2042 ASSERT(false);
@@ -1857,10 +2045,10 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup&
1857 addr += size; 2045 addr += size;
1858 } 2046 }
1859 2047
1860 return ResultSuccess; 2048 R_SUCCEED();
1861} 2049}
1862 2050
1863Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, 2051Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
1864 OperationType operation, PAddr map_addr) { 2052 OperationType operation, PAddr map_addr) {
1865 ASSERT(this->IsLockedByCurrentThread()); 2053 ASSERT(this->IsLockedByCurrentThread());
1866 2054
@@ -1870,12 +2058,12 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission
1870 2058
1871 switch (operation) { 2059 switch (operation) {
1872 case OperationType::Unmap: 2060 case OperationType::Unmap:
1873 system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize); 2061 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
1874 break; 2062 break;
1875 case OperationType::Map: { 2063 case OperationType::Map: {
1876 ASSERT(map_addr); 2064 ASSERT(map_addr);
1877 ASSERT(Common::IsAligned(map_addr, PageSize)); 2065 ASSERT(Common::IsAligned(map_addr, PageSize));
1878 system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr); 2066 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
1879 break; 2067 break;
1880 } 2068 }
1881 case OperationType::ChangePermissions: 2069 case OperationType::ChangePermissions:
@@ -1884,25 +2072,25 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission
1884 default: 2072 default:
1885 ASSERT(false); 2073 ASSERT(false);
1886 } 2074 }
1887 return ResultSuccess; 2075 R_SUCCEED();
1888} 2076}
1889 2077
1890VAddr KPageTable::GetRegionAddress(KMemoryState state) const { 2078VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1891 switch (state) { 2079 switch (state) {
1892 case KMemoryState::Free: 2080 case KMemoryState::Free:
1893 case KMemoryState::Kernel: 2081 case KMemoryState::Kernel:
1894 return address_space_start; 2082 return m_address_space_start;
1895 case KMemoryState::Normal: 2083 case KMemoryState::Normal:
1896 return heap_region_start; 2084 return m_heap_region_start;
1897 case KMemoryState::Ipc: 2085 case KMemoryState::Ipc:
1898 case KMemoryState::NonSecureIpc: 2086 case KMemoryState::NonSecureIpc:
1899 case KMemoryState::NonDeviceIpc: 2087 case KMemoryState::NonDeviceIpc:
1900 return alias_region_start; 2088 return m_alias_region_start;
1901 case KMemoryState::Stack: 2089 case KMemoryState::Stack:
1902 return stack_region_start; 2090 return m_stack_region_start;
1903 case KMemoryState::Static: 2091 case KMemoryState::Static:
1904 case KMemoryState::ThreadLocal: 2092 case KMemoryState::ThreadLocal:
1905 return kernel_map_region_start; 2093 return m_kernel_map_region_start;
1906 case KMemoryState::Io: 2094 case KMemoryState::Io:
1907 case KMemoryState::Shared: 2095 case KMemoryState::Shared:
1908 case KMemoryState::AliasCode: 2096 case KMemoryState::AliasCode:
@@ -1913,31 +2101,31 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1913 case KMemoryState::GeneratedCode: 2101 case KMemoryState::GeneratedCode:
1914 case KMemoryState::CodeOut: 2102 case KMemoryState::CodeOut:
1915 case KMemoryState::Coverage: 2103 case KMemoryState::Coverage:
1916 return alias_code_region_start; 2104 return m_alias_code_region_start;
1917 case KMemoryState::Code: 2105 case KMemoryState::Code:
1918 case KMemoryState::CodeData: 2106 case KMemoryState::CodeData:
1919 return code_region_start; 2107 return m_code_region_start;
1920 default: 2108 default:
1921 UNREACHABLE(); 2109 UNREACHABLE();
1922 } 2110 }
1923} 2111}
1924 2112
1925std::size_t KPageTable::GetRegionSize(KMemoryState state) const { 2113size_t KPageTable::GetRegionSize(KMemoryState state) const {
1926 switch (state) { 2114 switch (state) {
1927 case KMemoryState::Free: 2115 case KMemoryState::Free:
1928 case KMemoryState::Kernel: 2116 case KMemoryState::Kernel:
1929 return address_space_end - address_space_start; 2117 return m_address_space_end - m_address_space_start;
1930 case KMemoryState::Normal: 2118 case KMemoryState::Normal:
1931 return heap_region_end - heap_region_start; 2119 return m_heap_region_end - m_heap_region_start;
1932 case KMemoryState::Ipc: 2120 case KMemoryState::Ipc:
1933 case KMemoryState::NonSecureIpc: 2121 case KMemoryState::NonSecureIpc:
1934 case KMemoryState::NonDeviceIpc: 2122 case KMemoryState::NonDeviceIpc:
1935 return alias_region_end - alias_region_start; 2123 return m_alias_region_end - m_alias_region_start;
1936 case KMemoryState::Stack: 2124 case KMemoryState::Stack:
1937 return stack_region_end - stack_region_start; 2125 return m_stack_region_end - m_stack_region_start;
1938 case KMemoryState::Static: 2126 case KMemoryState::Static:
1939 case KMemoryState::ThreadLocal: 2127 case KMemoryState::ThreadLocal:
1940 return kernel_map_region_end - kernel_map_region_start; 2128 return m_kernel_map_region_end - m_kernel_map_region_start;
1941 case KMemoryState::Io: 2129 case KMemoryState::Io:
1942 case KMemoryState::Shared: 2130 case KMemoryState::Shared:
1943 case KMemoryState::AliasCode: 2131 case KMemoryState::AliasCode:
@@ -1948,16 +2136,16 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
1948 case KMemoryState::GeneratedCode: 2136 case KMemoryState::GeneratedCode:
1949 case KMemoryState::CodeOut: 2137 case KMemoryState::CodeOut:
1950 case KMemoryState::Coverage: 2138 case KMemoryState::Coverage:
1951 return alias_code_region_end - alias_code_region_start; 2139 return m_alias_code_region_end - m_alias_code_region_start;
1952 case KMemoryState::Code: 2140 case KMemoryState::Code:
1953 case KMemoryState::CodeData: 2141 case KMemoryState::CodeData:
1954 return code_region_end - code_region_start; 2142 return m_code_region_end - m_code_region_start;
1955 default: 2143 default:
1956 UNREACHABLE(); 2144 UNREACHABLE();
1957 } 2145 }
1958} 2146}
1959 2147
1960bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { 2148bool KPageTable::CanContain(VAddr addr, size_t size, KMemoryState state) const {
1961 const VAddr end = addr + size; 2149 const VAddr end = addr + size;
1962 const VAddr last = end - 1; 2150 const VAddr last = end - 1;
1963 2151
@@ -1966,10 +2154,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
1966 2154
1967 const bool is_in_region = 2155 const bool is_in_region =
1968 region_start <= addr && addr < end && last <= region_start + region_size - 1; 2156 region_start <= addr && addr < end && last <= region_start + region_size - 1;
1969 const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr || 2157 const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr ||
1970 heap_region_start == heap_region_end); 2158 m_heap_region_start == m_heap_region_end);
1971 const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr || 2159 const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
1972 alias_region_start == alias_region_end); 2160 m_alias_region_start == m_alias_region_end);
1973 switch (state) { 2161 switch (state) {
1974 case KMemoryState::Free: 2162 case KMemoryState::Free:
1975 case KMemoryState::Kernel: 2163 case KMemoryState::Kernel:
@@ -2008,23 +2196,23 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_
2008 KMemoryPermission perm, KMemoryAttribute attr_mask, 2196 KMemoryPermission perm, KMemoryAttribute attr_mask,
2009 KMemoryAttribute attr) const { 2197 KMemoryAttribute attr) const {
2010 // Validate the states match expectation. 2198 // Validate the states match expectation.
2011 R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); 2199 R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory);
2012 R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); 2200 R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory);
2013 R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory); 2201 R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
2014 2202
2015 return ResultSuccess; 2203 R_SUCCEED();
2016} 2204}
2017 2205
2018Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, 2206Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size,
2019 std::size_t size, KMemoryState state_mask, 2207 KMemoryState state_mask, KMemoryState state,
2020 KMemoryState state, KMemoryPermission perm_mask, 2208 KMemoryPermission perm_mask, KMemoryPermission perm,
2021 KMemoryPermission perm, KMemoryAttribute attr_mask, 2209 KMemoryAttribute attr_mask,
2022 KMemoryAttribute attr) const { 2210 KMemoryAttribute attr) const {
2023 ASSERT(this->IsLockedByCurrentThread()); 2211 ASSERT(this->IsLockedByCurrentThread());
2024 2212
2025 // Get information about the first block. 2213 // Get information about the first block.
2026 const VAddr last_addr = addr + size - 1; 2214 const VAddr last_addr = addr + size - 1;
2027 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); 2215 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
2028 KMemoryInfo info = it->GetMemoryInfo(); 2216 KMemoryInfo info = it->GetMemoryInfo();
2029 2217
2030 // If the start address isn't aligned, we need a block. 2218 // If the start address isn't aligned, we need a block.
@@ -2042,7 +2230,7 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA
2042 2230
2043 // Advance our iterator. 2231 // Advance our iterator.
2044 it++; 2232 it++;
2045 ASSERT(it != block_manager->cend()); 2233 ASSERT(it != m_memory_block_manager.cend());
2046 info = it->GetMemoryInfo(); 2234 info = it->GetMemoryInfo();
2047 } 2235 }
2048 2236
@@ -2054,12 +2242,12 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA
2054 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; 2242 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
2055 } 2243 }
2056 2244
2057 return ResultSuccess; 2245 R_SUCCEED();
2058} 2246}
2059 2247
2060Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, 2248Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
2061 KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, 2249 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
2062 VAddr addr, std::size_t size, KMemoryState state_mask, 2250 VAddr addr, size_t size, KMemoryState state_mask,
2063 KMemoryState state, KMemoryPermission perm_mask, 2251 KMemoryState state, KMemoryPermission perm_mask,
2064 KMemoryPermission perm, KMemoryAttribute attr_mask, 2252 KMemoryPermission perm, KMemoryAttribute attr_mask,
2065 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { 2253 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
@@ -2067,7 +2255,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
2067 2255
2068 // Get information about the first block. 2256 // Get information about the first block.
2069 const VAddr last_addr = addr + size - 1; 2257 const VAddr last_addr = addr + size - 1;
2070 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); 2258 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
2071 KMemoryInfo info = it->GetMemoryInfo(); 2259 KMemoryInfo info = it->GetMemoryInfo();
2072 2260
2073 // If the start address isn't aligned, we need a block. 2261 // If the start address isn't aligned, we need a block.
@@ -2075,14 +2263,14 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
2075 (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; 2263 (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
2076 2264
2077 // Validate all blocks in the range have correct state. 2265 // Validate all blocks in the range have correct state.
2078 const KMemoryState first_state = info.state; 2266 const KMemoryState first_state = info.m_state;
2079 const KMemoryPermission first_perm = info.perm; 2267 const KMemoryPermission first_perm = info.m_permission;
2080 const KMemoryAttribute first_attr = info.attribute; 2268 const KMemoryAttribute first_attr = info.m_attribute;
2081 while (true) { 2269 while (true) {
2082 // Validate the current block. 2270 // Validate the current block.
2083 R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory); 2271 R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory);
2084 R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory); 2272 R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory);
2085 R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr), 2273 R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr),
2086 ResultInvalidCurrentMemory); 2274 ResultInvalidCurrentMemory);
2087 2275
2088 // Validate against the provided masks. 2276 // Validate against the provided masks.
@@ -2095,7 +2283,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
2095 2283
2096 // Advance our iterator. 2284 // Advance our iterator.
2097 it++; 2285 it++;
2098 ASSERT(it != block_manager->cend()); 2286 ASSERT(it != m_memory_block_manager.cend());
2099 info = it->GetMemoryInfo(); 2287 info = it->GetMemoryInfo();
2100 } 2288 }
2101 2289
@@ -2116,7 +2304,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
2116 if (out_blocks_needed != nullptr) { 2304 if (out_blocks_needed != nullptr) {
2117 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; 2305 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
2118 } 2306 }
2119 return ResultSuccess; 2307 R_SUCCEED();
2120} 2308}
2121 2309
2122Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, 2310Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
@@ -2134,7 +2322,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr
2134 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); 2322 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2135 2323
2136 // Lock the table. 2324 // Lock the table.
2137 KScopedLightLock lk(general_lock); 2325 KScopedLightLock lk(m_general_lock);
2138 2326
2139 // Check that the output page group is empty, if it exists. 2327 // Check that the output page group is empty, if it exists.
2140 if (out_pg) { 2328 if (out_pg) {
@@ -2162,6 +2350,12 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr
2162 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); 2350 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2163 } 2351 }
2164 2352
2353 // Create an update allocator.
2354 Result allocator_result{ResultSuccess};
2355 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2356 m_memory_block_slab_manager, num_allocator_blocks);
2357 R_TRY(allocator_result);
2358
2165 // Decide on new perm and attr. 2359 // Decide on new perm and attr.
2166 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; 2360 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2167 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr); 2361 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
@@ -2172,9 +2366,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr
2172 } 2366 }
2173 2367
2174 // Apply the memory block updates. 2368 // Apply the memory block updates.
2175 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); 2369 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
2370 new_attr, KMemoryBlockDisableMergeAttribute::Locked,
2371 KMemoryBlockDisableMergeAttribute::None);
2176 2372
2177 return ResultSuccess; 2373 R_SUCCEED();
2178} 2374}
2179 2375
2180Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, 2376Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
@@ -2191,7 +2387,7 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask
2191 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); 2387 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2192 2388
2193 // Lock the table. 2389 // Lock the table.
2194 KScopedLightLock lk(general_lock); 2390 KScopedLightLock lk(m_general_lock);
2195 2391
2196 // Check the state. 2392 // Check the state.
2197 KMemoryState old_state{}; 2393 KMemoryState old_state{};
@@ -2213,15 +2409,23 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask
2213 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; 2409 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2214 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr); 2410 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2215 2411
2412 // Create an update allocator.
2413 Result allocator_result{ResultSuccess};
2414 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2415 m_memory_block_slab_manager, num_allocator_blocks);
2416 R_TRY(allocator_result);
2417
2216 // Update permission, if we need to. 2418 // Update permission, if we need to.
2217 if (new_perm != old_perm) { 2419 if (new_perm != old_perm) {
2218 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); 2420 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2219 } 2421 }
2220 2422
2221 // Apply the memory block updates. 2423 // Apply the memory block updates.
2222 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); 2424 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
2425 new_attr, KMemoryBlockDisableMergeAttribute::None,
2426 KMemoryBlockDisableMergeAttribute::Locked);
2223 2427
2224 return ResultSuccess; 2428 R_SUCCEED();
2225} 2429}
2226 2430
2227} // namespace Kernel 2431} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 25774f232..c6aeacd96 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -9,8 +9,10 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/page_table.h" 10#include "common/page_table.h"
11#include "core/file_sys/program_metadata.h" 11#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/k_dynamic_resource_manager.h"
12#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_block_manager.h"
14#include "core/hle/kernel/k_memory_layout.h" 16#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 17#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 18#include "core/hle/result.h"
@@ -34,58 +36,66 @@ public:
34 ~KPageTable(); 36 ~KPageTable();
35 37
36 Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, 38 Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
37 VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool); 39 VAddr code_addr, size_t code_size,
38 Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, 40 KMemoryBlockSlabManager* mem_block_slab_manager,
41 KMemoryManager::Pool pool);
42
43 void Finalize();
44
45 Result MapProcessCode(VAddr addr, size_t pages_count, KMemoryState state,
39 KMemoryPermission perm); 46 KMemoryPermission perm);
40 Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); 47 Result MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size);
41 Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, 48 Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size,
42 ICacheInvalidationStrategy icache_invalidation_strategy); 49 ICacheInvalidationStrategy icache_invalidation_strategy);
43 Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 50 Result UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table,
44 VAddr src_addr); 51 VAddr src_addr);
45 Result MapPhysicalMemory(VAddr addr, std::size_t size); 52 Result MapPhysicalMemory(VAddr addr, size_t size);
46 Result UnmapPhysicalMemory(VAddr addr, std::size_t size); 53 Result UnmapPhysicalMemory(VAddr addr, size_t size);
47 Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 54 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
48 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 55 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
49 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, 56 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
50 KMemoryPermission perm); 57 KMemoryPermission perm);
51 Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, 58 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
52 KMemoryState state, KMemoryPermission perm) { 59 KMemoryState state, KMemoryPermission perm) {
53 return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, 60 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
54 this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, 61 this->GetRegionAddress(state),
55 state, perm); 62 this->GetRegionSize(state) / PageSize, state, perm));
56 } 63 }
57 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); 64 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
58 Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); 65 Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
59 Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); 66 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
60 KMemoryInfo QueryInfo(VAddr addr); 67 KMemoryInfo QueryInfo(VAddr addr);
61 Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); 68 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
62 Result ResetTransferMemory(VAddr addr, std::size_t size); 69 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
63 Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); 70 Result SetMaxHeapSize(size_t size);
64 Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); 71 Result SetHeapSize(VAddr* out, size_t size);
65 Result SetMaxHeapSize(std::size_t size); 72 ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
66 Result SetHeapSize(VAddr* out, std::size_t size); 73 VAddr region_start, size_t region_num_pages,
67 ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, 74 KMemoryState state, KMemoryPermission perm,
68 bool is_map_only, VAddr region_start, 75 PAddr map_addr = 0);
69 std::size_t region_num_pages, KMemoryState state, 76
70 KMemoryPermission perm, PAddr map_addr = 0); 77 Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
71 Result LockForDeviceAddressSpace(VAddr addr, std::size_t size); 78 bool is_aligned);
72 Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 79 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size);
73 Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); 80
74 Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); 81 Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
82
83 Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
84 Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
75 Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, 85 Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
76 KMemoryState state_mask, KMemoryState state, 86 KMemoryState state_mask, KMemoryState state,
77 KMemoryPermission perm_mask, KMemoryPermission perm, 87 KMemoryPermission perm_mask, KMemoryPermission perm,
78 KMemoryAttribute attr_mask, KMemoryAttribute attr); 88 KMemoryAttribute attr_mask, KMemoryAttribute attr);
79 89
80 Common::PageTable& PageTableImpl() { 90 Common::PageTable& PageTableImpl() {
81 return page_table_impl; 91 return *m_page_table_impl;
82 } 92 }
83 93
84 const Common::PageTable& PageTableImpl() const { 94 const Common::PageTable& PageTableImpl() const {
85 return page_table_impl; 95 return *m_page_table_impl;
86 } 96 }
87 97
88 bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const; 98 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
89 99
90private: 100private:
91 enum class OperationType : u32 { 101 enum class OperationType : u32 {
@@ -96,67 +106,65 @@ private:
96 ChangePermissionsAndRefresh, 106 ChangePermissionsAndRefresh,
97 }; 107 };
98 108
99 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask | 109 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
100 KMemoryAttribute::IpcLocked | 110 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
101 KMemoryAttribute::DeviceShared;
102 111
103 Result InitializeMemoryLayout(VAddr start, VAddr end);
104 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); 112 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
105 Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, 113 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
106 bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, 114 bool is_pa_valid, VAddr region_start, size_t region_num_pages,
107 KMemoryState state, KMemoryPermission perm); 115 KMemoryState state, KMemoryPermission perm);
108 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); 116 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
109 bool IsRegionMapped(VAddr address, u64 size);
110 bool IsRegionContiguous(VAddr addr, u64 size) const; 117 bool IsRegionContiguous(VAddr addr, u64 size) const;
111 void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); 118 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
112 KMemoryInfo QueryInfoImpl(VAddr addr); 119 KMemoryInfo QueryInfoImpl(VAddr addr);
113 VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, 120 VAddr AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages,
114 std::size_t align); 121 size_t align);
115 Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, 122 Result Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group,
116 OperationType operation); 123 OperationType operation);
117 Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, 124 Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
118 OperationType operation, PAddr map_addr = 0); 125 PAddr map_addr = 0);
119 VAddr GetRegionAddress(KMemoryState state) const; 126 VAddr GetRegionAddress(KMemoryState state) const;
120 std::size_t GetRegionSize(KMemoryState state) const; 127 size_t GetRegionSize(KMemoryState state) const;
121 128
122 VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, 129 VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
123 std::size_t alignment, std::size_t offset, std::size_t guard_pages); 130 size_t alignment, size_t offset, size_t guard_pages);
124 131
125 Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, 132 Result CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size,
126 KMemoryState state_mask, KMemoryState state, 133 KMemoryState state_mask, KMemoryState state,
127 KMemoryPermission perm_mask, KMemoryPermission perm, 134 KMemoryPermission perm_mask, KMemoryPermission perm,
128 KMemoryAttribute attr_mask, KMemoryAttribute attr) const; 135 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
129 Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, 136 Result CheckMemoryStateContiguous(VAddr addr, size_t size, KMemoryState state_mask,
130 KMemoryState state, KMemoryPermission perm_mask, 137 KMemoryState state, KMemoryPermission perm_mask,
131 KMemoryPermission perm, KMemoryAttribute attr_mask, 138 KMemoryPermission perm, KMemoryAttribute attr_mask,
132 KMemoryAttribute attr) const { 139 KMemoryAttribute attr) const {
133 return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, 140 R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
134 perm, attr_mask, attr); 141 perm, attr_mask, attr));
135 } 142 }
136 143
137 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, 144 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
138 KMemoryPermission perm_mask, KMemoryPermission perm, 145 KMemoryPermission perm_mask, KMemoryPermission perm,
139 KMemoryAttribute attr_mask, KMemoryAttribute attr) const; 146 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
140 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, 147 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
141 KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr, 148 KMemoryAttribute* out_attr, size_t* out_blocks_needed, VAddr addr,
142 std::size_t size, KMemoryState state_mask, KMemoryState state, 149 size_t size, KMemoryState state_mask, KMemoryState state,
143 KMemoryPermission perm_mask, KMemoryPermission perm, 150 KMemoryPermission perm_mask, KMemoryPermission perm,
144 KMemoryAttribute attr_mask, KMemoryAttribute attr, 151 KMemoryAttribute attr_mask, KMemoryAttribute attr,
145 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; 152 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
146 Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, 153 Result CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
147 KMemoryState state_mask, KMemoryState state, 154 KMemoryState state_mask, KMemoryState state,
148 KMemoryPermission perm_mask, KMemoryPermission perm, 155 KMemoryPermission perm_mask, KMemoryPermission perm,
149 KMemoryAttribute attr_mask, KMemoryAttribute attr, 156 KMemoryAttribute attr_mask, KMemoryAttribute attr,
150 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { 157 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
151 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, 158 R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
152 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); 159 state_mask, state, perm_mask, perm, attr_mask, attr,
160 ignore_attr));
153 } 161 }
154 Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, 162 Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
155 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, 163 KMemoryPermission perm_mask, KMemoryPermission perm,
156 KMemoryAttribute attr_mask, KMemoryAttribute attr, 164 KMemoryAttribute attr_mask, KMemoryAttribute attr,
157 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { 165 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
158 return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, 166 R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
159 attr_mask, attr, ignore_attr); 167 attr_mask, attr, ignore_attr));
160 } 168 }
161 169
162 Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, 170 Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
@@ -174,13 +182,13 @@ private:
174 bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages); 182 bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages);
175 183
176 bool IsLockedByCurrentThread() const { 184 bool IsLockedByCurrentThread() const {
177 return general_lock.IsLockedByCurrentThread(); 185 return m_general_lock.IsLockedByCurrentThread();
178 } 186 }
179 187
180 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { 188 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
181 ASSERT(this->IsLockedByCurrentThread()); 189 ASSERT(this->IsLockedByCurrentThread());
182 190
183 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); 191 return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
184 } 192 }
185 193
186 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { 194 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
@@ -191,95 +199,93 @@ private:
191 return *out != 0; 199 return *out != 0;
192 } 200 }
193 201
194 mutable KLightLock general_lock; 202 mutable KLightLock m_general_lock;
195 mutable KLightLock map_physical_memory_lock; 203 mutable KLightLock m_map_physical_memory_lock;
196
197 std::unique_ptr<KMemoryBlockManager> block_manager;
198 204
199public: 205public:
200 constexpr VAddr GetAddressSpaceStart() const { 206 constexpr VAddr GetAddressSpaceStart() const {
201 return address_space_start; 207 return m_address_space_start;
202 } 208 }
203 constexpr VAddr GetAddressSpaceEnd() const { 209 constexpr VAddr GetAddressSpaceEnd() const {
204 return address_space_end; 210 return m_address_space_end;
205 } 211 }
206 constexpr std::size_t GetAddressSpaceSize() const { 212 constexpr size_t GetAddressSpaceSize() const {
207 return address_space_end - address_space_start; 213 return m_address_space_end - m_address_space_start;
208 } 214 }
209 constexpr VAddr GetHeapRegionStart() const { 215 constexpr VAddr GetHeapRegionStart() const {
210 return heap_region_start; 216 return m_heap_region_start;
211 } 217 }
212 constexpr VAddr GetHeapRegionEnd() const { 218 constexpr VAddr GetHeapRegionEnd() const {
213 return heap_region_end; 219 return m_heap_region_end;
214 } 220 }
215 constexpr std::size_t GetHeapRegionSize() const { 221 constexpr size_t GetHeapRegionSize() const {
216 return heap_region_end - heap_region_start; 222 return m_heap_region_end - m_heap_region_start;
217 } 223 }
218 constexpr VAddr GetAliasRegionStart() const { 224 constexpr VAddr GetAliasRegionStart() const {
219 return alias_region_start; 225 return m_alias_region_start;
220 } 226 }
221 constexpr VAddr GetAliasRegionEnd() const { 227 constexpr VAddr GetAliasRegionEnd() const {
222 return alias_region_end; 228 return m_alias_region_end;
223 } 229 }
224 constexpr std::size_t GetAliasRegionSize() const { 230 constexpr size_t GetAliasRegionSize() const {
225 return alias_region_end - alias_region_start; 231 return m_alias_region_end - m_alias_region_start;
226 } 232 }
227 constexpr VAddr GetStackRegionStart() const { 233 constexpr VAddr GetStackRegionStart() const {
228 return stack_region_start; 234 return m_stack_region_start;
229 } 235 }
230 constexpr VAddr GetStackRegionEnd() const { 236 constexpr VAddr GetStackRegionEnd() const {
231 return stack_region_end; 237 return m_stack_region_end;
232 } 238 }
233 constexpr std::size_t GetStackRegionSize() const { 239 constexpr size_t GetStackRegionSize() const {
234 return stack_region_end - stack_region_start; 240 return m_stack_region_end - m_stack_region_start;
235 } 241 }
236 constexpr VAddr GetKernelMapRegionStart() const { 242 constexpr VAddr GetKernelMapRegionStart() const {
237 return kernel_map_region_start; 243 return m_kernel_map_region_start;
238 } 244 }
239 constexpr VAddr GetKernelMapRegionEnd() const { 245 constexpr VAddr GetKernelMapRegionEnd() const {
240 return kernel_map_region_end; 246 return m_kernel_map_region_end;
241 } 247 }
242 constexpr VAddr GetCodeRegionStart() const { 248 constexpr VAddr GetCodeRegionStart() const {
243 return code_region_start; 249 return m_code_region_start;
244 } 250 }
245 constexpr VAddr GetCodeRegionEnd() const { 251 constexpr VAddr GetCodeRegionEnd() const {
246 return code_region_end; 252 return m_code_region_end;
247 } 253 }
248 constexpr VAddr GetAliasCodeRegionStart() const { 254 constexpr VAddr GetAliasCodeRegionStart() const {
249 return alias_code_region_start; 255 return m_alias_code_region_start;
250 } 256 }
251 constexpr VAddr GetAliasCodeRegionSize() const { 257 constexpr VAddr GetAliasCodeRegionSize() const {
252 return alias_code_region_end - alias_code_region_start; 258 return m_alias_code_region_end - m_alias_code_region_start;
253 } 259 }
254 std::size_t GetNormalMemorySize() { 260 size_t GetNormalMemorySize() {
255 KScopedLightLock lk(general_lock); 261 KScopedLightLock lk(m_general_lock);
256 return GetHeapSize() + mapped_physical_memory_size; 262 return GetHeapSize() + m_mapped_physical_memory_size;
257 } 263 }
258 constexpr std::size_t GetAddressSpaceWidth() const { 264 constexpr size_t GetAddressSpaceWidth() const {
259 return address_space_width; 265 return m_address_space_width;
260 } 266 }
261 constexpr std::size_t GetHeapSize() const { 267 constexpr size_t GetHeapSize() const {
262 return current_heap_end - heap_region_start; 268 return m_current_heap_end - m_heap_region_start;
263 } 269 }
264 constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { 270 constexpr bool IsInsideAddressSpace(VAddr address, size_t size) const {
265 return address_space_start <= address && address + size - 1 <= address_space_end - 1; 271 return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1;
266 } 272 }
267 constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const { 273 constexpr bool IsOutsideAliasRegion(VAddr address, size_t size) const {
268 return alias_region_start > address || address + size - 1 > alias_region_end - 1; 274 return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1;
269 } 275 }
270 constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const { 276 constexpr bool IsOutsideStackRegion(VAddr address, size_t size) const {
271 return stack_region_start > address || address + size - 1 > stack_region_end - 1; 277 return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1;
272 } 278 }
273 constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const { 279 constexpr bool IsInvalidRegion(VAddr address, size_t size) const {
274 return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; 280 return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1;
275 } 281 }
276 constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const { 282 constexpr bool IsInsideHeapRegion(VAddr address, size_t size) const {
277 return address + size > heap_region_start && heap_region_end > address; 283 return address + size > m_heap_region_start && m_heap_region_end > address;
278 } 284 }
279 constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const { 285 constexpr bool IsInsideAliasRegion(VAddr address, size_t size) const {
280 return address + size > alias_region_start && alias_region_end > address; 286 return address + size > m_alias_region_start && m_alias_region_end > address;
281 } 287 }
282 constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const { 288 constexpr bool IsOutsideASLRRegion(VAddr address, size_t size) const {
283 if (IsInvalidRegion(address, size)) { 289 if (IsInvalidRegion(address, size)) {
284 return true; 290 return true;
285 } 291 }
@@ -291,73 +297,78 @@ public:
291 } 297 }
292 return {}; 298 return {};
293 } 299 }
294 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { 300 constexpr bool IsInsideASLRRegion(VAddr address, size_t size) const {
295 return !IsOutsideASLRRegion(address, size); 301 return !IsOutsideASLRRegion(address, size);
296 } 302 }
297 constexpr std::size_t GetNumGuardPages() const { 303 constexpr size_t GetNumGuardPages() const {
298 return IsKernel() ? 1 : 4; 304 return IsKernel() ? 1 : 4;
299 } 305 }
300 PAddr GetPhysicalAddr(VAddr addr) const { 306 PAddr GetPhysicalAddr(VAddr addr) const {
301 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; 307 const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
302 ASSERT(backing_addr); 308 ASSERT(backing_addr);
303 return backing_addr + addr; 309 return backing_addr + addr;
304 } 310 }
305 constexpr bool Contains(VAddr addr) const { 311 constexpr bool Contains(VAddr addr) const {
306 return address_space_start <= addr && addr <= address_space_end - 1; 312 return m_address_space_start <= addr && addr <= m_address_space_end - 1;
307 } 313 }
308 constexpr bool Contains(VAddr addr, std::size_t size) const { 314 constexpr bool Contains(VAddr addr, size_t size) const {
309 return address_space_start <= addr && addr < addr + size && 315 return m_address_space_start <= addr && addr < addr + size &&
310 addr + size - 1 <= address_space_end - 1; 316 addr + size - 1 <= m_address_space_end - 1;
311 } 317 }
312 318
313private: 319private:
314 constexpr bool IsKernel() const { 320 constexpr bool IsKernel() const {
315 return is_kernel; 321 return m_is_kernel;
316 } 322 }
317 constexpr bool IsAslrEnabled() const { 323 constexpr bool IsAslrEnabled() const {
318 return is_aslr_enabled; 324 return m_enable_aslr;
319 } 325 }
320 326
321 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { 327 constexpr bool ContainsPages(VAddr addr, size_t num_pages) const {
322 return (address_space_start <= addr) && 328 return (m_address_space_start <= addr) &&
323 (num_pages <= (address_space_end - address_space_start) / PageSize) && 329 (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
324 (addr + num_pages * PageSize - 1 <= address_space_end - 1); 330 (addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
325 } 331 }
326 332
327private: 333private:
328 VAddr address_space_start{}; 334 VAddr m_address_space_start{};
329 VAddr address_space_end{}; 335 VAddr m_address_space_end{};
330 VAddr heap_region_start{}; 336 VAddr m_heap_region_start{};
331 VAddr heap_region_end{}; 337 VAddr m_heap_region_end{};
332 VAddr current_heap_end{}; 338 VAddr m_current_heap_end{};
333 VAddr alias_region_start{}; 339 VAddr m_alias_region_start{};
334 VAddr alias_region_end{}; 340 VAddr m_alias_region_end{};
335 VAddr stack_region_start{}; 341 VAddr m_stack_region_start{};
336 VAddr stack_region_end{}; 342 VAddr m_stack_region_end{};
337 VAddr kernel_map_region_start{}; 343 VAddr m_kernel_map_region_start{};
338 VAddr kernel_map_region_end{}; 344 VAddr m_kernel_map_region_end{};
339 VAddr code_region_start{}; 345 VAddr m_code_region_start{};
340 VAddr code_region_end{}; 346 VAddr m_code_region_end{};
341 VAddr alias_code_region_start{}; 347 VAddr m_alias_code_region_start{};
342 VAddr alias_code_region_end{}; 348 VAddr m_alias_code_region_end{};
343 349
344 std::size_t mapped_physical_memory_size{}; 350 size_t m_mapped_physical_memory_size{};
345 std::size_t max_heap_size{}; 351 size_t m_max_heap_size{};
346 std::size_t max_physical_memory_size{}; 352 size_t m_max_physical_memory_size{};
347 std::size_t address_space_width{}; 353 size_t m_address_space_width{};
348 354
349 bool is_kernel{}; 355 KMemoryBlockManager m_memory_block_manager;
350 bool is_aslr_enabled{}; 356
351 357 bool m_is_kernel{};
352 u32 heap_fill_value{}; 358 bool m_enable_aslr{};
353 const KMemoryRegion* cached_physical_heap_region{}; 359 bool m_enable_device_address_space_merge{};
354 360
355 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 361 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
356 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 362
357 363 u32 m_heap_fill_value{};
358 Common::PageTable page_table_impl; 364 const KMemoryRegion* m_cached_physical_heap_region{};
359 365
360 Core::System& system; 366 KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
367 KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
368
369 std::unique_ptr<Common::PageTable> m_page_table_impl;
370
371 Core::System& m_system;
361}; 372};
362 373
363} // namespace Kernel 374} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 7a5a9dc2a..77d00ae2c 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) {
57 57
58 server.EnqueueSession(session); 58 server.EnqueueSession(session);
59 59
60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
61 session_ptr->ClientConnected(server.AcceptSession());
62 } else {
63 ASSERT(false);
64 }
65
66 return ResultSuccess; 60 return ResultSuccess;
67} 61}
68 62
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index d3e99665f..8c3495e5a 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -72,7 +72,8 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string
72 72
73 process->name = std::move(process_name); 73 process->name = std::move(process_name);
74 process->resource_limit = res_limit; 74 process->resource_limit = res_limit;
75 process->status = ProcessStatus::Created; 75 process->system_resource_address = 0;
76 process->state = State::Created;
76 process->program_id = 0; 77 process->program_id = 0;
77 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() 78 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
78 : kernel.CreateNewUserProcessID(); 79 : kernel.CreateNewUserProcessID();
@@ -92,11 +93,12 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string
92 process->exception_thread = nullptr; 93 process->exception_thread = nullptr;
93 process->is_suspended = false; 94 process->is_suspended = false;
94 process->schedule_count = 0; 95 process->schedule_count = 0;
96 process->is_handle_table_initialized = false;
95 97
96 // Open a reference to the resource limit. 98 // Open a reference to the resource limit.
97 process->resource_limit->Open(); 99 process->resource_limit->Open();
98 100
99 return ResultSuccess; 101 R_SUCCEED();
100} 102}
101 103
102void KProcess::DoWorkerTaskImpl() { 104void KProcess::DoWorkerTaskImpl() {
@@ -121,9 +123,9 @@ void KProcess::DecrementRunningThreadCount() {
121 } 123 }
122} 124}
123 125
124u64 KProcess::GetTotalPhysicalMemoryAvailable() const { 126u64 KProcess::GetTotalPhysicalMemoryAvailable() {
125 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + 127 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
126 page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size + 128 page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
127 main_thread_stack_size}; 129 main_thread_stack_size};
128 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); 130 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
129 capacity != pool_size) { 131 capacity != pool_size) {
@@ -135,16 +137,16 @@ u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
135 return memory_usage_capacity; 137 return memory_usage_capacity;
136} 138}
137 139
138u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { 140u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() {
139 return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize(); 141 return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
140} 142}
141 143
142u64 KProcess::GetTotalPhysicalMemoryUsed() const { 144u64 KProcess::GetTotalPhysicalMemoryUsed() {
143 return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() + 145 return image_size + main_thread_stack_size + page_table.GetNormalMemorySize() +
144 GetSystemResourceSize(); 146 GetSystemResourceSize();
145} 147}
146 148
147u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { 149u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() {
148 return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); 150 return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
149} 151}
150 152
@@ -244,7 +246,7 @@ Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr ad
244 shmem->Open(); 246 shmem->Open();
245 shemen_info->Open(); 247 shemen_info->Open();
246 248
247 return ResultSuccess; 249 R_SUCCEED();
248} 250}
249 251
250void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, 252void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
@@ -289,12 +291,12 @@ Result KProcess::Reset() {
289 KScopedSchedulerLock sl{kernel}; 291 KScopedSchedulerLock sl{kernel};
290 292
291 // Validate that we're in a state that we can reset. 293 // Validate that we're in a state that we can reset.
292 R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); 294 R_UNLESS(state != State::Terminated, ResultInvalidState);
293 R_UNLESS(is_signaled, ResultInvalidState); 295 R_UNLESS(is_signaled, ResultInvalidState);
294 296
295 // Clear signaled. 297 // Clear signaled.
296 is_signaled = false; 298 is_signaled = false;
297 return ResultSuccess; 299 R_SUCCEED();
298} 300}
299 301
300Result KProcess::SetActivity(ProcessActivity activity) { 302Result KProcess::SetActivity(ProcessActivity activity) {
@@ -304,15 +306,13 @@ Result KProcess::SetActivity(ProcessActivity activity) {
304 KScopedSchedulerLock sl{kernel}; 306 KScopedSchedulerLock sl{kernel};
305 307
306 // Validate our state. 308 // Validate our state.
307 R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState); 309 R_UNLESS(state != State::Terminating, ResultInvalidState);
308 R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); 310 R_UNLESS(state != State::Terminated, ResultInvalidState);
309 311
310 // Either pause or resume. 312 // Either pause or resume.
311 if (activity == ProcessActivity::Paused) { 313 if (activity == ProcessActivity::Paused) {
312 // Verify that we're not suspended. 314 // Verify that we're not suspended.
313 if (is_suspended) { 315 R_UNLESS(!is_suspended, ResultInvalidState);
314 return ResultInvalidState;
315 }
316 316
317 // Suspend all threads. 317 // Suspend all threads.
318 for (auto* thread : GetThreadList()) { 318 for (auto* thread : GetThreadList()) {
@@ -325,9 +325,7 @@ Result KProcess::SetActivity(ProcessActivity activity) {
325 ASSERT(activity == ProcessActivity::Runnable); 325 ASSERT(activity == ProcessActivity::Runnable);
326 326
327 // Verify that we're suspended. 327 // Verify that we're suspended.
328 if (!is_suspended) { 328 R_UNLESS(is_suspended, ResultInvalidState);
329 return ResultInvalidState;
330 }
331 329
332 // Resume all threads. 330 // Resume all threads.
333 for (auto* thread : GetThreadList()) { 331 for (auto* thread : GetThreadList()) {
@@ -338,7 +336,7 @@ Result KProcess::SetActivity(ProcessActivity activity) {
338 SetSuspended(false); 336 SetSuspended(false);
339 } 337 }
340 338
341 return ResultSuccess; 339 R_SUCCEED();
342} 340}
343 341
344Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { 342Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) {
@@ -348,35 +346,38 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
348 system_resource_size = metadata.GetSystemResourceSize(); 346 system_resource_size = metadata.GetSystemResourceSize();
349 image_size = code_size; 347 image_size = code_size;
350 348
349 // We currently do not support process-specific system resource
350 UNIMPLEMENTED_IF(system_resource_size != 0);
351
351 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, 352 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
352 code_size + system_resource_size); 353 code_size + system_resource_size);
353 if (!memory_reservation.Succeeded()) { 354 if (!memory_reservation.Succeeded()) {
354 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", 355 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
355 code_size + system_resource_size); 356 code_size + system_resource_size);
356 return ResultLimitReached; 357 R_RETURN(ResultLimitReached);
357 } 358 }
358 // Initialize proces address space 359 // Initialize proces address space
359 if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 360 if (const Result result{page_table.InitializeForProcess(
360 0x8000000, code_size, 361 metadata.GetAddressSpaceType(), false, 0x8000000, code_size,
361 KMemoryManager::Pool::Application)}; 362 &kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)};
362 result.IsError()) { 363 result.IsError()) {
363 return result; 364 R_RETURN(result);
364 } 365 }
365 366
366 // Map process code region 367 // Map process code region
367 if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), 368 if (const Result result{page_table.MapProcessCode(page_table.GetCodeRegionStart(),
368 code_size / PageSize, KMemoryState::Code, 369 code_size / PageSize, KMemoryState::Code,
369 KMemoryPermission::None)}; 370 KMemoryPermission::None)};
370 result.IsError()) { 371 result.IsError()) {
371 return result; 372 R_RETURN(result);
372 } 373 }
373 374
374 // Initialize process capabilities 375 // Initialize process capabilities
375 const auto& caps{metadata.GetKernelCapabilities()}; 376 const auto& caps{metadata.GetKernelCapabilities()};
376 if (const Result result{ 377 if (const Result result{
377 capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; 378 capabilities.InitializeForUserProcess(caps.data(), caps.size(), page_table)};
378 result.IsError()) { 379 result.IsError()) {
379 return result; 380 R_RETURN(result);
380 } 381 }
381 382
382 // Set memory usage capacity 383 // Set memory usage capacity
@@ -384,12 +385,12 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
384 case FileSys::ProgramAddressSpaceType::Is32Bit: 385 case FileSys::ProgramAddressSpaceType::Is32Bit:
385 case FileSys::ProgramAddressSpaceType::Is36Bit: 386 case FileSys::ProgramAddressSpaceType::Is36Bit:
386 case FileSys::ProgramAddressSpaceType::Is39Bit: 387 case FileSys::ProgramAddressSpaceType::Is39Bit:
387 memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart(); 388 memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart();
388 break; 389 break;
389 390
390 case FileSys::ProgramAddressSpaceType::Is32BitNoMap: 391 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
391 memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() + 392 memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart() +
392 page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart(); 393 page_table.GetAliasRegionEnd() - page_table.GetAliasRegionStart();
393 break; 394 break;
394 395
395 default: 396 default:
@@ -397,10 +398,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
397 } 398 }
398 399
399 // Create TLS region 400 // Create TLS region
400 R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); 401 R_TRY(this->CreateThreadLocalRegion(std::addressof(plr_address)));
401 memory_reservation.Commit(); 402 memory_reservation.Commit();
402 403
403 return handle_table.Initialize(capabilities.GetHandleTableSize()); 404 R_RETURN(handle_table.Initialize(capabilities.GetHandleTableSize()));
404} 405}
405 406
406void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 407void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
@@ -409,15 +410,15 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
409 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); 410 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
410 411
411 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 412 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
412 ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError()); 413 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
413 414
414 ChangeStatus(ProcessStatus::Running); 415 ChangeState(State::Running);
415 416
416 SetupMainThread(kernel.System(), *this, main_thread_priority, main_thread_stack_top); 417 SetupMainThread(kernel.System(), *this, main_thread_priority, main_thread_stack_top);
417} 418}
418 419
419void KProcess::PrepareForTermination() { 420void KProcess::PrepareForTermination() {
420 ChangeStatus(ProcessStatus::Exiting); 421 ChangeState(State::Terminating);
421 422
422 const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) { 423 const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
423 for (auto* thread : in_thread_list) { 424 for (auto* thread : in_thread_list) {
@@ -437,15 +438,15 @@ void KProcess::PrepareForTermination() {
437 438
438 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); 439 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
439 440
440 this->DeleteThreadLocalRegion(tls_region_address); 441 this->DeleteThreadLocalRegion(plr_address);
441 tls_region_address = 0; 442 plr_address = 0;
442 443
443 if (resource_limit) { 444 if (resource_limit) {
444 resource_limit->Release(LimitableResource::PhysicalMemory, 445 resource_limit->Release(LimitableResource::PhysicalMemory,
445 main_thread_stack_size + image_size); 446 main_thread_stack_size + image_size);
446 } 447 }
447 448
448 ChangeStatus(ProcessStatus::Exited); 449 ChangeState(State::Terminated);
449} 450}
450 451
451void KProcess::Finalize() { 452void KProcess::Finalize() {
@@ -474,7 +475,7 @@ void KProcess::Finalize() {
474 } 475 }
475 476
476 // Finalize the page table. 477 // Finalize the page table.
477 page_table.reset(); 478 page_table.Finalize();
478 479
479 // Perform inherited finalization. 480 // Perform inherited finalization.
480 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); 481 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
@@ -499,7 +500,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) {
499 } 500 }
500 501
501 *out = tlr; 502 *out = tlr;
502 return ResultSuccess; 503 R_SUCCEED();
503 } 504 }
504 } 505 }
505 506
@@ -528,7 +529,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) {
528 // We succeeded! 529 // We succeeded!
529 tlp_guard.Cancel(); 530 tlp_guard.Cancel();
530 *out = tlr; 531 *out = tlr;
531 return ResultSuccess; 532 R_SUCCEED();
532} 533}
533 534
534Result KProcess::DeleteThreadLocalRegion(VAddr addr) { 535Result KProcess::DeleteThreadLocalRegion(VAddr addr) {
@@ -576,7 +577,7 @@ Result KProcess::DeleteThreadLocalRegion(VAddr addr) {
576 KThreadLocalPage::Free(kernel, page_to_free); 577 KThreadLocalPage::Free(kernel, page_to_free);
577 } 578 }
578 579
579 return ResultSuccess; 580 R_SUCCEED();
580} 581}
581 582
582bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size, 583bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size,
@@ -628,7 +629,7 @@ bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size,
628void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 629void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
629 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 630 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
630 Svc::MemoryPermission permission) { 631 Svc::MemoryPermission permission) {
631 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); 632 page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
632 }; 633 };
633 634
634 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), 635 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
@@ -645,19 +646,18 @@ bool KProcess::IsSignaled() const {
645} 646}
646 647
647KProcess::KProcess(KernelCore& kernel_) 648KProcess::KProcess(KernelCore& kernel_)
648 : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique<KPageTable>( 649 : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{kernel_.System()},
649 kernel_.System())},
650 handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, 650 handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()},
651 state_lock{kernel_}, list_lock{kernel_} {} 651 state_lock{kernel_}, list_lock{kernel_} {}
652 652
653KProcess::~KProcess() = default; 653KProcess::~KProcess() = default;
654 654
655void KProcess::ChangeStatus(ProcessStatus new_status) { 655void KProcess::ChangeState(State new_state) {
656 if (status == new_status) { 656 if (state == new_state) {
657 return; 657 return;
658 } 658 }
659 659
660 status = new_status; 660 state = new_state;
661 is_signaled = true; 661 is_signaled = true;
662 NotifyAvailable(); 662 NotifyAvailable();
663} 663}
@@ -668,17 +668,17 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
668 // The kernel always ensures that the given stack size is page aligned. 668 // The kernel always ensures that the given stack size is page aligned.
669 main_thread_stack_size = Common::AlignUp(stack_size, PageSize); 669 main_thread_stack_size = Common::AlignUp(stack_size, PageSize);
670 670
671 const VAddr start{page_table->GetStackRegionStart()}; 671 const VAddr start{page_table.GetStackRegionStart()};
672 const std::size_t size{page_table->GetStackRegionEnd() - start}; 672 const std::size_t size{page_table.GetStackRegionEnd() - start};
673 673
674 CASCADE_RESULT(main_thread_stack_top, 674 CASCADE_RESULT(main_thread_stack_top,
675 page_table->AllocateAndMapMemory( 675 page_table.AllocateAndMapMemory(
676 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, 676 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
677 KMemoryState::Stack, KMemoryPermission::UserReadWrite)); 677 KMemoryState::Stack, KMemoryPermission::UserReadWrite));
678 678
679 main_thread_stack_top += main_thread_stack_size; 679 main_thread_stack_top += main_thread_stack_size;
680 680
681 return ResultSuccess; 681 R_SUCCEED();
682} 682}
683 683
684} // namespace Kernel 684} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index d56d73bab..2e0cc3d0b 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/k_auto_object.h" 13#include "core/hle/kernel/k_auto_object.h"
14#include "core/hle/kernel/k_condition_variable.h" 14#include "core/hle/kernel/k_condition_variable.h"
15#include "core/hle/kernel/k_handle_table.h" 15#include "core/hle/kernel/k_handle_table.h"
16#include "core/hle/kernel/k_page_table.h"
16#include "core/hle/kernel/k_synchronization_object.h" 17#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/kernel/k_thread_local_page.h" 18#include "core/hle/kernel/k_thread_local_page.h"
18#include "core/hle/kernel/k_worker_task.h" 19#include "core/hle/kernel/k_worker_task.h"
@@ -31,7 +32,6 @@ class ProgramMetadata;
31namespace Kernel { 32namespace Kernel {
32 33
33class KernelCore; 34class KernelCore;
34class KPageTable;
35class KResourceLimit; 35class KResourceLimit;
36class KThread; 36class KThread;
37class KSharedMemoryInfo; 37class KSharedMemoryInfo;
@@ -45,24 +45,6 @@ enum class MemoryRegion : u16 {
45 BASE = 3, 45 BASE = 3,
46}; 46};
47 47
48/**
49 * Indicates the status of a Process instance.
50 *
51 * @note These match the values as used by kernel,
52 * so new entries should only be added if RE
53 * shows that a new value has been introduced.
54 */
55enum class ProcessStatus {
56 Created,
57 CreatedWithDebuggerAttached,
58 Running,
59 WaitingForDebuggerToAttach,
60 DebuggerAttached,
61 Exiting,
62 Exited,
63 DebugBreak,
64};
65
66enum class ProcessActivity : u32 { 48enum class ProcessActivity : u32 {
67 Runnable, 49 Runnable,
68 Paused, 50 Paused,
@@ -89,6 +71,17 @@ public:
89 explicit KProcess(KernelCore& kernel_); 71 explicit KProcess(KernelCore& kernel_);
90 ~KProcess() override; 72 ~KProcess() override;
91 73
74 enum class State {
75 Created = static_cast<u32>(Svc::ProcessState::Created),
76 CreatedAttached = static_cast<u32>(Svc::ProcessState::CreatedAttached),
77 Running = static_cast<u32>(Svc::ProcessState::Running),
78 Crashed = static_cast<u32>(Svc::ProcessState::Crashed),
79 RunningAttached = static_cast<u32>(Svc::ProcessState::RunningAttached),
80 Terminating = static_cast<u32>(Svc::ProcessState::Terminating),
81 Terminated = static_cast<u32>(Svc::ProcessState::Terminated),
82 DebugBreak = static_cast<u32>(Svc::ProcessState::DebugBreak),
83 };
84
92 enum : u64 { 85 enum : u64 {
93 /// Lowest allowed process ID for a kernel initial process. 86 /// Lowest allowed process ID for a kernel initial process.
94 InitialKIPIDMin = 1, 87 InitialKIPIDMin = 1,
@@ -114,12 +107,12 @@ public:
114 107
115 /// Gets a reference to the process' page table. 108 /// Gets a reference to the process' page table.
116 KPageTable& PageTable() { 109 KPageTable& PageTable() {
117 return *page_table; 110 return page_table;
118 } 111 }
119 112
120 /// Gets const a reference to the process' page table. 113 /// Gets const a reference to the process' page table.
121 const KPageTable& PageTable() const { 114 const KPageTable& PageTable() const {
122 return *page_table; 115 return page_table;
123 } 116 }
124 117
125 /// Gets a reference to the process' handle table. 118 /// Gets a reference to the process' handle table.
@@ -145,26 +138,25 @@ public:
145 } 138 }
146 139
147 Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { 140 Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
148 return condition_var.Wait(address, cv_key, tag, ns); 141 R_RETURN(condition_var.Wait(address, cv_key, tag, ns));
149 } 142 }
150 143
151 Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) { 144 Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) {
152 return address_arbiter.SignalToAddress(address, signal_type, value, count); 145 R_RETURN(address_arbiter.SignalToAddress(address, signal_type, value, count));
153 } 146 }
154 147
155 Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, 148 Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
156 s64 timeout) { 149 s64 timeout) {
157 return address_arbiter.WaitForAddress(address, arb_type, value, timeout); 150 R_RETURN(address_arbiter.WaitForAddress(address, arb_type, value, timeout));
158 } 151 }
159 152
160 /// Gets the address to the process' dedicated TLS region. 153 VAddr GetProcessLocalRegionAddress() const {
161 VAddr GetTLSRegionAddress() const { 154 return plr_address;
162 return tls_region_address;
163 } 155 }
164 156
165 /// Gets the current status of the process 157 /// Gets the current status of the process
166 ProcessStatus GetStatus() const { 158 State GetState() const {
167 return status; 159 return state;
168 } 160 }
169 161
170 /// Gets the unique ID that identifies this particular process. 162 /// Gets the unique ID that identifies this particular process.
@@ -286,18 +278,18 @@ public:
286 } 278 }
287 279
288 /// Retrieves the total physical memory available to this process in bytes. 280 /// Retrieves the total physical memory available to this process in bytes.
289 u64 GetTotalPhysicalMemoryAvailable() const; 281 u64 GetTotalPhysicalMemoryAvailable();
290 282
291 /// Retrieves the total physical memory available to this process in bytes, 283 /// Retrieves the total physical memory available to this process in bytes,
292 /// without the size of the personal system resource heap added to it. 284 /// without the size of the personal system resource heap added to it.
293 u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const; 285 u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource();
294 286
295 /// Retrieves the total physical memory used by this process in bytes. 287 /// Retrieves the total physical memory used by this process in bytes.
296 u64 GetTotalPhysicalMemoryUsed() const; 288 u64 GetTotalPhysicalMemoryUsed();
297 289
298 /// Retrieves the total physical memory used by this process in bytes, 290 /// Retrieves the total physical memory used by this process in bytes,
299 /// without the size of the personal system resource heap added to it. 291 /// without the size of the personal system resource heap added to it.
300 u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; 292 u64 GetTotalPhysicalMemoryUsedWithoutSystemResource();
301 293
302 /// Gets the list of all threads created with this process as their owner. 294 /// Gets the list of all threads created with this process as their owner.
303 std::list<KThread*>& GetThreadList() { 295 std::list<KThread*>& GetThreadList() {
@@ -415,19 +407,24 @@ private:
415 pinned_threads[core_id] = nullptr; 407 pinned_threads[core_id] = nullptr;
416 } 408 }
417 409
418 /// Changes the process status. If the status is different 410 void FinalizeHandleTable() {
419 /// from the current process status, then this will trigger 411 // Finalize the table.
420 /// a process signal. 412 handle_table.Finalize();
421 void ChangeStatus(ProcessStatus new_status); 413
414 // Note that the table is finalized.
415 is_handle_table_initialized = false;
416 }
417
418 void ChangeState(State new_state);
422 419
423 /// Allocates the main thread stack for the process, given the stack size in bytes. 420 /// Allocates the main thread stack for the process, given the stack size in bytes.
424 Result AllocateMainThreadStack(std::size_t stack_size); 421 Result AllocateMainThreadStack(std::size_t stack_size);
425 422
426 /// Memory manager for this process 423 /// Memory manager for this process
427 std::unique_ptr<KPageTable> page_table; 424 KPageTable page_table;
428 425
429 /// Current status of the process 426 /// Current status of the process
430 ProcessStatus status{}; 427 State state{};
431 428
432 /// The ID of this process 429 /// The ID of this process
433 u64 process_id = 0; 430 u64 process_id = 0;
@@ -443,6 +440,8 @@ private:
443 /// Resource limit descriptor for this process 440 /// Resource limit descriptor for this process
444 KResourceLimit* resource_limit{}; 441 KResourceLimit* resource_limit{};
445 442
443 VAddr system_resource_address{};
444
446 /// The ideal CPU core for this process, threads are scheduled on this core by default. 445 /// The ideal CPU core for this process, threads are scheduled on this core by default.
447 u8 ideal_core = 0; 446 u8 ideal_core = 0;
448 447
@@ -469,7 +468,7 @@ private:
469 KConditionVariable condition_var; 468 KConditionVariable condition_var;
470 469
471 /// Address indicating the location of the process' dedicated TLS region. 470 /// Address indicating the location of the process' dedicated TLS region.
472 VAddr tls_region_address = 0; 471 VAddr plr_address = 0;
473 472
474 /// Random values for svcGetInfo RandomEntropy 473 /// Random values for svcGetInfo RandomEntropy
475 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; 474 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
@@ -495,8 +494,12 @@ private:
495 /// Schedule count of this process 494 /// Schedule count of this process
496 s64 schedule_count{}; 495 s64 schedule_count{};
497 496
497 size_t memory_release_hint{};
498
498 bool is_signaled{}; 499 bool is_signaled{};
499 bool is_suspended{}; 500 bool is_suspended{};
501 bool is_immortal{};
502 bool is_handle_table_initialized{};
500 bool is_initialized{}; 503 bool is_initialized{};
501 504
502 std::atomic<u16> num_running_threads{}; 505 std::atomic<u16> num_running_threads{};
diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp
index 94c5464fe..5c942d47c 100644
--- a/src/core/hle/kernel/k_readable_event.cpp
+++ b/src/core/hle/kernel/k_readable_event.cpp
@@ -15,31 +15,44 @@ KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{ker
15 15
16KReadableEvent::~KReadableEvent() = default; 16KReadableEvent::~KReadableEvent() = default;
17 17
18void KReadableEvent::Initialize(KEvent* parent) {
19 m_is_signaled = false;
20 m_parent = parent;
21
22 if (m_parent != nullptr) {
23 m_parent->Open();
24 }
25}
26
18bool KReadableEvent::IsSignaled() const { 27bool KReadableEvent::IsSignaled() const {
19 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 28 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
20 29
21 return is_signaled; 30 return m_is_signaled;
22} 31}
23 32
24void KReadableEvent::Destroy() { 33void KReadableEvent::Destroy() {
25 if (parent) { 34 if (m_parent) {
26 parent->Close(); 35 {
36 KScopedSchedulerLock sl{kernel};
37 m_parent->OnReadableEventDestroyed();
38 }
39 m_parent->Close();
27 } 40 }
28} 41}
29 42
30Result KReadableEvent::Signal() { 43Result KReadableEvent::Signal() {
31 KScopedSchedulerLock lk{kernel}; 44 KScopedSchedulerLock lk{kernel};
32 45
33 if (!is_signaled) { 46 if (!m_is_signaled) {
34 is_signaled = true; 47 m_is_signaled = true;
35 NotifyAvailable(); 48 this->NotifyAvailable();
36 } 49 }
37 50
38 return ResultSuccess; 51 return ResultSuccess;
39} 52}
40 53
41Result KReadableEvent::Clear() { 54Result KReadableEvent::Clear() {
42 Reset(); 55 this->Reset();
43 56
44 return ResultSuccess; 57 return ResultSuccess;
45} 58}
@@ -47,11 +60,11 @@ Result KReadableEvent::Clear() {
47Result KReadableEvent::Reset() { 60Result KReadableEvent::Reset() {
48 KScopedSchedulerLock lk{kernel}; 61 KScopedSchedulerLock lk{kernel};
49 62
50 if (!is_signaled) { 63 if (!m_is_signaled) {
51 return ResultInvalidState; 64 return ResultInvalidState;
52 } 65 }
53 66
54 is_signaled = false; 67 m_is_signaled = false;
55 return ResultSuccess; 68 return ResultSuccess;
56} 69}
57 70
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
index 18dcad289..743f96bf5 100644
--- a/src/core/hle/kernel/k_readable_event.h
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -20,26 +20,23 @@ public:
20 explicit KReadableEvent(KernelCore& kernel_); 20 explicit KReadableEvent(KernelCore& kernel_);
21 ~KReadableEvent() override; 21 ~KReadableEvent() override;
22 22
23 void Initialize(KEvent* parent_event_, std::string&& name_) { 23 void Initialize(KEvent* parent);
24 is_signaled = false;
25 parent = parent_event_;
26 name = std::move(name_);
27 }
28 24
29 KEvent* GetParent() const { 25 KEvent* GetParent() const {
30 return parent; 26 return m_parent;
31 } 27 }
32 28
29 Result Signal();
30 Result Clear();
31
33 bool IsSignaled() const override; 32 bool IsSignaled() const override;
34 void Destroy() override; 33 void Destroy() override;
35 34
36 Result Signal();
37 Result Clear();
38 Result Reset(); 35 Result Reset();
39 36
40private: 37private:
41 bool is_signaled{}; 38 bool m_is_signaled{};
42 KEvent* parent{}; 39 KEvent* m_parent{};
43}; 40};
44 41
45} // namespace Kernel 42} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c34ce7a17..b1cabbca0 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
81 // HACK: we cannot schedule from this thread, it is not a core thread 81 // HACK: we cannot schedule from this thread, it is not a core thread
82 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); 82 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
83 83
84 // Special case to ensure dummy threads that are waiting block 84 // Ensure dummy threads that are waiting block.
85 GetCurrentThread(kernel).IfDummyThreadTryWait(); 85 GetCurrentThread(kernel).DummyThreadBeginWait();
86 86
87 ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); 87 ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
88 GetCurrentThread(kernel).EnableDispatch(); 88 GetCurrentThread(kernel).EnableDispatch();
@@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
314 idle_cores &= ~(1ULL << core_id); 314 idle_cores &= ~(1ULL << core_id);
315 } 315 }
316 316
317 // HACK: any waiting dummy threads can wake up now.
318 kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads();
319
320 // HACK: if we are a dummy thread, and we need to go sleep, indicate
321 // that for when the lock is released.
322 KThread* const cur_thread = GetCurrentThreadPointer(kernel);
323 if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) {
324 cur_thread->RequestDummyThreadWait();
325 }
326
317 return cores_needing_scheduling; 327 return cores_needing_scheduling;
318} 328}
319 329
@@ -531,11 +541,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
531 GetPriorityQueue(kernel).Remove(thread); 541 GetPriorityQueue(kernel).Remove(thread);
532 IncrementScheduledCount(thread); 542 IncrementScheduledCount(thread);
533 SetSchedulerUpdateNeeded(kernel); 543 SetSchedulerUpdateNeeded(kernel);
544
545 if (thread->IsDummyThread()) {
546 // HACK: if this is a dummy thread, it should no longer wake up when the
547 // scheduler lock is released.
548 kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread);
549 }
534 } else if (cur_state == ThreadState::Runnable) { 550 } else if (cur_state == ThreadState::Runnable) {
535 // If we're now runnable, then we weren't previously, and we should add. 551 // If we're now runnable, then we weren't previously, and we should add.
536 GetPriorityQueue(kernel).PushBack(thread); 552 GetPriorityQueue(kernel).PushBack(thread);
537 IncrementScheduledCount(thread); 553 IncrementScheduledCount(thread);
538 SetSchedulerUpdateNeeded(kernel); 554 SetSchedulerUpdateNeeded(kernel);
555
556 if (thread->IsDummyThread()) {
557 // HACK: if this is a dummy thread, it should wake up when the scheduler
558 // lock is released.
559 kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread);
560 }
539 } 561 }
540} 562}
541 563
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index e968f26ad..16968ba97 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -61,12 +61,6 @@ void KServerPort::Destroy() {
61 61
62 // Close our reference to our parent. 62 // Close our reference to our parent.
63 parent->Close(); 63 parent->Close();
64
65 // Release host emulation members.
66 session_handler.reset();
67
68 // Ensure that the global list tracking server objects does not hold on to a reference.
69 kernel.UnregisterServerObject(this);
70} 64}
71 65
72bool KServerPort::IsSignaled() const { 66bool KServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index fd4f4bd20..5fc7ee683 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -27,24 +27,6 @@ public:
27 27
28 void Initialize(KPort* parent_port_, std::string&& name_); 28 void Initialize(KPort* parent_port_, std::string&& name_);
29 29
30 /// Whether or not this server port has an HLE handler available.
31 bool HasSessionRequestHandler() const {
32 return !session_handler.expired();
33 }
34
35 /// Gets the HLE handler for this port.
36 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
37 return session_handler;
38 }
39
40 /**
41 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
42 * will inherit a reference to this handler.
43 */
44 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
45 session_handler = std::move(handler);
46 }
47
48 void EnqueueSession(KServerSession* pending_session); 30 void EnqueueSession(KServerSession* pending_session);
49 31
50 KServerSession* AcceptSession(); 32 KServerSession* AcceptSession();
@@ -65,7 +47,6 @@ private:
65 void CleanupSessions(); 47 void CleanupSessions();
66 48
67 SessionList session_list; 49 SessionList session_list;
68 SessionRequestHandlerWeakPtr session_handler;
69 KPort* parent{}; 50 KPort* parent{};
70}; 51};
71 52
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 802c646a6..aa1941f01 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <tuple> 4#include <tuple>
@@ -7,6 +7,8 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/scope_exit.h"
11#include "core/core.h"
10#include "core/core_timing.h" 12#include "core/core_timing.h"
11#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
@@ -18,49 +20,128 @@
18#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
19#include "core/hle/kernel/k_session.h" 21#include "core/hle/kernel/k_session.h"
20#include "core/hle/kernel/k_thread.h" 22#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/k_thread_queue.h"
21#include "core/hle/kernel/kernel.h" 24#include "core/hle/kernel/kernel.h"
22#include "core/hle/kernel/service_thread.h"
23#include "core/memory.h" 25#include "core/memory.h"
24 26
25namespace Kernel { 27namespace Kernel {
26 28
27KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} 29using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
30
31KServerSession::KServerSession(KernelCore& kernel_)
32 : KSynchronizationObject{kernel_}, m_lock{kernel_} {}
28 33
29KServerSession::~KServerSession() = default; 34KServerSession::~KServerSession() = default;
30 35
31void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 36void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
32 std::shared_ptr<SessionRequestManager> manager_) {
33 // Set member variables. 37 // Set member variables.
34 parent = parent_session_; 38 parent = parent_session_;
35 name = std::move(name_); 39 name = std::move(name_);
36
37 if (manager_) {
38 manager = manager_;
39 } else {
40 manager = std::make_shared<SessionRequestManager>(kernel);
41 }
42} 40}
43 41
44void KServerSession::Destroy() { 42void KServerSession::Destroy() {
45 parent->OnServerClosed(); 43 parent->OnServerClosed();
46 44
47 parent->Close(); 45 this->CleanupRequests();
48 46
49 // Release host emulation members. 47 parent->Close();
50 manager.reset();
51
52 // Ensure that the global list tracking server objects does not hold on to a reference.
53 kernel.UnregisterServerObject(this);
54} 48}
55 49
56void KServerSession::OnClientClosed() { 50void KServerSession::OnClientClosed() {
57 if (manager->HasSessionHandler()) { 51 KScopedLightLock lk{m_lock};
58 manager->SessionHandler().ClientDisconnected(this); 52
53 // Handle any pending requests.
54 KSessionRequest* prev_request = nullptr;
55 while (true) {
56 // Declare variables for processing the request.
57 KSessionRequest* request = nullptr;
58 KEvent* event = nullptr;
59 KThread* thread = nullptr;
60 bool cur_request = false;
61 bool terminate = false;
62
63 // Get the next request.
64 {
65 KScopedSchedulerLock sl{kernel};
66
67 if (m_current_request != nullptr && m_current_request != prev_request) {
68 // Set the request, open a reference as we process it.
69 request = m_current_request;
70 request->Open();
71 cur_request = true;
72
73 // Get thread and event for the request.
74 thread = request->GetThread();
75 event = request->GetEvent();
76
77 // If the thread is terminating, handle that.
78 if (thread->IsTerminationRequested()) {
79 request->ClearThread();
80 request->ClearEvent();
81 terminate = true;
82 }
83
84 prev_request = request;
85 } else if (!m_request_list.empty()) {
86 // Pop the request from the front of the list.
87 request = std::addressof(m_request_list.front());
88 m_request_list.pop_front();
89
90 // Get thread and event for the request.
91 thread = request->GetThread();
92 event = request->GetEvent();
93 }
94 }
95
96 // If there are no requests, we're done.
97 if (request == nullptr) {
98 break;
99 }
100
101 // All requests must have threads.
102 ASSERT(thread != nullptr);
103
104 // Ensure that we close the request when done.
105 SCOPE_EXIT({ request->Close(); });
106
107 // If we're terminating, close a reference to the thread and event.
108 if (terminate) {
109 thread->Close();
110 if (event != nullptr) {
111 event->Close();
112 }
113 }
114
115 // If we need to, reply.
116 if (event != nullptr && !cur_request) {
117 // There must be no mappings.
118 ASSERT(request->GetSendCount() == 0);
119 ASSERT(request->GetReceiveCount() == 0);
120 ASSERT(request->GetExchangeCount() == 0);
121
122 // // Get the process and page table.
123 // KProcess *client_process = thread->GetOwnerProcess();
124 // auto &client_pt = client_process->GetPageTable();
125
126 // // Reply to the request.
127 // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
128 // ResultSessionClosed);
129
130 // // Unlock the buffer.
131 // // NOTE: Nintendo does not check the result of this.
132 // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
133
134 // Signal the event.
135 event->Signal();
136 }
59 } 137 }
138
139 // Notify.
140 this->NotifyAvailable(ResultSessionClosed);
60} 141}
61 142
62bool KServerSession::IsSignaled() const { 143bool KServerSession::IsSignaled() const {
63 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 144 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
64 145
65 // If the client is closed, we're always signaled. 146 // If the client is closed, we're always signaled.
66 if (parent->IsClientClosed()) { 147 if (parent->IsClientClosed()) {
@@ -68,114 +149,271 @@ bool KServerSession::IsSignaled() const {
68 } 149 }
69 150
70 // Otherwise, we're signaled if we have a request and aren't handling one. 151 // Otherwise, we're signaled if we have a request and aren't handling one.
71 return false; 152 return !m_request_list.empty() && m_current_request == nullptr;
72} 153}
73 154
74void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { 155Result KServerSession::OnRequest(KSessionRequest* request) {
75 manager->AppendDomainHandler(std::move(handler)); 156 // Create the wait queue.
76} 157 ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
77 158
78std::size_t KServerSession::NumDomainRequestHandlers() const { 159 {
79 return manager->DomainHandlerCount(); 160 // Lock the scheduler.
80} 161 KScopedSchedulerLock sl{kernel};
81 162
82Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { 163 // Ensure that we can handle new requests.
83 if (!context.HasDomainMessageHeader()) { 164 R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
84 return ResultSuccess;
85 }
86 165
87 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 166 // Check that we're not terminating.
88 context.SetSessionRequestManager(manager); 167 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
89
90 // If there is a DomainMessageHeader, then this is CommandType "Request"
91 const auto& domain_message_header = context.GetDomainMessageHeader();
92 const u32 object_id{domain_message_header.object_id};
93 switch (domain_message_header.command) {
94 case IPC::DomainMessageHeader::CommandType::SendMessage:
95 if (object_id > manager->DomainHandlerCount()) {
96 LOG_CRITICAL(IPC,
97 "object_id {} is too big! This probably means a recent service call "
98 "to {} needed to return a new interface!",
99 object_id, name);
100 ASSERT(false);
101 return ResultSuccess; // Ignore error if asserts are off
102 }
103 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
104 return strong_ptr->HandleSyncRequest(*this, context);
105 } else {
106 ASSERT(false);
107 return ResultSuccess;
108 }
109 168
110 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { 169 // Get whether we're empty.
111 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); 170 const bool was_empty = m_request_list.empty();
112 171
113 manager->CloseDomainHandler(object_id - 1); 172 // Add the request to the list.
173 request->Open();
174 m_request_list.push_back(*request);
114 175
115 IPC::ResponseBuilder rb{context, 2}; 176 // If we were empty, signal.
116 rb.Push(ResultSuccess); 177 if (was_empty) {
117 return ResultSuccess; 178 this->NotifyAvailable();
118 } 179 }
180
181 // If we have a request event, this is asynchronous, and we don't need to wait.
182 R_SUCCEED_IF(request->GetEvent() != nullptr);
183
184 // This is a synchronous request, so we should wait for our request to complete.
185 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
186 GetCurrentThread(kernel).BeginWait(&wait_queue);
119 } 187 }
120 188
121 LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); 189 return GetCurrentThread(kernel).GetWaitResult();
122 ASSERT(false);
123 return ResultSuccess;
124} 190}
125 191
126Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { 192Result KServerSession::SendReply(bool is_hle) {
127 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; 193 // Lock the session.
128 auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread); 194 KScopedLightLock lk{m_lock};
129 195
130 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); 196 // Get the request.
197 KSessionRequest* request;
198 {
199 KScopedSchedulerLock sl{kernel};
131 200
132 // Ensure we have a session request handler 201 // Get the current request.
133 if (manager->HasSessionRequestHandler(*context)) { 202 request = m_current_request;
134 if (auto strong_ptr = manager->GetServiceThread().lock()) { 203 R_UNLESS(request != nullptr, ResultInvalidState);
135 strong_ptr->QueueSyncRequest(*parent, std::move(context)); 204
136 } else { 205 // Clear the current request, since we're processing it.
137 ASSERT_MSG(false, "strong_ptr is nullptr!"); 206 m_current_request = nullptr;
207 if (!m_request_list.empty()) {
208 this->NotifyAvailable();
138 } 209 }
139 } else {
140 ASSERT_MSG(false, "handler is invalid!");
141 } 210 }
142 211
143 return ResultSuccess; 212 // Close reference to the request once we're done processing it.
144} 213 SCOPE_EXIT({ request->Close(); });
214
215 // Extract relevant information from the request.
216 const uintptr_t client_message = request->GetAddress();
217 const size_t client_buffer_size = request->GetSize();
218 KThread* client_thread = request->GetThread();
219 KEvent* event = request->GetEvent();
220
221 // Check whether we're closed.
222 const bool closed = (client_thread == nullptr || parent->IsClientClosed());
145 223
146Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
147 Result result = ResultSuccess; 224 Result result = ResultSuccess;
225 if (!closed) {
226 // If we're not closed, send the reply.
227 if (is_hle) {
228 // HLE servers write directly to a pointer to the thread command buffer. Therefore
229 // the reply has already been written in this case.
230 } else {
231 Core::Memory::Memory& memory{kernel.System().Memory()};
232 KThread* server_thread{GetCurrentThreadPointer(kernel)};
233 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
148 234
149 // If the session has been converted to a domain, handle the domain request 235 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
150 if (manager->HasSessionRequestHandler(context)) { 236 auto* dst_msg_buffer = memory.GetPointer(client_message);
151 if (IsDomain() && context.HasDomainMessageHeader()) { 237 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
152 result = HandleDomainSyncRequest(context);
153 // If there is no domain header, the regular session handler is used
154 } else if (manager->HasSessionHandler()) {
155 // If this ServerSession has an associated HLE handler, forward the request to it.
156 result = manager->SessionHandler().HandleSyncRequest(*this, context);
157 } 238 }
158 } else { 239 } else {
159 ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); 240 result = ResultSessionClosed;
160 IPC::ResponseBuilder rb(context, 2);
161 rb.Push(ResultSuccess);
162 } 241 }
163 242
164 if (convert_to_domain) { 243 // Select a result for the client.
165 ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); 244 Result client_result = result;
166 manager->ConvertToDomain(); 245 if (closed && R_SUCCEEDED(result)) {
167 convert_to_domain = false; 246 result = ResultSessionClosed;
247 client_result = ResultSessionClosed;
248 } else {
249 result = ResultSuccess;
168 } 250 }
169 251
170 // The calling thread is waiting for this request to complete, so wake it up. 252 // If there's a client thread, update it.
171 context.GetThread().EndWait(result); 253 if (client_thread != nullptr) {
254 if (event != nullptr) {
255 // // Get the client process/page table.
256 // KProcess *client_process = client_thread->GetOwnerProcess();
257 // KPageTable *client_page_table = &client_process->PageTable();
258
259 // // If we need to, reply with an async error.
260 // if (R_FAILED(client_result)) {
261 // ReplyAsyncError(client_process, client_message, client_buffer_size,
262 // client_result);
263 // }
264
265 // // Unlock the client buffer.
266 // // NOTE: Nintendo does not check the result of this.
267 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
268
269 // Signal the event.
270 event->Signal();
271 } else {
272 // End the client thread's wait.
273 KScopedSchedulerLock sl{kernel};
274
275 if (!client_thread->IsTerminationRequested()) {
276 client_thread->EndWait(client_result);
277 }
278 }
279 }
172 280
173 return result; 281 return result;
174} 282}
175 283
176Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, 284Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
177 Core::Timing::CoreTiming& core_timing) { 285 std::weak_ptr<SessionRequestManager> manager) {
178 return QueueSyncRequest(thread, memory); 286 // Lock the session.
287 KScopedLightLock lk{m_lock};
288
289 // Get the request and client thread.
290 KSessionRequest* request;
291 KThread* client_thread;
292
293 {
294 KScopedSchedulerLock sl{kernel};
295
296 // Ensure that we can service the request.
297 R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
298
299 // Ensure we aren't already servicing a request.
300 R_UNLESS(m_current_request == nullptr, ResultNotFound);
301
302 // Ensure we have a request to service.
303 R_UNLESS(!m_request_list.empty(), ResultNotFound);
304
305 // Pop the first request from the list.
306 request = &m_request_list.front();
307 m_request_list.pop_front();
308
309 // Get the thread for the request.
310 client_thread = request->GetThread();
311 R_UNLESS(client_thread != nullptr, ResultSessionClosed);
312
313 // Open the client thread.
314 client_thread->Open();
315 }
316
317 SCOPE_EXIT({ client_thread->Close(); });
318
319 // Set the request as our current.
320 m_current_request = request;
321
322 // Get the client address.
323 uintptr_t client_message = request->GetAddress();
324 size_t client_buffer_size = request->GetSize();
325 // bool recv_list_broken = false;
326
327 // Receive the message.
328 Core::Memory::Memory& memory{kernel.System().Memory()};
329 if (out_context != nullptr) {
330 // HLE request.
331 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
332 *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
333 (*out_context)->SetSessionRequestManager(manager);
334 (*out_context)
335 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
336 cmd_buf);
337 } else {
338 KThread* server_thread{GetCurrentThreadPointer(kernel)};
339 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
340
341 auto* src_msg_buffer = memory.GetPointer(client_message);
342 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
343 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
344 }
345
346 // We succeeded.
347 return ResultSuccess;
348}
349
350void KServerSession::CleanupRequests() {
351 KScopedLightLock lk(m_lock);
352
353 // Clean up any pending requests.
354 while (true) {
355 // Get the next request.
356 KSessionRequest* request = nullptr;
357 {
358 KScopedSchedulerLock sl{kernel};
359
360 if (m_current_request) {
361 // Choose the current request if we have one.
362 request = m_current_request;
363 m_current_request = nullptr;
364 } else if (!m_request_list.empty()) {
365 // Pop the request from the front of the list.
366 request = &m_request_list.front();
367 m_request_list.pop_front();
368 }
369 }
370
371 // If there's no request, we're done.
372 if (request == nullptr) {
373 break;
374 }
375
376 // Close a reference to the request once it's cleaned up.
377 SCOPE_EXIT({ request->Close(); });
378
379 // Extract relevant information from the request.
380 // const uintptr_t client_message = request->GetAddress();
381 // const size_t client_buffer_size = request->GetSize();
382 KThread* client_thread = request->GetThread();
383 KEvent* event = request->GetEvent();
384
385 // KProcess *server_process = request->GetServerProcess();
386 // KProcess *client_process = (client_thread != nullptr) ?
387 // client_thread->GetOwnerProcess() : nullptr;
388 // KProcessPageTable *client_page_table = (client_process != nullptr) ?
389 // &client_process->GetPageTable() : nullptr;
390
391 // Cleanup the mappings.
392 // Result result = CleanupMap(request, server_process, client_page_table);
393
394 // If there's a client thread, update it.
395 if (client_thread != nullptr) {
396 if (event != nullptr) {
397 // // We need to reply async.
398 // ReplyAsyncError(client_process, client_message, client_buffer_size,
399 // (R_SUCCEEDED(result) ? ResultSessionClosed : result));
400
401 // // Unlock the client buffer.
402 // NOTE: Nintendo does not check the result of this.
403 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
404
405 // Signal the event.
406 event->Signal();
407 } else {
408 // End the client thread's wait.
409 KScopedSchedulerLock sl{kernel};
410
411 if (!client_thread->IsTerminationRequested()) {
412 client_thread->EndWait(ResultSessionClosed);
413 }
414 }
415 }
416 }
179} 417}
180 418
181} // namespace Kernel 419} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 6d0821945..6e189af8b 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -1,8 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <list>
6#include <memory> 7#include <memory>
7#include <string> 8#include <string>
8#include <utility> 9#include <utility>
@@ -10,24 +11,16 @@
10#include <boost/intrusive/list.hpp> 11#include <boost/intrusive/list.hpp>
11 12
12#include "core/hle/kernel/hle_ipc.h" 13#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/k_light_lock.h"
15#include "core/hle/kernel/k_session_request.h"
13#include "core/hle/kernel/k_synchronization_object.h" 16#include "core/hle/kernel/k_synchronization_object.h"
14#include "core/hle/result.h" 17#include "core/hle/result.h"
15 18
16namespace Core::Memory {
17class Memory;
18}
19
20namespace Core::Timing {
21class CoreTiming;
22struct EventType;
23} // namespace Core::Timing
24
25namespace Kernel { 19namespace Kernel {
26 20
27class HLERequestContext; 21class HLERequestContext;
28class KernelCore; 22class KernelCore;
29class KSession; 23class KSession;
30class SessionRequestHandler;
31class SessionRequestManager; 24class SessionRequestManager;
32class KThread; 25class KThread;
33 26
@@ -43,8 +36,7 @@ public:
43 36
44 void Destroy() override; 37 void Destroy() override;
45 38
46 void Initialize(KSession* parent_session_, std::string&& name_, 39 void Initialize(KSession* parent_session_, std::string&& name_);
47 std::shared_ptr<SessionRequestManager> manager_);
48 40
49 KSession* GetParent() { 41 KSession* GetParent() {
50 return parent; 42 return parent;
@@ -55,71 +47,30 @@ public:
55 } 47 }
56 48
57 bool IsSignaled() const override; 49 bool IsSignaled() const override;
58
59 void OnClientClosed(); 50 void OnClientClosed();
60 51
61 void ClientConnected(SessionRequestHandlerPtr handler) { 52 /// TODO: flesh these out to match the real kernel
62 manager->SetSessionHandler(std::move(handler)); 53 Result OnRequest(KSessionRequest* request);
63 } 54 Result SendReply(bool is_hle = false);
64 55 Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
65 void ClientDisconnected() { 56 std::weak_ptr<SessionRequestManager> manager = {});
66 manager = nullptr;
67 }
68
69 /**
70 * Handle a sync request from the emulated application.
71 *
72 * @param thread Thread that initiated the request.
73 * @param memory Memory context to handle the sync request under.
74 * @param core_timing Core timing context to schedule the request event under.
75 *
76 * @returns Result from the operation.
77 */
78 Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
79 Core::Timing::CoreTiming& core_timing);
80
81 /// Adds a new domain request handler to the collection of request handlers within
82 /// this ServerSession instance.
83 void AppendDomainHandler(SessionRequestHandlerPtr handler);
84
85 /// Retrieves the total number of domain request handlers that have been
86 /// appended to this ServerSession instance.
87 std::size_t NumDomainRequestHandlers() const;
88
89 /// Returns true if the session has been converted to a domain, otherwise False
90 bool IsDomain() const {
91 return manager->IsDomain();
92 }
93
94 /// Converts the session to a domain at the end of the current command
95 void ConvertToDomain() {
96 convert_to_domain = true;
97 }
98 57
99 /// Gets the session request manager, which forwards requests to the underlying service 58 Result SendReplyHLE() {
100 std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() { 59 return SendReply(true);
101 return manager;
102 } 60 }
103 61
104private: 62private:
105 /// Queues a sync request from the emulated application. 63 /// Frees up waiting client sessions when this server session is about to die
106 Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); 64 void CleanupRequests();
107
108 /// Completes a sync request from the emulated application.
109 Result CompleteSyncRequest(HLERequestContext& context);
110
111 /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
112 /// object handle.
113 Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
114
115 /// This session's HLE request handlers
116 std::shared_ptr<SessionRequestManager> manager;
117
118 /// When set to True, converts the session to a domain at the end of the command
119 bool convert_to_domain{};
120 65
121 /// KSession that owns this KServerSession 66 /// KSession that owns this KServerSession
122 KSession* parent{}; 67 KSession* parent{};
68
69 /// List of threads which are pending a reply.
70 boost::intrusive::list<KSessionRequest> m_request_list;
71 KSessionRequest* m_current_request{};
72
73 KLightLock m_lock;
123}; 74};
124 75
125} // namespace Kernel 76} // namespace Kernel
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index ee05aa282..7a6534ac3 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_)
13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} 13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
14KSession::~KSession() = default; 14KSession::~KSession() = default;
15 15
16void KSession::Initialize(KClientPort* port_, const std::string& name_, 16void KSession::Initialize(KClientPort* port_, const std::string& name_) {
17 std::shared_ptr<SessionRequestManager> manager_) {
18 // Increment reference count. 17 // Increment reference count.
19 // Because reference count is one on creation, this will result 18 // Because reference count is one on creation, this will result
20 // in a reference count of two. Thus, when both server and client are closed 19 // in a reference count of two. Thus, when both server and client are closed
@@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_,
26 KAutoObject::Create(std::addressof(client)); 25 KAutoObject::Create(std::addressof(client));
27 26
28 // Initialize our sub sessions. 27 // Initialize our sub sessions.
29 server.Initialize(this, name_ + ":Server", manager_); 28 server.Initialize(this, name_ + ":Server");
30 client.Initialize(this, name_ + ":Client"); 29 client.Initialize(this, name_ + ":Client");
31 30
32 // Set state and name. 31 // Set state and name.
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index c6ead403b..93e5e6f71 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -21,8 +21,7 @@ public:
21 explicit KSession(KernelCore& kernel_); 21 explicit KSession(KernelCore& kernel_);
22 ~KSession() override; 22 ~KSession() override;
23 23
24 void Initialize(KClientPort* port_, const std::string& name_, 24 void Initialize(KClientPort* port_, const std::string& name_);
25 std::shared_ptr<SessionRequestManager> manager_ = nullptr);
26 25
27 void Finalize() override; 26 void Finalize() override;
28 27
diff --git a/src/core/hle/kernel/k_session_request.cpp b/src/core/hle/kernel/k_session_request.cpp
new file mode 100644
index 000000000..520da6aa7
--- /dev/null
+++ b/src/core/hle/kernel/k_session_request.cpp
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_page_buffer.h"
5#include "core/hle/kernel/k_session_request.h"
6
7namespace Kernel {
8
9Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size,
10 KMemoryState state, size_t index) {
11 // At most 15 buffers of each type (4-bit descriptor counts).
12 ASSERT(index < ((1ul << 4) - 1) * 3);
13
14 // Get the mapping.
15 Mapping* mapping;
16 if (index < NumStaticMappings) {
17 mapping = &m_static_mappings[index];
18 } else {
19 // Allocate a page for the extra mappings.
20 if (m_mappings == nullptr) {
21 KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel);
22 R_UNLESS(page_buffer != nullptr, ResultOutOfMemory);
23
24 m_mappings = reinterpret_cast<Mapping*>(page_buffer);
25 }
26
27 mapping = &m_mappings[index - NumStaticMappings];
28 }
29
30 // Set the mapping.
31 mapping->Set(client, server, size, state);
32
33 return ResultSuccess;
34}
35
36Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size,
37 KMemoryState state) {
38 ASSERT(m_num_recv == 0);
39 ASSERT(m_num_exch == 0);
40 return this->PushMap(client, server, size, state, m_num_send++);
41}
42
43Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size,
44 KMemoryState state) {
45 ASSERT(m_num_exch == 0);
46 return this->PushMap(client, server, size, state, m_num_send + m_num_recv++);
47}
48
49Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size,
50 KMemoryState state) {
51 return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++);
52}
53
54void KSessionRequest::SessionMappings::Finalize() {
55 if (m_mappings) {
56 KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings));
57 m_mappings = nullptr;
58 }
59}
60
61} // namespace Kernel
diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h
new file mode 100644
index 000000000..e5558bc2c
--- /dev/null
+++ b/src/core/hle/kernel/k_session_request.h
@@ -0,0 +1,306 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "core/hle/kernel/k_auto_object.h"
9#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_memory_block.h"
11#include "core/hle/kernel/k_process.h"
12#include "core/hle/kernel/k_thread.h"
13#include "core/hle/kernel/slab_helpers.h"
14
15namespace Kernel {
16
17class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
18 public KAutoObject,
19 public boost::intrusive::list_base_hook<> {
20 KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
21
22public:
23 class SessionMappings {
24 private:
25 static constexpr size_t NumStaticMappings = 8;
26
27 class Mapping {
28 public:
29 constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) {
30 m_client_address = c;
31 m_server_address = s;
32 m_size = sz;
33 m_state = st;
34 }
35
36 constexpr VAddr GetClientAddress() const {
37 return m_client_address;
38 }
39 constexpr VAddr GetServerAddress() const {
40 return m_server_address;
41 }
42 constexpr size_t GetSize() const {
43 return m_size;
44 }
45 constexpr KMemoryState GetMemoryState() const {
46 return m_state;
47 }
48
49 private:
50 VAddr m_client_address;
51 VAddr m_server_address;
52 size_t m_size;
53 KMemoryState m_state;
54 };
55
56 public:
57 explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {}
58
59 void Initialize() {}
60 void Finalize();
61
62 size_t GetSendCount() const {
63 return m_num_send;
64 }
65 size_t GetReceiveCount() const {
66 return m_num_recv;
67 }
68 size_t GetExchangeCount() const {
69 return m_num_exch;
70 }
71
72 Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state);
73 Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state);
74 Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state);
75
76 VAddr GetSendClientAddress(size_t i) const {
77 return GetSendMapping(i).GetClientAddress();
78 }
79 VAddr GetSendServerAddress(size_t i) const {
80 return GetSendMapping(i).GetServerAddress();
81 }
82 size_t GetSendSize(size_t i) const {
83 return GetSendMapping(i).GetSize();
84 }
85 KMemoryState GetSendMemoryState(size_t i) const {
86 return GetSendMapping(i).GetMemoryState();
87 }
88
89 VAddr GetReceiveClientAddress(size_t i) const {
90 return GetReceiveMapping(i).GetClientAddress();
91 }
92 VAddr GetReceiveServerAddress(size_t i) const {
93 return GetReceiveMapping(i).GetServerAddress();
94 }
95 size_t GetReceiveSize(size_t i) const {
96 return GetReceiveMapping(i).GetSize();
97 }
98 KMemoryState GetReceiveMemoryState(size_t i) const {
99 return GetReceiveMapping(i).GetMemoryState();
100 }
101
102 VAddr GetExchangeClientAddress(size_t i) const {
103 return GetExchangeMapping(i).GetClientAddress();
104 }
105 VAddr GetExchangeServerAddress(size_t i) const {
106 return GetExchangeMapping(i).GetServerAddress();
107 }
108 size_t GetExchangeSize(size_t i) const {
109 return GetExchangeMapping(i).GetSize();
110 }
111 KMemoryState GetExchangeMemoryState(size_t i) const {
112 return GetExchangeMapping(i).GetMemoryState();
113 }
114
115 private:
116 Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index);
117
118 const Mapping& GetSendMapping(size_t i) const {
119 ASSERT(i < m_num_send);
120
121 const size_t index = i;
122 if (index < NumStaticMappings) {
123 return m_static_mappings[index];
124 } else {
125 return m_mappings[index - NumStaticMappings];
126 }
127 }
128
129 const Mapping& GetReceiveMapping(size_t i) const {
130 ASSERT(i < m_num_recv);
131
132 const size_t index = m_num_send + i;
133 if (index < NumStaticMappings) {
134 return m_static_mappings[index];
135 } else {
136 return m_mappings[index - NumStaticMappings];
137 }
138 }
139
140 const Mapping& GetExchangeMapping(size_t i) const {
141 ASSERT(i < m_num_exch);
142
143 const size_t index = m_num_send + m_num_recv + i;
144 if (index < NumStaticMappings) {
145 return m_static_mappings[index];
146 } else {
147 return m_mappings[index - NumStaticMappings];
148 }
149 }
150
151 private:
152 KernelCore& kernel;
153 std::array<Mapping, NumStaticMappings> m_static_mappings;
154 Mapping* m_mappings{};
155 u8 m_num_send{};
156 u8 m_num_recv{};
157 u8 m_num_exch{};
158 };
159
160public:
161 explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {}
162
163 static KSessionRequest* Create(KernelCore& kernel) {
164 KSessionRequest* req = KSessionRequest::Allocate(kernel);
165 if (req != nullptr) [[likely]] {
166 KAutoObject::Create(req);
167 }
168 return req;
169 }
170
171 void Destroy() override {
172 this->Finalize();
173 KSessionRequest::Free(kernel, this);
174 }
175
176 void Initialize(KEvent* event, uintptr_t address, size_t size) {
177 m_mappings.Initialize();
178
179 m_thread = GetCurrentThreadPointer(kernel);
180 m_event = event;
181 m_address = address;
182 m_size = size;
183
184 m_thread->Open();
185 if (m_event != nullptr) {
186 m_event->Open();
187 }
188 }
189
190 static void PostDestroy(uintptr_t arg) {}
191
192 KThread* GetThread() const {
193 return m_thread;
194 }
195 KEvent* GetEvent() const {
196 return m_event;
197 }
198 uintptr_t GetAddress() const {
199 return m_address;
200 }
201 size_t GetSize() const {
202 return m_size;
203 }
204 KProcess* GetServerProcess() const {
205 return m_server;
206 }
207
208 void SetServerProcess(KProcess* process) {
209 m_server = process;
210 m_server->Open();
211 }
212
213 void ClearThread() {
214 m_thread = nullptr;
215 }
216 void ClearEvent() {
217 m_event = nullptr;
218 }
219
220 size_t GetSendCount() const {
221 return m_mappings.GetSendCount();
222 }
223 size_t GetReceiveCount() const {
224 return m_mappings.GetReceiveCount();
225 }
226 size_t GetExchangeCount() const {
227 return m_mappings.GetExchangeCount();
228 }
229
230 Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) {
231 return m_mappings.PushSend(client, server, size, state);
232 }
233
234 Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) {
235 return m_mappings.PushReceive(client, server, size, state);
236 }
237
238 Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) {
239 return m_mappings.PushExchange(client, server, size, state);
240 }
241
242 VAddr GetSendClientAddress(size_t i) const {
243 return m_mappings.GetSendClientAddress(i);
244 }
245 VAddr GetSendServerAddress(size_t i) const {
246 return m_mappings.GetSendServerAddress(i);
247 }
248 size_t GetSendSize(size_t i) const {
249 return m_mappings.GetSendSize(i);
250 }
251 KMemoryState GetSendMemoryState(size_t i) const {
252 return m_mappings.GetSendMemoryState(i);
253 }
254
255 VAddr GetReceiveClientAddress(size_t i) const {
256 return m_mappings.GetReceiveClientAddress(i);
257 }
258 VAddr GetReceiveServerAddress(size_t i) const {
259 return m_mappings.GetReceiveServerAddress(i);
260 }
261 size_t GetReceiveSize(size_t i) const {
262 return m_mappings.GetReceiveSize(i);
263 }
264 KMemoryState GetReceiveMemoryState(size_t i) const {
265 return m_mappings.GetReceiveMemoryState(i);
266 }
267
268 VAddr GetExchangeClientAddress(size_t i) const {
269 return m_mappings.GetExchangeClientAddress(i);
270 }
271 VAddr GetExchangeServerAddress(size_t i) const {
272 return m_mappings.GetExchangeServerAddress(i);
273 }
274 size_t GetExchangeSize(size_t i) const {
275 return m_mappings.GetExchangeSize(i);
276 }
277 KMemoryState GetExchangeMemoryState(size_t i) const {
278 return m_mappings.GetExchangeMemoryState(i);
279 }
280
281private:
282 // NOTE: This is public and virtual in Nintendo's kernel.
283 void Finalize() override {
284 m_mappings.Finalize();
285
286 if (m_thread) {
287 m_thread->Close();
288 }
289 if (m_event) {
290 m_event->Close();
291 }
292 if (m_server) {
293 m_server->Close();
294 }
295 }
296
297private:
298 SessionMappings m_mappings;
299 KThread* m_thread{};
300 KProcess* m_server{};
301 KEvent* m_event{};
302 uintptr_t m_address{};
303 size_t m_size{};
304};
305
306} // namespace Kernel
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 8ff1545b6..a039cc591 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -50,7 +50,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
50 is_initialized = true; 50 is_initialized = true;
51 51
52 // Clear all pages in the memory. 52 // Clear all pages in the memory.
53 std::memset(device_memory_.GetPointer(physical_address_), 0, size_); 53 std::memset(device_memory_.GetPointer<void>(physical_address_), 0, size_);
54 54
55 return ResultSuccess; 55 return ResultSuccess;
56} 56}
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 34cb98456..5620c3660 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -54,7 +54,7 @@ public:
54 * @return A pointer to the shared memory block from the specified offset 54 * @return A pointer to the shared memory block from the specified offset
55 */ 55 */
56 u8* GetPointer(std::size_t offset = 0) { 56 u8* GetPointer(std::size_t offset = 0) {
57 return device_memory->GetPointer(physical_address + offset); 57 return device_memory->GetPointer<u8>(physical_address + offset);
58 } 58 }
59 59
60 /** 60 /**
@@ -63,7 +63,7 @@ public:
63 * @return A pointer to the shared memory block from the specified offset 63 * @return A pointer to the shared memory block from the specified offset
64 */ 64 */
65 const u8* GetPointer(std::size_t offset = 0) const { 65 const u8* GetPointer(std::size_t offset = 0) const {
66 return device_memory->GetPointer(physical_address + offset); 66 return device_memory->GetPointer<u8>(physical_address + offset);
67 } 67 }
68 68
69 void Finalize() override; 69 void Finalize() override;
diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h
index e43db8515..2bb6b6d08 100644
--- a/src/core/hle/kernel/k_shared_memory_info.h
+++ b/src/core/hle/kernel/k_shared_memory_info.h
@@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
15 public boost::intrusive::list_base_hook<> { 15 public boost::intrusive::list_base_hook<> {
16 16
17public: 17public:
18 explicit KSharedMemoryInfo() = default; 18 explicit KSharedMemoryInfo(KernelCore&) {}
19 KSharedMemoryInfo() = default;
19 20
20 constexpr void Initialize(KSharedMemory* shmem) { 21 constexpr void Initialize(KSharedMemory* shmem) {
21 shared_memory = shmem; 22 shared_memory = shmem;
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index 2b303537e..a8c77a7d4 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -8,6 +8,7 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/spin_lock.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
@@ -36,28 +37,34 @@ public:
36 } 37 }
37 38
38 void* Allocate() { 39 void* Allocate() {
39 Node* ret = m_head.load(); 40 // KScopedInterruptDisable di;
40 41
41 do { 42 m_lock.lock();
42 if (ret == nullptr) { 43
43 break; 44 Node* ret = m_head;
44 } 45 if (ret != nullptr) [[likely]] {
45 } while (!m_head.compare_exchange_weak(ret, ret->next)); 46 m_head = ret->next;
47 }
46 48
49 m_lock.unlock();
47 return ret; 50 return ret;
48 } 51 }
49 52
50 void Free(void* obj) { 53 void Free(void* obj) {
54 // KScopedInterruptDisable di;
55
56 m_lock.lock();
57
51 Node* node = static_cast<Node*>(obj); 58 Node* node = static_cast<Node*>(obj);
59 node->next = m_head;
60 m_head = node;
52 61
53 Node* cur_head = m_head.load(); 62 m_lock.unlock();
54 do {
55 node->next = cur_head;
56 } while (!m_head.compare_exchange_weak(cur_head, node));
57 } 63 }
58 64
59private: 65private:
60 std::atomic<Node*> m_head{}; 66 std::atomic<Node*> m_head{};
67 Common::SpinLock m_lock;
61}; 68};
62 69
63} // namespace impl 70} // namespace impl
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 174afc80d..cc88d08f0 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -30,6 +30,7 @@
30#include "core/hle/kernel/k_worker_task_manager.h" 30#include "core/hle/kernel/k_worker_task_manager.h"
31#include "core/hle/kernel/kernel.h" 31#include "core/hle/kernel/kernel.h"
32#include "core/hle/kernel/svc_results.h" 32#include "core/hle/kernel/svc_results.h"
33#include "core/hle/kernel/svc_types.h"
33#include "core/hle/result.h" 34#include "core/hle/result.h"
34#include "core/memory.h" 35#include "core/memory.h"
35 36
@@ -38,6 +39,9 @@
38#endif 39#endif
39 40
40namespace { 41namespace {
42
43constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1;
44
41static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, 45static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
42 u32 entry_point, u32 arg) { 46 u32 entry_point, u32 arg) {
43 context = {}; 47 context = {};
@@ -144,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack
144 physical_affinity_mask.SetAffinity(phys_core, true); 148 physical_affinity_mask.SetAffinity(phys_core, true);
145 149
146 // Set the thread state. 150 // Set the thread state.
147 thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; 151 thread_state = (type == ThreadType::Main || type == ThreadType::Dummy)
152 ? ThreadState::Runnable
153 : ThreadState::Initialized;
148 154
149 // Set TLS address. 155 // Set TLS address.
150 tls_address = 0; 156 tls_address = 0;
@@ -241,7 +247,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack
241 } 247 }
242 } 248 }
243 249
244 return ResultSuccess; 250 R_SUCCEED();
245} 251}
246 252
247Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, 253Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
@@ -254,7 +260,7 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
254 thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); 260 thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func));
255 thread->is_single_core = !Settings::values.use_multi_core.GetValue(); 261 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
256 262
257 return ResultSuccess; 263 R_SUCCEED();
258} 264}
259 265
260Result KThread::InitializeDummyThread(KThread* thread) { 266Result KThread::InitializeDummyThread(KThread* thread) {
@@ -264,31 +270,32 @@ Result KThread::InitializeDummyThread(KThread* thread) {
264 // Initialize emulation parameters. 270 // Initialize emulation parameters.
265 thread->stack_parameters.disable_count = 0; 271 thread->stack_parameters.disable_count = 0;
266 272
267 return ResultSuccess; 273 R_SUCCEED();
268} 274}
269 275
270Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { 276Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
271 return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, 277 R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {},
272 system.GetCpuManager().GetGuestActivateFunc()); 278 ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc()));
273} 279}
274 280
275Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { 281Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
276 return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, 282 R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {},
277 system.GetCpuManager().GetIdleThreadStartFunc()); 283 ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc()));
278} 284}
279 285
280Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, 286Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
281 KThreadFunction func, uintptr_t arg, s32 virt_core) { 287 KThreadFunction func, uintptr_t arg, s32 virt_core) {
282 return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, 288 R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr,
283 system.GetCpuManager().GetShutdownThreadStartFunc()); 289 ThreadType::HighPriority,
290 system.GetCpuManager().GetShutdownThreadStartFunc()));
284} 291}
285 292
286Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, 293Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
287 uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, 294 uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core,
288 KProcess* owner) { 295 KProcess* owner) {
289 system.Kernel().GlobalSchedulerContext().AddThread(thread); 296 system.Kernel().GlobalSchedulerContext().AddThread(thread);
290 return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, 297 R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
291 ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()); 298 ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()));
292} 299}
293 300
294void KThread::PostDestroy(uintptr_t arg) { 301void KThread::PostDestroy(uintptr_t arg) {
@@ -538,7 +545,7 @@ Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
538 *out_ideal_core = virtual_ideal_core_id; 545 *out_ideal_core = virtual_ideal_core_id;
539 *out_affinity_mask = virtual_affinity_mask; 546 *out_affinity_mask = virtual_affinity_mask;
540 547
541 return ResultSuccess; 548 R_SUCCEED();
542} 549}
543 550
544Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { 551Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
@@ -554,7 +561,7 @@ Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask)
554 *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask(); 561 *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask();
555 } 562 }
556 563
557 return ResultSuccess; 564 R_SUCCEED();
558} 565}
559 566
560Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { 567Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
@@ -666,7 +673,7 @@ Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
666 } while (retry_update); 673 } while (retry_update);
667 } 674 }
668 675
669 return ResultSuccess; 676 R_SUCCEED();
670} 677}
671 678
672void KThread::SetBasePriority(s32 value) { 679void KThread::SetBasePriority(s32 value) {
@@ -839,7 +846,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) {
839 } while (thread_is_current); 846 } while (thread_is_current);
840 } 847 }
841 848
842 return ResultSuccess; 849 R_SUCCEED();
843} 850}
844 851
845Result KThread::GetThreadContext3(std::vector<u8>& out) { 852Result KThread::GetThreadContext3(std::vector<u8>& out) {
@@ -874,7 +881,7 @@ Result KThread::GetThreadContext3(std::vector<u8>& out) {
874 } 881 }
875 } 882 }
876 883
877 return ResultSuccess; 884 R_SUCCEED();
878} 885}
879 886
880void KThread::AddWaiterImpl(KThread* thread) { 887void KThread::AddWaiterImpl(KThread* thread) {
@@ -1038,7 +1045,7 @@ Result KThread::Run() {
1038 // Set our state and finish. 1045 // Set our state and finish.
1039 SetState(ThreadState::Runnable); 1046 SetState(ThreadState::Runnable);
1040 1047
1041 return ResultSuccess; 1048 R_SUCCEED();
1042 } 1049 }
1043} 1050}
1044 1051
@@ -1073,6 +1080,78 @@ void KThread::Exit() {
1073 UNREACHABLE_MSG("KThread::Exit() would return"); 1080 UNREACHABLE_MSG("KThread::Exit() would return");
1074} 1081}
1075 1082
1083Result KThread::Terminate() {
1084 ASSERT(this != GetCurrentThreadPointer(kernel));
1085
1086 // Request the thread terminate if it hasn't already.
1087 if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) {
1088 // If the thread isn't terminated, wait for it to terminate.
1089 s32 index;
1090 KSynchronizationObject* objects[] = {this};
1091 R_TRY(KSynchronizationObject::Wait(kernel, std::addressof(index), objects, 1,
1092 Svc::WaitInfinite));
1093 }
1094
1095 R_SUCCEED();
1096}
1097
1098ThreadState KThread::RequestTerminate() {
1099 ASSERT(this != GetCurrentThreadPointer(kernel));
1100
1101 KScopedSchedulerLock sl{kernel};
1102
1103 // Determine if this is the first termination request.
1104 const bool first_request = [&]() -> bool {
1105 // Perform an atomic compare-and-swap from false to true.
1106 bool expected = false;
1107 return termination_requested.compare_exchange_strong(expected, true);
1108 }();
1109
1110 // If this is the first request, start termination procedure.
1111 if (first_request) {
1112 // If the thread is in initialized state, just change state to terminated.
1113 if (this->GetState() == ThreadState::Initialized) {
1114 thread_state = ThreadState::Terminated;
1115 return ThreadState::Terminated;
1116 }
1117
1118 // Register the terminating dpc.
1119 this->RegisterDpc(DpcFlag::Terminating);
1120
1121 // If the thread is pinned, unpin it.
1122 if (this->GetStackParameters().is_pinned) {
1123 this->GetOwnerProcess()->UnpinThread(this);
1124 }
1125
1126 // If the thread is suspended, continue it.
1127 if (this->IsSuspended()) {
1128 suspend_allowed_flags = 0;
1129 this->UpdateState();
1130 }
1131
1132 // Change the thread's priority to be higher than any system thread's.
1133 if (this->GetBasePriority() >= Svc::SystemThreadPriorityHighest) {
1134 this->SetBasePriority(TerminatingThreadPriority);
1135 }
1136
1137 // If the thread is runnable, send a termination interrupt to other cores.
1138 if (this->GetState() == ThreadState::Runnable) {
1139 if (const u64 core_mask =
1140 physical_affinity_mask.GetAffinityMask() & ~(1ULL << GetCurrentCoreId(kernel));
1141 core_mask != 0) {
1142 Kernel::KInterruptManager::SendInterProcessorInterrupt(kernel, core_mask);
1143 }
1144 }
1145
1146 // Wake up the thread.
1147 if (this->GetState() == ThreadState::Waiting) {
1148 wait_queue->CancelWait(this, ResultTerminationRequested, true);
1149 }
1150 }
1151
1152 return this->GetState();
1153}
1154
1076Result KThread::Sleep(s64 timeout) { 1155Result KThread::Sleep(s64 timeout) {
1077 ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); 1156 ASSERT(!kernel.GlobalSchedulerContext().IsLocked());
1078 ASSERT(this == GetCurrentThreadPointer(kernel)); 1157 ASSERT(this == GetCurrentThreadPointer(kernel));
@@ -1086,7 +1165,7 @@ Result KThread::Sleep(s64 timeout) {
1086 // Check if the thread should terminate. 1165 // Check if the thread should terminate.
1087 if (this->IsTerminationRequested()) { 1166 if (this->IsTerminationRequested()) {
1088 slp.CancelSleep(); 1167 slp.CancelSleep();
1089 return ResultTerminationRequested; 1168 R_THROW(ResultTerminationRequested);
1090 } 1169 }
1091 1170
1092 // Wait for the sleep to end. 1171 // Wait for the sleep to end.
@@ -1094,33 +1173,34 @@ Result KThread::Sleep(s64 timeout) {
1094 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); 1173 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
1095 } 1174 }
1096 1175
1097 return ResultSuccess; 1176 R_SUCCEED();
1098} 1177}
1099 1178
1100void KThread::IfDummyThreadTryWait() { 1179void KThread::RequestDummyThreadWait() {
1101 if (!IsDummyThread()) { 1180 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
1102 return; 1181 ASSERT(this->IsDummyThread());
1103 } 1182
1183 // We will block when the scheduler lock is released.
1184 dummy_thread_runnable.store(false);
1185}
1104 1186
1105 if (GetState() != ThreadState::Waiting) { 1187void KThread::DummyThreadBeginWait() {
1188 if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) {
1189 // Occurs in single core mode.
1106 return; 1190 return;
1107 } 1191 }
1108 1192
1109 ASSERT(!kernel.IsPhantomModeForSingleCore()); 1193 // Block until runnable is no longer false.
1110 1194 dummy_thread_runnable.wait(false);
1111 // Block until we are no longer waiting.
1112 std::unique_lock lk(dummy_wait_lock);
1113 dummy_wait_cv.wait(
1114 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1115} 1195}
1116 1196
1117void KThread::IfDummyThreadEndWait() { 1197void KThread::DummyThreadEndWait() {
1118 if (!IsDummyThread()) { 1198 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
1119 return; 1199 ASSERT(this->IsDummyThread());
1120 }
1121 1200
1122 // Wake up the waiting thread. 1201 // Wake up the waiting thread.
1123 dummy_wait_cv.notify_one(); 1202 dummy_thread_runnable.store(true);
1203 dummy_thread_runnable.notify_one();
1124} 1204}
1125 1205
1126void KThread::BeginWait(KThreadQueue* queue) { 1206void KThread::BeginWait(KThreadQueue* queue) {
@@ -1154,9 +1234,6 @@ void KThread::EndWait(Result wait_result_) {
1154 } 1234 }
1155 1235
1156 wait_queue->EndWait(this, wait_result_); 1236 wait_queue->EndWait(this, wait_result_);
1157
1158 // Special case for dummy threads to wakeup if necessary.
1159 IfDummyThreadEndWait();
1160 } 1237 }
1161} 1238}
1162 1239
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 9ee20208e..30aa10c9a 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -180,6 +180,10 @@ public:
180 180
181 void Exit(); 181 void Exit();
182 182
183 Result Terminate();
184
185 ThreadState RequestTerminate();
186
183 [[nodiscard]] u32 GetSuspendFlags() const { 187 [[nodiscard]] u32 GetSuspendFlags() const {
184 return suspend_allowed_flags & suspend_request_flags; 188 return suspend_allowed_flags & suspend_request_flags;
185 } 189 }
@@ -639,8 +643,9 @@ public:
639 // therefore will not block on guest kernel synchronization primitives. These methods handle 643 // therefore will not block on guest kernel synchronization primitives. These methods handle
640 // blocking as needed. 644 // blocking as needed.
641 645
642 void IfDummyThreadTryWait(); 646 void RequestDummyThreadWait();
643 void IfDummyThreadEndWait(); 647 void DummyThreadBeginWait();
648 void DummyThreadEndWait();
644 649
645 [[nodiscard]] uintptr_t GetArgument() const { 650 [[nodiscard]] uintptr_t GetArgument() const {
646 return argument; 651 return argument;
@@ -773,8 +778,7 @@ private:
773 bool is_single_core{}; 778 bool is_single_core{};
774 ThreadType thread_type{}; 779 ThreadType thread_type{};
775 StepState step_state{}; 780 StepState step_state{};
776 std::mutex dummy_wait_lock; 781 std::atomic<bool> dummy_thread_runnable{true};
777 std::condition_variable dummy_wait_cv;
778 782
779 // For debugging 783 // For debugging
780 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 784 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index 0a7f22680..5d466ace7 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -26,7 +26,7 @@ public:
26 static_assert(RegionsPerPage > 0); 26 static_assert(RegionsPerPage > 0);
27 27
28public: 28public:
29 constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { 29 constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) {
30 m_is_region_free.fill(true); 30 m_is_region_free.fill(true);
31 } 31 }
32 32
diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp
index 221f341ee..04042bf8f 100644
--- a/src/core/hle/kernel/k_worker_task_manager.cpp
+++ b/src/core/hle/kernel/k_worker_task_manager.cpp
@@ -23,7 +23,7 @@ void KWorkerTask::DoWorkerTask() {
23 } 23 }
24} 24}
25 25
26KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} 26KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
27 27
28void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { 28void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
29 ASSERT(type <= WorkerType::Count); 29 ASSERT(type <= WorkerType::Count);
diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp
deleted file mode 100644
index ff88c5acd..000000000
--- a/src/core/hle/kernel/k_writable_event.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/kernel/k_readable_event.h"
6#include "core/hle/kernel/k_writable_event.h"
7
8namespace Kernel {
9
10KWritableEvent::KWritableEvent(KernelCore& kernel_)
11 : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
12
13KWritableEvent::~KWritableEvent() = default;
14
15void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) {
16 parent = parent_event_;
17 name = std::move(name_);
18 parent->GetReadableEvent().Open();
19}
20
21Result KWritableEvent::Signal() {
22 return parent->GetReadableEvent().Signal();
23}
24
25Result KWritableEvent::Clear() {
26 return parent->GetReadableEvent().Clear();
27}
28
29void KWritableEvent::Destroy() {
30 // Close our references.
31 parent->GetReadableEvent().Close();
32 parent->Close();
33}
34
35} // namespace Kernel
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
deleted file mode 100644
index 3fd0c7d0a..000000000
--- a/src/core/hle/kernel/k_writable_event.h
+++ /dev/null
@@ -1,39 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_auto_object.h"
7#include "core/hle/kernel/slab_helpers.h"
8#include "core/hle/result.h"
9
10namespace Kernel {
11
12class KernelCore;
13class KEvent;
14
15class KWritableEvent final
16 : public KAutoObjectWithSlabHeapAndContainer<KWritableEvent, KAutoObjectWithList> {
17 KERNEL_AUTOOBJECT_TRAITS(KWritableEvent, KAutoObject);
18
19public:
20 explicit KWritableEvent(KernelCore& kernel_);
21 ~KWritableEvent() override;
22
23 void Destroy() override;
24
25 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
26
27 void Initialize(KEvent* parent_, std::string&& name_);
28 Result Signal();
29 Result Clear();
30
31 KEvent* GetParent() const {
32 return parent;
33 }
34
35private:
36 KEvent* parent{};
37};
38
39} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index ce7fa8275..09c36ee09 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -24,6 +24,7 @@
24#include "core/hardware_properties.h" 24#include "core/hardware_properties.h"
25#include "core/hle/kernel/init/init_slab_setup.h" 25#include "core/hle/kernel/init/init_slab_setup.h"
26#include "core/hle/kernel/k_client_port.h" 26#include "core/hle/kernel/k_client_port.h"
27#include "core/hle/kernel/k_dynamic_resource_manager.h"
27#include "core/hle/kernel/k_handle_table.h" 28#include "core/hle/kernel/k_handle_table.h"
28#include "core/hle/kernel/k_memory_layout.h" 29#include "core/hle/kernel/k_memory_layout.h"
29#include "core/hle/kernel/k_memory_manager.h" 30#include "core/hle/kernel/k_memory_manager.h"
@@ -47,8 +48,8 @@ namespace Kernel {
47 48
48struct KernelCore::Impl { 49struct KernelCore::Impl {
49 explicit Impl(Core::System& system_, KernelCore& kernel_) 50 explicit Impl(Core::System& system_, KernelCore& kernel_)
50 : time_manager{system_}, 51 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
51 service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} 52 service_thread_barrier{2}, system{system_} {}
52 53
53 void SetMulticore(bool is_multi) { 54 void SetMulticore(bool is_multi) {
54 is_multicore = is_multi; 55 is_multicore = is_multi;
@@ -59,7 +60,6 @@ struct KernelCore::Impl {
59 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 60 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
60 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 61 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
61 global_handle_table->Initialize(KHandleTable::MaxTableSize); 62 global_handle_table->Initialize(KHandleTable::MaxTableSize);
62 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
63 63
64 is_phantom_mode_for_singlecore = false; 64 is_phantom_mode_for_singlecore = false;
65 65
@@ -73,10 +73,20 @@ struct KernelCore::Impl {
73 InitializeMemoryLayout(); 73 InitializeMemoryLayout();
74 Init::InitializeKPageBufferSlabHeap(system); 74 Init::InitializeKPageBufferSlabHeap(system);
75 InitializeShutdownThreads(); 75 InitializeShutdownThreads();
76 InitializePreemption(kernel);
77 InitializePhysicalCores(); 76 InitializePhysicalCores();
77 InitializePreemption(kernel);
78
79 // Initialize the Dynamic Slab Heaps.
80 {
81 const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion();
82 ASSERT(pt_heap_region.GetEndAddress() != 0);
83
84 InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize());
85 }
78 86
79 RegisterHostThread(); 87 RegisterHostThread();
88
89 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
80 } 90 }
81 91
82 void InitializeCores() { 92 void InitializeCores() {
@@ -86,6 +96,15 @@ struct KernelCore::Impl {
86 } 96 }
87 } 97 }
88 98
99 void CloseCurrentProcess() {
100 (*current_process).Finalize();
101 // current_process->Close();
102 // TODO: The current process should be destroyed based on accurate ref counting after
103 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
104 (*current_process).Destroy();
105 current_process = nullptr;
106 }
107
89 void Shutdown() { 108 void Shutdown() {
90 is_shutting_down.store(true, std::memory_order_relaxed); 109 is_shutting_down.store(true, std::memory_order_relaxed);
91 SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); 110 SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
@@ -99,10 +118,6 @@ struct KernelCore::Impl {
99 next_user_process_id = KProcess::ProcessIDMin; 118 next_user_process_id = KProcess::ProcessIDMin;
100 next_thread_id = 1; 119 next_thread_id = 1;
101 120
102 for (auto& core : cores) {
103 core = nullptr;
104 }
105
106 global_handle_table->Finalize(); 121 global_handle_table->Finalize();
107 global_handle_table.reset(); 122 global_handle_table.reset();
108 123
@@ -152,15 +167,7 @@ struct KernelCore::Impl {
152 } 167 }
153 } 168 }
154 169
155 // Shutdown all processes. 170 CloseCurrentProcess();
156 if (current_process) {
157 (*current_process).Finalize();
158 // current_process->Close();
159 // TODO: The current process should be destroyed based on accurate ref counting after
160 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
161 (*current_process).Destroy();
162 current_process = nullptr;
163 }
164 171
165 // Track kernel objects that were not freed on shutdown 172 // Track kernel objects that were not freed on shutdown
166 { 173 {
@@ -178,17 +185,6 @@ struct KernelCore::Impl {
178 } 185 }
179 186
180 void CloseServices() { 187 void CloseServices() {
181 // Close all open server sessions and ports.
182 std::unordered_set<KAutoObject*> server_objects_;
183 {
184 std::scoped_lock lk(server_objects_lock);
185 server_objects_ = server_objects;
186 server_objects.clear();
187 }
188 for (auto* server_object : server_objects_) {
189 server_object->Close();
190 }
191
192 // Ensures all service threads gracefully shutdown. 188 // Ensures all service threads gracefully shutdown.
193 ClearServiceThreads(); 189 ClearServiceThreads();
194 } 190 }
@@ -257,6 +253,18 @@ struct KernelCore::Impl {
257 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); 253 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
258 } 254 }
259 255
256 void InitializeResourceManagers(VAddr address, size_t size) {
257 dynamic_page_manager = std::make_unique<KDynamicPageManager>();
258 memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
259 app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
260
261 dynamic_page_manager->Initialize(address, size);
262 static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
263 memory_block_heap->Initialize(dynamic_page_manager.get(),
264 ApplicationMemoryBlockSlabHeapSize);
265 app_memory_block_manager->Initialize(nullptr, memory_block_heap.get());
266 }
267
260 void InitializeShutdownThreads() { 268 void InitializeShutdownThreads() {
261 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 269 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
262 shutdown_threads[core_id] = KThread::Create(system.Kernel()); 270 shutdown_threads[core_id] = KThread::Create(system.Kernel());
@@ -328,6 +336,8 @@ struct KernelCore::Impl {
328 return this_id; 336 return this_id;
329 } 337 }
330 338
339 static inline thread_local bool is_phantom_mode_for_singlecore{false};
340
331 bool IsPhantomModeForSingleCore() const { 341 bool IsPhantomModeForSingleCore() const {
332 return is_phantom_mode_for_singlecore; 342 return is_phantom_mode_for_singlecore;
333 } 343 }
@@ -344,11 +354,6 @@ struct KernelCore::Impl {
344 static inline thread_local KThread* current_thread{nullptr}; 354 static inline thread_local KThread* current_thread{nullptr};
345 355
346 KThread* GetCurrentEmuThread() { 356 KThread* GetCurrentEmuThread() {
347 // If we are shutting down the kernel, none of this is relevant anymore.
348 if (IsShuttingDown()) {
349 return {};
350 }
351
352 const auto thread_id = GetCurrentHostThreadID(); 357 const auto thread_id = GetCurrentHostThreadID();
353 if (thread_id >= Core::Hardware::NUM_CPU_CORES) { 358 if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
354 return GetHostDummyThread(); 359 return GetHostDummyThread();
@@ -685,24 +690,21 @@ struct KernelCore::Impl {
685 return {}; 690 return {};
686 } 691 }
687 692
688 KClientPort* port = &search->second(system.ServiceManager(), system); 693 return &search->second(system.ServiceManager(), system);
689 RegisterServerObject(&port->GetParent()->GetServerPort());
690 return port;
691 } 694 }
692 695
693 void RegisterServerObject(KAutoObject* server_object) { 696 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
694 std::scoped_lock lk(server_objects_lock); 697 auto search = service_interface_handlers.find(name);
695 server_objects.insert(server_object); 698 if (search == service_interface_handlers.end()) {
696 } 699 return;
700 }
697 701
698 void UnregisterServerObject(KAutoObject* server_object) { 702 search->second(system.ServiceManager(), server_port);
699 std::scoped_lock lk(server_objects_lock);
700 server_objects.erase(server_object);
701 } 703 }
702 704
703 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 705 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
704 const std::string& name) { 706 const std::string& name) {
705 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 707 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name);
706 708
707 service_threads_manager.QueueWork( 709 service_threads_manager.QueueWork(
708 [this, service_thread]() { service_threads.emplace(service_thread); }); 710 [this, service_thread]() { service_threads.emplace(service_thread); });
@@ -724,10 +726,14 @@ struct KernelCore::Impl {
724 } 726 }
725 727
726 void ClearServiceThreads() { 728 void ClearServiceThreads() {
727 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 729 service_threads_manager.QueueWork([this] {
730 service_threads.clear();
731 default_service_thread.reset();
732 service_thread_barrier.Sync();
733 });
734 service_thread_barrier.Sync();
728 } 735 }
729 736
730 std::mutex server_objects_lock;
731 std::mutex registered_objects_lock; 737 std::mutex registered_objects_lock;
732 std::mutex registered_in_use_objects_lock; 738 std::mutex registered_in_use_objects_lock;
733 739
@@ -756,8 +762,8 @@ struct KernelCore::Impl {
756 /// Map of named ports managed by the kernel, which can be retrieved using 762 /// Map of named ports managed by the kernel, which can be retrieved using
757 /// the ConnectToPort SVC. 763 /// the ConnectToPort SVC.
758 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 764 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
765 std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
759 NamedPortTable named_ports; 766 NamedPortTable named_ports;
760 std::unordered_set<KAutoObject*> server_objects;
761 std::unordered_set<KAutoObject*> registered_objects; 767 std::unordered_set<KAutoObject*> registered_objects;
762 std::unordered_set<KAutoObject*> registered_in_use_objects; 768 std::unordered_set<KAutoObject*> registered_in_use_objects;
763 769
@@ -770,6 +776,11 @@ struct KernelCore::Impl {
770 // Kernel memory management 776 // Kernel memory management
771 std::unique_ptr<KMemoryManager> memory_manager; 777 std::unique_ptr<KMemoryManager> memory_manager;
772 778
779 // Dynamic slab managers
780 std::unique_ptr<KDynamicPageManager> dynamic_page_manager;
781 std::unique_ptr<KMemoryBlockSlabHeap> memory_block_heap;
782 std::unique_ptr<KMemoryBlockSlabManager> app_memory_block_manager;
783
773 // Shared memory for services 784 // Shared memory for services
774 Kernel::KSharedMemory* hid_shared_mem{}; 785 Kernel::KSharedMemory* hid_shared_mem{};
775 Kernel::KSharedMemory* font_shared_mem{}; 786 Kernel::KSharedMemory* font_shared_mem{};
@@ -784,13 +795,13 @@ struct KernelCore::Impl {
784 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; 795 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
785 std::weak_ptr<ServiceThread> default_service_thread; 796 std::weak_ptr<ServiceThread> default_service_thread;
786 Common::ThreadWorker service_threads_manager; 797 Common::ThreadWorker service_threads_manager;
798 Common::Barrier service_thread_barrier;
787 799
788 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; 800 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
789 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 801 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
790 802
791 bool is_multicore{}; 803 bool is_multicore{};
792 std::atomic_bool is_shutting_down{}; 804 std::atomic_bool is_shutting_down{};
793 bool is_phantom_mode_for_singlecore{};
794 u32 single_core_thread_id{}; 805 u32 single_core_thread_id{};
795 806
796 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 807 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -853,6 +864,10 @@ const KProcess* KernelCore::CurrentProcess() const {
853 return impl->current_process; 864 return impl->current_process;
854} 865}
855 866
867void KernelCore::CloseCurrentProcess() {
868 impl->CloseCurrentProcess();
869}
870
856const std::vector<KProcess*>& KernelCore::GetProcessList() const { 871const std::vector<KProcess*>& KernelCore::GetProcessList() const {
857 return impl->process_list; 872 return impl->process_list;
858} 873}
@@ -953,16 +968,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
953 impl->service_interface_factory.emplace(std::move(name), factory); 968 impl->service_interface_factory.emplace(std::move(name), factory);
954} 969}
955 970
956KClientPort* KernelCore::CreateNamedServicePort(std::string name) { 971void KernelCore::RegisterInterfaceForNamedService(std::string name,
957 return impl->CreateNamedServicePort(std::move(name)); 972 ServiceInterfaceHandlerFn&& handler) {
973 impl->service_interface_handlers.emplace(std::move(name), handler);
958} 974}
959 975
960void KernelCore::RegisterServerObject(KAutoObject* server_object) { 976KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
961 impl->RegisterServerObject(server_object); 977 return impl->CreateNamedServicePort(std::move(name));
962} 978}
963 979
964void KernelCore::UnregisterServerObject(KAutoObject* server_object) { 980void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
965 impl->UnregisterServerObject(server_object); 981 impl->RegisterNamedServiceHandler(std::move(name), server_port);
966} 982}
967 983
968void KernelCore::RegisterKernelObject(KAutoObject* object) { 984void KernelCore::RegisterKernelObject(KAutoObject* object) {
@@ -1041,6 +1057,14 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1041 return *impl->memory_manager; 1057 return *impl->memory_manager;
1042} 1058}
1043 1059
1060KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() {
1061 return *impl->app_memory_block_manager;
1062}
1063
1064const KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() const {
1065 return *impl->app_memory_block_manager;
1066}
1067
1044Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 1068Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1045 return *impl->hid_shared_mem; 1069 return *impl->hid_shared_mem;
1046} 1070}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index bcf016a97..4ae6b3923 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -37,6 +37,7 @@ class KClientSession;
37class KEvent; 37class KEvent;
38class KHandleTable; 38class KHandleTable;
39class KLinkedListNode; 39class KLinkedListNode;
40class KMemoryBlockSlabManager;
40class KMemoryLayout; 41class KMemoryLayout;
41class KMemoryManager; 42class KMemoryManager;
42class KPageBuffer; 43class KPageBuffer;
@@ -44,15 +45,16 @@ class KPort;
44class KProcess; 45class KProcess;
45class KResourceLimit; 46class KResourceLimit;
46class KScheduler; 47class KScheduler;
48class KServerPort;
47class KServerSession; 49class KServerSession;
48class KSession; 50class KSession;
51class KSessionRequest;
49class KSharedMemory; 52class KSharedMemory;
50class KSharedMemoryInfo; 53class KSharedMemoryInfo;
51class KThread; 54class KThread;
52class KThreadLocalPage; 55class KThreadLocalPage;
53class KTransferMemory; 56class KTransferMemory;
54class KWorkerTaskManager; 57class KWorkerTaskManager;
55class KWritableEvent;
56class KCodeMemory; 58class KCodeMemory;
57class PhysicalCore; 59class PhysicalCore;
58class ServiceThread; 60class ServiceThread;
@@ -62,6 +64,8 @@ class TimeManager;
62using ServiceInterfaceFactory = 64using ServiceInterfaceFactory =
63 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; 65 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
64 66
67using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
68
65namespace Init { 69namespace Init {
66struct KSlabResourceCounts; 70struct KSlabResourceCounts;
67} 71}
@@ -131,6 +135,9 @@ public:
131 /// Retrieves a const pointer to the current process. 135 /// Retrieves a const pointer to the current process.
132 const KProcess* CurrentProcess() const; 136 const KProcess* CurrentProcess() const;
133 137
138 /// Closes the current process.
139 void CloseCurrentProcess();
140
134 /// Retrieves the list of processes. 141 /// Retrieves the list of processes.
135 const std::vector<KProcess*>& GetProcessList() const; 142 const std::vector<KProcess*>& GetProcessList() const;
136 143
@@ -188,16 +195,14 @@ public:
188 /// Registers a named HLE service, passing a factory used to open a port to that service. 195 /// Registers a named HLE service, passing a factory used to open a port to that service.
189 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); 196 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
190 197
198 /// Registers a setup function for the named HLE service.
199 void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler);
200
191 /// Opens a port to a service previously registered with RegisterNamedService. 201 /// Opens a port to a service previously registered with RegisterNamedService.
192 KClientPort* CreateNamedServicePort(std::string name); 202 KClientPort* CreateNamedServicePort(std::string name);
193 203
194 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. 204 /// Accepts a session on a port created by CreateNamedServicePort.
195 /// This is necessary because we do not emulate processes for HLE sessions and ports. 205 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
196 void RegisterServerObject(KAutoObject* server_object);
197
198 /// Unregisters a server session or port previously registered with RegisterServerSession when
199 /// it was destroyed during the current emulation session.
200 void UnregisterServerObject(KAutoObject* server_object);
201 206
202 /// Registers all kernel objects with the global emulation state, this is purely for tracking 207 /// Registers all kernel objects with the global emulation state, this is purely for tracking
203 /// leaks after emulation has been shutdown. 208 /// leaks after emulation has been shutdown.
@@ -239,6 +244,12 @@ public:
239 /// Gets the virtual memory manager for the kernel. 244 /// Gets the virtual memory manager for the kernel.
240 const KMemoryManager& MemoryManager() const; 245 const KMemoryManager& MemoryManager() const;
241 246
247 /// Gets the application memory block manager for the kernel.
248 KMemoryBlockSlabManager& GetApplicationMemoryBlockManager();
249
250 /// Gets the application memory block manager for the kernel.
251 const KMemoryBlockSlabManager& GetApplicationMemoryBlockManager() const;
252
242 /// Gets the shared memory object for HID services. 253 /// Gets the shared memory object for HID services.
243 Kernel::KSharedMemory& GetHidSharedMem(); 254 Kernel::KSharedMemory& GetHidSharedMem();
244 255
@@ -345,14 +356,14 @@ public:
345 return slab_heap_container->thread; 356 return slab_heap_container->thread;
346 } else if constexpr (std::is_same_v<T, KTransferMemory>) { 357 } else if constexpr (std::is_same_v<T, KTransferMemory>) {
347 return slab_heap_container->transfer_memory; 358 return slab_heap_container->transfer_memory;
348 } else if constexpr (std::is_same_v<T, KWritableEvent>) {
349 return slab_heap_container->writeable_event;
350 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 359 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
351 return slab_heap_container->code_memory; 360 return slab_heap_container->code_memory;
352 } else if constexpr (std::is_same_v<T, KPageBuffer>) { 361 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
353 return slab_heap_container->page_buffer; 362 return slab_heap_container->page_buffer;
354 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { 363 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
355 return slab_heap_container->thread_local_page; 364 return slab_heap_container->thread_local_page;
365 } else if constexpr (std::is_same_v<T, KSessionRequest>) {
366 return slab_heap_container->session_request;
356 } 367 }
357 } 368 }
358 369
@@ -412,10 +423,10 @@ private:
412 KSlabHeap<KSharedMemoryInfo> shared_memory_info; 423 KSlabHeap<KSharedMemoryInfo> shared_memory_info;
413 KSlabHeap<KThread> thread; 424 KSlabHeap<KThread> thread;
414 KSlabHeap<KTransferMemory> transfer_memory; 425 KSlabHeap<KTransferMemory> transfer_memory;
415 KSlabHeap<KWritableEvent> writeable_event;
416 KSlabHeap<KCodeMemory> code_memory; 426 KSlabHeap<KCodeMemory> code_memory;
417 KSlabHeap<KPageBuffer> page_buffer; 427 KSlabHeap<KPageBuffer> page_buffer;
418 KSlabHeap<KThreadLocalPage> thread_local_page; 428 KSlabHeap<KThreadLocalPage> thread_local_page;
429 KSlabHeap<KSessionRequest> session_request;
419 }; 430 };
420 431
421 std::unique_ptr<SlabHeapContainer> slab_heap_container; 432 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 2e87b4ea4..c8fe42537 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -1,15 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <condition_variable>
5#include <functional> 4#include <functional>
5#include <map>
6#include <mutex> 6#include <mutex>
7#include <thread> 7#include <thread>
8#include <vector> 8#include <vector>
9#include <queue>
10 9
11#include "common/scope_exit.h" 10#include "common/scope_exit.h"
12#include "common/thread.h" 11#include "common/thread.h"
12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_scoped_resource_reservation.h"
13#include "core/hle/kernel/k_session.h" 16#include "core/hle/kernel/k_session.h"
14#include "core/hle/kernel/k_thread.h" 17#include "core/hle/kernel/k_thread.h"
15#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
@@ -19,101 +22,198 @@ namespace Kernel {
19 22
20class ServiceThread::Impl final { 23class ServiceThread::Impl final {
21public: 24public:
22 explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); 25 explicit Impl(KernelCore& kernel, const std::string& service_name);
23 ~Impl(); 26 ~Impl();
24 27
25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 28 void WaitAndProcessImpl();
29 void SessionClosed(KServerSession* server_session,
30 std::shared_ptr<SessionRequestManager> manager);
31 void LoopProcess();
32
33 void RegisterServerSession(KServerSession* session,
34 std::shared_ptr<SessionRequestManager> manager);
26 35
27private: 36private:
28 std::vector<std::jthread> threads; 37 KernelCore& kernel;
29 std::queue<std::function<void()>> requests; 38
30 std::mutex queue_mutex; 39 std::jthread m_thread;
31 std::condition_variable_any condition; 40 std::mutex m_session_mutex;
32 const std::string service_name; 41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
42 KEvent* m_wakeup_event;
43 KProcess* m_process;
44 std::atomic<bool> m_shutdown_requested;
45 const std::string m_service_name;
33}; 46};
34 47
35ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) 48void ServiceThread::Impl::WaitAndProcessImpl() {
36 : service_name{name} { 49 // Create local list of waitable sessions.
37 for (std::size_t i = 0; i < num_threads; ++i) { 50 std::vector<KSynchronizationObject*> objs;
38 threads.emplace_back([this, &kernel](std::stop_token stop_token) { 51 std::vector<std::shared_ptr<SessionRequestManager>> managers;
39 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
40 52
41 // Wait for first request before trying to acquire a render context 53 {
42 { 54 // Lock to get the set.
43 std::unique_lock lock{queue_mutex}; 55 std::scoped_lock lk{m_session_mutex};
44 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
45 }
46 56
47 if (stop_token.stop_requested()) { 57 // Reserve the needed quantity.
48 return; 58 objs.reserve(m_sessions.size() + 1);
49 } 59 managers.reserve(m_sessions.size());
50 60
51 // Allocate a dummy guest thread for this host thread. 61 // Copy to our local list.
52 kernel.RegisterHostThread(); 62 for (const auto& [session, manager] : m_sessions) {
63 objs.push_back(session);
64 managers.push_back(manager);
65 }
53 66
54 while (true) { 67 // Insert the wakeup event at the end.
55 std::function<void()> task; 68 objs.push_back(&m_wakeup_event->GetReadableEvent());
69 }
56 70
57 { 71 // Wait on the list of sessions.
58 std::unique_lock lock{queue_mutex}; 72 s32 index{-1};
59 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 73 Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
74 static_cast<s32>(objs.size()), -1);
75 ASSERT(!rc.IsFailure());
76
77 // If this was the wakeup event, clear it and finish.
78 if (index >= static_cast<s64>(objs.size() - 1)) {
79 m_wakeup_event->Clear();
80 return;
81 }
60 82
61 if (stop_token.stop_requested()) { 83 // This event is from a server session.
62 return; 84 auto* server_session = static_cast<KServerSession*>(objs[index]);
63 } 85 auto& manager = managers[index];
64 86
65 if (requests.empty()) { 87 // Fetch the HLE request context.
66 continue; 88 std::shared_ptr<HLERequestContext> context;
67 } 89 rc = server_session->ReceiveRequest(&context, manager);
68 90
69 task = std::move(requests.front()); 91 // If the session was closed, handle that.
70 requests.pop(); 92 if (rc == ResultSessionClosed) {
71 } 93 SessionClosed(server_session, manager);
72 94
73 task(); 95 // Finish.
74 } 96 return;
75 });
76 } 97 }
98
99 // TODO: handle other cases
100 ASSERT(rc == ResultSuccess);
101
102 // Perform the request.
103 Result service_rc = manager->CompleteSyncRequest(server_session, *context);
104
105 // Reply to the client.
106 rc = server_session->SendReplyHLE();
107
108 if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
109 SessionClosed(server_session, manager);
110 return;
111 }
112
113 // TODO: handle other cases
114 ASSERT(rc == ResultSuccess);
115 ASSERT(service_rc == ResultSuccess);
77} 116}
78 117
79void ServiceThread::Impl::QueueSyncRequest(KSession& session, 118void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
80 std::shared_ptr<HLERequestContext>&& context) { 119 std::shared_ptr<SessionRequestManager> manager) {
81 { 120 {
82 std::unique_lock lock{queue_mutex}; 121 // Lock to get the set.
122 std::scoped_lock lk{m_session_mutex};
123
124 // Erase the session.
125 ASSERT(m_sessions.erase(server_session) == 1);
126 }
83 127
84 auto* server_session{&session.GetServerSession()}; 128 // Close our reference to the server session.
129 server_session->Close();
130}
85 131
86 // Open a reference to the session to ensure it is not closes while the service request 132void ServiceThread::Impl::LoopProcess() {
87 // completes asynchronously. 133 Common::SetCurrentThreadName(m_service_name.c_str());
88 server_session->Open();
89 134
90 requests.emplace([server_session, context{std::move(context)}]() { 135 kernel.RegisterHostThread();
91 // Close the reference.
92 SCOPE_EXIT({ server_session->Close(); });
93 136
94 // Complete the service request. 137 while (!m_shutdown_requested.load()) {
95 server_session->CompleteSyncRequest(*context); 138 WaitAndProcessImpl();
96 });
97 } 139 }
98 condition.notify_one(); 140}
141
142void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
143 std::shared_ptr<SessionRequestManager> manager) {
144 // Open the server session.
145 server_session->Open();
146
147 {
148 // Lock to get the set.
149 std::scoped_lock lk{m_session_mutex};
150
151 // Insert the session and manager.
152 m_sessions[server_session] = manager;
153 }
154
155 // Signal the wakeup event.
156 m_wakeup_event->Signal();
99} 157}
100 158
101ServiceThread::Impl::~Impl() { 159ServiceThread::Impl::~Impl() {
102 condition.notify_all(); 160 // Shut down the processing thread.
103 for (auto& thread : threads) { 161 m_shutdown_requested.store(true);
104 thread.request_stop(); 162 m_wakeup_event->Signal();
105 thread.join(); 163 m_thread.join();
164
165 // Lock mutex.
166 m_session_mutex.lock();
167
168 // Close all remaining sessions.
169 for (const auto& [server_session, manager] : m_sessions) {
170 server_session->Close();
106 } 171 }
172
173 // Destroy remaining managers.
174 m_sessions.clear();
175
176 // Close event.
177 m_wakeup_event->GetReadableEvent().Close();
178 m_wakeup_event->Close();
179
180 // Close process.
181 m_process->Close();
182}
183
184ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
185 : kernel{kernel_}, m_service_name{service_name} {
186 // Initialize process.
187 m_process = KProcess::Create(kernel);
188 KProcess::Initialize(m_process, kernel.System(), service_name,
189 KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
190
191 // Reserve a new event from the process resource limit
192 KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
193 ASSERT(event_reservation.Succeeded());
194
195 // Initialize event.
196 m_wakeup_event = KEvent::Create(kernel);
197 m_wakeup_event->Initialize(m_process);
198
199 // Commit the event reservation.
200 event_reservation.Commit();
201
202 // Register the event.
203 KEvent::Register(kernel, m_wakeup_event);
204
205 // Start thread.
206 m_thread = std::jthread([this] { LoopProcess(); });
107} 207}
108 208
109ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) 209ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
110 : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} 210 : impl{std::make_unique<Impl>(kernel, name)} {}
111 211
112ServiceThread::~ServiceThread() = default; 212ServiceThread::~ServiceThread() = default;
113 213
114void ServiceThread::QueueSyncRequest(KSession& session, 214void ServiceThread::RegisterServerSession(KServerSession* session,
115 std::shared_ptr<HLERequestContext>&& context) { 215 std::shared_ptr<SessionRequestManager> manager) {
116 impl->QueueSyncRequest(session, std::move(context)); 216 impl->RegisterServerSession(session, manager);
117} 217}
118 218
119} // namespace Kernel 219} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index c5896f2bd..fb4325531 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -11,13 +11,15 @@ namespace Kernel {
11class HLERequestContext; 11class HLERequestContext;
12class KernelCore; 12class KernelCore;
13class KSession; 13class KSession;
14class SessionRequestManager;
14 15
15class ServiceThread final { 16class ServiceThread final {
16public: 17public:
17 explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); 18 explicit ServiceThread(KernelCore& kernel, const std::string& name);
18 ~ServiceThread(); 19 ~ServiceThread();
19 20
20 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 21 void RegisterServerSession(KServerSession* session,
22 std::shared_ptr<SessionRequestManager> manager);
21 23
22private: 24private:
23 class Impl; 25 class Impl;
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index 299a981a8..06b51e919 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -24,7 +24,7 @@ public:
24 } 24 }
25 25
26 static Derived* Allocate(KernelCore& kernel) { 26 static Derived* Allocate(KernelCore& kernel) {
27 return kernel.SlabHeap<Derived>().Allocate(); 27 return kernel.SlabHeap<Derived>().Allocate(kernel);
28 } 28 }
29 29
30 static void Free(KernelCore& kernel, Derived* obj) { 30 static void Free(KernelCore& kernel, Derived* obj) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 27e5a805d..4c819f4b6 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -24,17 +24,18 @@
24#include "core/hle/kernel/k_memory_block.h" 24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h" 25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h" 26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
27#include "core/hle/kernel/k_process.h" 28#include "core/hle/kernel/k_process.h"
28#include "core/hle/kernel/k_readable_event.h" 29#include "core/hle/kernel/k_readable_event.h"
29#include "core/hle/kernel/k_resource_limit.h" 30#include "core/hle/kernel/k_resource_limit.h"
30#include "core/hle/kernel/k_scheduler.h" 31#include "core/hle/kernel/k_scheduler.h"
31#include "core/hle/kernel/k_scoped_resource_reservation.h" 32#include "core/hle/kernel/k_scoped_resource_reservation.h"
33#include "core/hle/kernel/k_session.h"
32#include "core/hle/kernel/k_shared_memory.h" 34#include "core/hle/kernel/k_shared_memory.h"
33#include "core/hle/kernel/k_synchronization_object.h" 35#include "core/hle/kernel/k_synchronization_object.h"
34#include "core/hle/kernel/k_thread.h" 36#include "core/hle/kernel/k_thread.h"
35#include "core/hle/kernel/k_thread_queue.h" 37#include "core/hle/kernel/k_thread_queue.h"
36#include "core/hle/kernel/k_transfer_memory.h" 38#include "core/hle/kernel/k_transfer_memory.h"
37#include "core/hle/kernel/k_writable_event.h"
38#include "core/hle/kernel/kernel.h" 39#include "core/hle/kernel/kernel.h"
39#include "core/hle/kernel/physical_core.h" 40#include "core/hle/kernel/physical_core.h"
40#include "core/hle/kernel/svc.h" 41#include "core/hle/kernel/svc.h"
@@ -256,6 +257,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3
256 return UnmapMemory(system, dst_addr, src_addr, size); 257 return UnmapMemory(system, dst_addr, src_addr, size);
257} 258}
258 259
260template <typename T>
261Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
262 auto& process = *system.CurrentProcess();
263 auto& handle_table = process.GetHandleTable();
264
265 // Declare the session we're going to allocate.
266 T* session;
267
268 // Reserve a new session from the process resource limit.
269 // FIXME: LimitableResource_SessionCountMax
270 KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
271 if (session_reservation.Succeeded()) {
272 session = T::Create(system.Kernel());
273 } else {
274 return ResultLimitReached;
275
276 // // We couldn't reserve a session. Check that we support dynamically expanding the
277 // // resource limit.
278 // R_UNLESS(process.GetResourceLimit() ==
279 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
280 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
281
282 // // Try to allocate a session from unused slab memory.
283 // session = T::CreateFromUnusedSlabMemory();
284 // R_UNLESS(session != nullptr, ResultLimitReached);
285 // ON_RESULT_FAILURE { session->Close(); };
286
287 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
288 // // prevent request exhaustion.
289 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
290 // // no reason to not do this statically.
291 // if constexpr (std::same_as<T, KSession>) {
292 // for (size_t i = 0; i < 2; i++) {
293 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
294 // R_UNLESS(request != nullptr, ResultLimitReached);
295 // request->Close();
296 // }
297 // }
298
299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
302 }
303
304 // Check that we successfully created a session.
305 R_UNLESS(session != nullptr, ResultOutOfResource);
306
307 // Initialize the session.
308 session->Initialize(nullptr, fmt::format("{}", name));
309
310 // Commit the session reservation.
311 session_reservation.Commit();
312
313 // Ensure that we clean up the session (and its only references are handle table) on function
314 // end.
315 SCOPE_EXIT({
316 session->GetClientSession().Close();
317 session->GetServerSession().Close();
318 });
319
320 // Register the session.
321 T::Register(system.Kernel(), session);
322
323 // Add the server session to the handle table.
324 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
325
326 // Add the client session to the handle table.
327 const auto result = handle_table.Add(out_client, &session->GetClientSession());
328
329 if (!R_SUCCEEDED(result)) {
330 // Ensure that we maintaing a clean handle state on exit.
331 handle_table.Remove(*out_server);
332 }
333
334 return result;
335}
336
337static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
338 u32 is_light, u64 name) {
339 if (is_light) {
340 // return CreateSession<KLightSession>(system, out_server, out_client, name);
341 return ResultUnknown;
342 } else {
343 return CreateSession<KSession>(system, out_server, out_client, name);
344 }
345}
346
259/// Connect to an OS service given the port name, returns the handle to the port to out 347/// Connect to an OS service given the port name, returns the handle to the port to out
260static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { 348static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
261 auto& memory = system.Memory(); 349 auto& memory = system.Memory();
@@ -296,7 +384,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
296 // Create a session. 384 // Create a session.
297 KClientSession* session{}; 385 KClientSession* session{};
298 R_TRY(port->CreateSession(std::addressof(session))); 386 R_TRY(port->CreateSession(std::addressof(session)));
299 port->Close(); 387
388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
300 389
301 // Register the session in the table, close the extra reference. 390 // Register the session in the table, close the extra reference.
302 handle_table.Register(*out, session); 391 handle_table.Register(*out, session);
@@ -313,7 +402,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
313 return ConnectToNamedPort(system, out_handle, port_name_address); 402 return ConnectToNamedPort(system, out_handle, port_name_address);
314} 403}
315 404
316/// Makes a blocking IPC call to an OS service. 405/// Makes a blocking IPC call to a service.
317static Result SendSyncRequest(Core::System& system, Handle handle) { 406static Result SendSyncRequest(Core::System& system, Handle handle) {
318 auto& kernel = system.Kernel(); 407 auto& kernel = system.Kernel();
319 408
@@ -327,22 +416,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) {
327 416
328 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 417 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
329 418
330 { 419 return session->SendSyncRequest();
331 KScopedSchedulerLock lock(kernel);
332
333 // This is a synchronous request, so we should wait for our request to complete.
334 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
335 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
336 session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
337 }
338
339 return GetCurrentThread(kernel).GetWaitResult();
340} 420}
341 421
342static Result SendSyncRequest32(Core::System& system, Handle handle) { 422static Result SendSyncRequest32(Core::System& system, Handle handle) {
343 return SendSyncRequest(system, handle); 423 return SendSyncRequest(system, handle);
344} 424}
345 425
426static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
427 s32 num_handles, Handle reply_target, s64 timeout_ns) {
428 auto& kernel = system.Kernel();
429 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
430
431 // Convert handle list to object table.
432 std::vector<KSynchronizationObject*> objs(num_handles);
433 R_UNLESS(
434 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
435 ResultInvalidHandle);
436
437 // Ensure handles are closed when we're done.
438 SCOPE_EXIT({
439 for (auto i = 0; i < num_handles; ++i) {
440 objs[i]->Close();
441 }
442 });
443
444 // Reply to the target, if one is specified.
445 if (reply_target != InvalidHandle) {
446 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
447 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
448
449 // If we fail to reply, we want to set the output index to -1.
450 // ON_RESULT_FAILURE { *out_index = -1; };
451
452 // Send the reply.
453 // R_TRY(session->SendReply());
454
455 Result rc = session->SendReply();
456 if (!R_SUCCEEDED(rc)) {
457 *out_index = -1;
458 return rc;
459 }
460 }
461
462 // Wait for a message.
463 while (true) {
464 // Wait for an object.
465 s32 index;
466 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
467 static_cast<s32>(objs.size()), timeout_ns);
468 if (result == ResultTimedOut) {
469 return result;
470 }
471
472 // Receive the request.
473 if (R_SUCCEEDED(result)) {
474 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
475 if (session != nullptr) {
476 result = session->ReceiveRequest();
477 if (result == ResultNotFound) {
478 continue;
479 }
480 }
481 }
482
483 *out_index = index;
484 return result;
485 }
486}
487
346/// Get the ID for the specified thread. 488/// Get the ID for the specified thread.
347static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { 489static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
348 // Get the thread from its handle. 490 // Get the thread from its handle.
@@ -610,8 +752,8 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
610 } 752 }
611 753
612 system.GetReporter().SaveSvcBreakReport( 754 system.GetReporter().SaveSvcBreakReport(
613 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1, 755 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(),
614 info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); 756 info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
615 757
616 if (!break_reason.signal_debugger) { 758 if (!break_reason.signal_debugger) {
617 LOG_CRITICAL( 759 LOG_CRITICAL(
@@ -792,7 +934,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
792 return ResultSuccess; 934 return ResultSuccess;
793 935
794 case GetInfoType::UserExceptionContextAddr: 936 case GetInfoType::UserExceptionContextAddr:
795 *result = process->GetTLSRegionAddress(); 937 *result = process->GetProcessLocalRegionAddress();
796 return ResultSuccess; 938 return ResultSuccess;
797 939
798 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: 940 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
@@ -1747,7 +1889,7 @@ static void ExitProcess(Core::System& system) {
1747 auto* current_process = system.Kernel().CurrentProcess(); 1889 auto* current_process = system.Kernel().CurrentProcess();
1748 1890
1749 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 1891 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1750 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 1892 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
1751 "Process has already exited"); 1893 "Process has already exited");
1752 1894
1753 system.Exit(); 1895 system.Exit();
@@ -2303,11 +2445,11 @@ static Result SignalEvent(Core::System& system, Handle event_handle) {
2303 // Get the current handle table. 2445 // Get the current handle table.
2304 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2446 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2305 2447
2306 // Get the writable event. 2448 // Get the event.
2307 KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle); 2449 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2308 R_UNLESS(writable_event.IsNotNull(), ResultInvalidHandle); 2450 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
2309 2451
2310 return writable_event->Signal(); 2452 return event->Signal();
2311} 2453}
2312 2454
2313static Result SignalEvent32(Core::System& system, Handle event_handle) { 2455static Result SignalEvent32(Core::System& system, Handle event_handle) {
@@ -2322,9 +2464,9 @@ static Result ClearEvent(Core::System& system, Handle event_handle) {
2322 2464
2323 // Try to clear the writable event. 2465 // Try to clear the writable event.
2324 { 2466 {
2325 KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle); 2467 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2326 if (writable_event.IsNotNull()) { 2468 if (event.IsNotNull()) {
2327 return writable_event->Clear(); 2469 return event->Clear();
2328 } 2470 }
2329 } 2471 }
2330 2472
@@ -2362,24 +2504,24 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
2362 R_UNLESS(event != nullptr, ResultOutOfResource); 2504 R_UNLESS(event != nullptr, ResultOutOfResource);
2363 2505
2364 // Initialize the event. 2506 // Initialize the event.
2365 event->Initialize("CreateEvent", kernel.CurrentProcess()); 2507 event->Initialize(kernel.CurrentProcess());
2366 2508
2367 // Commit the thread reservation. 2509 // Commit the thread reservation.
2368 event_reservation.Commit(); 2510 event_reservation.Commit();
2369 2511
2370 // Ensure that we clean up the event (and its only references are handle table) on function end. 2512 // Ensure that we clean up the event (and its only references are handle table) on function end.
2371 SCOPE_EXIT({ 2513 SCOPE_EXIT({
2372 event->GetWritableEvent().Close();
2373 event->GetReadableEvent().Close(); 2514 event->GetReadableEvent().Close();
2515 event->Close();
2374 }); 2516 });
2375 2517
2376 // Register the event. 2518 // Register the event.
2377 KEvent::Register(kernel, event); 2519 KEvent::Register(kernel, event);
2378 2520
2379 // Add the writable event to the handle table. 2521 // Add the event to the handle table.
2380 R_TRY(handle_table.Add(out_write, std::addressof(event->GetWritableEvent()))); 2522 R_TRY(handle_table.Add(out_write, event));
2381 2523
2382 // Add the writable event to the handle table. 2524 // Ensure that we maintaing a clean handle state on exit.
2383 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); 2525 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
2384 2526
2385 // Add the readable event to the handle table. 2527 // Add the readable event to the handle table.
@@ -2416,7 +2558,7 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
2416 return ResultInvalidEnumValue; 2558 return ResultInvalidEnumValue;
2417 } 2559 }
2418 2560
2419 *out = static_cast<u64>(process->GetStatus()); 2561 *out = static_cast<u64>(process->GetState());
2420 return ResultSuccess; 2562 return ResultSuccess;
2421} 2563}
2422 2564
@@ -2860,10 +3002,10 @@ static const FunctionDef SVC_Table_64[] = {
2860 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, 3002 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
2861 {0x3E, nullptr, "Unknown3e"}, 3003 {0x3E, nullptr, "Unknown3e"},
2862 {0x3F, nullptr, "Unknown3f"}, 3004 {0x3F, nullptr, "Unknown3f"},
2863 {0x40, nullptr, "CreateSession"}, 3005 {0x40, SvcWrap64<CreateSession>, "CreateSession"},
2864 {0x41, nullptr, "AcceptSession"}, 3006 {0x41, nullptr, "AcceptSession"},
2865 {0x42, nullptr, "ReplyAndReceiveLight"}, 3007 {0x42, nullptr, "ReplyAndReceiveLight"},
2866 {0x43, nullptr, "ReplyAndReceive"}, 3008 {0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"},
2867 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, 3009 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
2868 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, 3010 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
2869 {0x46, nullptr, "MapIoRegion"}, 3011 {0x46, nullptr, "MapIoRegion"},
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
index 95750c3eb..85506710e 100644
--- a/src/core/hle/kernel/svc_common.h
+++ b/src/core/hle/kernel/svc_common.h
@@ -14,8 +14,11 @@ namespace Kernel::Svc {
14 14
15using namespace Common::Literals; 15using namespace Common::Literals;
16 16
17constexpr s32 ArgumentHandleCountMax = 0x40; 17constexpr inline s32 ArgumentHandleCountMax = 0x40;
18constexpr u32 HandleWaitMask{1u << 30}; 18
19constexpr inline u32 HandleWaitMask = 1u << 30;
20
21constexpr inline s64 WaitInfinite = -1;
19 22
20constexpr inline std::size_t HeapSizeAlignment = 2_MiB; 23constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
21 24
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 79e15183a..abb9847fe 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -95,6 +95,19 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
95constexpr inline s32 LowestThreadPriority = 63; 95constexpr inline s32 LowestThreadPriority = 63;
96constexpr inline s32 HighestThreadPriority = 0; 96constexpr inline s32 HighestThreadPriority = 0;
97 97
98constexpr inline s32 SystemThreadPriorityHighest = 16;
99
100enum class ProcessState : u32 {
101 Created = 0,
102 CreatedAttached = 1,
103 Running = 2,
104 Crashed = 3,
105 RunningAttached = 4,
106 Terminating = 5,
107 Terminated = 6,
108 DebugBreak = 7,
109};
110
98constexpr inline size_t ThreadLocalRegionSize = 0x200; 111constexpr inline size_t ThreadLocalRegionSize = 0x200;
99 112
100} // namespace Kernel::Svc 113} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 4bc49087e..272c54cf7 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -8,6 +8,7 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/kernel/svc_types.h" 9#include "core/hle/kernel/svc_types.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11#include "core/memory.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
@@ -346,6 +347,37 @@ void SvcWrap64(Core::System& system) {
346 FuncReturn(system, retval); 347 FuncReturn(system, retval);
347} 348}
348 349
350// Used by CreateSession
351template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
352void SvcWrap64(Core::System& system) {
353 Handle param_1 = 0;
354 Handle param_2 = 0;
355 const u32 retval = func(system, &param_1, &param_2, static_cast<u32>(Param(system, 2)),
356 static_cast<u32>(Param(system, 3)))
357 .raw;
358
359 system.CurrentArmInterface().SetReg(1, param_1);
360 system.CurrentArmInterface().SetReg(2, param_2);
361 FuncReturn(system, retval);
362}
363
364// Used by ReplyAndReceive
365template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
366void SvcWrap64(Core::System& system) {
367 s32 param_1 = 0;
368 s32 num_handles = static_cast<s32>(Param(system, 2));
369
370 std::vector<Handle> handles(num_handles);
371 system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
372
373 const u32 retval = func(system, &param_1, handles.data(), num_handles,
374 static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
375 .raw;
376
377 system.CurrentArmInterface().SetReg(1, param_1);
378 FuncReturn(system, retval);
379}
380
349// Used by WaitForAddress 381// Used by WaitForAddress
350template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> 382template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
351void SvcWrap64(Core::System& system) { 383void SvcWrap64(Core::System& system) {
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 47a1b829b..ef4b2d417 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/expected.h" 10#include "common/expected.h"
10 11
@@ -130,6 +131,18 @@ union Result {
130 [[nodiscard]] constexpr bool IsError() const { 131 [[nodiscard]] constexpr bool IsError() const {
131 return !IsSuccess(); 132 return !IsSuccess();
132 } 133 }
134
135 [[nodiscard]] constexpr bool IsFailure() const {
136 return !IsSuccess();
137 }
138
139 [[nodiscard]] constexpr u32 GetInnerValue() const {
140 return static_cast<u32>(module.Value()) | (description << module.bits);
141 }
142
143 [[nodiscard]] constexpr bool Includes(Result result) const {
144 return GetInnerValue() == result.GetInnerValue();
145 }
133}; 146};
134static_assert(std::is_trivial_v<Result>); 147static_assert(std::is_trivial_v<Result>);
135 148
@@ -349,19 +362,115 @@ private:
349 } \ 362 } \
350 } while (false) 363 } while (false)
351 364
352#define R_SUCCEEDED(res) (res.IsSuccess()) 365#define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess())
366#define R_FAILED(res) (static_cast<Result>(res).IsFailure())
353 367
354/// Evaluates a boolean expression, and succeeds if that expression is true. 368namespace ResultImpl {
355#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) 369template <auto EvaluateResult, class F>
370class ScopedResultGuard {
371 YUZU_NON_COPYABLE(ScopedResultGuard);
372 YUZU_NON_MOVEABLE(ScopedResultGuard);
373
374private:
375 Result& m_ref;
376 F m_f;
377
378public:
379 constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {}
380 constexpr ~ScopedResultGuard() {
381 if (EvaluateResult(m_ref)) {
382 m_f();
383 }
384 }
385};
386
387template <auto EvaluateResult>
388class ResultReferenceForScopedResultGuard {
389private:
390 Result& m_ref;
391
392public:
393 constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {}
394 constexpr operator Result&() const {
395 return m_ref;
396 }
397};
398
399template <auto EvaluateResult, typename F>
400constexpr ScopedResultGuard<EvaluateResult, F> operator+(
401 ResultReferenceForScopedResultGuard<EvaluateResult> ref, F&& f) {
402 return ScopedResultGuard<EvaluateResult, F>(static_cast<Result&>(ref), std::forward<F>(f));
403}
404
405constexpr bool EvaluateResultSuccess(const Result& r) {
406 return R_SUCCEEDED(r);
407}
408constexpr bool EvaluateResultFailure(const Result& r) {
409 return R_FAILED(r);
410}
411
412template <typename T>
413constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
414// Intentionally not defined
415
416template <>
417constexpr void UpdateCurrentResultReference<Result&>(Result& result_reference, Result result) {
418 result_reference = result;
419}
420
421template <>
422constexpr void UpdateCurrentResultReference<const Result>(Result result_reference, Result result) {}
423} // namespace ResultImpl
424
425#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \
426 [[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \
427 std::same_as<decltype(__TmpCurrentResultReference), Result&>; \
428 [[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \
429 [[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \
430 Result& __TmpCurrentResultReference = \
431 HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE
432
433#define ON_RESULT_RETURN_IMPL(...) \
434 static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \
435 auto RESULT_GUARD_STATE_##__COUNTER__ = \
436 ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \
437 __TmpCurrentResultReference) + \
438 [&]()
439
440#define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure)
441
442#define ON_RESULT_FAILURE \
443 DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
444 ON_RESULT_FAILURE_2
445
446#define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess)
447
448#define ON_RESULT_SUCCESS \
449 DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
450 ON_RESULT_SUCCESS_2
451
452constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
453
454/// Returns a result.
455#define R_RETURN(res_expr) \
456 { \
457 const Result _tmp_r_throw_rc = (res_expr); \
458 ResultImpl::UpdateCurrentResultReference<decltype(__TmpCurrentResultReference)>( \
459 __TmpCurrentResultReference, _tmp_r_throw_rc); \
460 return _tmp_r_throw_rc; \
461 }
462
463/// Returns ResultSuccess()
464#define R_SUCCEED() R_RETURN(ResultSuccess)
465
466/// Throws a result.
467#define R_THROW(res_expr) R_RETURN(res_expr)
356 468
357/// Evaluates a boolean expression, and returns a result unless that expression is true. 469/// Evaluates a boolean expression, and returns a result unless that expression is true.
358#define R_UNLESS(expr, res) \ 470#define R_UNLESS(expr, res) \
359 { \ 471 { \
360 if (!(expr)) { \ 472 if (!(expr)) { \
361 if (res.IsError()) { \ 473 R_THROW(res); \
362 LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
363 } \
364 return res; \
365 } \ 474 } \
366 } 475 }
367 476
@@ -369,7 +478,10 @@ private:
369#define R_TRY(res_expr) \ 478#define R_TRY(res_expr) \
370 { \ 479 { \
371 const auto _tmp_r_try_rc = (res_expr); \ 480 const auto _tmp_r_try_rc = (res_expr); \
372 if (_tmp_r_try_rc.IsError()) { \ 481 if (R_FAILED(_tmp_r_try_rc)) { \
373 return _tmp_r_try_rc; \ 482 R_THROW(_tmp_r_try_rc); \
374 } \ 483 } \
375 } 484 }
485
486/// Evaluates a boolean expression, and succeeds if that expression is true.
487#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index bb838e285..85a3f0802 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -512,10 +512,11 @@ protected:
512 512
513class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 513class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
514public: 514public:
515 explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) 515 explicit IManagerForApplication(Core::System& system_,
516 const std::shared_ptr<ProfileManager>& profile_manager_)
516 : ServiceFramework{system_, "IManagerForApplication"}, 517 : ServiceFramework{system_, "IManagerForApplication"},
517 ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)}, 518 ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)},
518 user_id{user_id_} { 519 profile_manager{profile_manager_} {
519 // clang-format off 520 // clang-format off
520 static const FunctionInfo functions[] = { 521 static const FunctionInfo functions[] = {
521 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, 522 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
@@ -545,7 +546,7 @@ private:
545 546
546 IPC::ResponseBuilder rb{ctx, 4}; 547 IPC::ResponseBuilder rb{ctx, 4};
547 rb.Push(ResultSuccess); 548 rb.Push(ResultSuccess);
548 rb.PushRaw<u64>(user_id.Hash()); 549 rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash());
549 } 550 }
550 551
551 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { 552 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
@@ -575,17 +576,20 @@ private:
575 576
576 IPC::ResponseBuilder rb{ctx, 4}; 577 IPC::ResponseBuilder rb{ctx, 4};
577 rb.Push(ResultSuccess); 578 rb.Push(ResultSuccess);
578 rb.PushRaw<u64>(user_id.Hash()); 579 rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash());
579 } 580 }
580 581
581 void StoreOpenContext(Kernel::HLERequestContext& ctx) { 582 void StoreOpenContext(Kernel::HLERequestContext& ctx) {
582 LOG_WARNING(Service_ACC, "(STUBBED) called"); 583 LOG_DEBUG(Service_ACC, "called");
584
585 profile_manager->StoreOpenedUsers();
586
583 IPC::ResponseBuilder rb{ctx, 2}; 587 IPC::ResponseBuilder rb{ctx, 2};
584 rb.Push(ResultSuccess); 588 rb.Push(ResultSuccess);
585 } 589 }
586 590
587 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; 591 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
588 Common::UUID user_id{}; 592 std::shared_ptr<ProfileManager> profile_manager;
589}; 593};
590 594
591// 6.0.0+ 595// 6.0.0+
@@ -790,7 +794,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
790 LOG_DEBUG(Service_ACC, "called"); 794 LOG_DEBUG(Service_ACC, "called");
791 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 795 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
792 rb.Push(ResultSuccess); 796 rb.Push(ResultSuccess);
793 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser()); 797 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
794} 798}
795 799
796void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { 800void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
@@ -849,22 +853,10 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
849 rb.Push(ResultSuccess); 853 rb.Push(ResultSuccess);
850} 854}
851 855
852void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
853 LOG_WARNING(Service_ACC, "(STUBBED) called");
854
855 // This is similar to GetBaasAccountManagerForApplication
856 // This command is used concurrently with ListOpenContextStoredUsers
857 // TODO: Find the differences between this and GetBaasAccountManagerForApplication
858 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
859 rb.Push(ResultSuccess);
860 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser());
861}
862
863void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { 856void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
864 LOG_WARNING(Service_ACC, "(STUBBED) called"); 857 LOG_DEBUG(Service_ACC, "called");
865 858
866 // TODO(ogniK): Handle open contexts 859 ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers());
867 ctx.WriteBuffer(profile_manager->GetOpenUsers());
868 IPC::ResponseBuilder rb{ctx, 2}; 860 IPC::ResponseBuilder rb{ctx, 2};
869 rb.Push(ResultSuccess); 861 rb.Push(ResultSuccess);
870} 862}
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 1621e7c0a..9411b0b92 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -35,7 +35,6 @@ public:
35 void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); 35 void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx);
36 void GetProfileEditor(Kernel::HLERequestContext& ctx); 36 void GetProfileEditor(Kernel::HLERequestContext& ctx);
37 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 37 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
38 void LoadOpenContext(Kernel::HLERequestContext& ctx);
39 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); 38 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
40 void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); 39 void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx);
41 void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); 40 void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 65023b8c2..54844bfe7 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -28,7 +28,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
28 {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, 28 {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
29 {111, nullptr, "ClearSaveDataThumbnail"}, 29 {111, nullptr, "ClearSaveDataThumbnail"},
30 {120, nullptr, "CreateGuestLoginRequest"}, 30 {120, nullptr, "CreateGuestLoginRequest"},
31 {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ 31 {130, nullptr, "LoadOpenContext"}, // 5.0.0+
32 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ 32 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
33 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ 33 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
34 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 34 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp
index c85b2e43a..713689d8f 100644
--- a/src/core/hle/service/acc/async_context.cpp
+++ b/src/core/hle/service/acc/async_context.cpp
@@ -64,7 +64,7 @@ void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) {
64 64
65void IAsyncContext::MarkComplete() { 65void IAsyncContext::MarkComplete() {
66 is_complete.store(true); 66 is_complete.store(true);
67 completion_event->GetWritableEvent().Signal(); 67 completion_event->Signal();
68} 68}
69 69
70} // namespace Service::Account 70} // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index a58da4d5f..481e0d141 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -261,6 +261,31 @@ UUID ProfileManager::GetLastOpenedUser() const {
261 return last_opened_user; 261 return last_opened_user;
262} 262}
263 263
264/// Gets the list of stored opened users.
265UserIDArray ProfileManager::GetStoredOpenedUsers() const {
266 UserIDArray output{};
267 std::ranges::transform(stored_opened_profiles, output.begin(), [](const ProfileInfo& p) {
268 if (p.is_open)
269 return p.user_uuid;
270 return Common::InvalidUUID;
271 });
272 std::stable_partition(output.begin(), output.end(),
273 [](const UUID& uuid) { return uuid.IsValid(); });
274 return output;
275}
276
277/// Captures the opened users, which can be queried across process launches with
278/// ListOpenContextStoredUsers.
279void ProfileManager::StoreOpenedUsers() {
280 size_t profile_index{};
281 stored_opened_profiles = {};
282 std::for_each(profiles.begin(), profiles.end(), [&](const auto& profile) {
283 if (profile.is_open) {
284 stored_opened_profiles[profile_index++] = profile;
285 }
286 });
287}
288
264/// Return the users profile base and the unknown arbitary data. 289/// Return the users profile base and the unknown arbitary data.
265bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, 290bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
266 UserData& data) const { 291 UserData& data) const {
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 135f7d0d5..993a5a57a 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -86,6 +86,8 @@ public:
86 UserIDArray GetOpenUsers() const; 86 UserIDArray GetOpenUsers() const;
87 UserIDArray GetAllUsers() const; 87 UserIDArray GetAllUsers() const;
88 Common::UUID GetLastOpenedUser() const; 88 Common::UUID GetLastOpenedUser() const;
89 UserIDArray GetStoredOpenedUsers() const;
90 void StoreOpenedUsers();
89 91
90 bool CanSystemRegisterUser() const; 92 bool CanSystemRegisterUser() const;
91 93
@@ -101,6 +103,7 @@ private:
101 bool RemoveProfileAtIndex(std::size_t index); 103 bool RemoveProfileAtIndex(std::size_t index);
102 104
103 std::array<ProfileInfo, MAX_USERS> profiles{}; 105 std::array<ProfileInfo, MAX_USERS> profiles{};
106 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
104 std::size_t user_count{}; 107 std::size_t user_count{};
105 Common::UUID last_opened_user{}; 108 Common::UUID last_opened_user{};
106}; 109};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 6fb7e198e..8ea7fd760 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -299,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, 299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
300 {110, nullptr, "SetApplicationAlbumUserData"}, 300 {110, nullptr, "SetApplicationAlbumUserData"},
301 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, 301 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
302 {130, nullptr, "SetRecordVolumeMuted"}, 302 {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
303 {1000, nullptr, "GetDebugStorageChannel"}, 303 {1000, nullptr, "GetDebugStorageChannel"},
304 }; 304 };
305 // clang-format on 305 // clang-format on
@@ -316,7 +316,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
316 316
317 accumulated_suspended_tick_changed_event = 317 accumulated_suspended_tick_changed_event =
318 service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent"); 318 service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent");
319 accumulated_suspended_tick_changed_event->GetWritableEvent().Signal(); 319 accumulated_suspended_tick_changed_event->Signal();
320} 320}
321 321
322ISelfController::~ISelfController() { 322ISelfController::~ISelfController() {
@@ -378,7 +378,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
378void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { 378void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
379 LOG_WARNING(Service_AM, "(STUBBED) called"); 379 LOG_WARNING(Service_AM, "(STUBBED) called");
380 380
381 launchable_event->GetWritableEvent().Signal(); 381 launchable_event->Signal();
382 382
383 IPC::ResponseBuilder rb{ctx, 2, 1}; 383 IPC::ResponseBuilder rb{ctx, 2, 1};
384 rb.Push(ResultSuccess); 384 rb.Push(ResultSuccess);
@@ -597,6 +597,17 @@ void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
597 rb.Push(ResultSuccess); 597 rb.Push(ResultSuccess);
598} 598}
599 599
600void ISelfController::SetRecordVolumeMuted(Kernel::HLERequestContext& ctx) {
601 IPC::RequestParser rp{ctx};
602
603 const auto is_record_volume_muted = rp.Pop<bool>();
604
605 LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted);
606
607 IPC::ResponseBuilder rb{ctx, 2};
608 rb.Push(ResultSuccess);
609}
610
600AppletMessageQueue::AppletMessageQueue(Core::System& system) 611AppletMessageQueue::AppletMessageQueue(Core::System& system)
601 : service_context{system, "AppletMessageQueue"} { 612 : service_context{system, "AppletMessageQueue"} {
602 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); 613 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
@@ -618,18 +629,18 @@ Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
618 629
619void AppletMessageQueue::PushMessage(AppletMessage msg) { 630void AppletMessageQueue::PushMessage(AppletMessage msg) {
620 messages.push(msg); 631 messages.push(msg);
621 on_new_message->GetWritableEvent().Signal(); 632 on_new_message->Signal();
622} 633}
623 634
624AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { 635AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
625 if (messages.empty()) { 636 if (messages.empty()) {
626 on_new_message->GetWritableEvent().Clear(); 637 on_new_message->Clear();
627 return AppletMessage::None; 638 return AppletMessage::None;
628 } 639 }
629 auto msg = messages.front(); 640 auto msg = messages.front();
630 messages.pop(); 641 messages.pop();
631 if (messages.empty()) { 642 if (messages.empty()) {
632 on_new_message->GetWritableEvent().Clear(); 643 on_new_message->Clear();
633 } 644 }
634 return msg; 645 return msg;
635} 646}
@@ -653,7 +664,7 @@ void AppletMessageQueue::FocusStateChanged() {
653void AppletMessageQueue::OperationModeChanged() { 664void AppletMessageQueue::OperationModeChanged() {
654 PushMessage(AppletMessage::OperationModeChanged); 665 PushMessage(AppletMessage::OperationModeChanged);
655 PushMessage(AppletMessage::PerformanceModeChanged); 666 PushMessage(AppletMessage::PerformanceModeChanged);
656 on_operation_mode_changed->GetWritableEvent().Signal(); 667 on_operation_mode_changed->Signal();
657} 668}
658 669
659ICommonStateGetter::ICommonStateGetter(Core::System& system_, 670ICommonStateGetter::ICommonStateGetter(Core::System& system_,
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index bb75c6281..a0fbfcfc5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -182,6 +182,7 @@ private:
182 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 182 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
183 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); 183 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
184 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); 184 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
185 void SetRecordVolumeMuted(Kernel::HLERequestContext& ctx);
185 186
186 enum class ScreenshotPermission : u32 { 187 enum class ScreenshotPermission : u32 {
187 Inherit = 0, 188 Inherit = 0,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index b5b8e4cad..7062df21c 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -65,7 +65,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
65 65
66 auto out = std::move(out_channel.front()); 66 auto out = std::move(out_channel.front());
67 out_channel.pop_front(); 67 out_channel.pop_front();
68 pop_out_data_event->GetWritableEvent().Clear(); 68 pop_out_data_event->Clear();
69 return out; 69 return out;
70} 70}
71 71
@@ -84,7 +84,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
84 84
85 auto out = std::move(out_interactive_channel.front()); 85 auto out = std::move(out_interactive_channel.front());
86 out_interactive_channel.pop_front(); 86 out_interactive_channel.pop_front();
87 pop_interactive_out_data_event->GetWritableEvent().Clear(); 87 pop_interactive_out_data_event->Clear();
88 return out; 88 return out;
89} 89}
90 90
@@ -103,7 +103,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storag
103 103
104void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) { 104void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
105 out_channel.emplace_back(std::move(storage)); 105 out_channel.emplace_back(std::move(storage));
106 pop_out_data_event->GetWritableEvent().Signal(); 106 pop_out_data_event->Signal();
107} 107}
108 108
109void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) { 109void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
@@ -112,11 +112,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& s
112 112
113void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) { 113void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
114 out_interactive_channel.emplace_back(std::move(storage)); 114 out_interactive_channel.emplace_back(std::move(storage));
115 pop_interactive_out_data_event->GetWritableEvent().Signal(); 115 pop_interactive_out_data_event->Signal();
116} 116}
117 117
118void AppletDataBroker::SignalStateChanged() { 118void AppletDataBroker::SignalStateChanged() {
119 state_changed_event->GetWritableEvent().Signal(); 119 state_changed_event->Signal();
120 120
121 switch (applet_mode) { 121 switch (applet_mode) {
122 case LibraryAppletMode::AllForeground: 122 case LibraryAppletMode::AllForeground:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e78a57657..12c6a5b1a 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -164,7 +164,7 @@ protected:
164 u32_le size; 164 u32_le size;
165 u32_le library_version; 165 u32_le library_version;
166 u32_le theme_color; 166 u32_le theme_color;
167 u8 play_startup_sound; 167 bool play_startup_sound;
168 u64_le system_tick; 168 u64_le system_tick;
169 }; 169 };
170 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); 170 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index 4a2ae5f88..5abf22ba4 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -45,9 +45,25 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
45 {32, nullptr, "GetActiveOutputTarget"}, 45 {32, nullptr, "GetActiveOutputTarget"},
46 {33, nullptr, "GetTargetDeviceInfo"}, 46 {33, nullptr, "GetTargetDeviceInfo"},
47 {34, nullptr, "AcquireTargetNotification"}, 47 {34, nullptr, "AcquireTargetNotification"},
48 {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
49 {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
50 {37, nullptr, "SetHearingProtectionSafeguardEnabled"},
51 {38, nullptr, "IsHearingProtectionSafeguardEnabled"},
52 {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
53 {40, nullptr, "GetSystemInformationForDebug"},
54 {41, nullptr, "SetVolumeButtonLongPressTime"},
55 {42, nullptr, "SetNativeVolumeForDebug"},
48 {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, 56 {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
49 {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, 57 {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
50 {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, 58 {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
59 {10100, nullptr, "GetAudioVolumeDataForPlayReport"},
60 {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
61 {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
62 {10103, nullptr, "GetAudioOutputTargetForPlayReport"},
63 {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
64 {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
65 {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
66 {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
51 }; 67 };
52 // clang-format on 68 // clang-format on
53 69
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 48a9a73a0..608925dfc 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn;
17class IAudioIn final : public ServiceFramework<IAudioIn> { 17class IAudioIn final : public ServiceFramework<IAudioIn> {
18public: 18public:
19 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, 19 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
20 std::string& device_name, const AudioInParameter& in_params, u32 handle, 20 const std::string& device_name, const AudioInParameter& in_params, u32 handle,
21 u64 applet_resource_user_id) 21 u64 applet_resource_user_id)
22 : ServiceFramework{system_, "IAudioIn"}, 22 : ServiceFramework{system_, "IAudioIn"},
23 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, 23 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 49c092301..122290c6a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut;
24class IAudioOut final : public ServiceFramework<IAudioOut> { 24class IAudioOut final : public ServiceFramework<IAudioOut> {
25public: 25public:
26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
27 size_t session_id, std::string& device_name, 27 size_t session_id, const std::string& device_name,
28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, 29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( 30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6fb07c37d..13423dca6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -52,6 +52,8 @@ public:
52 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, 52 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
53 {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, 53 {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
54 {11, nullptr, "ExecuteAudioRendererRendering"}, 54 {11, nullptr, "ExecuteAudioRendererRendering"},
55 {12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"},
56 {13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"},
55 }; 57 };
56 // clang-format on 58 // clang-format on
57 RegisterHandlers(functions); 59 RegisterHandlers(functions);
@@ -205,6 +207,30 @@ private:
205 LOG_DEBUG(Service_Audio, "called"); 207 LOG_DEBUG(Service_Audio, "called");
206 } 208 }
207 209
210 void SetVoiceDropParameter(Kernel::HLERequestContext& ctx) {
211 LOG_DEBUG(Service_Audio, "called");
212
213 IPC::RequestParser rp{ctx};
214 auto voice_drop_param{rp.Pop<f32>()};
215
216 auto& system_ = impl->GetSystem();
217 system_.SetVoiceDropParameter(voice_drop_param);
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(ResultSuccess);
221 }
222
223 void GetVoiceDropParameter(Kernel::HLERequestContext& ctx) {
224 LOG_DEBUG(Service_Audio, "called");
225
226 auto& system_ = impl->GetSystem();
227 auto voice_drop_param{system_.GetVoiceDropParameter()};
228
229 IPC::ResponseBuilder rb{ctx, 3};
230 rb.Push(ResultSuccess);
231 rb.Push(voice_drop_param);
232 }
233
208 KernelHelpers::ServiceContext service_context; 234 KernelHelpers::ServiceContext service_context;
209 Kernel::KEvent* rendered_event; 235 Kernel::KEvent* rendered_event;
210 Manager& manager; 236 Manager& manager;
@@ -239,7 +265,7 @@ public:
239 }; 265 };
240 RegisterHandlers(functions); 266 RegisterHandlers(functions);
241 267
242 event->GetWritableEvent().Signal(); 268 event->Signal();
243 } 269 }
244 270
245 ~IAudioDevice() override { 271 ~IAudioDevice() override {
@@ -325,7 +351,7 @@ private:
325 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { 351 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
326 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 352 LOG_DEBUG(Service_Audio, "(STUBBED) called");
327 353
328 event->GetWritableEvent().Signal(); 354 event->Signal();
329 355
330 IPC::ResponseBuilder rb{ctx, 2, 1}; 356 IPC::ResponseBuilder rb{ctx, 2, 1};
331 rb.Push(ResultSuccess); 357 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index cd0b405ff..847f76987 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -82,7 +82,7 @@ void ProgressServiceBackend::FinishDownload(Result result) {
82} 82}
83 83
84void ProgressServiceBackend::SignalUpdate() { 84void ProgressServiceBackend::SignalUpdate() {
85 update_event->GetWritableEvent().Signal(); 85 update_event->Signal();
86} 86}
87 87
88Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} 88Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index e23eae36a..c08274ef9 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -707,7 +707,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
707 {31, nullptr, "OpenGameCardFileSystem"}, 707 {31, nullptr, "OpenGameCardFileSystem"},
708 {32, nullptr, "ExtendSaveDataFileSystem"}, 708 {32, nullptr, "ExtendSaveDataFileSystem"},
709 {33, nullptr, "DeleteCacheStorage"}, 709 {33, nullptr, "DeleteCacheStorage"},
710 {34, nullptr, "GetCacheStorageSize"}, 710 {34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"},
711 {35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, 711 {35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
712 {36, nullptr, "OpenHostFileSystemWithOption"}, 712 {36, nullptr, "OpenHostFileSystemWithOption"},
713 {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, 713 {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
@@ -1107,6 +1107,18 @@ void FSP_SRV::GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx) {
1107 rb.Push(access_log_program_index); 1107 rb.Push(access_log_program_index);
1108} 1108}
1109 1109
1110void FSP_SRV::GetCacheStorageSize(Kernel::HLERequestContext& ctx) {
1111 IPC::RequestParser rp{ctx};
1112 const auto index{rp.Pop<s32>()};
1113
1114 LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index);
1115
1116 IPC::ResponseBuilder rb{ctx, 6};
1117 rb.Push(ResultSuccess);
1118 rb.Push(s64{0});
1119 rb.Push(s64{0});
1120}
1121
1110class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> { 1122class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
1111public: 1123public:
1112 explicit IMultiCommitManager(Core::System& system_) 1124 explicit IMultiCommitManager(Core::System& system_)
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 36f552e05..3d88b97f9 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -54,6 +54,7 @@ private:
54 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); 54 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
55 void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx); 55 void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx);
56 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); 56 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
57 void GetCacheStorageSize(Kernel::HLERequestContext& ctx);
57 58
58 FileSystemController& fsc; 59 FileSystemController& fsc;
59 const FileSys::ContentProvider& content_provider; 60 const FileSys::ContentProvider& content_provider;
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index e0db787fc..fad532115 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -26,7 +26,7 @@ public:
26 {10101, &IFriendService::GetFriendList, "GetFriendList"}, 26 {10101, &IFriendService::GetFriendList, "GetFriendList"},
27 {10102, nullptr, "UpdateFriendInfo"}, 27 {10102, nullptr, "UpdateFriendInfo"},
28 {10110, nullptr, "GetFriendProfileImage"}, 28 {10110, nullptr, "GetFriendProfileImage"},
29 {10120, nullptr, "IsFriendListCacheAvailable"}, 29 {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"},
30 {10121, nullptr, "EnsureFriendListAvailable"}, 30 {10121, nullptr, "EnsureFriendListAvailable"},
31 {10200, nullptr, "SendFriendRequestForApplication"}, 31 {10200, nullptr, "SendFriendRequestForApplication"},
32 {10211, nullptr, "AddFacedFriendRequestForApplication"}, 32 {10211, nullptr, "AddFacedFriendRequestForApplication"},
@@ -194,6 +194,17 @@ private:
194 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" 194 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
195 } 195 }
196 196
197 void CheckFriendListAvailability(Kernel::HLERequestContext& ctx) {
198 IPC::RequestParser rp{ctx};
199 const auto uuid{rp.PopRaw<Common::UUID>()};
200
201 LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
202
203 IPC::ResponseBuilder rb{ctx, 3};
204 rb.Push(ResultSuccess);
205 rb.Push(true);
206 }
207
197 KernelHelpers::ServiceContext service_context; 208 KernelHelpers::ServiceContext service_context;
198 209
199 Kernel::KEvent* completion_event; 210 Kernel::KEvent* completion_event;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index cb29004e8..2f871de31 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -16,7 +16,6 @@
16#include "core/hid/hid_core.h" 16#include "core/hid/hid_core.h"
17#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/kernel/k_writable_event.h"
20#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
21#include "core/hle/service/hid/errors.h" 20#include "core/hle/service/hid/errors.h"
22#include "core/hle/service/kernel_helpers.h" 21#include "core/hle/service/kernel_helpers.h"
@@ -167,7 +166,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
167 const auto& battery_level = controller.device->GetBattery(); 166 const auto& battery_level = controller.device->GetBattery();
168 auto* shared_memory = controller.shared_memory; 167 auto* shared_memory = controller.shared_memory;
169 if (controller_type == Core::HID::NpadStyleIndex::None) { 168 if (controller_type == Core::HID::NpadStyleIndex::None) {
170 controller.styleset_changed_event->GetWritableEvent().Signal(); 169 controller.styleset_changed_event->Signal();
171 return; 170 return;
172 } 171 }
173 172
@@ -660,7 +659,6 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
660 ASSERT(false); 659 ASSERT(false);
661 break; 660 break;
662 case Core::HID::NpadStyleIndex::ProController: 661 case Core::HID::NpadStyleIndex::ProController:
663 case Core::HID::NpadStyleIndex::Pokeball:
664 set_motion_state(sixaxis_fullkey_state, motion_state[0]); 662 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
665 break; 663 break;
666 case Core::HID::NpadStyleIndex::Handheld: 664 case Core::HID::NpadStyleIndex::Handheld:
@@ -676,6 +674,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
676 case Core::HID::NpadStyleIndex::JoyconRight: 674 case Core::HID::NpadStyleIndex::JoyconRight:
677 set_motion_state(sixaxis_right_lifo_state, motion_state[1]); 675 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
678 break; 676 break;
677 case Core::HID::NpadStyleIndex::Pokeball:
678 using namespace std::literals::chrono_literals;
679 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
680 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
681 break;
679 default: 682 default:
680 break; 683 break;
681 } 684 }
@@ -742,8 +745,9 @@ void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
742} 745}
743 746
744void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 747void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
745 ASSERT(max_length < supported_npad_id_types.size()); 748 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
746 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); 749 ASSERT(max_length <= copy_amount);
750 std::memcpy(data, supported_npad_id_types.data(), copy_amount);
747} 751}
748 752
749std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { 753std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
@@ -864,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
864 return false; 868 return false;
865 } 869 }
866 870
867 if (!controller.device->IsVibrationEnabled()) { 871 if (!controller.device->IsVibrationEnabled(device_index)) {
868 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || 872 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
869 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { 873 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
870 // Send an empty vibration to stop any vibrations. 874 // Send an empty vibration to stop any vibrations.
@@ -997,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
997 } 1001 }
998 1002
999 controller.vibration[device_index].device_mounted = 1003 controller.vibration[device_index].device_mounted =
1000 controller.device->TestVibration(device_index); 1004 controller.device->IsVibrationEnabled(device_index);
1001} 1005}
1002 1006
1003void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 1007void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
@@ -1029,7 +1033,7 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
1029 1033
1030void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { 1034void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
1031 const auto& controller = GetControllerFromNpadIdType(npad_id); 1035 const auto& controller = GetControllerFromNpadIdType(npad_id);
1032 controller.styleset_changed_event->GetWritableEvent().Signal(); 1036 controller.styleset_changed_event->Signal();
1033} 1037}
1034 1038
1035void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, 1039void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
@@ -1498,25 +1502,25 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1498 Core::HID::NpadStyleTag style = GetSupportedStyleSet(); 1502 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1499 switch (controller) { 1503 switch (controller) {
1500 case Core::HID::NpadStyleIndex::ProController: 1504 case Core::HID::NpadStyleIndex::ProController:
1501 return style.fullkey; 1505 return style.fullkey.As<bool>();
1502 case Core::HID::NpadStyleIndex::JoyconDual: 1506 case Core::HID::NpadStyleIndex::JoyconDual:
1503 return style.joycon_dual; 1507 return style.joycon_dual.As<bool>();
1504 case Core::HID::NpadStyleIndex::JoyconLeft: 1508 case Core::HID::NpadStyleIndex::JoyconLeft:
1505 return style.joycon_left; 1509 return style.joycon_left.As<bool>();
1506 case Core::HID::NpadStyleIndex::JoyconRight: 1510 case Core::HID::NpadStyleIndex::JoyconRight:
1507 return style.joycon_right; 1511 return style.joycon_right.As<bool>();
1508 case Core::HID::NpadStyleIndex::GameCube: 1512 case Core::HID::NpadStyleIndex::GameCube:
1509 return style.gamecube; 1513 return style.gamecube.As<bool>();
1510 case Core::HID::NpadStyleIndex::Pokeball: 1514 case Core::HID::NpadStyleIndex::Pokeball:
1511 return style.palma; 1515 return style.palma.As<bool>();
1512 case Core::HID::NpadStyleIndex::NES: 1516 case Core::HID::NpadStyleIndex::NES:
1513 return style.lark; 1517 return style.lark.As<bool>();
1514 case Core::HID::NpadStyleIndex::SNES: 1518 case Core::HID::NpadStyleIndex::SNES:
1515 return style.lucia; 1519 return style.lucia.As<bool>();
1516 case Core::HID::NpadStyleIndex::N64: 1520 case Core::HID::NpadStyleIndex::N64:
1517 return style.lagoon; 1521 return style.lagoon.As<bool>();
1518 case Core::HID::NpadStyleIndex::SegaGenesis: 1522 case Core::HID::NpadStyleIndex::SegaGenesis:
1519 return style.lager; 1523 return style.lager.As<bool>();
1520 default: 1524 default:
1521 return false; 1525 return false;
1522 } 1526 }
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
new file mode 100644
index 000000000..4564ea1e2
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -0,0 +1,229 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hid/hid_types.h"
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/kernel/k_readable_event.h"
10#include "core/hle/service/hid/controllers/palma.h"
11#include "core/hle/service/kernel_helpers.h"
12
13namespace Service::HID {
14
15Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
16 KernelHelpers::ServiceContext& service_context_)
17 : ControllerBase{hid_core_}, service_context{service_context_} {
18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
20}
21
22Controller_Palma::~Controller_Palma() = default;
23
24void Controller_Palma::OnInit() {}
25
26void Controller_Palma::OnRelease() {}
27
28void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) {
30 return;
31 }
32}
33
34Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
35 PalmaConnectionHandle& handle) {
36 active_handle.npad_id = npad_id;
37 handle = active_handle;
38 return ResultSuccess;
39}
40
41Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
42 if (handle.npad_id != active_handle.npad_id) {
43 return InvalidPalmaHandle;
44 }
45 ActivateController();
46 return ResultSuccess;
47}
48
49Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
50 const PalmaConnectionHandle& handle) const {
51 if (handle.npad_id != active_handle.npad_id) {
52 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
53 }
54 return operation_complete_event->GetReadableEvent();
55}
56
57Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
58 PalmaOperationType& operation_type,
59 PalmaOperationData& data) const {
60 if (handle.npad_id != active_handle.npad_id) {
61 return InvalidPalmaHandle;
62 }
63 operation_type = operation.operation;
64 data = operation.data;
65 return ResultSuccess;
66}
67
68Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
69 u64 palma_activity) {
70 if (handle.npad_id != active_handle.npad_id) {
71 return InvalidPalmaHandle;
72 }
73 operation.operation = PalmaOperationType::PlayActivity;
74 operation.result = PalmaResultSuccess;
75 operation.data = {};
76 operation_complete_event->Signal();
77 return ResultSuccess;
78}
79
80Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
81 PalmaFrModeType fr_mode_) {
82 if (handle.npad_id != active_handle.npad_id) {
83 return InvalidPalmaHandle;
84 }
85 fr_mode = fr_mode_;
86 return ResultSuccess;
87}
88
89Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
90 if (handle.npad_id != active_handle.npad_id) {
91 return InvalidPalmaHandle;
92 }
93 operation.operation = PalmaOperationType::ReadStep;
94 operation.result = PalmaResultSuccess;
95 operation.data = {};
96 operation_complete_event->Signal();
97 return ResultSuccess;
98}
99
100Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
101 if (handle.npad_id != active_handle.npad_id) {
102 return InvalidPalmaHandle;
103 }
104 return ResultSuccess;
105}
106
107Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
108 if (handle.npad_id != active_handle.npad_id) {
109 return InvalidPalmaHandle;
110 }
111 return ResultSuccess;
112}
113
114void Controller_Palma::ReadPalmaApplicationSection() {}
115
116void Controller_Palma::WritePalmaApplicationSection() {}
117
118Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
119 if (handle.npad_id != active_handle.npad_id) {
120 return InvalidPalmaHandle;
121 }
122 operation.operation = PalmaOperationType::ReadUniqueCode;
123 operation.result = PalmaResultSuccess;
124 operation.data = {};
125 operation_complete_event->Signal();
126 return ResultSuccess;
127}
128
129Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
130 if (handle.npad_id != active_handle.npad_id) {
131 return InvalidPalmaHandle;
132 }
133 operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
134 operation.result = PalmaResultSuccess;
135 operation.data = {};
136 operation_complete_event->Signal();
137 return ResultSuccess;
138}
139
140void Controller_Palma::WritePalmaActivityEntry() {}
141
142Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
143 u64 unknown) {
144 if (handle.npad_id != active_handle.npad_id) {
145 return InvalidPalmaHandle;
146 }
147 operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
148 operation.result = PalmaResultSuccess;
149 operation.data = {};
150 operation_complete_event->Signal();
151 return ResultSuccess;
152}
153
154Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
155 u8* t_mem, u64 size) {
156 if (handle.npad_id != active_handle.npad_id) {
157 return InvalidPalmaHandle;
158 }
159 operation.operation = PalmaOperationType::WriteWaveEntry;
160 operation.result = PalmaResultSuccess;
161 operation.data = {};
162 operation_complete_event->Signal();
163 return ResultSuccess;
164}
165
166Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
167 s32 database_id_version_) {
168 if (handle.npad_id != active_handle.npad_id) {
169 return InvalidPalmaHandle;
170 }
171 database_id_version = database_id_version_;
172 operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
173 operation.result = PalmaResultSuccess;
174 operation.data[0] = {};
175 operation_complete_event->Signal();
176 return ResultSuccess;
177}
178
179Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
180 const PalmaConnectionHandle& handle) {
181 if (handle.npad_id != active_handle.npad_id) {
182 return InvalidPalmaHandle;
183 }
184 operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
185 operation.result = PalmaResultSuccess;
186 operation.data = {};
187 operation.data[0] = static_cast<u8>(database_id_version);
188 operation_complete_event->Signal();
189 return ResultSuccess;
190}
191
192void Controller_Palma::SuspendPalmaFeature() {}
193
194Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
195 if (handle.npad_id != active_handle.npad_id) {
196 return InvalidPalmaHandle;
197 }
198 return operation.result;
199}
200void Controller_Palma::ReadPalmaPlayLog() {}
201
202void Controller_Palma::ResetPalmaPlayLog() {}
203
204void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
205 // If true controllers are able to be paired
206 is_connectable = is_all_connectable;
207}
208
209void Controller_Palma::SetIsPalmaPairedConnectable() {}
210
211Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
212 if (handle.npad_id != active_handle.npad_id) {
213 return InvalidPalmaHandle;
214 }
215 // TODO: Do something
216 return ResultSuccess;
217}
218
219void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
220
221void Controller_Palma::CancelWritePalmaWaveEntry() {}
222
223void Controller_Palma::EnablePalmaBoostMode() {}
224
225void Controller_Palma::GetPalmaBluetoothAddress() {}
226
227void Controller_Palma::SetDisallowedPalmaConnection() {}
228
229} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
new file mode 100644
index 000000000..1d7fc94e1
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -0,0 +1,163 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "core/hle/service/hid/controllers/controller_base.h"
10#include "core/hle/service/hid/errors.h"
11
12namespace Kernel {
13class KEvent;
14class KReadableEvent;
15} // namespace Kernel
16
17namespace Service::KernelHelpers {
18class ServiceContext;
19}
20
21namespace Core::HID {
22class EmulatedController;
23} // namespace Core::HID
24
25namespace Service::HID {
26class Controller_Palma final : public ControllerBase {
27public:
28 using PalmaOperationData = std::array<u8, 0x140>;
29
30 // This is nn::hid::PalmaOperationType
31 enum class PalmaOperationType {
32 PlayActivity,
33 SetFrModeType,
34 ReadStep,
35 EnableStep,
36 ResetStep,
37 ReadApplicationSection,
38 WriteApplicationSection,
39 ReadUniqueCode,
40 SetUniqueCodeInvalid,
41 WriteActivityEntry,
42 WriteRgbLedPatternEntry,
43 WriteWaveEntry,
44 ReadDataBaseIdentificationVersion,
45 WriteDataBaseIdentificationVersion,
46 SuspendFeature,
47 ReadPlayLog,
48 ResetPlayLog,
49 };
50
51 // This is nn::hid::PalmaWaveSet
52 enum class PalmaWaveSet : u64 {
53 Small,
54 Medium,
55 Large,
56 };
57
58 // This is nn::hid::PalmaFrModeType
59 enum class PalmaFrModeType : u64 {
60 Off,
61 B01,
62 B02,
63 B03,
64 Downloaded,
65 };
66
67 // This is nn::hid::PalmaFeature
68 enum class PalmaFeature : u64 {
69 FrMode,
70 RumbleFeedback,
71 Step,
72 MuteSwitch,
73 };
74
75 // This is nn::hid::PalmaOperationInfo
76 struct PalmaOperationInfo {
77 PalmaOperationType operation{};
78 Result result{PalmaResultSuccess};
79 PalmaOperationData data{};
80 };
81 static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
82
83 // This is nn::hid::PalmaActivityEntry
84 struct PalmaActivityEntry {
85 u32 rgb_led_pattern_index;
86 INSERT_PADDING_BYTES(2);
87 PalmaWaveSet wave_set;
88 u32 wave_index;
89 INSERT_PADDING_BYTES(12);
90 };
91 static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
92
93 struct PalmaConnectionHandle {
94 Core::HID::NpadIdType npad_id;
95 INSERT_PADDING_BYTES(4); // Unknown
96 };
97 static_assert(sizeof(PalmaConnectionHandle) == 0x8,
98 "PalmaConnectionHandle has incorrect size.");
99
100 explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
101 KernelHelpers::ServiceContext& service_context_);
102 ~Controller_Palma() override;
103
104 // Called when the controller is initialized
105 void OnInit() override;
106
107 // When the controller is released
108 void OnRelease() override;
109
110 // When the controller is requesting an update for the shared memory
111 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
112
113 Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
114 Result InitializePalma(const PalmaConnectionHandle& handle);
115 Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
116 const PalmaConnectionHandle& handle) const;
117 Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
118 PalmaOperationType& operation_type,
119 PalmaOperationData& data) const;
120 Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
121 Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
122 Result ReadPalmaStep(const PalmaConnectionHandle& handle);
123 Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
124 Result ResetPalmaStep(const PalmaConnectionHandle& handle);
125 Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
126 Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
127 Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
128 Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem,
129 u64 size);
130 Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
131 s32 database_id_version_);
132 Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
133 Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
134 void SetIsPalmaAllConnectable(bool is_all_connectable);
135 Result PairPalma(const PalmaConnectionHandle& handle);
136 void SetPalmaBoostMode(bool boost_mode);
137
138private:
139 void ReadPalmaApplicationSection();
140 void WritePalmaApplicationSection();
141 void WritePalmaActivityEntry();
142 void SuspendPalmaFeature();
143 void ReadPalmaPlayLog();
144 void ResetPalmaPlayLog();
145 void SetIsPalmaPairedConnectable();
146 void CancelWritePalmaWaveEntry();
147 void EnablePalmaBoostMode();
148 void GetPalmaBluetoothAddress();
149 void SetDisallowedPalmaConnection();
150
151 bool is_connectable{};
152 s32 database_id_version{};
153 PalmaOperationInfo operation{};
154 PalmaFrModeType fr_mode{};
155 PalmaConnectionHandle active_handle{};
156
157 Core::HID::EmulatedController* controller;
158
159 Kernel::KEvent* operation_complete_event;
160 KernelHelpers::ServiceContext& service_context;
161};
162
163} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 4613a4e60..76208e9a4 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -7,6 +7,7 @@
7 7
8namespace Service::HID { 8namespace Service::HID {
9 9
10constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
10constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; 11constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
11constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; 12constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
12constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; 13constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
@@ -17,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
17constexpr Result NpadIsSameType{ErrorModule::HID, 602}; 18constexpr Result NpadIsSameType{ErrorModule::HID, 602};
18constexpr Result InvalidNpadId{ErrorModule::HID, 709}; 19constexpr Result InvalidNpadId{ErrorModule::HID, 709};
19constexpr Result NpadNotConnected{ErrorModule::HID, 710}; 20constexpr Result NpadNotConnected{ErrorModule::HID, 710};
21constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
20 22
21} // namespace Service::HID 23} // namespace Service::HID
22 24
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 3d3457160..79375bd2f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -27,6 +27,7 @@
27#include "core/hle/service/hid/controllers/keyboard.h" 27#include "core/hle/service/hid/controllers/keyboard.h"
28#include "core/hle/service/hid/controllers/mouse.h" 28#include "core/hle/service/hid/controllers/mouse.h"
29#include "core/hle/service/hid/controllers/npad.h" 29#include "core/hle/service/hid/controllers/npad.h"
30#include "core/hle/service/hid/controllers/palma.h"
30#include "core/hle/service/hid/controllers/stubbed.h" 31#include "core/hle/service/hid/controllers/stubbed.h"
31#include "core/hle/service/hid/controllers/touchscreen.h" 32#include "core/hle/service/hid/controllers/touchscreen.h"
32#include "core/hle/service/hid/controllers/xpad.h" 33#include "core/hle/service/hid/controllers/xpad.h"
@@ -35,7 +36,8 @@ namespace Service::HID {
35 36
36// Updating period for each HID device. 37// Updating period for each HID device.
37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew 38// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) 39// Correct pad_update_ns is 4ms this is overclocked to lower input lag
40constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) 41constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
40constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) 42constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
41 43
@@ -60,6 +62,7 @@ IAppletResource::IAppletResource(Core::System& system_,
60 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); 62 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
61 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); 63 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
62 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); 64 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
65 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
63 66
64 // Homebrew doesn't try to activate some controllers, so we activate them by default 67 // Homebrew doesn't try to activate some controllers, so we activate them by default
65 GetController<Controller_NPad>(HidController::NPad).ActivateController(); 68 GetController<Controller_NPad>(HidController::NPad).ActivateController();
@@ -310,36 +313,36 @@ Hid::Hid(Core::System& system_)
310 {406, nullptr, "GetNpadLeftRightInterfaceType"}, 313 {406, nullptr, "GetNpadLeftRightInterfaceType"},
311 {407, nullptr, "GetNpadOfHighestBatteryLevel"}, 314 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
312 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, 315 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
313 {500, nullptr, "GetPalmaConnectionHandle"}, 316 {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
314 {501, nullptr, "InitializePalma"}, 317 {501, &Hid::InitializePalma, "InitializePalma"},
315 {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, 318 {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
316 {503, nullptr, "GetPalmaOperationInfo"}, 319 {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
317 {504, nullptr, "PlayPalmaActivity"}, 320 {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"},
318 {505, nullptr, "SetPalmaFrModeType"}, 321 {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"},
319 {506, nullptr, "ReadPalmaStep"}, 322 {506, &Hid::ReadPalmaStep, "ReadPalmaStep"},
320 {507, nullptr, "EnablePalmaStep"}, 323 {507, &Hid::EnablePalmaStep, "EnablePalmaStep"},
321 {508, nullptr, "ResetPalmaStep"}, 324 {508, &Hid::ResetPalmaStep, "ResetPalmaStep"},
322 {509, nullptr, "ReadPalmaApplicationSection"}, 325 {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
323 {510, nullptr, "WritePalmaApplicationSection"}, 326 {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
324 {511, nullptr, "ReadPalmaUniqueCode"}, 327 {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
325 {512, nullptr, "SetPalmaUniqueCodeInvalid"}, 328 {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
326 {513, nullptr, "WritePalmaActivityEntry"}, 329 {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
327 {514, nullptr, "WritePalmaRgbLedPatternEntry"}, 330 {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
328 {515, nullptr, "WritePalmaWaveEntry"}, 331 {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
329 {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, 332 {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
330 {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, 333 {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
331 {518, nullptr, "SuspendPalmaFeature"}, 334 {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"},
332 {519, nullptr, "GetPalmaOperationResult"}, 335 {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"},
333 {520, nullptr, "ReadPalmaPlayLog"}, 336 {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
334 {521, nullptr, "ResetPalmaPlayLog"}, 337 {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
335 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, 338 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
336 {523, nullptr, "SetIsPalmaPairedConnectable"}, 339 {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
337 {524, nullptr, "PairPalma"}, 340 {524, &Hid::PairPalma, "PairPalma"},
338 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, 341 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
339 {526, nullptr, "CancelWritePalmaWaveEntry"}, 342 {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
340 {527, nullptr, "EnablePalmaBoostMode"}, 343 {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
341 {528, nullptr, "GetPalmaBluetoothAddress"}, 344 {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
342 {529, nullptr, "SetDisallowedPalmaConnection"}, 345 {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
343 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, 346 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
344 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, 347 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
345 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, 348 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
@@ -1878,14 +1881,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) {
1878 rb.Push(false); 1881 rb.Push(false);
1879} 1882}
1880 1883
1884void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) {
1885 IPC::RequestParser rp{ctx};
1886 struct Parameters {
1887 Core::HID::NpadIdType npad_id;
1888 INSERT_PADDING_WORDS_NOINIT(1);
1889 u64 applet_resource_user_id;
1890 };
1891 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1892
1893 const auto parameters{rp.PopRaw<Parameters>()};
1894
1895 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1896 parameters.npad_id, parameters.applet_resource_user_id);
1897
1898 Controller_Palma::PalmaConnectionHandle handle;
1899 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1900 const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle);
1901
1902 IPC::ResponseBuilder rb{ctx, 4};
1903 rb.Push(result);
1904 rb.PushRaw(handle);
1905}
1906
1907void Hid::InitializePalma(Kernel::HLERequestContext& ctx) {
1908 IPC::RequestParser rp{ctx};
1909 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1910
1911 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1912
1913 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1914 const auto result = controller.InitializePalma(connection_handle);
1915
1916 IPC::ResponseBuilder rb{ctx, 2};
1917 rb.Push(result);
1918}
1919
1920void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) {
1921 IPC::RequestParser rp{ctx};
1922 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1923
1924 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1925
1926 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1927
1928 IPC::ResponseBuilder rb{ctx, 2, 1};
1929 rb.Push(ResultSuccess);
1930 rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle));
1931}
1932
1933void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) {
1934 IPC::RequestParser rp{ctx};
1935 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1936
1937 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1938
1939 Controller_Palma::PalmaOperationType operation_type;
1940 Controller_Palma::PalmaOperationData data;
1941 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1942 const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data);
1943
1944 if (result.IsError()) {
1945 IPC::ResponseBuilder rb{ctx, 2};
1946 rb.Push(result);
1947 }
1948
1949 ctx.WriteBuffer(data);
1950 IPC::ResponseBuilder rb{ctx, 4};
1951 rb.Push(result);
1952 rb.Push(static_cast<u64>(operation_type));
1953}
1954
1955void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) {
1956 IPC::RequestParser rp{ctx};
1957 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1958 const auto palma_activity{rp.Pop<u64>()};
1959
1960 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
1961 connection_handle.npad_id, palma_activity);
1962
1963 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1964 const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity);
1965
1966 IPC::ResponseBuilder rb{ctx, 2};
1967 rb.Push(result);
1968}
1969
1970void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) {
1971 IPC::RequestParser rp{ctx};
1972 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1973 const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()};
1974
1975 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
1976 connection_handle.npad_id, fr_mode);
1977
1978 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1979 const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode);
1980
1981 IPC::ResponseBuilder rb{ctx, 2};
1982 rb.Push(result);
1983}
1984
1985void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) {
1986 IPC::RequestParser rp{ctx};
1987 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1988
1989 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1990
1991 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1992 const auto result = controller.ReadPalmaStep(connection_handle);
1993
1994 IPC::ResponseBuilder rb{ctx, 2};
1995 rb.Push(result);
1996}
1997
1998void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) {
1999 IPC::RequestParser rp{ctx};
2000 struct Parameters {
2001 bool is_enabled;
2002 INSERT_PADDING_WORDS_NOINIT(1);
2003 Controller_Palma::PalmaConnectionHandle connection_handle;
2004 };
2005 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2006
2007 const auto parameters{rp.PopRaw<Parameters>()};
2008
2009 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2010 parameters.connection_handle.npad_id, parameters.is_enabled);
2011
2012 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2013 const auto result =
2014 controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2015
2016 IPC::ResponseBuilder rb{ctx, 2};
2017 rb.Push(result);
2018}
2019
2020void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) {
2021 IPC::RequestParser rp{ctx};
2022 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2023
2024 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2025
2026 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2027 const auto result = controller.ResetPalmaStep(connection_handle);
2028
2029 IPC::ResponseBuilder rb{ctx, 2};
2030 rb.Push(result);
2031}
2032
2033void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) {
2034 LOG_WARNING(Service_HID, "(STUBBED) called");
2035
2036 IPC::ResponseBuilder rb{ctx, 2};
2037 rb.Push(ResultSuccess);
2038}
2039
2040void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) {
2041 LOG_WARNING(Service_HID, "(STUBBED) called");
2042
2043 IPC::ResponseBuilder rb{ctx, 2};
2044 rb.Push(ResultSuccess);
2045}
2046
2047void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) {
2048 IPC::RequestParser rp{ctx};
2049 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2050
2051 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2052
2053 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2054 .ReadPalmaUniqueCode(connection_handle);
2055
2056 IPC::ResponseBuilder rb{ctx, 2};
2057 rb.Push(ResultSuccess);
2058}
2059
2060void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) {
2061 IPC::RequestParser rp{ctx};
2062 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2063
2064 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2065
2066 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2067 .SetPalmaUniqueCodeInvalid(connection_handle);
2068
2069 IPC::ResponseBuilder rb{ctx, 2};
2070 rb.Push(ResultSuccess);
2071}
2072
2073void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) {
2074 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2075
2076 IPC::ResponseBuilder rb{ctx, 2};
2077 rb.Push(ResultSuccess);
2078}
2079
2080void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) {
2081 IPC::RequestParser rp{ctx};
2082 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2083 const auto unknown{rp.Pop<u64>()};
2084
2085 const auto buffer = ctx.ReadBuffer();
2086
2087 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2088 connection_handle.npad_id, unknown);
2089
2090 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2091 .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2092
2093 IPC::ResponseBuilder rb{ctx, 2};
2094 rb.Push(ResultSuccess);
2095}
2096
2097void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
2098 IPC::RequestParser rp{ctx};
2099 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2100 const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()};
2101 const auto unknown{rp.Pop<u64>()};
2102 const auto t_mem_size{rp.Pop<u64>()};
2103 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2104 const auto size{rp.Pop<u64>()};
2105
2106 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2107
2108 auto t_mem =
2109 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
2110
2111 if (t_mem.IsNull()) {
2112 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2113 IPC::ResponseBuilder rb{ctx, 2};
2114 rb.Push(ResultUnknown);
2115 return;
2116 }
2117
2118 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2119
2120 LOG_WARNING(Service_HID,
2121 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2122 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2123 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2124
2125 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2126 .WritePalmaWaveEntry(connection_handle, wave_set,
2127 system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size);
2128
2129 IPC::ResponseBuilder rb{ctx, 2};
2130 rb.Push(ResultSuccess);
2131}
2132
2133void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) {
2134 IPC::RequestParser rp{ctx};
2135 struct Parameters {
2136 s32 database_id_version;
2137 INSERT_PADDING_WORDS_NOINIT(1);
2138 Controller_Palma::PalmaConnectionHandle connection_handle;
2139 };
2140 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2141
2142 const auto parameters{rp.PopRaw<Parameters>()};
2143
2144 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2145 parameters.connection_handle.npad_id, parameters.database_id_version);
2146
2147 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2148 .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
2149 parameters.database_id_version);
2150
2151 IPC::ResponseBuilder rb{ctx, 2};
2152 rb.Push(ResultSuccess);
2153}
2154
2155void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) {
2156 IPC::RequestParser rp{ctx};
2157 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2158
2159 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2160
2161 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2162 .GetPalmaDataBaseIdentificationVersion(connection_handle);
2163
2164 IPC::ResponseBuilder rb{ctx, 2};
2165 rb.Push(ResultSuccess);
2166}
2167
2168void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) {
2169 LOG_WARNING(Service_HID, "(STUBBED) called");
2170
2171 IPC::ResponseBuilder rb{ctx, 2};
2172 rb.Push(ResultSuccess);
2173}
2174
2175void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) {
2176 IPC::RequestParser rp{ctx};
2177 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2178
2179 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2180
2181 const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma)
2182 .GetPalmaOperationResult(connection_handle);
2183
2184 IPC::ResponseBuilder rb{ctx, 2};
2185 rb.Push(result);
2186}
2187
2188void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) {
2189 LOG_WARNING(Service_HID, "(STUBBED) called");
2190
2191 IPC::ResponseBuilder rb{ctx, 2};
2192 rb.Push(ResultSuccess);
2193}
2194
2195void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) {
2196 LOG_WARNING(Service_HID, "(STUBBED) called");
2197
2198 IPC::ResponseBuilder rb{ctx, 2};
2199 rb.Push(ResultSuccess);
2200}
2201
1881void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { 2202void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
1882 IPC::RequestParser rp{ctx}; 2203 IPC::RequestParser rp{ctx};
1883 const auto applet_resource_user_id{rp.Pop<u64>()}; 2204 struct Parameters {
1884 const auto is_palma_all_connectable{rp.Pop<bool>()}; 2205 bool is_palma_all_connectable;
2206 INSERT_PADDING_BYTES_NOINIT(7);
2207 u64 applet_resource_user_id;
2208 };
2209 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2210
2211 const auto parameters{rp.PopRaw<Parameters>()};
1885 2212
1886 LOG_WARNING(Service_HID, 2213 LOG_WARNING(Service_HID,
1887 "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", 2214 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
1888 applet_resource_user_id, is_palma_all_connectable); 2215 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2216
2217 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2218 .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2219
2220 IPC::ResponseBuilder rb{ctx, 2};
2221 rb.Push(ResultSuccess);
2222}
2223
2224void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) {
2225 LOG_WARNING(Service_HID, "(STUBBED) called");
2226
2227 IPC::ResponseBuilder rb{ctx, 2};
2228 rb.Push(ResultSuccess);
2229}
2230
2231void Hid::PairPalma(Kernel::HLERequestContext& ctx) {
2232 IPC::RequestParser rp{ctx};
2233 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2234
2235 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2236
2237 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2238 .PairPalma(connection_handle);
1889 2239
1890 IPC::ResponseBuilder rb{ctx, 2}; 2240 IPC::ResponseBuilder rb{ctx, 2};
1891 rb.Push(ResultSuccess); 2241 rb.Push(ResultSuccess);
@@ -1897,6 +2247,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
1897 2247
1898 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); 2248 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
1899 2249
2250 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2251 .SetPalmaBoostMode(palma_boost_mode);
2252
2253 IPC::ResponseBuilder rb{ctx, 2};
2254 rb.Push(ResultSuccess);
2255}
2256
2257void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
2258 LOG_WARNING(Service_HID, "(STUBBED) called");
2259
2260 IPC::ResponseBuilder rb{ctx, 2};
2261 rb.Push(ResultSuccess);
2262}
2263
2264void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) {
2265 LOG_WARNING(Service_HID, "(STUBBED) called");
2266
2267 IPC::ResponseBuilder rb{ctx, 2};
2268 rb.Push(ResultSuccess);
2269}
2270
2271void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) {
2272 LOG_WARNING(Service_HID, "(STUBBED) called");
2273
2274 IPC::ResponseBuilder rb{ctx, 2};
2275 rb.Push(ResultSuccess);
2276}
2277
2278void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) {
2279 LOG_WARNING(Service_HID, "(STUBBED) called");
2280
1900 IPC::ResponseBuilder rb{ctx, 2}; 2281 IPC::ResponseBuilder rb{ctx, 2};
1901 rb.Push(ResultSuccess); 2282 rb.Push(ResultSuccess);
1902} 2283}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index ac4333022..340d26fdc 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -33,6 +33,7 @@ enum class HidController : std::size_t {
33 NPad, 33 NPad,
34 Gesture, 34 Gesture,
35 ConsoleSixAxisSensor, 35 ConsoleSixAxisSensor,
36 Palma,
36 37
37 MaxControllers, 38 MaxControllers,
38}; 39};
@@ -166,8 +167,36 @@ private:
166 void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 167 void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
167 void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); 168 void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
168 void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); 169 void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
170 void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx);
171 void InitializePalma(Kernel::HLERequestContext& ctx);
172 void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx);
173 void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx);
174 void PlayPalmaActivity(Kernel::HLERequestContext& ctx);
175 void SetPalmaFrModeType(Kernel::HLERequestContext& ctx);
176 void ReadPalmaStep(Kernel::HLERequestContext& ctx);
177 void EnablePalmaStep(Kernel::HLERequestContext& ctx);
178 void ResetPalmaStep(Kernel::HLERequestContext& ctx);
179 void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx);
180 void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx);
181 void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx);
182 void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx);
183 void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx);
184 void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx);
185 void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
186 void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
187 void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
188 void SuspendPalmaFeature(Kernel::HLERequestContext& ctx);
189 void GetPalmaOperationResult(Kernel::HLERequestContext& ctx);
190 void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx);
191 void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx);
169 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); 192 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
193 void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx);
194 void PairPalma(Kernel::HLERequestContext& ctx);
170 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 195 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
196 void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
197 void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx);
198 void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx);
199 void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx);
171 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 200 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
172 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 201 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
173 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); 202 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
index ad223d649..57f1a2a26 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.cpp
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -131,12 +131,12 @@ bool RingController::SetCommand(const std::vector<u8>& data) {
131 case RingConCommands::ReadRepCount: 131 case RingConCommands::ReadRepCount:
132 case RingConCommands::ReadTotalPushCount: 132 case RingConCommands::ReadTotalPushCount:
133 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); 133 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
134 send_command_async_event->GetWritableEvent().Signal(); 134 send_command_async_event->Signal();
135 return true; 135 return true;
136 case RingConCommands::ResetRepCount: 136 case RingConCommands::ResetRepCount:
137 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); 137 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
138 total_rep_count = 0; 138 total_rep_count = 0;
139 send_command_async_event->GetWritableEvent().Signal(); 139 send_command_async_event->Signal();
140 return true; 140 return true;
141 case RingConCommands::SaveCalData: { 141 case RingConCommands::SaveCalData: {
142 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); 142 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
@@ -144,14 +144,14 @@ bool RingController::SetCommand(const std::vector<u8>& data) {
144 SaveCalData save_info{}; 144 SaveCalData save_info{};
145 std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); 145 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
146 user_calibration = save_info.calibration; 146 user_calibration = save_info.calibration;
147 send_command_async_event->GetWritableEvent().Signal(); 147 send_command_async_event->Signal();
148 return true; 148 return true;
149 } 149 }
150 default: 150 default:
151 LOG_ERROR(Service_HID, "Command not implemented {}", command); 151 LOG_ERROR(Service_HID, "Command not implemented {}", command);
152 command = RingConCommands::Error; 152 command = RingConCommands::Error;
153 // Signal a reply to avoid softlocking the game 153 // Signal a reply to avoid softlocking the game
154 send_command_async_event->GetWritableEvent().Signal(); 154 send_command_async_event->Signal();
155 return false; 155 return false;
156 } 156 }
157} 157}
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index c4b44cbf9..6a3453457 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -542,7 +542,8 @@ Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_h
542 542
543Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( 543Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
544 const Core::IrSensor::IrCameraHandle& camera_handle) { 544 const Core::IrSensor::IrCameraHandle& camera_handle) {
545 ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); 545 const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device));
546 ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id");
546 return shared_memory->device[camera_handle.npad_id]; 547 return shared_memory->device[camera_handle.npad_id];
547} 548}
548 549
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
index cf4930794..d63423aff 100644
--- a/src/core/hle/service/hid/irsensor/pointing_processor.h
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -37,10 +37,10 @@ private:
37 u8 pointing_status; 37 u8 pointing_status;
38 INSERT_PADDING_BYTES(3); 38 INSERT_PADDING_BYTES(3);
39 u32 unknown; 39 u32 unknown;
40 float unkown_float1; 40 float unknown_float1;
41 float position_x; 41 float position_x;
42 float position_y; 42 float position_y;
43 float unkown_float2; 43 float unknown_float2;
44 Core::IrSensor::IrsRect window_of_interest; 44 Core::IrSensor::IrsRect window_of_interest;
45 }; 45 };
46 static_assert(sizeof(PointingProcessorMarkerData) == 0x20, 46 static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index 3e317367b..af133af93 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/kernel/k_readable_event.h" 9#include "core/hle/kernel/k_readable_event.h"
10#include "core/hle/kernel/k_resource_limit.h" 10#include "core/hle/kernel/k_resource_limit.h"
11#include "core/hle/kernel/k_scoped_resource_reservation.h" 11#include "core/hle/kernel/k_scoped_resource_reservation.h"
12#include "core/hle/kernel/k_writable_event.h"
13#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
14 13
15namespace Service::KernelHelpers { 14namespace Service::KernelHelpers {
@@ -46,7 +45,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
46 } 45 }
47 46
48 // Initialize the event. 47 // Initialize the event.
49 event->Initialize(std::move(name), process); 48 event->Initialize(process);
50 49
51 // Commit the thread reservation. 50 // Commit the thread reservation.
52 event_reservation.Commit(); 51 event_reservation.Commit();
@@ -59,7 +58,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
59 58
60void ServiceContext::CloseEvent(Kernel::KEvent* event) { 59void ServiceContext::CloseEvent(Kernel::KEvent* event) {
61 event->GetReadableEvent().Close(); 60 event->GetReadableEvent().Close();
62 event->GetWritableEvent().Close(); 61 event->Close();
63} 62}
64 63
65} // namespace Service::KernelHelpers 64} // namespace Service::KernelHelpers
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
new file mode 100644
index 000000000..8f3c04550
--- /dev/null
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
@@ -0,0 +1,633 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ldn/lan_discovery.h"
5#include "core/internal_network/network.h"
6#include "core/internal_network/network_interface.h"
7
8namespace Service::LDN {
9
10LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_)
11 : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_),
12 discovery(discovery_) {}
13
14LanStation::~LanStation() = default;
15
16NodeStatus LanStation::GetStatus() const {
17 return status;
18}
19
20void LanStation::OnClose() {
21 LOG_INFO(Service_LDN, "OnClose {}", node_id);
22 Reset();
23 discovery->UpdateNodes();
24}
25
26void LanStation::Reset() {
27 status = NodeStatus::Disconnected;
28};
29
30void LanStation::OverrideInfo() {
31 bool connected = GetStatus() == NodeStatus::Connected;
32 node_info->node_id = node_id;
33 node_info->is_connected = connected ? 1 : 0;
34}
35
36LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
37 : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
38 room_network{room_network_} {}
39
40LANDiscovery::~LANDiscovery() {
41 if (inited) {
42 Result rc = Finalize();
43 LOG_INFO(Service_LDN, "Finalize: {}", rc.raw);
44 }
45}
46
47void LANDiscovery::InitNetworkInfo() {
48 network_info.common.bssid = GetFakeMac();
49 network_info.common.channel = WifiChannel::Wifi24_6;
50 network_info.common.link_level = LinkLevel::Good;
51 network_info.common.network_type = PackedNetworkType::Ldn;
52 network_info.common.ssid = fake_ssid;
53
54 auto& nodes = network_info.ldn.nodes;
55 for (std::size_t i = 0; i < NodeCountMax; i++) {
56 nodes[i].node_id = static_cast<s8>(i);
57 nodes[i].is_connected = 0;
58 }
59}
60
61void LANDiscovery::InitNodeStateChange() {
62 for (auto& node_update : node_changes) {
63 node_update.state_change = NodeStateChange::None;
64 }
65 for (auto& node_state : node_last_states) {
66 node_state = 0;
67 }
68}
69
70State LANDiscovery::GetState() const {
71 return state;
72}
73
74void LANDiscovery::SetState(State new_state) {
75 state = new_state;
76}
77
78Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
79 if (state == State::AccessPointCreated || state == State::StationConnected) {
80 std::memcpy(&out_network, &network_info, sizeof(network_info));
81 return ResultSuccess;
82 }
83
84 return ResultBadState;
85}
86
87Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
88 std::vector<NodeLatestUpdate>& out_updates,
89 std::size_t buffer_count) {
90 if (buffer_count > NodeCountMax) {
91 return ResultInvalidBufferCount;
92 }
93
94 if (state == State::AccessPointCreated || state == State::StationConnected) {
95 std::memcpy(&out_network, &network_info, sizeof(network_info));
96 for (std::size_t i = 0; i < buffer_count; i++) {
97 out_updates[i].state_change = node_changes[i].state_change;
98 node_changes[i].state_change = NodeStateChange::None;
99 }
100 return ResultSuccess;
101 }
102
103 return ResultBadState;
104}
105
106DisconnectReason LANDiscovery::GetDisconnectReason() const {
107 return disconnect_reason;
108}
109
110Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
111 const ScanFilter& filter) {
112 if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) ||
113 filter.network_type <= NetworkType::All) {
114 if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) {
115 return ResultBadInput;
116 }
117 }
118
119 {
120 std::scoped_lock lock{packet_mutex};
121 scan_results.clear();
122
123 SendBroadcast(Network::LDNPacketType::Scan);
124 }
125
126 LOG_INFO(Service_LDN, "Waiting for scan replies");
127 std::this_thread::sleep_for(std::chrono::seconds(1));
128
129 std::scoped_lock lock{packet_mutex};
130 for (const auto& [key, info] : scan_results) {
131 if (count >= networks.size()) {
132 break;
133 }
134
135 if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) {
136 if (filter.network_id.intent_id.local_communication_id !=
137 info.network_id.intent_id.local_communication_id) {
138 continue;
139 }
140 }
141 if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) {
142 if (filter.network_id.session_id != info.network_id.session_id) {
143 continue;
144 }
145 }
146 if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) {
147 if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) {
148 continue;
149 }
150 }
151 if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) {
152 if (filter.ssid != info.common.ssid) {
153 continue;
154 }
155 }
156 if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) {
157 if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) {
158 continue;
159 }
160 }
161
162 networks[count++] = info;
163 }
164
165 return ResultSuccess;
166}
167
168Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) {
169 std::scoped_lock lock{packet_mutex};
170 const std::size_t size = data.size();
171 if (size > AdvertiseDataSizeMax) {
172 return ResultAdvertiseDataTooLarge;
173 }
174
175 std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size);
176 network_info.ldn.advertise_data_size = static_cast<u16>(size);
177
178 UpdateNodes();
179
180 return ResultSuccess;
181}
182
183Result LANDiscovery::OpenAccessPoint() {
184 std::scoped_lock lock{packet_mutex};
185 disconnect_reason = DisconnectReason::None;
186 if (state == State::None) {
187 return ResultBadState;
188 }
189
190 ResetStations();
191 SetState(State::AccessPointOpened);
192
193 return ResultSuccess;
194}
195
196Result LANDiscovery::CloseAccessPoint() {
197 std::scoped_lock lock{packet_mutex};
198 if (state == State::None) {
199 return ResultBadState;
200 }
201
202 if (state == State::AccessPointCreated) {
203 DestroyNetwork();
204 }
205
206 ResetStations();
207 SetState(State::Initialized);
208
209 return ResultSuccess;
210}
211
212Result LANDiscovery::OpenStation() {
213 std::scoped_lock lock{packet_mutex};
214 disconnect_reason = DisconnectReason::None;
215 if (state == State::None) {
216 return ResultBadState;
217 }
218
219 ResetStations();
220 SetState(State::StationOpened);
221
222 return ResultSuccess;
223}
224
225Result LANDiscovery::CloseStation() {
226 std::scoped_lock lock{packet_mutex};
227 if (state == State::None) {
228 return ResultBadState;
229 }
230
231 if (state == State::StationConnected) {
232 Disconnect();
233 }
234
235 ResetStations();
236 SetState(State::Initialized);
237
238 return ResultSuccess;
239}
240
241Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config,
242 const UserConfig& user_config,
243 const NetworkConfig& network_config) {
244 std::scoped_lock lock{packet_mutex};
245
246 if (state != State::AccessPointOpened) {
247 return ResultBadState;
248 }
249
250 InitNetworkInfo();
251 network_info.ldn.node_count_max = network_config.node_count_max;
252 network_info.ldn.security_mode = security_config.security_mode;
253
254 if (network_config.channel == WifiChannel::Default) {
255 network_info.common.channel = WifiChannel::Wifi24_6;
256 } else {
257 network_info.common.channel = network_config.channel;
258 }
259
260 std::independent_bits_engine<std::mt19937, 64, u64> bits_engine;
261 network_info.network_id.session_id.high = bits_engine();
262 network_info.network_id.session_id.low = bits_engine();
263 network_info.network_id.intent_id = network_config.intent_id;
264
265 NodeInfo& node0 = network_info.ldn.nodes[0];
266 const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version);
267 if (rc2.IsError()) {
268 return ResultAccessPointConnectionFailed;
269 }
270
271 SetState(State::AccessPointCreated);
272
273 InitNodeStateChange();
274 node0.is_connected = 1;
275 UpdateNodes();
276
277 return rc2;
278}
279
280Result LANDiscovery::DestroyNetwork() {
281 for (auto local_ip : connected_clients) {
282 SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip);
283 }
284
285 ResetStations();
286
287 SetState(State::AccessPointOpened);
288 lan_event();
289
290 return ResultSuccess;
291}
292
293Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
294 u16 local_communication_version) {
295 std::scoped_lock lock{packet_mutex};
296 if (network_info_.ldn.node_count == 0) {
297 return ResultInvalidNodeCount;
298 }
299
300 Result rc = GetNodeInfo(node_info, user_config, local_communication_version);
301 if (rc.IsError()) {
302 return ResultConnectionFailed;
303 }
304
305 Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address;
306 std::reverse(std::begin(node_host), std::end(node_host)); // htonl
307 host_ip = node_host;
308 SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip);
309
310 InitNodeStateChange();
311
312 std::this_thread::sleep_for(std::chrono::seconds(1));
313
314 return ResultSuccess;
315}
316
317Result LANDiscovery::Disconnect() {
318 if (host_ip) {
319 SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip);
320 }
321
322 SetState(State::StationOpened);
323 lan_event();
324
325 return ResultSuccess;
326}
327
328Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) {
329 std::scoped_lock lock{packet_mutex};
330 if (inited) {
331 return ResultSuccess;
332 }
333
334 for (auto& station : stations) {
335 station.discovery = this;
336 station.node_info = &network_info.ldn.nodes[station.node_id];
337 station.Reset();
338 }
339
340 connected_clients.clear();
341 lan_event = lan_event_;
342
343 SetState(State::Initialized);
344
345 inited = true;
346 return ResultSuccess;
347}
348
349Result LANDiscovery::Finalize() {
350 std::scoped_lock lock{packet_mutex};
351 Result rc = ResultSuccess;
352
353 if (inited) {
354 if (state == State::AccessPointCreated) {
355 DestroyNetwork();
356 }
357 if (state == State::StationConnected) {
358 Disconnect();
359 }
360
361 ResetStations();
362 inited = false;
363 }
364
365 SetState(State::None);
366
367 return rc;
368}
369
370void LANDiscovery::ResetStations() {
371 for (auto& station : stations) {
372 station.Reset();
373 }
374 connected_clients.clear();
375}
376
377void LANDiscovery::UpdateNodes() {
378 u8 count = 0;
379 for (auto& station : stations) {
380 bool connected = station.GetStatus() == NodeStatus::Connected;
381 if (connected) {
382 count++;
383 }
384 station.OverrideInfo();
385 }
386 network_info.ldn.node_count = count + 1;
387
388 for (auto local_ip : connected_clients) {
389 SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
390 }
391
392 OnNetworkInfoChanged();
393}
394
395void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
396 network_info = info;
397 if (state == State::StationOpened) {
398 SetState(State::StationConnected);
399 }
400 OnNetworkInfoChanged();
401}
402
403void LANDiscovery::OnDisconnectFromHost() {
404 LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state));
405 host_ip = std::nullopt;
406 if (state == State::StationConnected) {
407 SetState(State::StationOpened);
408 lan_event();
409 }
410}
411
412void LANDiscovery::OnNetworkInfoChanged() {
413 if (IsNodeStateChanged()) {
414 lan_event();
415 }
416 return;
417}
418
419Network::IPv4Address LANDiscovery::GetLocalIp() const {
420 Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF};
421 if (auto room_member = room_network.GetRoomMember().lock()) {
422 if (room_member->IsConnected()) {
423 local_ip = room_member->GetFakeIpAddress();
424 }
425 }
426 return local_ip;
427}
428
429template <typename Data>
430void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data,
431 Ipv4Address remote_ip) {
432 Network::LDNPacket packet;
433 packet.type = type;
434
435 packet.broadcast = false;
436 packet.local_ip = GetLocalIp();
437 packet.remote_ip = remote_ip;
438
439 packet.data.resize(sizeof(data));
440 std::memcpy(packet.data.data(), &data, sizeof(data));
441 SendPacket(packet);
442}
443
444void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) {
445 Network::LDNPacket packet;
446 packet.type = type;
447
448 packet.broadcast = false;
449 packet.local_ip = GetLocalIp();
450 packet.remote_ip = remote_ip;
451
452 SendPacket(packet);
453}
454
455template <typename Data>
456void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) {
457 Network::LDNPacket packet;
458 packet.type = type;
459
460 packet.broadcast = true;
461 packet.local_ip = GetLocalIp();
462
463 packet.data.resize(sizeof(data));
464 std::memcpy(packet.data.data(), &data, sizeof(data));
465 SendPacket(packet);
466}
467
468void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
469 Network::LDNPacket packet;
470 packet.type = type;
471
472 packet.broadcast = true;
473 packet.local_ip = GetLocalIp();
474
475 SendPacket(packet);
476}
477
478void LANDiscovery::SendPacket(const Network::LDNPacket& packet) {
479 if (auto room_member = room_network.GetRoomMember().lock()) {
480 if (room_member->IsConnected()) {
481 room_member->SendLdnPacket(packet);
482 }
483 }
484}
485
486void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) {
487 std::scoped_lock lock{packet_mutex};
488 switch (packet.type) {
489 case Network::LDNPacketType::Scan: {
490 LOG_INFO(Frontend, "Scan packet received!");
491 if (state == State::AccessPointCreated) {
492 // Reply to the sender
493 SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip);
494 }
495 break;
496 }
497 case Network::LDNPacketType::ScanResp: {
498 LOG_INFO(Frontend, "ScanResp packet received!");
499
500 NetworkInfo info{};
501 std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
502 scan_results.insert({info.common.bssid, info});
503
504 break;
505 }
506 case Network::LDNPacketType::Connect: {
507 LOG_INFO(Frontend, "Connect packet received!");
508
509 NodeInfo info{};
510 std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
511
512 connected_clients.push_back(packet.local_ip);
513
514 for (LanStation& station : stations) {
515 if (station.status != NodeStatus::Connected) {
516 *station.node_info = info;
517 station.status = NodeStatus::Connected;
518 break;
519 }
520 }
521
522 UpdateNodes();
523
524 break;
525 }
526 case Network::LDNPacketType::Disconnect: {
527 LOG_INFO(Frontend, "Disconnect packet received!");
528
529 connected_clients.erase(
530 std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip),
531 connected_clients.end());
532
533 NodeInfo info{};
534 std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
535
536 for (LanStation& station : stations) {
537 if (station.status == NodeStatus::Connected &&
538 station.node_info->mac_address == info.mac_address) {
539 station.OnClose();
540 break;
541 }
542 }
543
544 break;
545 }
546 case Network::LDNPacketType::DestroyNetwork: {
547 ResetStations();
548 OnDisconnectFromHost();
549 break;
550 }
551 case Network::LDNPacketType::SyncNetwork: {
552 if (state == State::StationOpened || state == State::StationConnected) {
553 LOG_INFO(Frontend, "SyncNetwork packet received!");
554 NetworkInfo info{};
555 std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
556
557 OnSyncNetwork(info);
558 } else {
559 LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
560 }
561
562 break;
563 }
564 default: {
565 LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type));
566 break;
567 }
568 }
569}
570
571bool LANDiscovery::IsNodeStateChanged() {
572 bool changed = false;
573 const auto& nodes = network_info.ldn.nodes;
574 for (int i = 0; i < NodeCountMax; i++) {
575 if (nodes[i].is_connected != node_last_states[i]) {
576 if (nodes[i].is_connected) {
577 node_changes[i].state_change |= NodeStateChange::Connect;
578 } else {
579 node_changes[i].state_change |= NodeStateChange::Disconnect;
580 }
581 node_last_states[i] = nodes[i].is_connected;
582 changed = true;
583 }
584 }
585 return changed;
586}
587
588bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const {
589 const auto flag_value = static_cast<u32>(flag);
590 const auto search_flag_value = static_cast<u32>(search_flag);
591 return (flag_value & search_flag_value) == search_flag_value;
592}
593
594int LANDiscovery::GetStationCount() const {
595 return static_cast<int>(
596 std::count_if(stations.begin(), stations.end(), [](const auto& station) {
597 return station.GetStatus() != NodeStatus::Disconnected;
598 }));
599}
600
601MacAddress LANDiscovery::GetFakeMac() const {
602 MacAddress mac{};
603 mac.raw[0] = 0x02;
604 mac.raw[1] = 0x00;
605
606 const auto ip = GetLocalIp();
607 memcpy(mac.raw.data() + 2, &ip, sizeof(ip));
608
609 return mac;
610}
611
612Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig,
613 u16 localCommunicationVersion) {
614 const auto network_interface = Network::GetSelectedNetworkInterface();
615
616 if (!network_interface) {
617 LOG_ERROR(Service_LDN, "No network interface available");
618 return ResultNoIpAddress;
619 }
620
621 node.mac_address = GetFakeMac();
622 node.is_connected = 1;
623 std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1);
624 node.local_communication_version = localCommunicationVersion;
625
626 Ipv4Address current_address = GetLocalIp();
627 std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
628 node.ipv4_address = current_address;
629
630 return ResultSuccess;
631}
632
633} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
new file mode 100644
index 000000000..3833cd764
--- /dev/null
+++ b/src/core/hle/service/ldn/lan_discovery.h
@@ -0,0 +1,134 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstring>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <optional>
12#include <random>
13#include <span>
14#include <thread>
15#include <unordered_map>
16
17#include "common/logging/log.h"
18#include "common/socket_types.h"
19#include "core/hle/result.h"
20#include "core/hle/service/ldn/ldn_results.h"
21#include "core/hle/service/ldn/ldn_types.h"
22#include "network/network.h"
23
24namespace Service::LDN {
25
26class LANDiscovery;
27
28class LanStation {
29public:
30 LanStation(s8 node_id_, LANDiscovery* discovery_);
31 ~LanStation();
32
33 void OnClose();
34 NodeStatus GetStatus() const;
35 void Reset();
36 void OverrideInfo();
37
38protected:
39 friend class LANDiscovery;
40 NodeInfo* node_info;
41 NodeStatus status;
42 s8 node_id;
43 LANDiscovery* discovery;
44};
45
46class LANDiscovery {
47public:
48 using LanEventFunc = std::function<void()>;
49
50 LANDiscovery(Network::RoomNetwork& room_network_);
51 ~LANDiscovery();
52
53 State GetState() const;
54 void SetState(State new_state);
55
56 Result GetNetworkInfo(NetworkInfo& out_network) const;
57 Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates,
58 std::size_t buffer_count);
59
60 DisconnectReason GetDisconnectReason() const;
61 Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter);
62 Result SetAdvertiseData(std::span<const u8> data);
63
64 Result OpenAccessPoint();
65 Result CloseAccessPoint();
66
67 Result OpenStation();
68 Result CloseStation();
69
70 Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config,
71 const NetworkConfig& network_config);
72 Result DestroyNetwork();
73
74 Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
75 u16 local_communication_version);
76 Result Disconnect();
77
78 Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true);
79 Result Finalize();
80
81 void ReceivePacket(const Network::LDNPacket& packet);
82
83protected:
84 friend class LanStation;
85
86 void InitNetworkInfo();
87 void InitNodeStateChange();
88
89 void ResetStations();
90 void UpdateNodes();
91
92 void OnSyncNetwork(const NetworkInfo& info);
93 void OnDisconnectFromHost();
94 void OnNetworkInfoChanged();
95
96 bool IsNodeStateChanged();
97 bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const;
98 int GetStationCount() const;
99 MacAddress GetFakeMac() const;
100 Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config,
101 u16 local_communication_version);
102
103 Network::IPv4Address GetLocalIp() const;
104 template <typename Data>
105 void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip);
106 void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip);
107 template <typename Data>
108 void SendBroadcast(Network::LDNPacketType type, const Data& data);
109 void SendBroadcast(Network::LDNPacketType type);
110 void SendPacket(const Network::LDNPacket& packet);
111
112 static const LanEventFunc empty_func;
113 static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"};
114
115 bool inited{};
116 std::mutex packet_mutex;
117 std::array<LanStation, StationCountMax> stations;
118 std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
119 std::array<u8, NodeCountMax> node_last_states{};
120 std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
121 NodeInfo node_info{};
122 NetworkInfo network_info{};
123 State state{State::None};
124 DisconnectReason disconnect_reason{DisconnectReason::None};
125
126 // TODO (flTobi): Should this be an std::set?
127 std::vector<Ipv4Address> connected_clients;
128 std::optional<Ipv4Address> host_ip;
129
130 LanEventFunc lan_event;
131
132 Network::RoomNetwork& room_network;
133};
134} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index c11daff54..6df563136 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -4,11 +4,13 @@
4#include <memory> 4#include <memory>
5 5
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/service/ldn/lan_discovery.h"
7#include "core/hle/service/ldn/ldn.h" 8#include "core/hle/service/ldn/ldn.h"
8#include "core/hle/service/ldn/ldn_results.h" 9#include "core/hle/service/ldn/ldn_results.h"
9#include "core/hle/service/ldn/ldn_types.h" 10#include "core/hle/service/ldn/ldn_types.h"
10#include "core/internal_network/network.h" 11#include "core/internal_network/network.h"
11#include "core/internal_network/network_interface.h" 12#include "core/internal_network/network_interface.h"
13#include "network/network.h"
12 14
13// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent 15// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
14#undef CreateEvent 16#undef CreateEvent
@@ -105,13 +107,13 @@ class IUserLocalCommunicationService final
105public: 107public:
106 explicit IUserLocalCommunicationService(Core::System& system_) 108 explicit IUserLocalCommunicationService(Core::System& system_)
107 : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, 109 : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
108 service_context{system, "IUserLocalCommunicationService"}, room_network{ 110 service_context{system, "IUserLocalCommunicationService"},
109 system_.GetRoomNetwork()} { 111 room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
110 // clang-format off 112 // clang-format off
111 static const FunctionInfo functions[] = { 113 static const FunctionInfo functions[] = {
112 {0, &IUserLocalCommunicationService::GetState, "GetState"}, 114 {0, &IUserLocalCommunicationService::GetState, "GetState"},
113 {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, 115 {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
114 {2, nullptr, "GetIpv4Address"}, 116 {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"},
115 {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, 117 {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
116 {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, 118 {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
117 {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, 119 {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
@@ -119,7 +121,7 @@ public:
119 {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, 121 {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
120 {102, &IUserLocalCommunicationService::Scan, "Scan"}, 122 {102, &IUserLocalCommunicationService::Scan, "Scan"},
121 {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, 123 {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
122 {104, nullptr, "SetWirelessControllerRestriction"}, 124 {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"},
123 {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, 125 {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
124 {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, 126 {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
125 {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, 127 {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
@@ -148,16 +150,30 @@ public:
148 } 150 }
149 151
150 ~IUserLocalCommunicationService() { 152 ~IUserLocalCommunicationService() {
153 if (is_initialized) {
154 if (auto room_member = room_network.GetRoomMember().lock()) {
155 room_member->Unbind(ldn_packet_received);
156 }
157 }
158
151 service_context.CloseEvent(state_change_event); 159 service_context.CloseEvent(state_change_event);
152 } 160 }
153 161
162 /// Callback to parse and handle a received LDN packet.
163 void OnLDNPacketReceived(const Network::LDNPacket& packet) {
164 lan_discovery.ReceivePacket(packet);
165 }
166
154 void OnEventFired() { 167 void OnEventFired() {
155 state_change_event->GetWritableEvent().Signal(); 168 state_change_event->Signal();
156 } 169 }
157 170
158 void GetState(Kernel::HLERequestContext& ctx) { 171 void GetState(Kernel::HLERequestContext& ctx) {
159 State state = State::Error; 172 State state = State::Error;
160 LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); 173
174 if (is_initialized) {
175 state = lan_discovery.GetState();
176 }
161 177
162 IPC::ResponseBuilder rb{ctx, 3}; 178 IPC::ResponseBuilder rb{ctx, 3};
163 rb.Push(ResultSuccess); 179 rb.Push(ResultSuccess);
@@ -175,7 +191,7 @@ public:
175 } 191 }
176 192
177 NetworkInfo network_info{}; 193 NetworkInfo network_info{};
178 const auto rc = ResultSuccess; 194 const auto rc = lan_discovery.GetNetworkInfo(network_info);
179 if (rc.IsError()) { 195 if (rc.IsError()) {
180 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); 196 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
181 IPC::ResponseBuilder rb{ctx, 2}; 197 IPC::ResponseBuilder rb{ctx, 2};
@@ -183,28 +199,50 @@ public:
183 return; 199 return;
184 } 200 }
185 201
186 LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
187 network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
188
189 ctx.WriteBuffer<NetworkInfo>(network_info); 202 ctx.WriteBuffer<NetworkInfo>(network_info);
190 IPC::ResponseBuilder rb{ctx, 2}; 203 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(rc); 204 rb.Push(ResultSuccess);
192 } 205 }
193 206
194 void GetDisconnectReason(Kernel::HLERequestContext& ctx) { 207 void GetIpv4Address(Kernel::HLERequestContext& ctx) {
195 const auto disconnect_reason = DisconnectReason::None; 208 const auto network_interface = Network::GetSelectedNetworkInterface();
209
210 if (!network_interface) {
211 LOG_ERROR(Service_LDN, "No network interface available");
212 IPC::ResponseBuilder rb{ctx, 2};
213 rb.Push(ResultNoIpAddress);
214 return;
215 }
196 216
197 LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); 217 Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)};
218 Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)};
219
220 // When we're connected to a room, spoof the hosts IP address
221 if (auto room_member = room_network.GetRoomMember().lock()) {
222 if (room_member->IsConnected()) {
223 current_address = room_member->GetFakeIpAddress();
224 }
225 }
226
227 std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
228 std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl
229
230 IPC::ResponseBuilder rb{ctx, 4};
231 rb.Push(ResultSuccess);
232 rb.PushRaw(current_address);
233 rb.PushRaw(subnet_mask);
234 }
198 235
236 void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
199 IPC::ResponseBuilder rb{ctx, 3}; 237 IPC::ResponseBuilder rb{ctx, 3};
200 rb.Push(ResultSuccess); 238 rb.Push(ResultSuccess);
201 rb.PushEnum(disconnect_reason); 239 rb.PushEnum(lan_discovery.GetDisconnectReason());
202 } 240 }
203 241
204 void GetSecurityParameter(Kernel::HLERequestContext& ctx) { 242 void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
205 SecurityParameter security_parameter{}; 243 SecurityParameter security_parameter{};
206 NetworkInfo info{}; 244 NetworkInfo info{};
207 const Result rc = ResultSuccess; 245 const Result rc = lan_discovery.GetNetworkInfo(info);
208 246
209 if (rc.IsError()) { 247 if (rc.IsError()) {
210 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); 248 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
@@ -217,8 +255,6 @@ public:
217 std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), 255 std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
218 sizeof(SecurityParameter::data)); 256 sizeof(SecurityParameter::data));
219 257
220 LOG_WARNING(Service_LDN, "(STUBBED) called");
221
222 IPC::ResponseBuilder rb{ctx, 10}; 258 IPC::ResponseBuilder rb{ctx, 10};
223 rb.Push(rc); 259 rb.Push(rc);
224 rb.PushRaw<SecurityParameter>(security_parameter); 260 rb.PushRaw<SecurityParameter>(security_parameter);
@@ -227,7 +263,7 @@ public:
227 void GetNetworkConfig(Kernel::HLERequestContext& ctx) { 263 void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
228 NetworkConfig config{}; 264 NetworkConfig config{};
229 NetworkInfo info{}; 265 NetworkInfo info{};
230 const Result rc = ResultSuccess; 266 const Result rc = lan_discovery.GetNetworkInfo(info);
231 267
232 if (rc.IsError()) { 268 if (rc.IsError()) {
233 LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); 269 LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
@@ -241,12 +277,6 @@ public:
241 config.node_count_max = info.ldn.node_count_max; 277 config.node_count_max = info.ldn.node_count_max;
242 config.local_communication_version = info.ldn.nodes[0].local_communication_version; 278 config.local_communication_version = info.ldn.nodes[0].local_communication_version;
243 279
244 LOG_WARNING(Service_LDN,
245 "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
246 "local_communication_version={}",
247 config.intent_id.local_communication_id, config.intent_id.scene_id,
248 config.channel, config.node_count_max, config.local_communication_version);
249
250 IPC::ResponseBuilder rb{ctx, 10}; 280 IPC::ResponseBuilder rb{ctx, 10};
251 rb.Push(rc); 281 rb.Push(rc);
252 rb.PushRaw<NetworkConfig>(config); 282 rb.PushRaw<NetworkConfig>(config);
@@ -265,17 +295,17 @@ public:
265 const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); 295 const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
266 296
267 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { 297 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
268 LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, 298 LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
269 node_buffer_count); 299 node_buffer_count);
270 IPC::ResponseBuilder rb{ctx, 2}; 300 IPC::ResponseBuilder rb{ctx, 2};
271 rb.Push(ResultBadInput); 301 rb.Push(ResultBadInput);
272 return; 302 return;
273 } 303 }
274 304
275 NetworkInfo info; 305 NetworkInfo info{};
276 std::vector<NodeLatestUpdate> latest_update(node_buffer_count); 306 std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
277 307
278 const auto rc = ResultSuccess; 308 const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size());
279 if (rc.IsError()) { 309 if (rc.IsError()) {
280 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); 310 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
281 IPC::ResponseBuilder rb{ctx, 2}; 311 IPC::ResponseBuilder rb{ctx, 2};
@@ -283,9 +313,6 @@ public:
283 return; 313 return;
284 } 314 }
285 315
286 LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
287 info.common.ssid.GetStringValue(), info.ldn.node_count);
288
289 ctx.WriteBuffer(info, 0); 316 ctx.WriteBuffer(info, 0);
290 ctx.WriteBuffer(latest_update, 1); 317 ctx.WriteBuffer(latest_update, 1);
291 318
@@ -317,92 +344,78 @@ public:
317 344
318 u16 count = 0; 345 u16 count = 0;
319 std::vector<NetworkInfo> network_infos(network_info_size); 346 std::vector<NetworkInfo> network_infos(network_info_size);
347 Result rc = lan_discovery.Scan(network_infos, count, scan_filter);
320 348
321 LOG_WARNING(Service_LDN, 349 LOG_INFO(Service_LDN,
322 "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", 350 "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}",
323 channel, scan_filter.flag, scan_filter.network_type); 351 channel, scan_filter.flag, scan_filter.network_type, is_private);
324 352
325 ctx.WriteBuffer(network_infos); 353 ctx.WriteBuffer(network_infos);
326 354
327 IPC::ResponseBuilder rb{ctx, 3}; 355 IPC::ResponseBuilder rb{ctx, 3};
328 rb.Push(ResultSuccess); 356 rb.Push(rc);
329 rb.Push<u32>(count); 357 rb.Push<u32>(count);
330 } 358 }
331 359
332 void OpenAccessPoint(Kernel::HLERequestContext& ctx) { 360 void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) {
333 LOG_WARNING(Service_LDN, "(STUBBED) called"); 361 LOG_WARNING(Service_LDN, "(STUBBED) called");
334 362
335 IPC::ResponseBuilder rb{ctx, 2}; 363 IPC::ResponseBuilder rb{ctx, 2};
336 rb.Push(ResultSuccess); 364 rb.Push(ResultSuccess);
337 } 365 }
338 366
367 void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
368 LOG_INFO(Service_LDN, "called");
369
370 IPC::ResponseBuilder rb{ctx, 2};
371 rb.Push(lan_discovery.OpenAccessPoint());
372 }
373
339 void CloseAccessPoint(Kernel::HLERequestContext& ctx) { 374 void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
340 LOG_WARNING(Service_LDN, "(STUBBED) called"); 375 LOG_INFO(Service_LDN, "called");
341 376
342 IPC::ResponseBuilder rb{ctx, 2}; 377 IPC::ResponseBuilder rb{ctx, 2};
343 rb.Push(ResultSuccess); 378 rb.Push(lan_discovery.CloseAccessPoint());
344 } 379 }
345 380
346 void CreateNetwork(Kernel::HLERequestContext& ctx) { 381 void CreateNetwork(Kernel::HLERequestContext& ctx) {
347 IPC::RequestParser rp{ctx}; 382 LOG_INFO(Service_LDN, "called");
348 struct Parameters {
349 SecurityConfig security_config;
350 UserConfig user_config;
351 INSERT_PADDING_WORDS_NOINIT(1);
352 NetworkConfig network_config;
353 };
354 static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
355 383
356 const auto parameters{rp.PopRaw<Parameters>()}; 384 CreateNetworkImpl(ctx);
385 }
357 386
358 LOG_WARNING(Service_LDN, 387 void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
359 "(STUBBED) called, passphrase_size={}, security_mode={}, " 388 LOG_INFO(Service_LDN, "called");
360 "local_communication_version={}",
361 parameters.security_config.passphrase_size,
362 parameters.security_config.security_mode,
363 parameters.network_config.local_communication_version);
364 389
365 IPC::ResponseBuilder rb{ctx, 2}; 390 CreateNetworkImpl(ctx, true);
366 rb.Push(ResultSuccess);
367 } 391 }
368 392
369 void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { 393 void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
370 IPC::RequestParser rp{ctx}; 394 IPC::RequestParser rp{ctx};
371 struct Parameters {
372 SecurityConfig security_config;
373 SecurityParameter security_parameter;
374 UserConfig user_config;
375 NetworkConfig network_config;
376 };
377 static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
378
379 const auto parameters{rp.PopRaw<Parameters>()};
380 395
381 LOG_WARNING(Service_LDN, 396 const auto security_config{rp.PopRaw<SecurityConfig>()};
382 "(STUBBED) called, passphrase_size={}, security_mode={}, " 397 [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
383 "local_communication_version={}", 398 : SecurityParameter{}};
384 parameters.security_config.passphrase_size, 399 const auto user_config{rp.PopRaw<UserConfig>()};
385 parameters.security_config.security_mode, 400 rp.Pop<u32>(); // Padding
386 parameters.network_config.local_communication_version); 401 const auto network_Config{rp.PopRaw<NetworkConfig>()};
387 402
388 IPC::ResponseBuilder rb{ctx, 2}; 403 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(ResultSuccess); 404 rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config));
390 } 405 }
391 406
392 void DestroyNetwork(Kernel::HLERequestContext& ctx) { 407 void DestroyNetwork(Kernel::HLERequestContext& ctx) {
393 LOG_WARNING(Service_LDN, "(STUBBED) called"); 408 LOG_INFO(Service_LDN, "called");
394 409
395 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
396 rb.Push(ResultSuccess); 411 rb.Push(lan_discovery.DestroyNetwork());
397 } 412 }
398 413
399 void SetAdvertiseData(Kernel::HLERequestContext& ctx) { 414 void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
400 std::vector<u8> read_buffer = ctx.ReadBuffer(); 415 std::vector<u8> read_buffer = ctx.ReadBuffer();
401 416
402 LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
403
404 IPC::ResponseBuilder rb{ctx, 2}; 417 IPC::ResponseBuilder rb{ctx, 2};
405 rb.Push(ResultSuccess); 418 rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
406 } 419 }
407 420
408 void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { 421 void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
@@ -420,17 +433,17 @@ public:
420 } 433 }
421 434
422 void OpenStation(Kernel::HLERequestContext& ctx) { 435 void OpenStation(Kernel::HLERequestContext& ctx) {
423 LOG_WARNING(Service_LDN, "(STUBBED) called"); 436 LOG_INFO(Service_LDN, "called");
424 437
425 IPC::ResponseBuilder rb{ctx, 2}; 438 IPC::ResponseBuilder rb{ctx, 2};
426 rb.Push(ResultSuccess); 439 rb.Push(lan_discovery.OpenStation());
427 } 440 }
428 441
429 void CloseStation(Kernel::HLERequestContext& ctx) { 442 void CloseStation(Kernel::HLERequestContext& ctx) {
430 LOG_WARNING(Service_LDN, "(STUBBED) called"); 443 LOG_INFO(Service_LDN, "called");
431 444
432 IPC::ResponseBuilder rb{ctx, 2}; 445 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(ResultSuccess); 446 rb.Push(lan_discovery.CloseStation());
434 } 447 }
435 448
436 void Connect(Kernel::HLERequestContext& ctx) { 449 void Connect(Kernel::HLERequestContext& ctx) {
@@ -445,16 +458,13 @@ public:
445 458
446 const auto parameters{rp.PopRaw<Parameters>()}; 459 const auto parameters{rp.PopRaw<Parameters>()};
447 460
448 LOG_WARNING(Service_LDN, 461 LOG_INFO(Service_LDN,
449 "(STUBBED) called, passphrase_size={}, security_mode={}, " 462 "called, passphrase_size={}, security_mode={}, "
450 "local_communication_version={}", 463 "local_communication_version={}",
451 parameters.security_config.passphrase_size, 464 parameters.security_config.passphrase_size,
452 parameters.security_config.security_mode, 465 parameters.security_config.security_mode, parameters.local_communication_version);
453 parameters.local_communication_version);
454 466
455 const std::vector<u8> read_buffer = ctx.ReadBuffer(); 467 const std::vector<u8> read_buffer = ctx.ReadBuffer();
456 NetworkInfo network_info{};
457
458 if (read_buffer.size() != sizeof(NetworkInfo)) { 468 if (read_buffer.size() != sizeof(NetworkInfo)) {
459 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); 469 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
460 IPC::ResponseBuilder rb{ctx, 2}; 470 IPC::ResponseBuilder rb{ctx, 2};
@@ -462,40 +472,47 @@ public:
462 return; 472 return;
463 } 473 }
464 474
475 NetworkInfo network_info{};
465 std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); 476 std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
466 477
467 IPC::ResponseBuilder rb{ctx, 2}; 478 IPC::ResponseBuilder rb{ctx, 2};
468 rb.Push(ResultSuccess); 479 rb.Push(lan_discovery.Connect(network_info, parameters.user_config,
480 static_cast<u16>(parameters.local_communication_version)));
469 } 481 }
470 482
471 void Disconnect(Kernel::HLERequestContext& ctx) { 483 void Disconnect(Kernel::HLERequestContext& ctx) {
472 LOG_WARNING(Service_LDN, "(STUBBED) called"); 484 LOG_INFO(Service_LDN, "called");
473 485
474 IPC::ResponseBuilder rb{ctx, 2}; 486 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(ResultSuccess); 487 rb.Push(lan_discovery.Disconnect());
476 } 488 }
477 void Initialize(Kernel::HLERequestContext& ctx) {
478 LOG_WARNING(Service_LDN, "(STUBBED) called");
479 489
490 void Initialize(Kernel::HLERequestContext& ctx) {
480 const auto rc = InitializeImpl(ctx); 491 const auto rc = InitializeImpl(ctx);
492 if (rc.IsError()) {
493 LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
494 }
481 495
482 IPC::ResponseBuilder rb{ctx, 2}; 496 IPC::ResponseBuilder rb{ctx, 2};
483 rb.Push(rc); 497 rb.Push(rc);
484 } 498 }
485 499
486 void Finalize(Kernel::HLERequestContext& ctx) { 500 void Finalize(Kernel::HLERequestContext& ctx) {
487 LOG_WARNING(Service_LDN, "(STUBBED) called"); 501 if (auto room_member = room_network.GetRoomMember().lock()) {
502 room_member->Unbind(ldn_packet_received);
503 }
488 504
489 is_initialized = false; 505 is_initialized = false;
490 506
491 IPC::ResponseBuilder rb{ctx, 2}; 507 IPC::ResponseBuilder rb{ctx, 2};
492 rb.Push(ResultSuccess); 508 rb.Push(lan_discovery.Finalize());
493 } 509 }
494 510
495 void Initialize2(Kernel::HLERequestContext& ctx) { 511 void Initialize2(Kernel::HLERequestContext& ctx) {
496 LOG_WARNING(Service_LDN, "(STUBBED) called");
497
498 const auto rc = InitializeImpl(ctx); 512 const auto rc = InitializeImpl(ctx);
513 if (rc.IsError()) {
514 LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
515 }
499 516
500 IPC::ResponseBuilder rb{ctx, 2}; 517 IPC::ResponseBuilder rb{ctx, 2};
501 rb.Push(rc); 518 rb.Push(rc);
@@ -508,14 +525,26 @@ public:
508 return ResultAirplaneModeEnabled; 525 return ResultAirplaneModeEnabled;
509 } 526 }
510 527
528 if (auto room_member = room_network.GetRoomMember().lock()) {
529 ldn_packet_received = room_member->BindOnLdnPacketReceived(
530 [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
531 } else {
532 LOG_ERROR(Service_LDN, "Couldn't bind callback!");
533 return ResultAirplaneModeEnabled;
534 }
535
536 lan_discovery.Initialize([&]() { OnEventFired(); });
511 is_initialized = true; 537 is_initialized = true;
512 // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented 538 return ResultSuccess;
513 return ResultAirplaneModeEnabled;
514 } 539 }
515 540
516 KernelHelpers::ServiceContext service_context; 541 KernelHelpers::ServiceContext service_context;
517 Kernel::KEvent* state_change_event; 542 Kernel::KEvent* state_change_event;
518 Network::RoomNetwork& room_network; 543 Network::RoomNetwork& room_network;
544 LANDiscovery lan_discovery;
545
546 // Callback identifier for the OnLDNPacketReceived event.
547 Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
519 548
520 bool is_initialized{}; 549 bool is_initialized{};
521}; 550};
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
index 6231e936d..44c2c773b 100644
--- a/src/core/hle/service/ldn/ldn_types.h
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -31,6 +31,8 @@ enum class NodeStateChange : u8 {
31 DisconnectAndConnect, 31 DisconnectAndConnect,
32}; 32};
33 33
34DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange)
35
34enum class ScanFilterFlag : u32 { 36enum class ScanFilterFlag : u32 {
35 None = 0, 37 None = 0,
36 LocalCommunicationId = 1 << 0, 38 LocalCommunicationId = 1 << 0,
@@ -100,13 +102,13 @@ enum class AcceptPolicy : u8 {
100 102
101enum class WifiChannel : s16 { 103enum class WifiChannel : s16 {
102 Default = 0, 104 Default = 0,
103 wifi24_1 = 1, 105 Wifi24_1 = 1,
104 wifi24_6 = 6, 106 Wifi24_6 = 6,
105 wifi24_11 = 11, 107 Wifi24_11 = 11,
106 wifi50_36 = 36, 108 Wifi50_36 = 36,
107 wifi50_40 = 40, 109 Wifi50_40 = 40,
108 wifi50_44 = 44, 110 Wifi50_44 = 44,
109 wifi50_48 = 48, 111 Wifi50_48 = 48,
110}; 112};
111 113
112enum class LinkLevel : s8 { 114enum class LinkLevel : s8 {
@@ -116,6 +118,11 @@ enum class LinkLevel : s8 {
116 Excellent, 118 Excellent,
117}; 119};
118 120
121enum class NodeStatus : u8 {
122 Disconnected,
123 Connected,
124};
125
119struct NodeLatestUpdate { 126struct NodeLatestUpdate {
120 NodeStateChange state_change; 127 NodeStateChange state_change;
121 INSERT_PADDING_BYTES(0x7); // Unknown 128 INSERT_PADDING_BYTES(0x7); // Unknown
@@ -150,7 +157,7 @@ struct Ssid {
150 157
151 Ssid() = default; 158 Ssid() = default;
152 159
153 explicit Ssid(std::string_view data) { 160 constexpr explicit Ssid(std::string_view data) {
154 length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); 161 length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
155 data.copy(raw.data(), length); 162 data.copy(raw.data(), length);
156 raw[length] = 0; 163 raw[length] = 0;
@@ -159,19 +166,18 @@ struct Ssid {
159 std::string GetStringValue() const { 166 std::string GetStringValue() const {
160 return std::string(raw.data()); 167 return std::string(raw.data());
161 } 168 }
162};
163static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
164 169
165struct Ipv4Address { 170 bool operator==(const Ssid& b) const {
166 union { 171 return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0);
167 u32 raw{}; 172 }
168 std::array<u8, 4> bytes;
169 };
170 173
171 std::string GetStringValue() const { 174 bool operator!=(const Ssid& b) const {
172 return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); 175 return !operator==(b);
173 } 176 }
174}; 177};
178static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
179
180using Ipv4Address = std::array<u8, 4>;
175static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); 181static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
176 182
177struct MacAddress { 183struct MacAddress {
@@ -181,6 +187,14 @@ struct MacAddress {
181}; 187};
182static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); 188static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
183 189
190struct MACAddressHash {
191 size_t operator()(const MacAddress& address) const {
192 u64 value{};
193 std::memcpy(&value, address.raw.data(), sizeof(address.raw));
194 return value;
195 }
196};
197
184struct ScanFilter { 198struct ScanFilter {
185 NetworkId network_id; 199 NetworkId network_id;
186 NetworkType network_type; 200 NetworkType network_type;
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index becd6d1b9..652441bc2 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -290,7 +290,7 @@ public:
290 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; 290 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
291 const auto start_info{page_table.QueryInfo(start - 1)}; 291 const auto start_info{page_table.QueryInfo(start - 1)};
292 292
293 if (start_info.state != Kernel::KMemoryState::Free) { 293 if (start_info.GetState() != Kernel::KMemoryState::Free) {
294 return {}; 294 return {};
295 } 295 }
296 296
@@ -300,7 +300,7 @@ public:
300 300
301 const auto end_info{page_table.QueryInfo(start + size)}; 301 const auto end_info{page_table.QueryInfo(start + size)};
302 302
303 if (end_info.state != Kernel::KMemoryState::Free) { 303 if (end_info.GetState() != Kernel::KMemoryState::Free) {
304 return {}; 304 return {};
305 } 305 }
306 306
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index c484a9c8d..3a2fe938f 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -427,12 +427,11 @@ CharInfo MiiManager::BuildDefault(std::size_t index) {
427 return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); 427 return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
428} 428}
429 429
430CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { 430CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
431 Service::Mii::MiiManager manager; 431 Service::Mii::MiiManager manager;
432 auto mii = manager.BuildDefault(0); 432 auto mii = manager.BuildDefault(0);
433 433
434 // Check if mii data exist 434 if (!ValidateV3Info(mii_v3)) {
435 if (mii_v3.mii_name[0] == 0) {
436 return mii; 435 return mii;
437 } 436 }
438 437
@@ -443,8 +442,15 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
443 mii.height = mii_v3.height; 442 mii.height = mii_v3.height;
444 mii.build = mii_v3.build; 443 mii.build = mii_v3.build;
445 444
446 memset(mii.name.data(), 0, sizeof(mii.name)); 445 // Copy name until string terminator
447 memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); 446 mii.name = {};
447 for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
448 mii.name[index] = mii_v3.mii_name[index];
449 if (mii.name[index] == 0) {
450 break;
451 }
452 }
453
448 mii.font_region = mii_v3.region_information.character_set; 454 mii.font_region = mii_v3.region_information.character_set;
449 455
450 mii.faceline_type = mii_v3.appearance_bits1.face_shape; 456 mii.faceline_type = mii_v3.appearance_bits1.face_shape;
@@ -504,6 +510,151 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
504 return mii; 510 return mii;
505} 511}
506 512
513Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
514 Service::Mii::MiiManager manager;
515 Ver3StoreData mii_v3{};
516
517 // TODO: We are ignoring a bunch of data from the mii_v3
518
519 mii_v3.version = 1;
520 mii_v3.mii_information.gender.Assign(mii.gender);
521 mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
522 mii_v3.height = mii.height;
523 mii_v3.build = mii.build;
524
525 // Copy name until string terminator
526 mii_v3.mii_name = {};
527 for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
528 mii_v3.mii_name[index] = mii.name[index];
529 if (mii_v3.mii_name[index] == 0) {
530 break;
531 }
532 }
533
534 mii_v3.region_information.character_set.Assign(mii.font_region);
535
536 mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
537 mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color);
538 mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
539 mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
540
541 mii_v3.hair_style = mii.hair_type;
542 mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color);
543 mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
544
545 mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
546 mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color);
547 mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
548 mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
549 mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
550 mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
551 mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
552
553 mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
554 mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color);
555 mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
556 mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
557 mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
558 mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
559 mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
560
561 mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
562 mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
563 mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
564
565 mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
566 mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color);
567 mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
568 mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
569 mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
570
571 mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
572 mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
573 mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
574
575 mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
576 mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color);
577
578 mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type);
579 mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color);
580 mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
581 mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
582
583 mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
584 mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
585 mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
586 mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
587
588 // TODO: Validate mii_v3 data
589
590 return mii_v3;
591}
592
593bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
594 bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
595
596 is_valid = is_valid && (mii_v3.mii_name[0] != 0);
597
598 is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
599 is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
600 is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
601 is_valid = is_valid && (mii_v3.height < 128);
602 is_valid = is_valid && (mii_v3.build < 128);
603
604 is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
605 is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
606 is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
607 is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
608
609 is_valid = is_valid && (mii_v3.hair_style < 132);
610 is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
611
612 is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
613 is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
614 is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
615 is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
616 is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
617 is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
618 is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
619
620 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
621 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
622 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
623 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
624 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
625 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
626 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
627
628 is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
629 is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
630 is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
631
632 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
633 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
634 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
635 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
636 is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
637
638 is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
639 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
640 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
641
642 is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
643 is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
644
645 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
646 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
647 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
648 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
649
650 is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
651 is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
652 is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
653 is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
654
655 return is_valid;
656}
657
507ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { 658ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
508 std::vector<MiiInfoElement> result; 659 std::vector<MiiInfoElement> result;
509 660
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index d847de0bd..83ad3d343 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -22,7 +22,9 @@ public:
22 ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); 22 ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
23 CharInfo BuildRandom(Age age, Gender gender, Race race); 23 CharInfo BuildRandom(Age age, Gender gender, Race race);
24 CharInfo BuildDefault(std::size_t index); 24 CharInfo BuildDefault(std::size_t index);
25 CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; 25 CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
26 Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
27 bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
26 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); 28 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
27 Result GetIndex(const CharInfo& info, u32& index); 29 Result GetIndex(const CharInfo& info, u32& index);
28 30
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 13a843a28..046c5f18f 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -106,10 +106,10 @@ public:
106 {1, &IUser::FinalizeOld, "FinalizeOld"}, 106 {1, &IUser::FinalizeOld, "FinalizeOld"},
107 {2, &IUser::GetStateOld, "GetStateOld"}, 107 {2, &IUser::GetStateOld, "GetStateOld"},
108 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, 108 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
109 {400, nullptr, "Initialize"}, 109 {400, &IUser::InitializeOld, "Initialize"},
110 {401, nullptr, "Finalize"}, 110 {401, &IUser::FinalizeOld, "Finalize"},
111 {402, nullptr, "GetState"}, 111 {402, &IUser::GetStateOld, "GetState"},
112 {403, nullptr, "IsNfcEnabled"}, 112 {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
113 {404, nullptr, "ListDevices"}, 113 {404, nullptr, "ListDevices"},
114 {405, nullptr, "GetDeviceState"}, 114 {405, nullptr, "GetDeviceState"},
115 {406, nullptr, "GetNpadId"}, 115 {406, nullptr, "GetNpadId"},
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index 31dd3a307..167e29572 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -9,6 +9,7 @@
9#include <mbedtls/hmac_drbg.h> 9#include <mbedtls/hmac_drbg.h>
10 10
11#include "common/fs/file.h" 11#include "common/fs/file.h"
12#include "common/fs/fs.h"
12#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "core/hle/service/mii/mii_manager.h" 15#include "core/hle/service/mii/mii_manager.h"
@@ -20,14 +21,15 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
20 const auto& amiibo_data = ntag_file.user_memory; 21 const auto& amiibo_data = ntag_file.user_memory;
21 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); 22 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
22 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); 23 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
23 LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); 24 LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter));
24 25
25 LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); 26 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
26 LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); 27 LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
27 LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); 28 LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
28 LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); 29 LOG_DEBUG(Service_NFP, "model_number=0x{0:x}",
29 LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); 30 static_cast<u16>(amiibo_data.model_info.model_number));
30 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); 31 LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
32 LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type);
31 33
32 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); 34 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
33 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); 35 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0);
@@ -35,11 +37,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
35 37
36 // Validate UUID 38 // Validate UUID
37 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` 39 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
38 if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) { 40 if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
41 ntag_file.uuid.uid[3]) {
39 return false; 42 return false;
40 } 43 }
41 if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) != 44 if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
42 ntag_file.uuid[8]) { 45 ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
43 return false; 46 return false;
44 } 47 }
45 48
@@ -53,11 +56,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
53 if (amiibo_data.constant_value != 0xA5) { 56 if (amiibo_data.constant_value != 0xA5) {
54 return false; 57 return false;
55 } 58 }
56 if (amiibo_data.model_info.constant_value != 0x02) { 59 if (amiibo_data.model_info.tag_type != PackedTagType::Type2) {
60 return false;
61 }
62 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {
57 return false; 63 return false;
58 } 64 }
59 // dynamic_lock value apparently is not constant
60 // ntag_file.dynamic_lock == 0x0F0001
61 if (ntag_file.CFG0 != 0x04000000U) { 65 if (ntag_file.CFG0 != 0x04000000U) {
62 return false; 66 return false;
63 } 67 }
@@ -70,7 +74,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
70NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { 74NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
71 NTAG215File encoded_data{}; 75 NTAG215File encoded_data{};
72 76
73 memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2)); 77 encoded_data.uid = nfc_data.uuid.uid;
78 encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
74 encoded_data.static_lock = nfc_data.static_lock; 79 encoded_data.static_lock = nfc_data.static_lock;
75 encoded_data.compability_container = nfc_data.compability_container; 80 encoded_data.compability_container = nfc_data.compability_container;
76 encoded_data.hmac_data = nfc_data.user_memory.hmac_data; 81 encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
@@ -82,10 +87,10 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
82 encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; 87 encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter;
83 encoded_data.application_area_id = nfc_data.user_memory.application_area_id; 88 encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
84 encoded_data.unknown = nfc_data.user_memory.unknown; 89 encoded_data.unknown = nfc_data.user_memory.unknown;
85 encoded_data.hash = nfc_data.user_memory.hash; 90 encoded_data.unknown2 = nfc_data.user_memory.unknown2;
86 encoded_data.application_area = nfc_data.user_memory.application_area; 91 encoded_data.application_area = nfc_data.user_memory.application_area;
87 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; 92 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
88 memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid)); 93 encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
89 encoded_data.model_info = nfc_data.user_memory.model_info; 94 encoded_data.model_info = nfc_data.user_memory.model_info;
90 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; 95 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
91 encoded_data.dynamic_lock = nfc_data.dynamic_lock; 96 encoded_data.dynamic_lock = nfc_data.dynamic_lock;
@@ -99,8 +104,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
99EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { 104EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
100 EncryptedNTAG215File nfc_data{}; 105 EncryptedNTAG215File nfc_data{};
101 106
102 memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2)); 107 nfc_data.uuid.uid = encoded_data.uid;
103 memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid)); 108 nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
109 nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
104 nfc_data.static_lock = encoded_data.static_lock; 110 nfc_data.static_lock = encoded_data.static_lock;
105 nfc_data.compability_container = encoded_data.compability_container; 111 nfc_data.compability_container = encoded_data.compability_container;
106 nfc_data.user_memory.hmac_data = encoded_data.hmac_data; 112 nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
@@ -112,7 +118,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
112 nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; 118 nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter;
113 nfc_data.user_memory.application_area_id = encoded_data.application_area_id; 119 nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
114 nfc_data.user_memory.unknown = encoded_data.unknown; 120 nfc_data.user_memory.unknown = encoded_data.unknown;
115 nfc_data.user_memory.hash = encoded_data.hash; 121 nfc_data.user_memory.unknown2 = encoded_data.unknown2;
116 nfc_data.user_memory.application_area = encoded_data.application_area; 122 nfc_data.user_memory.application_area = encoded_data.application_area;
117 nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; 123 nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag;
118 nfc_data.user_memory.model_info = encoded_data.model_info; 124 nfc_data.user_memory.model_info = encoded_data.model_info;
@@ -127,10 +133,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
127 133
128u32 GetTagPassword(const TagUuid& uuid) { 134u32 GetTagPassword(const TagUuid& uuid) {
129 // Verifiy that the generated password is correct 135 // Verifiy that the generated password is correct
130 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); 136 u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
131 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; 137 password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
132 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; 138 password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
133 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; 139 password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
134 return password; 140 return password;
135} 141}
136 142
@@ -138,15 +144,13 @@ HashSeed GetSeed(const NTAG215File& data) {
138 HashSeed seed{ 144 HashSeed seed{
139 .magic = data.write_counter, 145 .magic = data.write_counter,
140 .padding = {}, 146 .padding = {},
141 .uuid1 = {}, 147 .uid_1 = data.uid,
142 .uuid2 = {}, 148 .nintendo_id_1 = data.nintendo_id,
149 .uid_2 = data.uid,
150 .nintendo_id_2 = data.nintendo_id,
143 .keygen_salt = data.keygen_salt, 151 .keygen_salt = data.keygen_salt,
144 }; 152 };
145 153
146 // Copy the first 8 bytes of uuid
147 memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1));
148 memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2));
149
150 return seed; 154 return seed;
151} 155}
152 156
@@ -165,8 +169,10 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
165 output.insert(output.end(), key.magic_bytes.begin(), 169 output.insert(output.end(), key.magic_bytes.begin(),
166 key.magic_bytes.begin() + key.magic_length); 170 key.magic_bytes.begin() + key.magic_length);
167 171
168 output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end()); 172 output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
169 output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end()); 173 output.emplace_back(seed.nintendo_id_1);
174 output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
175 output.emplace_back(seed.nintendo_id_2);
170 176
171 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { 177 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
172 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); 178 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
@@ -177,7 +183,6 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
177 183
178void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, 184void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
179 const std::vector<u8>& seed) { 185 const std::vector<u8>& seed) {
180
181 // Initialize context 186 // Initialize context
182 ctx.used = false; 187 ctx.used = false;
183 ctx.counter = 0; 188 ctx.counter = 0;
@@ -250,14 +255,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
250 reinterpret_cast<unsigned char*>(&out_data.settings)); 255 reinterpret_cast<unsigned char*>(&out_data.settings));
251 256
252 // Copy the rest of the data directly 257 // Copy the rest of the data directly
253 out_data.uuid2 = in_data.uuid2; 258 out_data.uid = in_data.uid;
259 out_data.nintendo_id = in_data.nintendo_id;
260 out_data.lock_bytes = in_data.lock_bytes;
254 out_data.static_lock = in_data.static_lock; 261 out_data.static_lock = in_data.static_lock;
255 out_data.compability_container = in_data.compability_container; 262 out_data.compability_container = in_data.compability_container;
256 263
257 out_data.constant_value = in_data.constant_value; 264 out_data.constant_value = in_data.constant_value;
258 out_data.write_counter = in_data.write_counter; 265 out_data.write_counter = in_data.write_counter;
259 266
260 out_data.uuid = in_data.uuid;
261 out_data.model_info = in_data.model_info; 267 out_data.model_info = in_data.model_info;
262 out_data.keygen_salt = in_data.keygen_salt; 268 out_data.keygen_salt = in_data.keygen_salt;
263 out_data.dynamic_lock = in_data.dynamic_lock; 269 out_data.dynamic_lock = in_data.dynamic_lock;
@@ -274,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
274 Common::FS::FileType::BinaryFile}; 280 Common::FS::FileType::BinaryFile};
275 281
276 if (!keys_file.IsOpen()) { 282 if (!keys_file.IsOpen()) {
277 LOG_ERROR(Service_NFP, "No keys detected"); 283 LOG_ERROR(Service_NFP, "Failed to open key file");
278 return false; 284 return false;
279 } 285 }
280 286
@@ -290,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
290 return true; 296 return true;
291} 297}
292 298
299bool IsKeyAvailable() {
300 const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
301 return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin");
302}
303
293bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { 304bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
294 InternalKey locked_secret{}; 305 InternalKey locked_secret{};
295 InternalKey unfixed_info{}; 306 InternalKey unfixed_info{};
@@ -309,7 +320,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t
309 // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! 320 // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
310 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; 321 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
311 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), 322 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
312 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), 323 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
313 input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); 324 input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
314 325
315 // Regenerate data HMAC 326 // Regenerate data HMAC
@@ -350,7 +361,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
350 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; 361 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
351 constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; 362 constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
352 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), 363 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
353 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), 364 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
354 input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); 365 input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
355 366
356 // Init mbedtls HMAC context 367 // Init mbedtls HMAC context
@@ -364,7 +375,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
364 input_length2); // Data 375 input_length2); // Data
365 mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), 376 mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
366 sizeof(HashData)); // Tag HMAC 377 sizeof(HashData)); // Tag HMAC
367 mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uuid), 378 mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid),
368 input_length); 379 input_length);
369 mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); 380 mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
370 381
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index af7335912..1fa61174e 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -5,7 +5,7 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "core/hle/service/nfp/amiibo_types.h" 8#include "core/hle/service/nfp/nfp_types.h"
9 9
10struct mbedtls_md_context_t; 10struct mbedtls_md_context_t;
11 11
@@ -22,10 +22,12 @@ using HmacKey = std::array<u8, 0x10>;
22using DrgbOutput = std::array<u8, 0x20>; 22using DrgbOutput = std::array<u8, 0x20>;
23 23
24struct HashSeed { 24struct HashSeed {
25 u16 magic; 25 u16_be magic;
26 std::array<u8, 0xE> padding; 26 std::array<u8, 0xE> padding;
27 std::array<u8, 0x8> uuid1; 27 UniqueSerialNumber uid_1;
28 std::array<u8, 0x8> uuid2; 28 u8 nintendo_id_1;
29 UniqueSerialNumber uid_2;
30 u8 nintendo_id_2;
29 std::array<u8, 0x20> keygen_salt; 31 std::array<u8, 0x20> keygen_salt;
30}; 32};
31static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); 33static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
@@ -89,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
89/// Loads both amiibo keys from key_retail.bin 91/// Loads both amiibo keys from key_retail.bin
90bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); 92bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
91 93
94/// Returns true if key_retail.bin exist
95bool IsKeyAvailable();
96
92/// Decodes encripted amiibo data returns true if output is valid 97/// Decodes encripted amiibo data returns true if output is valid
93bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); 98bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
94 99
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index e0ed3f771..0cb55ca49 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -1,1098 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include <atomic>
6
7#include "common/fs/file.h"
8#include "common/fs/path_util.h"
9#include "common/logging/log.h" 4#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
15#include "core/hle/ipc_helpers.h" 5#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/k_event.h"
17#include "core/hle/service/mii/mii_manager.h"
18#include "core/hle/service/nfp/amiibo_crypto.h"
19#include "core/hle/service/nfp/nfp.h" 6#include "core/hle/service/nfp/nfp.h"
20#include "core/hle/service/nfp/nfp_user.h" 7#include "core/hle/service/nfp/nfp_user.h"
21 8
22namespace Service::NFP { 9namespace Service::NFP {
23namespace ErrCodes {
24constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
25constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
26constexpr Result NfcDisabled(ErrorModule::NFP, 80);
27constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
28constexpr Result TagRemoved(ErrorModule::NFP, 97);
29constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
30constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
31constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
32} // namespace ErrCodes
33
34IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
35 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name},
36 nfp_interface{nfp_interface_} {
37 static const FunctionInfo functions[] = {
38 {0, &IUser::Initialize, "Initialize"},
39 {1, &IUser::Finalize, "Finalize"},
40 {2, &IUser::ListDevices, "ListDevices"},
41 {3, &IUser::StartDetection, "StartDetection"},
42 {4, &IUser::StopDetection, "StopDetection"},
43 {5, &IUser::Mount, "Mount"},
44 {6, &IUser::Unmount, "Unmount"},
45 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
46 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
47 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
48 {10, &IUser::Flush, "Flush"},
49 {11, nullptr, "Restore"},
50 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
51 {13, &IUser::GetTagInfo, "GetTagInfo"},
52 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
53 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
54 {16, &IUser::GetModelInfo, "GetModelInfo"},
55 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
56 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
57 {19, &IUser::GetState, "GetState"},
58 {20, &IUser::GetDeviceState, "GetDeviceState"},
59 {21, &IUser::GetNpadId, "GetNpadId"},
60 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
61 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
62 {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
63 };
64 RegisterHandlers(functions);
65
66 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
67}
68
69void IUser::Initialize(Kernel::HLERequestContext& ctx) {
70 LOG_INFO(Service_NFC, "called");
71
72 state = State::Initialized;
73
74 // TODO(german77): Loop through all interfaces
75 nfp_interface.Initialize();
76
77 IPC::ResponseBuilder rb{ctx, 2, 0};
78 rb.Push(ResultSuccess);
79}
80
81void IUser::Finalize(Kernel::HLERequestContext& ctx) {
82 LOG_INFO(Service_NFP, "called");
83
84 state = State::NonInitialized;
85
86 // TODO(german77): Loop through all interfaces
87 nfp_interface.Finalize();
88
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(ResultSuccess);
91}
92
93void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
94 LOG_INFO(Service_NFP, "called");
95
96 if (state == State::NonInitialized) {
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ErrCodes::NfcDisabled);
99 return;
100 }
101
102 std::vector<u64> devices;
103
104 // TODO(german77): Loop through all interfaces
105 devices.push_back(nfp_interface.GetHandle());
106
107 if (devices.size() == 0) {
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ErrCodes::DeviceNotFound);
110 return;
111 }
112
113 ctx.WriteBuffer(devices);
114
115 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(ResultSuccess);
117 rb.Push(static_cast<s32>(devices.size()));
118}
119
120void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
121 IPC::RequestParser rp{ctx};
122 const auto device_handle{rp.Pop<u64>()};
123 const auto nfp_protocol{rp.Pop<s32>()};
124 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
125
126 if (state == State::NonInitialized) {
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ErrCodes::NfcDisabled);
129 return;
130 }
131
132 // TODO(german77): Loop through all interfaces
133 if (device_handle == nfp_interface.GetHandle()) {
134 const auto result = nfp_interface.StartDetection(nfp_protocol);
135 IPC::ResponseBuilder rb{ctx, 2};
136 rb.Push(result);
137 return;
138 }
139
140 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
141
142 IPC::ResponseBuilder rb{ctx, 2};
143 rb.Push(ErrCodes::DeviceNotFound);
144}
145
146void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
147 IPC::RequestParser rp{ctx};
148 const auto device_handle{rp.Pop<u64>()};
149 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
150
151 if (state == State::NonInitialized) {
152 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(ErrCodes::NfcDisabled);
154 return;
155 }
156
157 // TODO(german77): Loop through all interfaces
158 if (device_handle == nfp_interface.GetHandle()) {
159 const auto result = nfp_interface.StopDetection();
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(result);
162 return;
163 }
164
165 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
166
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(ErrCodes::DeviceNotFound);
169}
170
171void IUser::Mount(Kernel::HLERequestContext& ctx) {
172 IPC::RequestParser rp{ctx};
173 const auto device_handle{rp.Pop<u64>()};
174 const auto model_type{rp.PopEnum<ModelType>()};
175 const auto mount_target{rp.PopEnum<MountTarget>()};
176 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
177 model_type, mount_target);
178
179 if (state == State::NonInitialized) {
180 IPC::ResponseBuilder rb{ctx, 2};
181 rb.Push(ErrCodes::NfcDisabled);
182 return;
183 }
184
185 // TODO(german77): Loop through all interfaces
186 if (device_handle == nfp_interface.GetHandle()) {
187 const auto result = nfp_interface.Mount();
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(result);
190 return;
191 }
192
193 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
194
195 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ErrCodes::DeviceNotFound);
197}
198
199void IUser::Unmount(Kernel::HLERequestContext& ctx) {
200 IPC::RequestParser rp{ctx};
201 const auto device_handle{rp.Pop<u64>()};
202 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
203
204 if (state == State::NonInitialized) {
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(ErrCodes::NfcDisabled);
207 return;
208 }
209
210 // TODO(german77): Loop through all interfaces
211 if (device_handle == nfp_interface.GetHandle()) {
212 const auto result = nfp_interface.Unmount();
213 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(result);
215 return;
216 }
217
218 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
219
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ErrCodes::DeviceNotFound);
222}
223
224void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
225 IPC::RequestParser rp{ctx};
226 const auto device_handle{rp.Pop<u64>()};
227 const auto access_id{rp.Pop<u32>()};
228 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle,
229 access_id);
230
231 if (state == State::NonInitialized) {
232 IPC::ResponseBuilder rb{ctx, 2};
233 rb.Push(ErrCodes::NfcDisabled);
234 return;
235 }
236
237 // TODO(german77): Loop through all interfaces
238 if (device_handle == nfp_interface.GetHandle()) {
239 const auto result = nfp_interface.OpenApplicationArea(access_id);
240 IPC::ResponseBuilder rb{ctx, 2};
241 rb.Push(result);
242 return;
243 }
244
245 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
246
247 IPC::ResponseBuilder rb{ctx, 2};
248 rb.Push(ErrCodes::DeviceNotFound);
249}
250
251void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
252 IPC::RequestParser rp{ctx};
253 const auto device_handle{rp.Pop<u64>()};
254 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
255
256 if (state == State::NonInitialized) {
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ErrCodes::NfcDisabled);
259 return;
260 }
261
262 // TODO(german77): Loop through all interfaces
263 if (device_handle == nfp_interface.GetHandle()) {
264 ApplicationArea data{};
265 const auto result = nfp_interface.GetApplicationArea(data);
266 ctx.WriteBuffer(data);
267 IPC::ResponseBuilder rb{ctx, 3};
268 rb.Push(result);
269 rb.Push(static_cast<u32>(data.size()));
270 return;
271 }
272
273 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
274
275 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(ErrCodes::DeviceNotFound);
277}
278
279void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
280 IPC::RequestParser rp{ctx};
281 const auto device_handle{rp.Pop<u64>()};
282 const auto data{ctx.ReadBuffer()};
283 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle,
284 data.size());
285
286 if (state == State::NonInitialized) {
287 IPC::ResponseBuilder rb{ctx, 2};
288 rb.Push(ErrCodes::NfcDisabled);
289 return;
290 }
291
292 // TODO(german77): Loop through all interfaces
293 if (device_handle == nfp_interface.GetHandle()) {
294 const auto result = nfp_interface.SetApplicationArea(data);
295 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(result);
297 return;
298 }
299
300 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
301
302 IPC::ResponseBuilder rb{ctx, 2};
303 rb.Push(ErrCodes::DeviceNotFound);
304}
305
306void IUser::Flush(Kernel::HLERequestContext& ctx) {
307 IPC::RequestParser rp{ctx};
308 const auto device_handle{rp.Pop<u64>()};
309 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
310
311 if (state == State::NonInitialized) {
312 IPC::ResponseBuilder rb{ctx, 2};
313 rb.Push(ErrCodes::NfcDisabled);
314 return;
315 }
316
317 // TODO(german77): Loop through all interfaces
318 if (device_handle == nfp_interface.GetHandle()) {
319 const auto result = nfp_interface.Flush();
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(result);
322 return;
323 }
324
325 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
326
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ErrCodes::DeviceNotFound);
329}
330
331void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx};
333 const auto device_handle{rp.Pop<u64>()};
334 const auto access_id{rp.Pop<u32>()};
335 const auto data{ctx.ReadBuffer()};
336 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
337 device_handle, access_id, data.size());
338
339 if (state == State::NonInitialized) {
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(ErrCodes::NfcDisabled);
342 return;
343 }
344
345 // TODO(german77): Loop through all interfaces
346 if (device_handle == nfp_interface.GetHandle()) {
347 const auto result = nfp_interface.CreateApplicationArea(access_id, data);
348 IPC::ResponseBuilder rb{ctx, 2};
349 rb.Push(result);
350 return;
351 }
352
353 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
354
355 IPC::ResponseBuilder rb{ctx, 2};
356 rb.Push(ErrCodes::DeviceNotFound);
357}
358
359void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto device_handle{rp.Pop<u64>()};
362 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
363
364 if (state == State::NonInitialized) {
365 IPC::ResponseBuilder rb{ctx, 2};
366 rb.Push(ErrCodes::NfcDisabled);
367 return;
368 }
369
370 // TODO(german77): Loop through all interfaces
371 if (device_handle == nfp_interface.GetHandle()) {
372 TagInfo tag_info{};
373 const auto result = nfp_interface.GetTagInfo(tag_info);
374 ctx.WriteBuffer(tag_info);
375 IPC::ResponseBuilder rb{ctx, 2};
376 rb.Push(result);
377 return;
378 }
379
380 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
381
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ErrCodes::DeviceNotFound);
384}
385
386void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
387 IPC::RequestParser rp{ctx};
388 const auto device_handle{rp.Pop<u64>()};
389 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
390
391 if (state == State::NonInitialized) {
392 IPC::ResponseBuilder rb{ctx, 2};
393 rb.Push(ErrCodes::NfcDisabled);
394 return;
395 }
396
397 // TODO(german77): Loop through all interfaces
398 if (device_handle == nfp_interface.GetHandle()) {
399 RegisterInfo register_info{};
400 const auto result = nfp_interface.GetRegisterInfo(register_info);
401 ctx.WriteBuffer(register_info);
402 IPC::ResponseBuilder rb{ctx, 2};
403 rb.Push(result);
404 return;
405 }
406
407 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
408
409 IPC::ResponseBuilder rb{ctx, 2};
410 rb.Push(ErrCodes::DeviceNotFound);
411}
412
413void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
414 IPC::RequestParser rp{ctx};
415 const auto device_handle{rp.Pop<u64>()};
416 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
417
418 if (state == State::NonInitialized) {
419 IPC::ResponseBuilder rb{ctx, 2};
420 rb.Push(ErrCodes::NfcDisabled);
421 return;
422 }
423
424 // TODO(german77): Loop through all interfaces
425 if (device_handle == nfp_interface.GetHandle()) {
426 CommonInfo common_info{};
427 const auto result = nfp_interface.GetCommonInfo(common_info);
428 ctx.WriteBuffer(common_info);
429 IPC::ResponseBuilder rb{ctx, 2};
430 rb.Push(result);
431 return;
432 }
433
434 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
435
436 IPC::ResponseBuilder rb{ctx, 2};
437 rb.Push(ErrCodes::DeviceNotFound);
438}
439
440void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
441 IPC::RequestParser rp{ctx};
442 const auto device_handle{rp.Pop<u64>()};
443 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
444
445 if (state == State::NonInitialized) {
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ErrCodes::NfcDisabled);
448 return;
449 }
450
451 // TODO(german77): Loop through all interfaces
452 if (device_handle == nfp_interface.GetHandle()) {
453 ModelInfo model_info{};
454 const auto result = nfp_interface.GetModelInfo(model_info);
455 ctx.WriteBuffer(model_info);
456 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(result);
458 return;
459 }
460
461 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
462
463 IPC::ResponseBuilder rb{ctx, 2};
464 rb.Push(ErrCodes::DeviceNotFound);
465}
466
467void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
468 IPC::RequestParser rp{ctx};
469 const auto device_handle{rp.Pop<u64>()};
470 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
471
472 if (state == State::NonInitialized) {
473 IPC::ResponseBuilder rb{ctx, 2};
474 rb.Push(ErrCodes::NfcDisabled);
475 return;
476 }
477
478 // TODO(german77): Loop through all interfaces
479 if (device_handle == nfp_interface.GetHandle()) {
480 IPC::ResponseBuilder rb{ctx, 2, 1};
481 rb.Push(ResultSuccess);
482 rb.PushCopyObjects(nfp_interface.GetActivateEvent());
483 return;
484 }
485
486 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
487
488 IPC::ResponseBuilder rb{ctx, 2};
489 rb.Push(ErrCodes::DeviceNotFound);
490}
491
492void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
493 IPC::RequestParser rp{ctx};
494 const auto device_handle{rp.Pop<u64>()};
495 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
496
497 if (state == State::NonInitialized) {
498 IPC::ResponseBuilder rb{ctx, 2};
499 rb.Push(ErrCodes::NfcDisabled);
500 return;
501 }
502
503 // TODO(german77): Loop through all interfaces
504 if (device_handle == nfp_interface.GetHandle()) {
505 IPC::ResponseBuilder rb{ctx, 2, 1};
506 rb.Push(ResultSuccess);
507 rb.PushCopyObjects(nfp_interface.GetDeactivateEvent());
508 return;
509 }
510
511 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
512
513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ErrCodes::DeviceNotFound);
515}
516
517void IUser::GetState(Kernel::HLERequestContext& ctx) {
518 LOG_DEBUG(Service_NFC, "called");
519
520 IPC::ResponseBuilder rb{ctx, 3, 0};
521 rb.Push(ResultSuccess);
522 rb.PushEnum(state);
523}
524
525void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
526 IPC::RequestParser rp{ctx};
527 const auto device_handle{rp.Pop<u64>()};
528 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
529
530 // TODO(german77): Loop through all interfaces
531 if (device_handle == nfp_interface.GetHandle()) {
532 IPC::ResponseBuilder rb{ctx, 3};
533 rb.Push(ResultSuccess);
534 rb.PushEnum(nfp_interface.GetCurrentState());
535 return;
536 }
537
538 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
539
540 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(ErrCodes::DeviceNotFound);
542}
543
544void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
545 IPC::RequestParser rp{ctx};
546 const auto device_handle{rp.Pop<u64>()};
547 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
548
549 if (state == State::NonInitialized) {
550 IPC::ResponseBuilder rb{ctx, 2};
551 rb.Push(ErrCodes::NfcDisabled);
552 return;
553 }
554
555 // TODO(german77): Loop through all interfaces
556 if (device_handle == nfp_interface.GetHandle()) {
557 IPC::ResponseBuilder rb{ctx, 3};
558 rb.Push(ResultSuccess);
559 rb.PushEnum(nfp_interface.GetNpadId());
560 return;
561 }
562
563 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
564
565 IPC::ResponseBuilder rb{ctx, 2};
566 rb.Push(ErrCodes::DeviceNotFound);
567}
568
569void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
570 IPC::RequestParser rp{ctx};
571 const auto device_handle{rp.Pop<u64>()};
572 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
573
574 // TODO(german77): Loop through all interfaces
575 if (device_handle == nfp_interface.GetHandle()) {
576 IPC::ResponseBuilder rb{ctx, 3};
577 rb.Push(ResultSuccess);
578 rb.Push(sizeof(ApplicationArea));
579 return;
580 }
581
582 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
583
584 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(ErrCodes::DeviceNotFound);
586}
587
588void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
589 LOG_DEBUG(Service_NFP, "(STUBBED) called");
590
591 if (state == State::NonInitialized) {
592 IPC::ResponseBuilder rb{ctx, 2};
593 rb.Push(ErrCodes::NfcDisabled);
594 return;
595 }
596
597 IPC::ResponseBuilder rb{ctx, 2, 1};
598 rb.Push(ResultSuccess);
599 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
600}
601
602void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
603 IPC::RequestParser rp{ctx};
604 const auto device_handle{rp.Pop<u64>()};
605 const auto access_id{rp.Pop<u32>()};
606 const auto data{ctx.ReadBuffer()};
607 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
608 device_handle, access_id, data.size());
609
610 if (state == State::NonInitialized) {
611 IPC::ResponseBuilder rb{ctx, 2};
612 rb.Push(ErrCodes::NfcDisabled);
613 return;
614 }
615
616 // TODO(german77): Loop through all interfaces
617 if (device_handle == nfp_interface.GetHandle()) {
618 const auto result = nfp_interface.RecreateApplicationArea(access_id, data);
619 IPC::ResponseBuilder rb{ctx, 2};
620 rb.Push(result);
621 return;
622 }
623
624 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
625
626 IPC::ResponseBuilder rb{ctx, 2};
627 rb.Push(ErrCodes::DeviceNotFound);
628}
629
630Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
631 const char* name)
632 : ServiceFramework{system_, name}, module{std::move(module_)},
633 npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} {
634 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
635 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
636}
637
638Module::Interface::~Interface() = default;
639
640void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
641 LOG_DEBUG(Service_NFP, "called");
642
643 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
644 rb.Push(ResultSuccess);
645 rb.PushIpcInterface<IUser>(*this, system);
646}
647
648bool Module::Interface::LoadAmiiboFile(const std::string& filename) {
649 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
650 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
651 Common::FS::FileType::BinaryFile};
652
653 if (!amiibo_file.IsOpen()) {
654 LOG_ERROR(Service_NFP, "Amiibo is already on use");
655 return false;
656 }
657
658 // Workaround for files with missing password data
659 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
660 if (amiibo_file.Read(buffer) < tag_size_without_password) {
661 LOG_ERROR(Service_NFP, "Failed to read amiibo file");
662 return false;
663 }
664 memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
665
666 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
667 LOG_INFO(Service_NFP, "Invalid amiibo");
668 return false;
669 }
670
671 file_path = filename;
672 return true;
673}
674
675bool Module::Interface::LoadAmiibo(const std::string& filename) {
676 if (device_state != DeviceState::SearchingForTag) {
677 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
678 return false;
679 }
680
681 if (!LoadAmiiboFile(filename)) {
682 return false;
683 }
684
685 device_state = DeviceState::TagFound;
686 activate_event->GetWritableEvent().Signal();
687 return true;
688}
689
690void Module::Interface::CloseAmiibo() {
691 LOG_INFO(Service_NFP, "Remove amiibo");
692 device_state = DeviceState::TagRemoved;
693 is_data_decoded = false;
694 is_application_area_initialized = false;
695 encrypted_tag_data = {};
696 tag_data = {};
697 deactivate_event->GetWritableEvent().Signal();
698}
699
700Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const {
701 return activate_event->GetReadableEvent();
702}
703
704Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const {
705 return deactivate_event->GetReadableEvent();
706}
707
708void Module::Interface::Initialize() {
709 device_state = DeviceState::Initialized;
710 is_data_decoded = false;
711 is_application_area_initialized = false;
712 encrypted_tag_data = {};
713 tag_data = {};
714}
715
716void Module::Interface::Finalize() {
717 if (device_state == DeviceState::TagMounted) {
718 Unmount();
719 }
720 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
721 StopDetection();
722 }
723 device_state = DeviceState::Unaviable;
724}
725
726Result Module::Interface::StartDetection(s32 protocol_) {
727 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
728
729 // TODO(german77): Add callback for when nfc data is available
730
731 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
732 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
733 device_state = DeviceState::SearchingForTag;
734 protocol = protocol_;
735 return ResultSuccess;
736 }
737
738 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
739 return ErrCodes::WrongDeviceState;
740}
741
742Result Module::Interface::StopDetection() {
743 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
744 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
745
746 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
747 CloseAmiibo();
748 return ResultSuccess;
749 }
750 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
751 device_state = DeviceState::Initialized;
752 return ResultSuccess;
753 }
754
755 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
756 return ErrCodes::WrongDeviceState;
757}
758
759Result Module::Interface::Flush() {
760 // Ignore write command if we can't encrypt the data
761 if (!is_data_decoded) {
762 return ResultSuccess;
763 }
764
765 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
766 EncryptedNTAG215File tmp_encrypted_tag_data{};
767 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
768 Common::FS::FileType::BinaryFile};
769
770 if (!amiibo_file.IsOpen()) {
771 LOG_ERROR(Core, "Amiibo is already on use");
772 return ErrCodes::WriteAmiiboFailed;
773 }
774
775 // Workaround for files with missing password data
776 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
777 if (amiibo_file.Read(buffer) < tag_size_without_password) {
778 LOG_ERROR(Core, "Failed to read amiibo file");
779 return ErrCodes::WriteAmiiboFailed;
780 }
781 memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
782
783 if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) {
784 LOG_INFO(Service_NFP, "Invalid amiibo");
785 return ErrCodes::WriteAmiiboFailed;
786 }
787
788 bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0;
789 bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id ==
790 tag_data.model_info.character_id;
791 if (!is_uuid_equal || !is_character_equal) {
792 LOG_ERROR(Service_NFP, "Not the same amiibo");
793 return ErrCodes::WriteAmiiboFailed;
794 }
795
796 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
797 LOG_ERROR(Service_NFP, "Failed to encode data");
798 return ErrCodes::WriteAmiiboFailed;
799 }
800
801 // Return to the start of the file
802 if (!amiibo_file.Seek(0)) {
803 LOG_ERROR(Service_NFP, "Error writting to file");
804 return ErrCodes::WriteAmiiboFailed;
805 }
806
807 if (!amiibo_file.Write(encrypted_tag_data)) {
808 LOG_ERROR(Service_NFP, "Error writting to file");
809 return ErrCodes::WriteAmiiboFailed;
810 }
811
812 return ResultSuccess;
813}
814
815Result Module::Interface::Mount() {
816 if (device_state != DeviceState::TagFound) {
817 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
818 return ErrCodes::WrongDeviceState;
819 }
820 10
821 is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); 11class IUserManager final : public ServiceFramework<IUserManager> {
822 LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); 12public:
823 13 explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
824 is_application_area_initialized = false; 14 // clang-format off
825 device_state = DeviceState::TagMounted; 15 static const FunctionInfo functions[] = {
826 return ResultSuccess; 16 {0, &IUserManager::CreateUserInterface, "CreateUserInterface"},
827}
828
829Result Module::Interface::Unmount() {
830 if (device_state != DeviceState::TagMounted) {
831 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
832 return ErrCodes::WrongDeviceState;
833 }
834
835 is_data_decoded = false;
836 is_application_area_initialized = false;
837 device_state = DeviceState::TagFound;
838 return ResultSuccess;
839}
840
841Result Module::Interface::GetTagInfo(TagInfo& tag_info) const {
842 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
843 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
844 return ErrCodes::WrongDeviceState;
845 }
846
847 tag_info = {
848 .uuid = encrypted_tag_data.uuid,
849 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()),
850 .protocol = protocol,
851 .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type),
852 };
853
854 return ResultSuccess;
855}
856
857Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
858 if (device_state != DeviceState::TagMounted) {
859 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
860 return ErrCodes::WrongDeviceState;
861 }
862
863 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
864 const auto& settings = tag_data.settings;
865 // TODO: Validate this data
866 common_info = {
867 .last_write_year = settings.write_date.GetYear(),
868 .last_write_month = settings.write_date.GetMonth(),
869 .last_write_day = settings.write_date.GetDay(),
870 .write_counter = settings.crc_counter,
871 .version = 1,
872 .application_area_size = sizeof(ApplicationArea),
873 };
874 return ResultSuccess;
875 }
876
877 // Generate a generic answer
878 common_info = {
879 .last_write_year = 2022,
880 .last_write_month = 2,
881 .last_write_day = 7,
882 .write_counter = 0,
883 .version = 1,
884 .application_area_size = sizeof(ApplicationArea),
885 };
886 return ResultSuccess;
887}
888
889Result Module::Interface::GetModelInfo(ModelInfo& model_info) const {
890 if (device_state != DeviceState::TagMounted) {
891 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
892 return ErrCodes::WrongDeviceState;
893 }
894
895 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
896 model_info = {
897 .character_id = model_info_data.character_id,
898 .character_variant = model_info_data.character_variant,
899 .amiibo_type = model_info_data.amiibo_type,
900 .model_number = model_info_data.model_number,
901 .series = model_info_data.series,
902 .constant_value = model_info_data.constant_value,
903 };
904 return ResultSuccess;
905}
906
907Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
908 if (device_state != DeviceState::TagMounted) {
909 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
910 if (device_state == DeviceState::TagRemoved) {
911 return ErrCodes::TagRemoved;
912 }
913 return ErrCodes::WrongDeviceState;
914 }
915
916 Service::Mii::MiiManager manager;
917
918 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
919 const auto& settings = tag_data.settings;
920
921 // TODO: Validate this data
922 register_info = {
923 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
924 .first_write_year = settings.init_date.GetYear(),
925 .first_write_month = settings.init_date.GetMonth(),
926 .first_write_day = settings.init_date.GetDay(),
927 .amiibo_name = GetAmiiboName(settings),
928 .font_region = {},
929 }; 17 };
18 // clang-format on
930 19
931 return ResultSuccess; 20 RegisterHandlers(functions);
932 } 21 }
933 22
934 // Generate a generic answer 23private:
935 register_info = { 24 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
936 .mii_char_info = manager.BuildDefault(0), 25 LOG_DEBUG(Service_NFP, "called");
937 .first_write_year = 2022,
938 .first_write_month = 2,
939 .first_write_day = 7,
940 .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0},
941 .font_region = {},
942 };
943 return ResultSuccess;
944}
945 26
946Result Module::Interface::OpenApplicationArea(u32 access_id) { 27 if (user_interface == nullptr) {
947 if (device_state != DeviceState::TagMounted) { 28 user_interface = std::make_shared<IUser>(system);
948 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
949 if (device_state == DeviceState::TagRemoved) {
950 return ErrCodes::TagRemoved;
951 } 29 }
952 return ErrCodes::WrongDeviceState;
953 }
954
955 // Fallback for lack of amiibo keys
956 if (!is_data_decoded) {
957 LOG_WARNING(Service_NFP, "Application area is not initialized");
958 return ErrCodes::ApplicationAreaIsNotInitialized;
959 }
960 30
961 if (tag_data.settings.settings.appdata_initialized == 0) { 31 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
962 LOG_WARNING(Service_NFP, "Application area is not initialized"); 32 rb.Push(ResultSuccess);
963 return ErrCodes::ApplicationAreaIsNotInitialized; 33 rb.PushIpcInterface<IUser>(user_interface);
964 }
965
966 if (tag_data.application_area_id != access_id) {
967 LOG_WARNING(Service_NFP, "Wrong application area id");
968 return ErrCodes::WrongApplicationAreaId;
969 }
970
971 is_application_area_initialized = true;
972 return ResultSuccess;
973}
974
975Result Module::Interface::GetApplicationArea(ApplicationArea& data) const {
976 if (device_state != DeviceState::TagMounted) {
977 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
978 if (device_state == DeviceState::TagRemoved) {
979 return ErrCodes::TagRemoved;
980 }
981 return ErrCodes::WrongDeviceState;
982 }
983
984 if (!is_application_area_initialized) {
985 LOG_ERROR(Service_NFP, "Application area is not initialized");
986 return ErrCodes::ApplicationAreaIsNotInitialized;
987 }
988
989 data = tag_data.application_area;
990
991 return ResultSuccess;
992}
993
994Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
995 if (device_state != DeviceState::TagMounted) {
996 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
997 if (device_state == DeviceState::TagRemoved) {
998 return ErrCodes::TagRemoved;
999 }
1000 return ErrCodes::WrongDeviceState;
1001 }
1002
1003 if (!is_application_area_initialized) {
1004 LOG_ERROR(Service_NFP, "Application area is not initialized");
1005 return ErrCodes::ApplicationAreaIsNotInitialized;
1006 }
1007
1008 if (data.size() != sizeof(ApplicationArea)) {
1009 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1010 return ResultUnknown;
1011 }
1012
1013 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1014 return ResultSuccess;
1015}
1016
1017Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
1018 if (device_state != DeviceState::TagMounted) {
1019 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1020 if (device_state == DeviceState::TagRemoved) {
1021 return ErrCodes::TagRemoved;
1022 }
1023 return ErrCodes::WrongDeviceState;
1024 }
1025
1026 if (tag_data.settings.settings.appdata_initialized != 0) {
1027 LOG_ERROR(Service_NFP, "Application area already exist");
1028 return ErrCodes::ApplicationAreaExist;
1029 }
1030
1031 if (data.size() != sizeof(ApplicationArea)) {
1032 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1033 return ResultUnknown;
1034 }
1035
1036 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1037 tag_data.application_area_id = access_id;
1038
1039 return ResultSuccess;
1040}
1041
1042Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
1043 if (device_state != DeviceState::TagMounted) {
1044 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1045 if (device_state == DeviceState::TagRemoved) {
1046 return ErrCodes::TagRemoved;
1047 }
1048 return ErrCodes::WrongDeviceState;
1049 }
1050
1051 if (data.size() != sizeof(ApplicationArea)) {
1052 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1053 return ResultUnknown;
1054 }
1055
1056 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1057 tag_data.application_area_id = access_id;
1058
1059 return ResultSuccess;
1060}
1061
1062u64 Module::Interface::GetHandle() const {
1063 // Generate a handle based of the npad id
1064 return static_cast<u64>(npad_id);
1065}
1066
1067DeviceState Module::Interface::GetCurrentState() const {
1068 return device_state;
1069}
1070
1071Core::HID::NpadIdType Module::Interface::GetNpadId() const {
1072 // Return first connected npad id as a workaround for lack of a single nfc interface per
1073 // controller
1074 return system.HIDCore().GetFirstNpadId();
1075}
1076
1077AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const {
1078 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
1079 AmiiboName amiibo_name{};
1080
1081 // Convert from big endian to little endian
1082 for (std::size_t i = 0; i < amiibo_name_length; i++) {
1083 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
1084 } 34 }
1085 35
1086 // Convert from utf16 to utf8 36 std::shared_ptr<IUser> user_interface;
1087 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); 37};
1088 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
1089
1090 return amiibo_name;
1091}
1092 38
1093void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 39void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
1094 auto module = std::make_shared<Module>(); 40 std::make_shared<IUserManager>(system)->InstallAsService(service_manager);
1095 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
1096} 41}
1097 42
1098} // namespace Service::NFP 43} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 0de0b48e7..a25c362b8 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -3,170 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
7#include <vector>
8
9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/amiibo_types.h"
13#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
14 7
15namespace Kernel {
16class KEvent;
17class KReadableEvent;
18} // namespace Kernel
19
20namespace Core::HID {
21enum class NpadIdType : u32;
22} // namespace Core::HID
23
24namespace Service::NFP { 8namespace Service::NFP {
25using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
26
27struct TagInfo {
28 TagUuid uuid;
29 u8 uuid_length;
30 INSERT_PADDING_BYTES(0x15);
31 s32 protocol;
32 u32 tag_type;
33 INSERT_PADDING_BYTES(0x30);
34};
35static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
36
37struct CommonInfo {
38 u16 last_write_year;
39 u8 last_write_month;
40 u8 last_write_day;
41 u16 write_counter;
42 u16 version;
43 u32 application_area_size;
44 INSERT_PADDING_BYTES(0x34);
45};
46static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
47
48struct ModelInfo {
49 u16 character_id;
50 u8 character_variant;
51 AmiiboType amiibo_type;
52 u16 model_number;
53 AmiiboSeries series;
54 u8 constant_value; // Must be 02
55 INSERT_PADDING_BYTES(0x38); // Unknown
56};
57static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
58
59struct RegisterInfo {
60 Service::Mii::CharInfo mii_char_info;
61 u16 first_write_year;
62 u8 first_write_month;
63 u8 first_write_day;
64 AmiiboName amiibo_name;
65 u8 font_region;
66 INSERT_PADDING_BYTES(0x7A);
67};
68static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
69
70class Module final {
71public:
72 class Interface : public ServiceFramework<Interface> {
73 public:
74 explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
75 const char* name);
76 ~Interface() override;
77
78 void CreateUserInterface(Kernel::HLERequestContext& ctx);
79 bool LoadAmiibo(const std::string& filename);
80 bool LoadAmiiboFile(const std::string& filename);
81 void CloseAmiibo();
82
83 void Initialize();
84 void Finalize();
85
86 Result StartDetection(s32 protocol_);
87 Result StopDetection();
88 Result Mount();
89 Result Unmount();
90 Result Flush();
91
92 Result GetTagInfo(TagInfo& tag_info) const;
93 Result GetCommonInfo(CommonInfo& common_info) const;
94 Result GetModelInfo(ModelInfo& model_info) const;
95 Result GetRegisterInfo(RegisterInfo& register_info) const;
96
97 Result OpenApplicationArea(u32 access_id);
98 Result GetApplicationArea(ApplicationArea& data) const;
99 Result SetApplicationArea(const std::vector<u8>& data);
100 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
101 Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data);
102
103 u64 GetHandle() const;
104 DeviceState GetCurrentState() const;
105 Core::HID::NpadIdType GetNpadId() const;
106
107 Kernel::KReadableEvent& GetActivateEvent() const;
108 Kernel::KReadableEvent& GetDeactivateEvent() const;
109
110 protected:
111 std::shared_ptr<Module> module;
112
113 private:
114 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
115
116 const Core::HID::NpadIdType npad_id;
117
118 bool is_data_decoded{};
119 bool is_application_area_initialized{};
120 s32 protocol;
121 std::string file_path{};
122 Kernel::KEvent* activate_event;
123 Kernel::KEvent* deactivate_event;
124 DeviceState device_state{DeviceState::Unaviable};
125 KernelHelpers::ServiceContext service_context;
126
127 NTAG215File tag_data{};
128 EncryptedNTAG215File encrypted_tag_data{};
129 };
130};
131
132class IUser final : public ServiceFramework<IUser> {
133public:
134 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
135
136private:
137 void Initialize(Kernel::HLERequestContext& ctx);
138 void Finalize(Kernel::HLERequestContext& ctx);
139 void ListDevices(Kernel::HLERequestContext& ctx);
140 void StartDetection(Kernel::HLERequestContext& ctx);
141 void StopDetection(Kernel::HLERequestContext& ctx);
142 void Mount(Kernel::HLERequestContext& ctx);
143 void Unmount(Kernel::HLERequestContext& ctx);
144 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
145 void GetApplicationArea(Kernel::HLERequestContext& ctx);
146 void SetApplicationArea(Kernel::HLERequestContext& ctx);
147 void Flush(Kernel::HLERequestContext& ctx);
148 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
149 void GetTagInfo(Kernel::HLERequestContext& ctx);
150 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
151 void GetCommonInfo(Kernel::HLERequestContext& ctx);
152 void GetModelInfo(Kernel::HLERequestContext& ctx);
153 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
154 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
155 void GetState(Kernel::HLERequestContext& ctx);
156 void GetDeviceState(Kernel::HLERequestContext& ctx);
157 void GetNpadId(Kernel::HLERequestContext& ctx);
158 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
159 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
160 void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
161
162 KernelHelpers::ServiceContext service_context;
163
164 // TODO(german77): We should have a vector of interfaces
165 Module::Interface& nfp_interface;
166
167 State state{State::NonInitialized};
168 Kernel::KEvent* availability_change_event;
169};
170 9
171void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 10void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
172 11
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
new file mode 100644
index 000000000..b19672560
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -0,0 +1,690 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <atomic>
6
7#include "common/fs/file.h"
8#include "common/fs/path_util.h"
9#include "common/input.h"
10#include "common/logging/log.h"
11#include "common/string_util.h"
12#include "common/tiny_mt.h"
13#include "core/core.h"
14#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h"
16#include "core/hid/hid_types.h"
17#include "core/hle/ipc_helpers.h"
18#include "core/hle/kernel/k_event.h"
19#include "core/hle/service/mii/mii_manager.h"
20#include "core/hle/service/mii/types.h"
21#include "core/hle/service/nfp/amiibo_crypto.h"
22#include "core/hle/service/nfp/nfp.h"
23#include "core/hle/service/nfp/nfp_device.h"
24#include "core/hle/service/nfp/nfp_result.h"
25#include "core/hle/service/nfp/nfp_user.h"
26#include "core/hle/service/time/time_manager.h"
27#include "core/hle/service/time/time_zone_content_manager.h"
28#include "core/hle/service/time/time_zone_types.h"
29
30namespace Service::NFP {
31NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
32 KernelHelpers::ServiceContext& service_context_,
33 Kernel::KEvent* availability_change_event_)
34 : npad_id{npad_id_}, system{system_}, service_context{service_context_},
35 availability_change_event{availability_change_event_} {
36 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
37 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
38 npad_device = system.HIDCore().GetEmulatedController(npad_id);
39
40 Core::HID::ControllerUpdateCallback engine_callback{
41 .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
42 .is_npad_service = false,
43 };
44 is_controller_set = true;
45 callback_key = npad_device->SetCallback(engine_callback);
46
47 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
48 current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
49}
50
51NfpDevice::~NfpDevice() {
52 if (!is_controller_set) {
53 return;
54 }
55 npad_device->DeleteCallback(callback_key);
56 is_controller_set = false;
57};
58
59void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
60 if (type == Core::HID::ControllerTriggerType::Connected ||
61 type == Core::HID::ControllerTriggerType::Disconnected) {
62 availability_change_event->Signal();
63 return;
64 }
65
66 if (type != Core::HID::ControllerTriggerType::Nfc) {
67 return;
68 }
69
70 if (!npad_device->IsConnected()) {
71 return;
72 }
73
74 const auto nfc_status = npad_device->GetNfc();
75 switch (nfc_status.state) {
76 case Common::Input::NfcState::NewAmiibo:
77 LoadAmiibo(nfc_status.data);
78 break;
79 case Common::Input::NfcState::AmiiboRemoved:
80 if (device_state != DeviceState::SearchingForTag) {
81 CloseAmiibo();
82 }
83 break;
84 default:
85 break;
86 }
87}
88
89bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
90 if (device_state != DeviceState::SearchingForTag) {
91 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
92 return false;
93 }
94
95 if (data.size() != sizeof(EncryptedNTAG215File)) {
96 LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size());
97 return false;
98 }
99
100 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
101
102 device_state = DeviceState::TagFound;
103 deactivate_event->GetReadableEvent().Clear();
104 activate_event->Signal();
105 return true;
106}
107
108void NfpDevice::CloseAmiibo() {
109 LOG_INFO(Service_NFP, "Remove amiibo");
110
111 if (device_state == DeviceState::TagMounted) {
112 Unmount();
113 }
114
115 device_state = DeviceState::TagRemoved;
116 encrypted_tag_data = {};
117 tag_data = {};
118 activate_event->GetReadableEvent().Clear();
119 deactivate_event->Signal();
120}
121
122Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const {
123 return activate_event->GetReadableEvent();
124}
125
126Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const {
127 return deactivate_event->GetReadableEvent();
128}
129
130void NfpDevice::Initialize() {
131 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
132 encrypted_tag_data = {};
133 tag_data = {};
134}
135
136void NfpDevice::Finalize() {
137 if (device_state == DeviceState::TagMounted) {
138 Unmount();
139 }
140 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
141 StopDetection();
142 }
143 device_state = DeviceState::Unavailable;
144}
145
146Result NfpDevice::StartDetection(s32 protocol_) {
147 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
148 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
149 return WrongDeviceState;
150 }
151
152 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
153 LOG_ERROR(Service_NFP, "Nfc not supported");
154 return NfcDisabled;
155 }
156
157 device_state = DeviceState::SearchingForTag;
158 protocol = protocol_;
159 return ResultSuccess;
160}
161
162Result NfpDevice::StopDetection() {
163 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
164
165 if (device_state == DeviceState::Initialized) {
166 return ResultSuccess;
167 }
168
169 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
170 CloseAmiibo();
171 return ResultSuccess;
172 }
173 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
174 device_state = DeviceState::Initialized;
175 return ResultSuccess;
176 }
177
178 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
179 return WrongDeviceState;
180}
181
182Result NfpDevice::Flush() {
183 if (device_state != DeviceState::TagMounted) {
184 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
185 if (device_state == DeviceState::TagRemoved) {
186 return TagRemoved;
187 }
188 return WrongDeviceState;
189 }
190
191 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
192 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
193 return WrongDeviceState;
194 }
195
196 auto& settings = tag_data.settings;
197
198 const auto& current_date = GetAmiiboDate(current_posix_time);
199 if (settings.write_date.raw_date != current_date.raw_date) {
200 settings.write_date = current_date;
201 settings.crc_counter++;
202 // TODO: Find how to calculate the crc check
203 // settings.crc = CalculateCRC(settings);
204 }
205
206 tag_data.write_counter++;
207
208 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
209 LOG_ERROR(Service_NFP, "Failed to encode data");
210 return WriteAmiiboFailed;
211 }
212
213 std::vector<u8> data(sizeof(encrypted_tag_data));
214 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
215
216 if (!npad_device->WriteNfc(data)) {
217 LOG_ERROR(Service_NFP, "Error writing to file");
218 return WriteAmiiboFailed;
219 }
220
221 is_data_moddified = false;
222
223 return ResultSuccess;
224}
225
226Result NfpDevice::Mount(MountTarget mount_target_) {
227 if (device_state != DeviceState::TagFound) {
228 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
229 return WrongDeviceState;
230 }
231
232 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
233 LOG_ERROR(Service_NFP, "Not an amiibo");
234 return NotAnAmiibo;
235 }
236
237 // Mark amiibos as read only when keys are missing
238 if (!AmiiboCrypto::IsKeyAvailable()) {
239 LOG_ERROR(Service_NFP, "No keys detected");
240 device_state = DeviceState::TagMounted;
241 mount_target = MountTarget::Rom;
242 return ResultSuccess;
243 }
244
245 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
246 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
247 return CorruptedData;
248 }
249
250 device_state = DeviceState::TagMounted;
251 mount_target = mount_target_;
252 return ResultSuccess;
253}
254
255Result NfpDevice::Unmount() {
256 if (device_state != DeviceState::TagMounted) {
257 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
258 if (device_state == DeviceState::TagRemoved) {
259 return TagRemoved;
260 }
261 return WrongDeviceState;
262 }
263
264 // Save data before unloading the amiibo
265 if (is_data_moddified) {
266 Flush();
267 }
268
269 device_state = DeviceState::TagFound;
270 mount_target = MountTarget::None;
271 is_app_area_open = false;
272
273 return ResultSuccess;
274}
275
276Result NfpDevice::GetTagInfo(TagInfo& tag_info) const {
277 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
278 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
279 if (device_state == DeviceState::TagRemoved) {
280 return TagRemoved;
281 }
282 return WrongDeviceState;
283 }
284
285 tag_info = {
286 .uuid = encrypted_tag_data.uuid.uid,
287 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
288 .protocol = TagProtocol::TypeA,
289 .tag_type = TagType::Type2,
290 };
291
292 return ResultSuccess;
293}
294
295Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
296 if (device_state != DeviceState::TagMounted) {
297 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
298 if (device_state == DeviceState::TagRemoved) {
299 return TagRemoved;
300 }
301 return WrongDeviceState;
302 }
303
304 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
305 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
306 return WrongDeviceState;
307 }
308
309 const auto& settings = tag_data.settings;
310
311 // TODO: Validate this data
312 common_info = {
313 .last_write_date = settings.write_date.GetWriteDate(),
314 .write_counter = tag_data.write_counter,
315 .version = 0,
316 .application_area_size = sizeof(ApplicationArea),
317 };
318 return ResultSuccess;
319}
320
321Result NfpDevice::GetModelInfo(ModelInfo& model_info) const {
322 if (device_state != DeviceState::TagMounted) {
323 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
324 if (device_state == DeviceState::TagRemoved) {
325 return TagRemoved;
326 }
327 return WrongDeviceState;
328 }
329
330 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
331 model_info = {
332 .character_id = model_info_data.character_id,
333 .character_variant = model_info_data.character_variant,
334 .amiibo_type = model_info_data.amiibo_type,
335 .model_number = model_info_data.model_number,
336 .series = model_info_data.series,
337 };
338 return ResultSuccess;
339}
340
341Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
342 if (device_state != DeviceState::TagMounted) {
343 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
344 if (device_state == DeviceState::TagRemoved) {
345 return TagRemoved;
346 }
347 return WrongDeviceState;
348 }
349
350 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
351 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
352 return WrongDeviceState;
353 }
354
355 if (tag_data.settings.settings.amiibo_initialized == 0) {
356 return RegistrationIsNotInitialized;
357 }
358
359 Service::Mii::MiiManager manager;
360 const auto& settings = tag_data.settings;
361
362 // TODO: Validate this data
363 register_info = {
364 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
365 .creation_date = settings.init_date.GetWriteDate(),
366 .amiibo_name = GetAmiiboName(settings),
367 .font_region = {},
368 };
369
370 return ResultSuccess;
371}
372
373Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) {
374 if (device_state != DeviceState::TagMounted) {
375 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
376 if (device_state == DeviceState::TagRemoved) {
377 return TagRemoved;
378 }
379 return WrongDeviceState;
380 }
381
382 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
383 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
384 return WrongDeviceState;
385 }
386
387 Service::Mii::MiiManager manager;
388 auto& settings = tag_data.settings;
389
390 settings.init_date = GetAmiiboDate(current_posix_time);
391 settings.write_date = GetAmiiboDate(current_posix_time);
392 settings.crc_counter++;
393 // TODO: Find how to calculate the crc check
394 // settings.crc = CalculateCRC(settings);
395
396 SetAmiiboName(settings, amiibo_name);
397 tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0));
398 settings.settings.amiibo_initialized.Assign(1);
399
400 return Flush();
401}
402
403Result NfpDevice::RestoreAmiibo() {
404 if (device_state != DeviceState::TagMounted) {
405 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
406 if (device_state == DeviceState::TagRemoved) {
407 return TagRemoved;
408 }
409 return WrongDeviceState;
410 }
411
412 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
413 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
414 return WrongDeviceState;
415 }
416
417 // TODO: Load amiibo from backup on system
418 LOG_ERROR(Service_NFP, "Not Implemented");
419 return ResultSuccess;
420}
421
422Result NfpDevice::DeleteAllData() {
423 const auto result = DeleteApplicationArea();
424 if (result.IsError()) {
425 return result;
426 }
427
428 if (device_state != DeviceState::TagMounted) {
429 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
430 if (device_state == DeviceState::TagRemoved) {
431 return TagRemoved;
432 }
433 return WrongDeviceState;
434 }
435
436 Common::TinyMT rng{};
437 rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
438 tag_data.settings.settings.amiibo_initialized.Assign(0);
439
440 return Flush();
441}
442
443Result NfpDevice::OpenApplicationArea(u32 access_id) {
444 if (device_state != DeviceState::TagMounted) {
445 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
446 if (device_state == DeviceState::TagRemoved) {
447 return TagRemoved;
448 }
449 return WrongDeviceState;
450 }
451
452 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
453 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
454 return WrongDeviceState;
455 }
456
457 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
458 LOG_WARNING(Service_NFP, "Application area is not initialized");
459 return ApplicationAreaIsNotInitialized;
460 }
461
462 if (tag_data.application_area_id != access_id) {
463 LOG_WARNING(Service_NFP, "Wrong application area id");
464 return WrongApplicationAreaId;
465 }
466
467 is_app_area_open = true;
468
469 return ResultSuccess;
470}
471
472Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
473 if (device_state != DeviceState::TagMounted) {
474 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
475 if (device_state == DeviceState::TagRemoved) {
476 return TagRemoved;
477 }
478 return WrongDeviceState;
479 }
480
481 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
482 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
483 return WrongDeviceState;
484 }
485
486 if (!is_app_area_open) {
487 LOG_ERROR(Service_NFP, "Application area is not open");
488 return WrongDeviceState;
489 }
490
491 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
492 LOG_ERROR(Service_NFP, "Application area is not initialized");
493 return ApplicationAreaIsNotInitialized;
494 }
495
496 if (data.size() > sizeof(ApplicationArea)) {
497 data.resize(sizeof(ApplicationArea));
498 }
499
500 memcpy(data.data(), tag_data.application_area.data(), data.size());
501
502 return ResultSuccess;
503}
504
505Result NfpDevice::SetApplicationArea(std::span<const u8> data) {
506 if (device_state != DeviceState::TagMounted) {
507 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
508 if (device_state == DeviceState::TagRemoved) {
509 return TagRemoved;
510 }
511 return WrongDeviceState;
512 }
513
514 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
515 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
516 return WrongDeviceState;
517 }
518
519 if (!is_app_area_open) {
520 LOG_ERROR(Service_NFP, "Application area is not open");
521 return WrongDeviceState;
522 }
523
524 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
525 LOG_ERROR(Service_NFP, "Application area is not initialized");
526 return ApplicationAreaIsNotInitialized;
527 }
528
529 if (data.size() > sizeof(ApplicationArea)) {
530 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
531 return ResultUnknown;
532 }
533
534 Common::TinyMT rng{};
535 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
536 // Fill remaining data with random numbers
537 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
538 sizeof(ApplicationArea) - data.size());
539
540 tag_data.applicaton_write_counter++;
541 is_data_moddified = true;
542
543 return ResultSuccess;
544}
545
546Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) {
547 if (device_state != DeviceState::TagMounted) {
548 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
549 if (device_state == DeviceState::TagRemoved) {
550 return TagRemoved;
551 }
552 return WrongDeviceState;
553 }
554
555 if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
556 LOG_ERROR(Service_NFP, "Application area already exist");
557 return ApplicationAreaExist;
558 }
559
560 return RecreateApplicationArea(access_id, data);
561}
562
563Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) {
564 if (device_state != DeviceState::TagMounted) {
565 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
566 if (device_state == DeviceState::TagRemoved) {
567 return TagRemoved;
568 }
569 return WrongDeviceState;
570 }
571
572 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
573 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
574 return WrongDeviceState;
575 }
576
577 if (data.size() > sizeof(ApplicationArea)) {
578 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
579 return WrongApplicationAreaSize;
580 }
581
582 Common::TinyMT rng{};
583 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
584 // Fill remaining data with random numbers
585 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
586 sizeof(ApplicationArea) - data.size());
587
588 // TODO: Investigate why the title id needs to be moddified
589 tag_data.title_id = system.GetCurrentProcessProgramID();
590 tag_data.title_id = tag_data.title_id | 0x30000000ULL;
591 tag_data.settings.settings.appdata_initialized.Assign(1);
592 tag_data.application_area_id = access_id;
593 tag_data.applicaton_write_counter++;
594 tag_data.unknown = {};
595
596 return Flush();
597}
598
599Result NfpDevice::DeleteApplicationArea() {
600 if (device_state != DeviceState::TagMounted) {
601 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
602 if (device_state == DeviceState::TagRemoved) {
603 return TagRemoved;
604 }
605 return WrongDeviceState;
606 }
607
608 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
609 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
610 return WrongDeviceState;
611 }
612
613 Common::TinyMT rng{};
614 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea));
615 rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64));
616 rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
617 tag_data.settings.settings.appdata_initialized.Assign(0);
618 tag_data.applicaton_write_counter++;
619 tag_data.unknown = {};
620
621 return Flush();
622}
623
624u64 NfpDevice::GetHandle() const {
625 // Generate a handle based of the npad id
626 return static_cast<u64>(npad_id);
627}
628
629u32 NfpDevice::GetApplicationAreaSize() const {
630 return sizeof(ApplicationArea);
631}
632
633DeviceState NfpDevice::GetCurrentState() const {
634 return device_state;
635}
636
637Core::HID::NpadIdType NfpDevice::GetNpadId() const {
638 return npad_id;
639}
640
641AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
642 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
643 AmiiboName amiibo_name{};
644
645 // Convert from big endian to little endian
646 for (std::size_t i = 0; i < amiibo_name_length; i++) {
647 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
648 }
649
650 // Convert from utf16 to utf8
651 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
652 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
653
654 return amiibo_name;
655}
656
657void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) {
658 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
659
660 // Convert from utf8 to utf16
661 const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
662 memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(),
663 amiibo_name_utf16.size() * sizeof(char16_t));
664
665 // Convert from little endian to big endian
666 for (std::size_t i = 0; i < amiibo_name_length; i++) {
667 settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
668 }
669}
670
671AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
672 const auto& time_zone_manager =
673 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
674 Time::TimeZone::CalendarInfo calendar_info{};
675 AmiiboDate amiibo_date{};
676
677 amiibo_date.SetYear(2000);
678 amiibo_date.SetMonth(1);
679 amiibo_date.SetDay(1);
680
681 if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
682 amiibo_date.SetYear(calendar_info.time.year);
683 amiibo_date.SetMonth(calendar_info.time.month);
684 amiibo_date.SetDay(calendar_info.time.day);
685 }
686
687 return amiibo_date;
688}
689
690} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
new file mode 100644
index 000000000..76d0e9ae4
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -0,0 +1,100 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <vector>
8
9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/nfp/nfp_types.h"
12#include "core/hle/service/service.h"
13
14namespace Kernel {
15class KEvent;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace Core {
20class System;
21} // namespace Core
22
23namespace Core::HID {
24class EmulatedController;
25enum class ControllerTriggerType;
26enum class NpadIdType : u32;
27} // namespace Core::HID
28
29namespace Service::NFP {
30class NfpDevice {
31public:
32 NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
33 KernelHelpers::ServiceContext& service_context_,
34 Kernel::KEvent* availability_change_event_);
35 ~NfpDevice();
36
37 void Initialize();
38 void Finalize();
39
40 Result StartDetection(s32 protocol_);
41 Result StopDetection();
42 Result Mount(MountTarget mount_target);
43 Result Unmount();
44 Result Flush();
45
46 Result GetTagInfo(TagInfo& tag_info) const;
47 Result GetCommonInfo(CommonInfo& common_info) const;
48 Result GetModelInfo(ModelInfo& model_info) const;
49 Result GetRegisterInfo(RegisterInfo& register_info) const;
50
51 Result SetNicknameAndOwner(const AmiiboName& amiibo_name);
52 Result RestoreAmiibo();
53 Result DeleteAllData();
54
55 Result OpenApplicationArea(u32 access_id);
56 Result GetApplicationArea(std::vector<u8>& data) const;
57 Result SetApplicationArea(std::span<const u8> data);
58 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
59 Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
60 Result DeleteApplicationArea();
61
62 u64 GetHandle() const;
63 u32 GetApplicationAreaSize() const;
64 DeviceState GetCurrentState() const;
65 Core::HID::NpadIdType GetNpadId() const;
66
67 Kernel::KReadableEvent& GetActivateEvent() const;
68 Kernel::KReadableEvent& GetDeactivateEvent() const;
69
70private:
71 void NpadUpdate(Core::HID::ControllerTriggerType type);
72 bool LoadAmiibo(std::span<const u8> data);
73 void CloseAmiibo();
74
75 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
76 void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
77 AmiiboDate GetAmiiboDate(s64 posix_time) const;
78
79 bool is_controller_set{};
80 int callback_key;
81 const Core::HID::NpadIdType npad_id;
82 Core::System& system;
83 Core::HID::EmulatedController* npad_device = nullptr;
84 KernelHelpers::ServiceContext& service_context;
85 Kernel::KEvent* activate_event = nullptr;
86 Kernel::KEvent* deactivate_event = nullptr;
87 Kernel::KEvent* availability_change_event = nullptr;
88
89 bool is_data_moddified{};
90 bool is_app_area_open{};
91 s32 protocol{};
92 s64 current_posix_time{};
93 MountTarget mount_target{MountTarget::None};
94 DeviceState device_state{DeviceState::Unavailable};
95
96 NTAG215File tag_data{};
97 EncryptedNTAG215File encrypted_tag_data{};
98};
99
100} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h
new file mode 100644
index 000000000..d8e4cf094
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::NFP {
9
10constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
11constexpr Result InvalidArgument(ErrorModule::NFP, 65);
12constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68);
13constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
14constexpr Result NfcDisabled(ErrorModule::NFP, 80);
15constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
16constexpr Result TagRemoved(ErrorModule::NFP, 97);
17constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
18constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
19constexpr Result CorruptedData(ErrorModule::NFP, 144);
20constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
21constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
22constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
23
24} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/nfp_types.h
index bf2de811a..63d5917cb 100644
--- a/src/core/hle/service/nfp/amiibo_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "common/swap.h"
8#include "core/hle/service/mii/types.h" 9#include "core/hle/service/mii/types.h"
9 10
10namespace Service::NFP { 11namespace Service::NFP {
@@ -16,18 +17,13 @@ enum class ServiceType : u32 {
16 System, 17 System,
17}; 18};
18 19
19enum class State : u32 {
20 NonInitialized,
21 Initialized,
22};
23
24enum class DeviceState : u32 { 20enum class DeviceState : u32 {
25 Initialized, 21 Initialized,
26 SearchingForTag, 22 SearchingForTag,
27 TagFound, 23 TagFound,
28 TagRemoved, 24 TagRemoved,
29 TagMounted, 25 TagMounted,
30 Unaviable, 26 Unavailable,
31 Finalized, 27 Finalized,
32}; 28};
33 29
@@ -36,6 +32,7 @@ enum class ModelType : u32 {
36}; 32};
37 33
38enum class MountTarget : u32 { 34enum class MountTarget : u32 {
35 None,
39 Rom, 36 Rom,
40 Ram, 37 Ram,
41 All, 38 All,
@@ -73,21 +70,101 @@ enum class AmiiboSeries : u8 {
73 Diablo, 70 Diablo,
74}; 71};
75 72
76using TagUuid = std::array<u8, 10>; 73enum class TagType : u32 {
74 None,
75 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
76 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
77 Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
78 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
79 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
80};
81
82enum class PackedTagType : u8 {
83 None,
84 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
85 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
86 Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
87 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
88 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
89};
90
91enum class TagProtocol : u32 {
92 None,
93 TypeA, // ISO14443A
94 TypeB, // ISO14443B
95 TypeF, // Sony Felica
96};
97
98using UniqueSerialNumber = std::array<u8, 7>;
99using LockBytes = std::array<u8, 2>;
77using HashData = std::array<u8, 0x20>; 100using HashData = std::array<u8, 0x20>;
78using ApplicationArea = std::array<u8, 0xD8>; 101using ApplicationArea = std::array<u8, 0xD8>;
102using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
103
104struct TagUuid {
105 UniqueSerialNumber uid;
106 u8 nintendo_id;
107 LockBytes lock_bytes;
108};
109static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
110
111struct WriteDate {
112 u16 year;
113 u8 month;
114 u8 day;
115};
116static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size");
79 117
80struct AmiiboDate { 118struct AmiiboDate {
81 u16 raw_date{}; 119 u16 raw_date{};
82 120
121 u16 GetValue() const {
122 return Common::swap16(raw_date);
123 }
124
83 u16 GetYear() const { 125 u16 GetYear() const {
84 return static_cast<u16>(((raw_date & 0xFE00) >> 9) + 2000); 126 return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
85 } 127 }
86 u8 GetMonth() const { 128 u8 GetMonth() const {
87 return static_cast<u8>(((raw_date & 0x01E0) >> 5) - 1); 129 return static_cast<u8>((GetValue() & 0x01E0) >> 5);
88 } 130 }
89 u8 GetDay() const { 131 u8 GetDay() const {
90 return static_cast<u8>(raw_date & 0x001F); 132 return static_cast<u8>(GetValue() & 0x001F);
133 }
134
135 WriteDate GetWriteDate() const {
136 if (!IsValidDate()) {
137 return {
138 .year = 2000,
139 .month = 1,
140 .day = 1,
141 };
142 }
143 return {
144 .year = GetYear(),
145 .month = GetMonth(),
146 .day = GetDay(),
147 };
148 }
149
150 void SetYear(u16 year) {
151 const u16 year_converted = static_cast<u16>((year - 2000) << 9);
152 raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
153 }
154 void SetMonth(u8 month) {
155 const u16 month_converted = static_cast<u16>(month << 5);
156 raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
157 }
158 void SetDay(u8 day) {
159 const u16 day_converted = static_cast<u16>(day);
160 raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted);
161 }
162
163 bool IsValidDate() const {
164 const bool is_day_valid = GetDay() > 0 && GetDay() < 32;
165 const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13;
166 const bool is_year_valid = GetYear() >= 2000;
167 return is_year_valid && is_month_valid && is_day_valid;
91 } 168 }
92}; 169};
93static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); 170static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
@@ -117,9 +194,9 @@ struct AmiiboModelInfo {
117 u16 character_id; 194 u16 character_id;
118 u8 character_variant; 195 u8 character_variant;
119 AmiiboType amiibo_type; 196 AmiiboType amiibo_type;
120 u16 model_number; 197 u16_be model_number;
121 AmiiboSeries series; 198 AmiiboSeries series;
122 u8 constant_value; // Must be 02 199 PackedTagType tag_type;
123 INSERT_PADDING_BYTES(0x4); // Unknown 200 INSERT_PADDING_BYTES(0x4); // Unknown
124}; 201};
125static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); 202static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
@@ -134,7 +211,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz
134#pragma pack(1) 211#pragma pack(1)
135struct EncryptedAmiiboFile { 212struct EncryptedAmiiboFile {
136 u8 constant_value; // Must be A5 213 u8 constant_value; // Must be A5
137 u16 write_counter; // Number of times the amiibo has been written? 214 u16_be write_counter; // Number of times the amiibo has been written?
138 INSERT_PADDING_BYTES(0x1); // Unknown 1 215 INSERT_PADDING_BYTES(0x1); // Unknown 1
139 AmiiboSettings settings; // Encrypted amiibo settings 216 AmiiboSettings settings; // Encrypted amiibo settings
140 HashData hmac_tag; // Hash 217 HashData hmac_tag; // Hash
@@ -146,18 +223,18 @@ struct EncryptedAmiiboFile {
146 u16_be applicaton_write_counter; // Encrypted Counter 223 u16_be applicaton_write_counter; // Encrypted Counter
147 u32_be application_area_id; // Encrypted Game id 224 u32_be application_area_id; // Encrypted Game id
148 std::array<u8, 0x2> unknown; 225 std::array<u8, 0x2> unknown;
149 HashData hash; // Probably a SHA256-HMAC hash? 226 std::array<u32, 0x8> unknown2;
150 ApplicationArea application_area; // Encrypted Game data 227 ApplicationArea application_area; // Encrypted Game data
151}; 228};
152static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); 229static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
153 230
154struct NTAG215File { 231struct NTAG215File {
155 std::array<u8, 0x2> uuid2; 232 LockBytes lock_bytes; // Tag UUID
156 u16 static_lock; // Set defined pages as read only 233 u16 static_lock; // Set defined pages as read only
157 u32 compability_container; // Defines available memory 234 u32 compability_container; // Defines available memory
158 HashData hmac_data; // Hash 235 HashData hmac_data; // Hash
159 u8 constant_value; // Must be A5 236 u8 constant_value; // Must be A5
160 u16 write_counter; // Number of times the amiibo has been written? 237 u16_be write_counter; // Number of times the amiibo has been written?
161 INSERT_PADDING_BYTES(0x1); // Unknown 1 238 INSERT_PADDING_BYTES(0x1); // Unknown 1
162 AmiiboSettings settings; 239 AmiiboSettings settings;
163 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data 240 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
@@ -165,10 +242,11 @@ struct NTAG215File {
165 u16_be applicaton_write_counter; // Encrypted Counter 242 u16_be applicaton_write_counter; // Encrypted Counter
166 u32_be application_area_id; 243 u32_be application_area_id;
167 std::array<u8, 0x2> unknown; 244 std::array<u8, 0x2> unknown;
168 HashData hash; // Probably a SHA256-HMAC hash? 245 std::array<u32, 0x8> unknown2;
169 ApplicationArea application_area; // Encrypted Game data 246 ApplicationArea application_area; // Encrypted Game data
170 HashData hmac_tag; // Hash 247 HashData hmac_tag; // Hash
171 std::array<u8, 0x8> uuid; 248 UniqueSerialNumber uid; // Unique serial number
249 u8 nintendo_id; // Tag UUID
172 AmiiboModelInfo model_info; 250 AmiiboModelInfo model_info;
173 HashData keygen_salt; // Salt 251 HashData keygen_salt; // Salt
174 u32 dynamic_lock; // Dynamic lock 252 u32 dynamic_lock; // Dynamic lock
@@ -194,4 +272,44 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an
194static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, 272static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
195 "EncryptedNTAG215File must be trivially copyable."); 273 "EncryptedNTAG215File must be trivially copyable.");
196 274
275struct TagInfo {
276 UniqueSerialNumber uuid;
277 INSERT_PADDING_BYTES(0x3);
278 u8 uuid_length;
279 INSERT_PADDING_BYTES(0x15);
280 TagProtocol protocol;
281 TagType tag_type;
282 INSERT_PADDING_BYTES(0x30);
283};
284static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
285
286struct CommonInfo {
287 WriteDate last_write_date;
288 u16 write_counter;
289 u8 version;
290 INSERT_PADDING_BYTES(0x1);
291 u32 application_area_size;
292 INSERT_PADDING_BYTES(0x34);
293};
294static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
295
296struct ModelInfo {
297 u16 character_id;
298 u8 character_variant;
299 AmiiboType amiibo_type;
300 u16 model_number;
301 AmiiboSeries series;
302 INSERT_PADDING_BYTES(0x39); // Unknown
303};
304static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
305
306struct RegisterInfo {
307 Service::Mii::CharInfo mii_char_info;
308 WriteDate creation_date;
309 AmiiboName amiibo_name;
310 u8 font_region;
311 INSERT_PADDING_BYTES(0x7A);
312};
313static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
314
197} // namespace Service::NFP 315} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 2d7b156cf..33e2ef518 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -1,18 +1,671 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include <atomic>
6
7#include "common/logging/log.h"
8#include "core/core.h"
9#include "core/hid/hid_types.h"
10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/k_event.h"
12#include "core/hle/service/nfp/nfp_device.h"
13#include "core/hle/service/nfp/nfp_result.h"
4#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
5 15
6namespace Service::NFP { 16namespace Service::NFP {
7 17
8NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) 18IUser::IUser(Core::System& system_)
9 : Interface(std::move(module_), system_, "nfp:user") { 19 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
10 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
11 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, 21 {0, &IUser::Initialize, "Initialize"},
22 {1, &IUser::Finalize, "Finalize"},
23 {2, &IUser::ListDevices, "ListDevices"},
24 {3, &IUser::StartDetection, "StartDetection"},
25 {4, &IUser::StopDetection, "StopDetection"},
26 {5, &IUser::Mount, "Mount"},
27 {6, &IUser::Unmount, "Unmount"},
28 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
29 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
30 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
31 {10, &IUser::Flush, "Flush"},
32 {11, &IUser::Restore, "Restore"},
33 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
34 {13, &IUser::GetTagInfo, "GetTagInfo"},
35 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
36 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
37 {16, &IUser::GetModelInfo, "GetModelInfo"},
38 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
39 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
40 {19, &IUser::GetState, "GetState"},
41 {20, &IUser::GetDeviceState, "GetDeviceState"},
42 {21, &IUser::GetNpadId, "GetNpadId"},
43 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
44 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
45 {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
12 }; 46 };
13 RegisterHandlers(functions); 47 RegisterHandlers(functions);
48
49 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
50
51 for (u32 device_index = 0; device_index < 10; device_index++) {
52 devices[device_index] =
53 std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system,
54 service_context, availability_change_event);
55 }
56}
57
58void IUser::Initialize(Kernel::HLERequestContext& ctx) {
59 LOG_INFO(Service_NFC, "called");
60
61 state = State::Initialized;
62
63 for (auto& device : devices) {
64 device->Initialize();
65 }
66
67 IPC::ResponseBuilder rb{ctx, 2, 0};
68 rb.Push(ResultSuccess);
69}
70
71void IUser::Finalize(Kernel::HLERequestContext& ctx) {
72 LOG_INFO(Service_NFP, "called");
73
74 state = State::NonInitialized;
75
76 for (auto& device : devices) {
77 device->Finalize();
78 }
79
80 IPC::ResponseBuilder rb{ctx, 2};
81 rb.Push(ResultSuccess);
82}
83
84void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
85 LOG_INFO(Service_NFP, "called");
86
87 if (state == State::NonInitialized) {
88 IPC::ResponseBuilder rb{ctx, 2};
89 rb.Push(NfcDisabled);
90 return;
91 }
92
93 if (!ctx.CanWriteBuffer()) {
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(InvalidArgument);
96 return;
97 }
98
99 if (ctx.GetWriteBufferSize() == 0) {
100 IPC::ResponseBuilder rb{ctx, 2};
101 rb.Push(InvalidArgument);
102 return;
103 }
104
105 std::vector<u64> nfp_devices;
106 const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64);
107
108 for (auto& device : devices) {
109 if (nfp_devices.size() >= max_allowed_devices) {
110 continue;
111 }
112 if (device->GetCurrentState() != DeviceState::Unavailable) {
113 nfp_devices.push_back(device->GetHandle());
114 }
115 }
116
117 if (nfp_devices.size() == 0) {
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(DeviceNotFound);
120 return;
121 }
122
123 ctx.WriteBuffer(nfp_devices);
124
125 IPC::ResponseBuilder rb{ctx, 3};
126 rb.Push(ResultSuccess);
127 rb.Push(static_cast<s32>(nfp_devices.size()));
128}
129
130void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
131 IPC::RequestParser rp{ctx};
132 const auto device_handle{rp.Pop<u64>()};
133 const auto nfp_protocol{rp.Pop<s32>()};
134 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
135
136 if (state == State::NonInitialized) {
137 IPC::ResponseBuilder rb{ctx, 2};
138 rb.Push(NfcDisabled);
139 return;
140 }
141
142 auto device = GetNfpDevice(device_handle);
143
144 if (!device.has_value()) {
145 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(DeviceNotFound);
147 return;
148 }
149
150 const auto result = device.value()->StartDetection(nfp_protocol);
151 IPC::ResponseBuilder rb{ctx, 2};
152 rb.Push(result);
153}
154
155void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
156 IPC::RequestParser rp{ctx};
157 const auto device_handle{rp.Pop<u64>()};
158 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
159
160 if (state == State::NonInitialized) {
161 IPC::ResponseBuilder rb{ctx, 2};
162 rb.Push(NfcDisabled);
163 return;
164 }
165
166 auto device = GetNfpDevice(device_handle);
167
168 if (!device.has_value()) {
169 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(DeviceNotFound);
171 return;
172 }
173
174 const auto result = device.value()->StopDetection();
175 IPC::ResponseBuilder rb{ctx, 2};
176 rb.Push(result);
177}
178
179void IUser::Mount(Kernel::HLERequestContext& ctx) {
180 IPC::RequestParser rp{ctx};
181 const auto device_handle{rp.Pop<u64>()};
182 const auto model_type{rp.PopEnum<ModelType>()};
183 const auto mount_target{rp.PopEnum<MountTarget>()};
184 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
185 model_type, mount_target);
186
187 if (state == State::NonInitialized) {
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(NfcDisabled);
190 return;
191 }
192
193 auto device = GetNfpDevice(device_handle);
194
195 if (!device.has_value()) {
196 IPC::ResponseBuilder rb{ctx, 2};
197 rb.Push(DeviceNotFound);
198 return;
199 }
200
201 const auto result = device.value()->Mount(mount_target);
202 IPC::ResponseBuilder rb{ctx, 2};
203 rb.Push(result);
204}
205
206void IUser::Unmount(Kernel::HLERequestContext& ctx) {
207 IPC::RequestParser rp{ctx};
208 const auto device_handle{rp.Pop<u64>()};
209 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
210
211 if (state == State::NonInitialized) {
212 IPC::ResponseBuilder rb{ctx, 2};
213 rb.Push(NfcDisabled);
214 return;
215 }
216
217 auto device = GetNfpDevice(device_handle);
218
219 if (!device.has_value()) {
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(DeviceNotFound);
222 return;
223 }
224
225 const auto result = device.value()->Unmount();
226 IPC::ResponseBuilder rb{ctx, 2};
227 rb.Push(result);
228}
229
230void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
231 IPC::RequestParser rp{ctx};
232 const auto device_handle{rp.Pop<u64>()};
233 const auto access_id{rp.Pop<u32>()};
234 LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id);
235
236 if (state == State::NonInitialized) {
237 IPC::ResponseBuilder rb{ctx, 2};
238 rb.Push(NfcDisabled);
239 return;
240 }
241
242 auto device = GetNfpDevice(device_handle);
243
244 if (!device.has_value()) {
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(DeviceNotFound);
247 return;
248 }
249
250 const auto result = device.value()->OpenApplicationArea(access_id);
251 IPC::ResponseBuilder rb{ctx, 2};
252 rb.Push(result);
253}
254
255void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto device_handle{rp.Pop<u64>()};
258 const auto data_size = ctx.GetWriteBufferSize();
259 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
260
261 if (state == State::NonInitialized) {
262 IPC::ResponseBuilder rb{ctx, 2};
263 rb.Push(NfcDisabled);
264 return;
265 }
266
267 if (!ctx.CanWriteBuffer()) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(InvalidArgument);
270 return;
271 }
272
273 auto device = GetNfpDevice(device_handle);
274
275 if (!device.has_value()) {
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(DeviceNotFound);
278 return;
279 }
280
281 std::vector<u8> data(data_size);
282 const auto result = device.value()->GetApplicationArea(data);
283 ctx.WriteBuffer(data);
284 IPC::ResponseBuilder rb{ctx, 3};
285 rb.Push(result);
286 rb.Push(static_cast<u32>(data_size));
287}
288
289void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
290 IPC::RequestParser rp{ctx};
291 const auto device_handle{rp.Pop<u64>()};
292 const auto data{ctx.ReadBuffer()};
293 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size());
294
295 if (state == State::NonInitialized) {
296 IPC::ResponseBuilder rb{ctx, 2};
297 rb.Push(NfcDisabled);
298 return;
299 }
300
301 if (!ctx.CanReadBuffer()) {
302 IPC::ResponseBuilder rb{ctx, 2};
303 rb.Push(InvalidArgument);
304 return;
305 }
306
307 auto device = GetNfpDevice(device_handle);
308
309 if (!device.has_value()) {
310 IPC::ResponseBuilder rb{ctx, 2};
311 rb.Push(DeviceNotFound);
312 return;
313 }
314
315 const auto result = device.value()->SetApplicationArea(data);
316 IPC::ResponseBuilder rb{ctx, 2};
317 rb.Push(result);
318}
319
320void IUser::Flush(Kernel::HLERequestContext& ctx) {
321 IPC::RequestParser rp{ctx};
322 const auto device_handle{rp.Pop<u64>()};
323 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
324
325 if (state == State::NonInitialized) {
326 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(NfcDisabled);
328 return;
329 }
330
331 auto device = GetNfpDevice(device_handle);
332
333 if (!device.has_value()) {
334 IPC::ResponseBuilder rb{ctx, 2};
335 rb.Push(DeviceNotFound);
336 return;
337 }
338
339 const auto result = device.value()->Flush();
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(result);
342}
343
344void IUser::Restore(Kernel::HLERequestContext& ctx) {
345 IPC::RequestParser rp{ctx};
346 const auto device_handle{rp.Pop<u64>()};
347 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
348
349 if (state == State::NonInitialized) {
350 IPC::ResponseBuilder rb{ctx, 2};
351 rb.Push(NfcDisabled);
352 return;
353 }
354
355 auto device = GetNfpDevice(device_handle);
356
357 if (!device.has_value()) {
358 IPC::ResponseBuilder rb{ctx, 2};
359 rb.Push(DeviceNotFound);
360 return;
361 }
362
363 const auto result = device.value()->RestoreAmiibo();
364 IPC::ResponseBuilder rb{ctx, 2};
365 rb.Push(result);
366}
367
368void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
369 IPC::RequestParser rp{ctx};
370 const auto device_handle{rp.Pop<u64>()};
371 const auto access_id{rp.Pop<u32>()};
372 const auto data{ctx.ReadBuffer()};
373 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
374 access_id, data.size());
375
376 if (state == State::NonInitialized) {
377 IPC::ResponseBuilder rb{ctx, 2};
378 rb.Push(NfcDisabled);
379 return;
380 }
381
382 if (!ctx.CanReadBuffer()) {
383 IPC::ResponseBuilder rb{ctx, 2};
384 rb.Push(InvalidArgument);
385 return;
386 }
387
388 auto device = GetNfpDevice(device_handle);
389
390 if (!device.has_value()) {
391 IPC::ResponseBuilder rb{ctx, 2};
392 rb.Push(DeviceNotFound);
393 return;
394 }
395
396 const auto result = device.value()->CreateApplicationArea(access_id, data);
397 IPC::ResponseBuilder rb{ctx, 2};
398 rb.Push(result);
399}
400
401void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
402 IPC::RequestParser rp{ctx};
403 const auto device_handle{rp.Pop<u64>()};
404 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
405
406 if (state == State::NonInitialized) {
407 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(NfcDisabled);
409 return;
410 }
411
412 auto device = GetNfpDevice(device_handle);
413
414 if (!device.has_value()) {
415 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(DeviceNotFound);
417 return;
418 }
419
420 TagInfo tag_info{};
421 const auto result = device.value()->GetTagInfo(tag_info);
422 ctx.WriteBuffer(tag_info);
423 IPC::ResponseBuilder rb{ctx, 2};
424 rb.Push(result);
425}
426
427void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
428 IPC::RequestParser rp{ctx};
429 const auto device_handle{rp.Pop<u64>()};
430 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
431
432 if (state == State::NonInitialized) {
433 IPC::ResponseBuilder rb{ctx, 2};
434 rb.Push(NfcDisabled);
435 return;
436 }
437
438 auto device = GetNfpDevice(device_handle);
439
440 if (!device.has_value()) {
441 IPC::ResponseBuilder rb{ctx, 2};
442 rb.Push(DeviceNotFound);
443 return;
444 }
445
446 RegisterInfo register_info{};
447 const auto result = device.value()->GetRegisterInfo(register_info);
448 ctx.WriteBuffer(register_info);
449 IPC::ResponseBuilder rb{ctx, 2};
450 rb.Push(result);
451}
452
453void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
454 IPC::RequestParser rp{ctx};
455 const auto device_handle{rp.Pop<u64>()};
456 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
457
458 if (state == State::NonInitialized) {
459 IPC::ResponseBuilder rb{ctx, 2};
460 rb.Push(NfcDisabled);
461 return;
462 }
463
464 auto device = GetNfpDevice(device_handle);
465
466 if (!device.has_value()) {
467 IPC::ResponseBuilder rb{ctx, 2};
468 rb.Push(DeviceNotFound);
469 return;
470 }
471
472 CommonInfo common_info{};
473 const auto result = device.value()->GetCommonInfo(common_info);
474 ctx.WriteBuffer(common_info);
475 IPC::ResponseBuilder rb{ctx, 2};
476 rb.Push(result);
477}
478
479void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
480 IPC::RequestParser rp{ctx};
481 const auto device_handle{rp.Pop<u64>()};
482 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
483
484 if (state == State::NonInitialized) {
485 IPC::ResponseBuilder rb{ctx, 2};
486 rb.Push(NfcDisabled);
487 return;
488 }
489
490 auto device = GetNfpDevice(device_handle);
491
492 if (!device.has_value()) {
493 IPC::ResponseBuilder rb{ctx, 2};
494 rb.Push(DeviceNotFound);
495 return;
496 }
497
498 ModelInfo model_info{};
499 const auto result = device.value()->GetModelInfo(model_info);
500 ctx.WriteBuffer(model_info);
501 IPC::ResponseBuilder rb{ctx, 2};
502 rb.Push(result);
503}
504
505void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
506 IPC::RequestParser rp{ctx};
507 const auto device_handle{rp.Pop<u64>()};
508 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
509
510 if (state == State::NonInitialized) {
511 IPC::ResponseBuilder rb{ctx, 2};
512 rb.Push(NfcDisabled);
513 return;
514 }
515
516 auto device = GetNfpDevice(device_handle);
517
518 if (!device.has_value()) {
519 IPC::ResponseBuilder rb{ctx, 2};
520 rb.Push(DeviceNotFound);
521 return;
522 }
523
524 IPC::ResponseBuilder rb{ctx, 2, 1};
525 rb.Push(ResultSuccess);
526 rb.PushCopyObjects(device.value()->GetActivateEvent());
527}
528
529void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
530 IPC::RequestParser rp{ctx};
531 const auto device_handle{rp.Pop<u64>()};
532 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
533
534 if (state == State::NonInitialized) {
535 IPC::ResponseBuilder rb{ctx, 2};
536 rb.Push(NfcDisabled);
537 return;
538 }
539
540 auto device = GetNfpDevice(device_handle);
541
542 if (!device.has_value()) {
543 IPC::ResponseBuilder rb{ctx, 2};
544 rb.Push(DeviceNotFound);
545 return;
546 }
547
548 IPC::ResponseBuilder rb{ctx, 2, 1};
549 rb.Push(ResultSuccess);
550 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
551}
552
553void IUser::GetState(Kernel::HLERequestContext& ctx) {
554 LOG_DEBUG(Service_NFC, "called");
555
556 IPC::ResponseBuilder rb{ctx, 3, 0};
557 rb.Push(ResultSuccess);
558 rb.PushEnum(state);
559}
560
561void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
562 IPC::RequestParser rp{ctx};
563 const auto device_handle{rp.Pop<u64>()};
564 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
565
566 auto device = GetNfpDevice(device_handle);
567
568 if (!device.has_value()) {
569 IPC::ResponseBuilder rb{ctx, 2};
570 rb.Push(DeviceNotFound);
571 return;
572 }
573
574 IPC::ResponseBuilder rb{ctx, 3};
575 rb.Push(ResultSuccess);
576 rb.PushEnum(device.value()->GetCurrentState());
577}
578
579void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
580 IPC::RequestParser rp{ctx};
581 const auto device_handle{rp.Pop<u64>()};
582 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
583
584 if (state == State::NonInitialized) {
585 IPC::ResponseBuilder rb{ctx, 2};
586 rb.Push(NfcDisabled);
587 return;
588 }
589
590 auto device = GetNfpDevice(device_handle);
591
592 if (!device.has_value()) {
593 IPC::ResponseBuilder rb{ctx, 2};
594 rb.Push(DeviceNotFound);
595 return;
596 }
597
598 IPC::ResponseBuilder rb{ctx, 3};
599 rb.Push(ResultSuccess);
600 rb.PushEnum(device.value()->GetNpadId());
601}
602
603void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
604 IPC::RequestParser rp{ctx};
605 const auto device_handle{rp.Pop<u64>()};
606 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
607
608 auto device = GetNfpDevice(device_handle);
609
610 if (!device.has_value()) {
611 IPC::ResponseBuilder rb{ctx, 2};
612 rb.Push(DeviceNotFound);
613 return;
614 }
615
616 IPC::ResponseBuilder rb{ctx, 3};
617 rb.Push(ResultSuccess);
618 rb.Push(device.value()->GetApplicationAreaSize());
14} 619}
15 620
16NFP_User::~NFP_User() = default; 621void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
622 LOG_INFO(Service_NFP, "called");
623
624 if (state == State::NonInitialized) {
625 IPC::ResponseBuilder rb{ctx, 2};
626 rb.Push(NfcDisabled);
627 return;
628 }
629
630 IPC::ResponseBuilder rb{ctx, 2, 1};
631 rb.Push(ResultSuccess);
632 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
633}
634
635void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
636 IPC::RequestParser rp{ctx};
637 const auto device_handle{rp.Pop<u64>()};
638 const auto access_id{rp.Pop<u32>()};
639 const auto data{ctx.ReadBuffer()};
640 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
641 access_id, data.size());
642
643 if (state == State::NonInitialized) {
644 IPC::ResponseBuilder rb{ctx, 2};
645 rb.Push(NfcDisabled);
646 return;
647 }
648
649 auto device = GetNfpDevice(device_handle);
650
651 if (!device.has_value()) {
652 IPC::ResponseBuilder rb{ctx, 2};
653 rb.Push(DeviceNotFound);
654 return;
655 }
656
657 const auto result = device.value()->RecreateApplicationArea(access_id, data);
658 IPC::ResponseBuilder rb{ctx, 2};
659 rb.Push(result);
660}
661
662std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) {
663 for (auto& device : devices) {
664 if (device->GetHandle() == handle) {
665 return device;
666 }
667 }
668 return std::nullopt;
669}
17 670
18} // namespace Service::NFP 671} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 519ff56ee..47aff3695 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -3,14 +3,56 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/nfp/nfp.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
7 8
8namespace Service::NFP { 9namespace Service::NFP {
10class NfpDevice;
9 11
10class NFP_User final : public Module::Interface { 12class IUser final : public ServiceFramework<IUser> {
11public: 13public:
12 explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); 14 explicit IUser(Core::System& system_);
13 ~NFP_User() override; 15
16private:
17 enum class State : u32 {
18 NonInitialized,
19 Initialized,
20 };
21
22 void Initialize(Kernel::HLERequestContext& ctx);
23 void Finalize(Kernel::HLERequestContext& ctx);
24 void ListDevices(Kernel::HLERequestContext& ctx);
25 void StartDetection(Kernel::HLERequestContext& ctx);
26 void StopDetection(Kernel::HLERequestContext& ctx);
27 void Mount(Kernel::HLERequestContext& ctx);
28 void Unmount(Kernel::HLERequestContext& ctx);
29 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
30 void GetApplicationArea(Kernel::HLERequestContext& ctx);
31 void SetApplicationArea(Kernel::HLERequestContext& ctx);
32 void Flush(Kernel::HLERequestContext& ctx);
33 void Restore(Kernel::HLERequestContext& ctx);
34 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
35 void GetTagInfo(Kernel::HLERequestContext& ctx);
36 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
37 void GetCommonInfo(Kernel::HLERequestContext& ctx);
38 void GetModelInfo(Kernel::HLERequestContext& ctx);
39 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
40 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
41 void GetState(Kernel::HLERequestContext& ctx);
42 void GetDeviceState(Kernel::HLERequestContext& ctx);
43 void GetNpadId(Kernel::HLERequestContext& ctx);
44 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
45 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
46 void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
47
48 std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
49
50 KernelHelpers::ServiceContext service_context;
51
52 std::array<std::shared_ptr<NfpDevice>, 10> devices{};
53
54 State state{State::NonInitialized};
55 Kernel::KEvent* availability_change_event;
14}; 56};
15 57
16} // namespace Service::NFP 58} // namespace Service::NFP
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index b2bb7426d..5a8a91e0b 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -328,7 +328,7 @@ private:
328 void StartTask(Kernel::HLERequestContext& ctx) { 328 void StartTask(Kernel::HLERequestContext& ctx) {
329 // No need to connect to the internet, just finish the task straight away. 329 // No need to connect to the internet, just finish the task straight away.
330 LOG_DEBUG(Service_NIM, "called"); 330 LOG_DEBUG(Service_NIM, "called");
331 finished_event->GetWritableEvent().Signal(); 331 finished_event->Signal();
332 IPC::ResponseBuilder rb{ctx, 2}; 332 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(ResultSuccess); 333 rb.Push(ResultSuccess);
334 } 334 }
@@ -350,7 +350,7 @@ private:
350 350
351 void Cancel(Kernel::HLERequestContext& ctx) { 351 void Cancel(Kernel::HLERequestContext& ctx) {
352 LOG_DEBUG(Service_NIM, "called"); 352 LOG_DEBUG(Service_NIM, "called");
353 finished_event->GetWritableEvent().Clear(); 353 finished_event->Clear();
354 IPC::ResponseBuilder rb{ctx, 2}; 354 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultSuccess); 355 rb.Push(ResultSuccess);
356 } 356 }
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index f7318c3cb..f59a1a63d 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -8,6 +8,7 @@
8#include "core/file_sys/patch_manager.h" 8#include "core/file_sys/patch_manager.h"
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/service/glue/glue_manager.h"
11#include "core/hle/service/ns/errors.h" 12#include "core/hle/service/ns/errors.h"
12#include "core/hle/service/ns/iplatform_service_manager.h" 13#include "core/hle/service/ns/iplatform_service_manager.h"
13#include "core/hle/service/ns/language.h" 14#include "core/hle/service/ns/language.h"
@@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
581 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { 582 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
582 // clang-format off 583 // clang-format off
583 static const FunctionInfo functions[] = { 584 static const FunctionInfo functions[] = {
584 {0, nullptr, "GetApplicationControlData"}, 585 {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
585 {1, nullptr, "GetApplicationDesiredLanguage"}, 586 {1, nullptr, "GetApplicationDesiredLanguage"},
586 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, 587 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
587 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, 588 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
@@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
594 595
595IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; 596IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
596 597
598void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
599 Kernel::HLERequestContext& ctx) {
600 enum class ApplicationControlSource : u8 {
601 CacheOnly,
602 Storage,
603 StorageOnly,
604 };
605
606 struct RequestParameters {
607 ApplicationControlSource source;
608 u64 application_id;
609 };
610 static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
611
612 IPC::RequestParser rp{ctx};
613 const auto parameters{rp.PopRaw<RequestParameters>()};
614 const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)};
615 const auto result = nacp_data ? ResultSuccess : ResultUnknown;
616
617 if (nacp_data) {
618 ctx.WriteBuffer(nacp_data->data(), nacp_data->size());
619 }
620
621 IPC::ResponseBuilder rb{ctx, 2};
622 rb.Push(result);
623}
624
597NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { 625NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
598 // clang-format off 626 // clang-format off
599 static const FunctionInfo functions[] = { 627 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 4dc191518..9c18e935c 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final
78public: 78public:
79 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); 79 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
80 ~IReadOnlyApplicationControlDataInterface() override; 80 ~IReadOnlyApplicationControlDataInterface() override;
81
82private:
83 void GetApplicationControlData(Kernel::HLERequestContext& ctx);
81}; 84};
82 85
83class NS final : public ServiceFramework<NS> { 86class NS final : public ServiceFramework<NS> {
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
new file mode 100644
index 000000000..37ca24f5d
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "core/hle/service/nvdrv/core/container.h"
6#include "core/hle/service/nvdrv/core/nvmap.h"
7#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
8#include "video_core/host1x/host1x.h"
9
10namespace Service::Nvidia::NvCore {
11
12struct ContainerImpl {
13 explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_)
14 : file{host1x_}, manager{host1x_}, device_file_data{} {}
15 NvMap file;
16 SyncpointManager manager;
17 Container::Host1xDeviceFileData device_file_data;
18};
19
20Container::Container(Tegra::Host1x::Host1x& host1x_) {
21 impl = std::make_unique<ContainerImpl>(host1x_);
22}
23
24Container::~Container() = default;
25
26NvMap& Container::GetNvMapFile() {
27 return impl->file;
28}
29
30const NvMap& Container::GetNvMapFile() const {
31 return impl->file;
32}
33
34Container::Host1xDeviceFileData& Container::Host1xDeviceFile() {
35 return impl->device_file_data;
36}
37
38const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const {
39 return impl->device_file_data;
40}
41
42SyncpointManager& Container::GetSyncpointManager() {
43 return impl->manager;
44}
45
46const SyncpointManager& Container::GetSyncpointManager() const {
47 return impl->manager;
48}
49
50} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h
new file mode 100644
index 000000000..b4b63ac90
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.h
@@ -0,0 +1,52 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <deque>
8#include <memory>
9#include <unordered_map>
10
11#include "core/hle/service/nvdrv/nvdata.h"
12
13namespace Tegra::Host1x {
14class Host1x;
15} // namespace Tegra::Host1x
16
17namespace Service::Nvidia::NvCore {
18
19class NvMap;
20class SyncpointManager;
21
22struct ContainerImpl;
23
24class Container {
25public:
26 explicit Container(Tegra::Host1x::Host1x& host1x);
27 ~Container();
28
29 NvMap& GetNvMapFile();
30
31 const NvMap& GetNvMapFile() const;
32
33 SyncpointManager& GetSyncpointManager();
34
35 const SyncpointManager& GetSyncpointManager() const;
36
37 struct Host1xDeviceFileData {
38 std::unordered_map<DeviceFD, u32> fd_to_id{};
39 std::deque<u32> syncpts_accumulated{};
40 u32 nvdec_next_id{};
41 u32 vic_next_id{};
42 };
43
44 Host1xDeviceFileData& Host1xDeviceFile();
45
46 const Host1xDeviceFileData& Host1xDeviceFile() const;
47
48private:
49 std::unique_ptr<ContainerImpl> impl;
50};
51
52} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
new file mode 100644
index 000000000..a51ca5444
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -0,0 +1,273 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/memory.h"
10#include "video_core/host1x/host1x.h"
11
12using Core::Memory::YUZU_PAGESIZE;
13
14namespace Service::Nvidia::NvCore {
15NvMap::Handle::Handle(u64 size_, Id id_)
16 : size(size_), aligned_size(size), orig_size(size), id(id_) {
17 flags.raw = 0;
18}
19
20NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) {
21 std::scoped_lock lock(mutex);
22
23 // Handles cannot be allocated twice
24 if (allocated) {
25 return NvResult::AccessDenied;
26 }
27
28 flags = pFlags;
29 kind = pKind;
30 align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
31
32 // This flag is only applicable for handles with an address passed
33 if (pAddress) {
34 flags.keep_uncached_after_free.Assign(0);
35 } else {
36 LOG_CRITICAL(Service_NVDRV,
37 "Mapping nvmap handles without a CPU side address is unimplemented!");
38 }
39
40 size = Common::AlignUp(size, YUZU_PAGESIZE);
41 aligned_size = Common::AlignUp(size, align);
42 address = pAddress;
43 allocated = true;
44
45 return NvResult::Success;
46}
47
48NvResult NvMap::Handle::Duplicate(bool internal_session) {
49 std::scoped_lock lock(mutex);
50 // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
51 if (!allocated) [[unlikely]] {
52 return NvResult::BadValue;
53 }
54
55 // If we internally use FromId the duplication tracking of handles won't work accurately due to
56 // us not implementing per-process handle refs.
57 if (internal_session) {
58 internal_dupes++;
59 } else {
60 dupes++;
61 }
62
63 return NvResult::Success;
64}
65
66NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {}
67
68void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
69 std::scoped_lock lock(handles_lock);
70
71 handles.emplace(handle_description->id, std::move(handle_description));
72}
73
74void NvMap::UnmapHandle(Handle& handle_description) {
75 // Remove pending unmap queue entry if needed
76 if (handle_description.unmap_queue_entry) {
77 unmap_queue.erase(*handle_description.unmap_queue_entry);
78 handle_description.unmap_queue_entry.reset();
79 }
80
81 // Free and unmap the handle from the SMMU
82 host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
83 handle_description.aligned_size);
84 host1x.Allocator().Free(handle_description.pin_virt_address,
85 static_cast<u32>(handle_description.aligned_size));
86 handle_description.pin_virt_address = 0;
87}
88
89bool NvMap::TryRemoveHandle(const Handle& handle_description) {
90 // No dupes left, we can remove from handle map
91 if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
92 std::scoped_lock lock(handles_lock);
93
94 auto it{handles.find(handle_description.id)};
95 if (it != handles.end()) {
96 handles.erase(it);
97 }
98
99 return true;
100 } else {
101 return false;
102 }
103}
104
105NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
106 if (!size) [[unlikely]] {
107 return NvResult::BadValue;
108 }
109
110 u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
111 auto handle_description{std::make_shared<Handle>(size, id)};
112 AddHandle(handle_description);
113
114 result_out = handle_description;
115 return NvResult::Success;
116}
117
118std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
119 std::scoped_lock lock(handles_lock);
120 try {
121 return handles.at(handle);
122 } catch (std::out_of_range&) {
123 return nullptr;
124 }
125}
126
127VAddr NvMap::GetHandleAddress(Handle::Id handle) {
128 std::scoped_lock lock(handles_lock);
129 try {
130 return handles.at(handle)->address;
131 } catch (std::out_of_range&) {
132 return 0;
133 }
134}
135
136u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
137 auto handle_description{GetHandle(handle)};
138 if (!handle_description) [[unlikely]] {
139 return 0;
140 }
141
142 std::scoped_lock lock(handle_description->mutex);
143 if (!handle_description->pins) {
144 // If we're in the unmap queue we can just remove ourselves and return since we're already
145 // mapped
146 {
147 // Lock now to prevent our queue entry from being removed for allocation in-between the
148 // following check and erase
149 std::scoped_lock queueLock(unmap_queue_lock);
150 if (handle_description->unmap_queue_entry) {
151 unmap_queue.erase(*handle_description->unmap_queue_entry);
152 handle_description->unmap_queue_entry.reset();
153
154 handle_description->pins++;
155 return handle_description->pin_virt_address;
156 }
157 }
158
159 // If not then allocate some space and map it
160 u32 address{};
161 auto& smmu_allocator = host1x.Allocator();
162 auto& smmu_memory_manager = host1x.MemoryManager();
163 while (!(address =
164 smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) {
165 // Free handles until the allocation succeeds
166 std::scoped_lock queueLock(unmap_queue_lock);
167 if (auto freeHandleDesc{unmap_queue.front()}) {
168 // Handles in the unmap queue are guaranteed not to be pinned so don't bother
169 // checking if they are before unmapping
170 std::scoped_lock freeLock(freeHandleDesc->mutex);
171 if (handle_description->pin_virt_address)
172 UnmapHandle(*freeHandleDesc);
173 } else {
174 LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
175 }
176 }
177
178 smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address,
179 handle_description->aligned_size);
180 handle_description->pin_virt_address = address;
181 }
182
183 handle_description->pins++;
184 return handle_description->pin_virt_address;
185}
186
187void NvMap::UnpinHandle(Handle::Id handle) {
188 auto handle_description{GetHandle(handle)};
189 if (!handle_description) {
190 return;
191 }
192
193 std::scoped_lock lock(handle_description->mutex);
194 if (--handle_description->pins < 0) {
195 LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
196 } else if (!handle_description->pins) {
197 std::scoped_lock queueLock(unmap_queue_lock);
198
199 // Add to the unmap queue allowing this handle's memory to be freed if needed
200 unmap_queue.push_back(handle_description);
201 handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
202 }
203}
204
205void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
206 auto handle_description{GetHandle(handle)};
207 if (!handle_description) {
208 LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
209 return;
210 }
211
212 auto result = handle_description->Duplicate(internal_session);
213 if (result != NvResult::Success) {
214 LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
215 }
216}
217
218std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
219 std::weak_ptr<Handle> hWeak{GetHandle(handle)};
220 FreeInfo freeInfo;
221
222 // We use a weak ptr here so we can tell when the handle has been freed and report that back to
223 // guest
224 if (auto handle_description = hWeak.lock()) {
225 std::scoped_lock lock(handle_description->mutex);
226
227 if (internal_session) {
228 if (--handle_description->internal_dupes < 0)
229 LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
230 } else {
231 if (--handle_description->dupes < 0) {
232 LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
233 } else if (handle_description->dupes == 0) {
234 // Force unmap the handle
235 if (handle_description->pin_virt_address) {
236 std::scoped_lock queueLock(unmap_queue_lock);
237 UnmapHandle(*handle_description);
238 }
239
240 handle_description->pins = 0;
241 }
242 }
243
244 // Try to remove the shared ptr to the handle from the map, if nothing else is using the
245 // handle then it will now be freed when `handle_description` goes out of scope
246 if (TryRemoveHandle(*handle_description)) {
247 LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
248 } else {
249 LOG_DEBUG(Service_NVDRV,
250 "Tried to free nvmap handle: {} but didn't as it still has duplicates",
251 handle);
252 }
253
254 freeInfo = {
255 .address = handle_description->address,
256 .size = handle_description->size,
257 .was_uncached = handle_description->flags.map_uncached.Value() != 0,
258 .can_unlock = true,
259 };
260 } else {
261 return std::nullopt;
262 }
263
264 // If the handle hasn't been freed from memory, mark that
265 if (!hWeak.expired()) {
266 LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
267 freeInfo.can_unlock = false;
268 }
269
270 return freeInfo;
271}
272
273} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h
new file mode 100644
index 000000000..a8e573890
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.h
@@ -0,0 +1,176 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <atomic>
8#include <list>
9#include <memory>
10#include <mutex>
11#include <optional>
12#include <unordered_map>
13#include <assert.h>
14
15#include "common/bit_field.h"
16#include "common/common_types.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Tegra {
20
21namespace Host1x {
22class Host1x;
23} // namespace Host1x
24
25} // namespace Tegra
26
27namespace Service::Nvidia::NvCore {
28/**
29 * @brief The nvmap core class holds the global state for nvmap and provides methods to manage
30 * handles
31 */
32class NvMap {
33public:
34 /**
35 * @brief A handle to a contiguous block of memory in an application's address space
36 */
37 struct Handle {
38 std::mutex mutex;
39
40 u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU
41 u64 size; //!< Page-aligned size of the memory the handle refers to
42 u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
43 u64 orig_size; //!< Original unaligned size of the memory this handle refers to
44
45 s32 dupes{1}; //!< How many guest references there are to this handle
46 s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle
47
48 using Id = u32;
49 Id id; //!< A globally unique identifier for this handle
50
51 s32 pins{};
52 u32 pin_virt_address{};
53 std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
54
55 union Flags {
56 u32 raw;
57 BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
58 BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
59 //!< allocated with a fixed address
60 BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
61 } flags{};
62 static_assert(sizeof(Flags) == sizeof(u32));
63
64 u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to,
65 //!< this can also be in the nvdrv tmem
66 bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
67 //!< call
68
69 u8 kind{}; //!< Used for memory compression
70 bool allocated{}; //!< If the handle has been allocated with `Alloc`
71
72 u64 dma_map_addr{}; //! remove me after implementing pinning.
73
74 Handle(u64 size, Id id);
75
76 /**
77 * @brief Sets up the handle with the given memory config, can allocate memory from the tmem
78 * if a 0 address is passed
79 */
80 [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress);
81
82 /**
83 * @brief Increases the dupe counter of the handle for the given session
84 */
85 [[nodiscard]] NvResult Duplicate(bool internal_session);
86
87 /**
88 * @brief Obtains a pointer to the handle's memory and marks the handle it as having been
89 * mapped
90 */
91 u8* GetPointer() {
92 if (!address) {
93 return nullptr;
94 }
95
96 is_shared_mem_mapped = true;
97 return reinterpret_cast<u8*>(address);
98 }
99 };
100
101 /**
102 * @brief Encapsulates the result of a FreeHandle operation
103 */
104 struct FreeInfo {
105 u64 address; //!< Address the handle referred to before deletion
106 u64 size; //!< Page-aligned handle size
107 bool was_uncached; //!< If the handle was allocated as uncached
108 bool can_unlock; //!< If the address region is ready to be unlocked
109 };
110
111 explicit NvMap(Tegra::Host1x::Host1x& host1x);
112
113 /**
114 * @brief Creates an unallocated handle of the given size
115 */
116 [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);
117
118 std::shared_ptr<Handle> GetHandle(Handle::Id handle);
119
120 VAddr GetHandleAddress(Handle::Id handle);
121
122 /**
123 * @brief Maps a handle into the SMMU address space
124 * @note This operation is refcounted, the number of calls to this must eventually match the
125 * number of calls to `UnpinHandle`
126 * @return The SMMU virtual address that the handle has been mapped to
127 */
128 u32 PinHandle(Handle::Id handle);
129
130 /**
131 * @brief When this has been called an equal number of times to `PinHandle` for the supplied
132 * handle it will be added to a list of handles to be freed when necessary
133 */
134 void UnpinHandle(Handle::Id handle);
135
136 /**
137 * @brief Tries to duplicate a handle
138 */
139 void DuplicateHandle(Handle::Id handle, bool internal_session = false);
140
141 /**
142 * @brief Tries to free a handle and remove a single dupe
143 * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned
144 * describing the prior state of the handle
145 */
146 std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);
147
148private:
149 std::list<std::shared_ptr<Handle>> unmap_queue{};
150 std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
151
152 std::unordered_map<Handle::Id, std::shared_ptr<Handle>>
153 handles{}; //!< Main owning map of handles
154 std::mutex handles_lock; //!< Protects access to `handles`
155
156 static constexpr u32 HandleIdIncrement{
157 4}; //!< Each new handle ID is an increment of 4 from the previous
158 std::atomic<u32> next_handle_id{HandleIdIncrement};
159 Tegra::Host1x::Host1x& host1x;
160
161 void AddHandle(std::shared_ptr<Handle> handle);
162
163 /**
164 * @brief Unmaps and frees the SMMU memory region a handle is mapped to
165 * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this
166 */
167 void UnmapHandle(Handle& handle_description);
168
169 /**
170 * @brief Removes a handle from the map taking its dupes into account
171 * @note handle_description.mutex MUST be locked when calling this
172 * @return If the handle was removed from the map
173 */
174 bool TryRemoveHandle(const Handle& handle_description);
175};
176} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
new file mode 100644
index 000000000..eda2041a0
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "common/assert.h"
6#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
7#include "video_core/host1x/host1x.h"
8
9namespace Service::Nvidia::NvCore {
10
11SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {
12 constexpr u32 VBlank0SyncpointId{26};
13 constexpr u32 VBlank1SyncpointId{27};
14
15 // Reserve both vblank syncpoints as client managed as they use Continuous Mode
16 // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
17 // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
18 ReserveSyncpoint(VBlank0SyncpointId, true);
19 ReserveSyncpoint(VBlank1SyncpointId, true);
20
21 for (u32 syncpoint_id : channel_syncpoints) {
22 if (syncpoint_id) {
23 ReserveSyncpoint(syncpoint_id, false);
24 }
25 }
26}
27
28SyncpointManager::~SyncpointManager() = default;
29
30u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) {
31 if (syncpoints.at(id).reserved) {
32 ASSERT_MSG(false, "Requested syncpoint is in use");
33 return 0;
34 }
35
36 syncpoints.at(id).reserved = true;
37 syncpoints.at(id).interface_managed = client_managed;
38
39 return id;
40}
41
42u32 SyncpointManager::FindFreeSyncpoint() {
43 for (u32 i{1}; i < syncpoints.size(); i++) {
44 if (!syncpoints[i].reserved) {
45 return i;
46 }
47 }
48 ASSERT_MSG(false, "Failed to find a free syncpoint!");
49 return 0;
50}
51
52u32 SyncpointManager::AllocateSyncpoint(bool client_managed) {
53 std::lock_guard lock(reservation_lock);
54 return ReserveSyncpoint(FindFreeSyncpoint(), client_managed);
55}
56
57void SyncpointManager::FreeSyncpoint(u32 id) {
58 std::lock_guard lock(reservation_lock);
59 ASSERT(syncpoints.at(id).reserved);
60 syncpoints.at(id).reserved = false;
61}
62
63bool SyncpointManager::IsSyncpointAllocated(u32 id) {
64 return (id <= SyncpointCount) && syncpoints[id].reserved;
65}
66
67bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
68 const SyncpointInfo& syncpoint{syncpoints.at(id)};
69
70 if (!syncpoint.reserved) {
71 ASSERT(false);
72 return 0;
73 }
74
75 // If the interface manages counters then we don't keep track of the maximum value as it handles
76 // sanity checking the values then
77 if (syncpoint.interface_managed) {
78 return static_cast<s32>(syncpoint.counter_min - threshold) >= 0;
79 } else {
80 return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold);
81 }
82}
83
84u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) {
85 if (!syncpoints.at(id).reserved) {
86 ASSERT(false);
87 return 0;
88 }
89
90 return syncpoints.at(id).counter_max += amount;
91}
92
93u32 SyncpointManager::ReadSyncpointMinValue(u32 id) {
94 if (!syncpoints.at(id).reserved) {
95 ASSERT(false);
96 return 0;
97 }
98
99 return syncpoints.at(id).counter_min;
100}
101
102u32 SyncpointManager::UpdateMin(u32 id) {
103 if (!syncpoints.at(id).reserved) {
104 ASSERT(false);
105 return 0;
106 }
107
108 syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id);
109 return syncpoints.at(id).counter_min;
110}
111
112NvFence SyncpointManager::GetSyncpointFence(u32 id) {
113 if (!syncpoints.at(id).reserved) {
114 ASSERT(false);
115 return NvFence{};
116 }
117
118 return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max};
119}
120
121} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
new file mode 100644
index 000000000..b76ef9032
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
@@ -0,0 +1,134 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <array>
8#include <atomic>
9#include <mutex>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Tegra::Host1x {
15class Host1x;
16} // namespace Tegra::Host1x
17
18namespace Service::Nvidia::NvCore {
19
20enum class ChannelType : u32 {
21 MsEnc = 0,
22 VIC = 1,
23 GPU = 2,
24 NvDec = 3,
25 Display = 4,
26 NvJpg = 5,
27 TSec = 6,
28 Max = 7
29};
30
31/**
32 * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached
33 * versions of the HW syncpoints which are intermittently synced
34 * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them
35 * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
36 * @url
37 * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
38 */
39class SyncpointManager final {
40public:
41 explicit SyncpointManager(Tegra::Host1x::Host1x& host1x);
42 ~SyncpointManager();
43
44 /**
45 * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints
46 */
47 bool IsSyncpointAllocated(u32 id);
48
49 /**
50 * @brief Finds a free syncpoint and reserves it
51 * @return The ID of the reserved syncpoint
52 */
53 u32 AllocateSyncpoint(bool client_managed);
54
55 /**
56 * @url
57 * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259
58 */
59 bool HasSyncpointExpired(u32 id, u32 threshold) const;
60
61 bool IsFenceSignalled(NvFence fence) const {
62 return HasSyncpointExpired(fence.id, fence.value);
63 }
64
65 /**
66 * @brief Atomically increments the maximum value of a syncpoint by the given amount
67 * @return The new max value of the syncpoint
68 */
69 u32 IncrementSyncpointMaxExt(u32 id, u32 amount);
70
71 /**
72 * @return The minimum value of the syncpoint
73 */
74 u32 ReadSyncpointMinValue(u32 id);
75
76 /**
77 * @brief Synchronises the minimum value of the syncpoint to with the GPU
78 * @return The new minimum value of the syncpoint
79 */
80 u32 UpdateMin(u32 id);
81
82 /**
83 * @brief Frees the usage of a syncpoint.
84 */
85 void FreeSyncpoint(u32 id);
86
87 /**
88 * @return A fence that will be signalled once this syncpoint hits its maximum value
89 */
90 NvFence GetSyncpointFence(u32 id);
91
92 static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{
93 0x0, // `MsEnc` is unimplemented
94 0xC, // `VIC`
95 0x0, // `GPU` syncpoints are allocated per-channel instead
96 0x36, // `NvDec`
97 0x0, // `Display` is unimplemented
98 0x37, // `NvJpg`
99 0x0, // `TSec` is unimplemented
100 }; //!< Maps each channel ID to a constant syncpoint
101
102private:
103 /**
104 * @note reservation_lock should be locked when calling this
105 */
106 u32 ReserveSyncpoint(u32 id, bool client_managed);
107
108 /**
109 * @return The ID of the first free syncpoint
110 */
111 u32 FindFreeSyncpoint();
112
113 struct SyncpointInfo {
114 std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was
115 //!< when it was last synchronized with host1x)
116 std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to
117 //!< the current usage
118 bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a
119 //!< client interface is a HW block that can handle host1x
120 //!< transactions on behalf of a host1x client (Which would
121 //!< otherwise need to be manually synced using PIO which is
122 //!< synchronous and requires direct cooperation of the CPU)
123 bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved
124 //!< value
125 };
126
127 constexpr static std::size_t SyncpointCount{192};
128 std::array<SyncpointInfo, SyncpointCount> syncpoints{};
129 std::mutex reservation_lock;
130
131 Tegra::Host1x::Host1x& host1x;
132};
133
134} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 696e8121e..204b0e757 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -11,6 +11,10 @@ namespace Core {
11class System; 11class System;
12} 12}
13 13
14namespace Kernel {
15class KEvent;
16}
17
14namespace Service::Nvidia::Devices { 18namespace Service::Nvidia::Devices {
15 19
16/// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to 20/// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to
@@ -64,6 +68,10 @@ public:
64 */ 68 */
65 virtual void OnClose(DeviceFD fd) = 0; 69 virtual void OnClose(DeviceFD fd) = 0;
66 70
71 virtual Kernel::KEvent* QueryEvent(u32 event_id) {
72 return nullptr;
73 }
74
67protected: 75protected:
68 Core::System& system; 76 Core::System& system;
69}; 77};
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 604711914..4122fc98d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -5,15 +5,16 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h"
8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 10#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h"
10#include "core/perf_stats.h" 11#include "core/perf_stats.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12 13
13namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
14 15
15nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) 16nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
16 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} 17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
17nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
18 19
19NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -39,8 +40,9 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
39 40
40void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
41 u32 height, u32 stride, android::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
42 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect,
43 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
45 const VAddr addr = nvmap.GetHandleAddress(buffer_handle);
44 LOG_TRACE(Service, 46 LOG_TRACE(Service,
45 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 47 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
46 addr, offset, width, height, stride, format); 48 addr, offset, width, height, stride, format);
@@ -48,10 +50,15 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form
48 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, 50 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
49 stride, format, transform, crop_rect}; 51 stride, format, transform, crop_rect};
50 52
53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
51 system.GetPerfStats().EndSystemFrame(); 54 system.GetPerfStats().EndSystemFrame();
52 system.GPU().SwapBuffers(&framebuffer);
53 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); 55 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
54 system.GetPerfStats().BeginSystemFrame(); 56 system.GetPerfStats().BeginSystemFrame();
55} 57}
56 58
59Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) {
60 LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id);
61 return nullptr;
62}
63
57} // namespace Service::Nvidia::Devices 64} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 67b105e02..04217ab12 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -11,13 +11,18 @@
11#include "core/hle/service/nvflinger/buffer_transform_flags.h" 11#include "core/hle/service/nvflinger/buffer_transform_flags.h"
12#include "core/hle/service/nvflinger/pixel_format.h" 12#include "core/hle/service/nvflinger/pixel_format.h"
13 13
14namespace Service::Nvidia::NvCore {
15class Container;
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
14namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
15 20
16class nvmap; 21class nvmap;
17 22
18class nvdisp_disp0 final : public nvdevice { 23class nvdisp_disp0 final : public nvdevice {
19public: 24public:
20 explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); 25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
21 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
22 27
23 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -33,10 +38,14 @@ public:
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 38 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, 39 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 u32 stride, android::BufferTransformFlags transform, 40 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 41 const Common::Rectangle<int>& crop_rect,
42 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences);
43
44 Kernel::KEvent* QueryEvent(u32 event_id) override;
37 45
38private: 46private:
39 std::shared_ptr<nvmap> nvmap_dev; 47 NvCore::Container& container;
48 NvCore::NvMap& nvmap;
40}; 49};
41 50
42} // namespace Service::Nvidia::Devices 51} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 9867a648d..b635e6ed1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -1,21 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <cstring> 5#include <cstring>
5#include <utility> 6#include <utility>
6 7
8#include "common/alignment.h"
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
9#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/service/nvdrv/core/container.h"
13#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 14#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
11#include "core/hle/service/nvdrv/devices/nvmap.h" 15#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
16#include "core/hle/service/nvdrv/nvdrv.h"
17#include "video_core/control/channel_state.h"
18#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 19#include "video_core/memory_manager.h"
13#include "video_core/rasterizer_interface.h" 20#include "video_core/rasterizer_interface.h"
14 21
15namespace Service::Nvidia::Devices { 22namespace Service::Nvidia::Devices {
16 23
17nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) 24nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core)
18 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} 25 : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{},
26 gmmu{} {}
27
19nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
20 29
21NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -82,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
82 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
83 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
84 93
85 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 94 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
86 if (params.big_page_size == 0) { 95
87 params.big_page_size = DEFAULT_BIG_PAGE_SIZE; 96 std::scoped_lock lock(mutex);
97
98 if (vm.initialised) {
99 ASSERT_MSG(false, "Cannot initialise an address space twice!");
100 return NvResult::InvalidState;
101 }
102
103 if (params.big_page_size) {
104 if (!std::has_single_bit(params.big_page_size)) {
105 LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size);
106 return NvResult::BadValue;
107 }
108
109 if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
110 LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size);
111 return NvResult::BadValue;
112 }
113
114 vm.big_page_size = params.big_page_size;
115 vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size));
116
117 vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT;
118 }
119
120 // If this is unspecified then default values should be used
121 if (params.va_range_start) {
122 vm.va_range_start = params.va_range_start;
123 vm.va_range_split = params.va_range_split;
124 vm.va_range_end = params.va_range_end;
88 } 125 }
89 126
90 big_page_size = params.big_page_size; 127 const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
128 const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
129 vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
130
131 const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)};
132 const auto end_big_pages{
133 static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
134 vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
135
136 gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits,
137 VM::PAGE_SIZE_BITS);
138 system.GPU().InitAddressSpace(*gmmu);
139 vm.initialised = true;
91 140
92 return NvResult::Success; 141 return NvResult::Success;
93} 142}
@@ -99,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<
99 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 148 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
100 params.page_size, params.flags); 149 params.page_size, params.flags);
101 150
102 const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; 151 std::scoped_lock lock(mutex);
103 if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { 152
104 params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); 153 if (!vm.initialised) {
154 return NvResult::BadValue;
155 }
156
157 if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) {
158 return NvResult::BadValue;
159 }
160
161 if (params.page_size != vm.big_page_size &&
162 ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) {
163 UNIMPLEMENTED_MSG("Sparse small pages are not implemented!");
164 return NvResult::NotImplemented;
165 }
166
167 const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
168 : vm.big_page_size_bits};
169
170 auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
171 : *vm.big_page_allocator};
172
173 if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
174 allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages);
105 } else { 175 } else {
106 params.offset = system.GPU().MemoryManager().Allocate(size, params.align); 176 params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits;
177 if (!params.offset) {
178 ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
179 return NvResult::InsufficientMemory;
180 }
107 } 181 }
108 182
109 auto result = NvResult::Success; 183 u64 size{static_cast<u64>(params.pages) * params.page_size};
110 if (!params.offset) { 184
111 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); 185 if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) {
112 result = NvResult::InsufficientMemory; 186 gmmu->MapSparse(params.offset, size);
113 } 187 }
114 188
189 allocation_map[params.offset] = {
190 .size = size,
191 .mappings{},
192 .page_size = params.page_size,
193 .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None,
194 .big_pages = params.page_size != VM::YUZU_PAGESIZE,
195 };
196
115 std::memcpy(output.data(), &params, output.size()); 197 std::memcpy(output.data(), &params, output.size());
116 return result; 198 return NvResult::Success;
199}
200
201void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
202 auto mapping{mapping_map.at(offset)};
203
204 if (!mapping->fixed) {
205 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
206 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
207
208 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
209 static_cast<u32>(mapping->size >> page_size_bits));
210 }
211
212 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
213 // Only FreeSpace can unmap them fully
214 if (mapping->sparse_alloc) {
215 gmmu->MapSparse(offset, mapping->size, mapping->big_page);
216 } else {
217 gmmu->Unmap(offset, mapping->size);
218 }
219
220 mapping_map.erase(offset);
117} 221}
118 222
119NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -123,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
123 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, 227 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
124 params.pages, params.page_size); 228 params.pages, params.page_size);
125 229
126 system.GPU().MemoryManager().Unmap(params.offset, 230 std::scoped_lock lock(mutex);
127 static_cast<std::size_t>(params.pages) * params.page_size); 231
232 if (!vm.initialised) {
233 return NvResult::BadValue;
234 }
235
236 try {
237 auto allocation{allocation_map[params.offset]};
238
239 if (allocation.page_size != params.page_size ||
240 allocation.size != (static_cast<u64>(params.pages) * params.page_size)) {
241 return NvResult::BadValue;
242 }
243
244 for (const auto& mapping : allocation.mappings) {
245 FreeMappingLocked(mapping->offset);
246 }
247
248 // Unset sparse flag if required
249 if (allocation.sparse) {
250 gmmu->Unmap(params.offset, allocation.size);
251 }
252
253 auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
254 : *vm.big_page_allocator};
255 u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
256 : vm.big_page_size_bits};
257
258 allocator.Free(static_cast<u32>(params.offset >> page_size_bits),
259 static_cast<u32>(allocation.size >> page_size_bits));
260 allocation_map.erase(params.offset);
261 } catch (const std::out_of_range&) {
262 return NvResult::BadValue;
263 }
128 264
129 std::memcpy(output.data(), &params, output.size()); 265 std::memcpy(output.data(), &params, output.size());
130 return NvResult::Success; 266 return NvResult::Success;
@@ -135,35 +271,53 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
135 271
136 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
137 273
138 auto result = NvResult::Success;
139 std::vector<IoctlRemapEntry> entries(num_entries); 274 std::vector<IoctlRemapEntry> entries(num_entries);
140 std::memcpy(entries.data(), input.data(), input.size()); 275 std::memcpy(entries.data(), input.data(), input.size());
141 276
277 std::scoped_lock lock(mutex);
278
279 if (!vm.initialised) {
280 return NvResult::BadValue;
281 }
282
142 for (const auto& entry : entries) { 283 for (const auto& entry : entries) {
143 LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 284 GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages)
144 entry.offset, entry.nvmap_handle, entry.pages); 285 << vm.big_page_size_bits};
286 u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits};
145 287
146 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; 288 auto alloc{allocation_map.upper_bound(virtual_address)};
147 if (!object) { 289
148 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); 290 if (alloc-- == allocation_map.begin() ||
149 result = NvResult::InvalidState; 291 (virtual_address - alloc->first) + size > alloc->second.size) {
150 break; 292 LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!");
293 return NvResult::BadValue;
151 } 294 }
152 295
153 const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; 296 if (!alloc->second.sparse) {
154 const auto size{static_cast<u64>(entry.pages) << 0x10}; 297 LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!");
155 const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; 298 return NvResult::BadValue;
156 const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; 299 }
157 300
158 if (!addr) { 301 const bool use_big_pages = alloc->second.big_pages;
159 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); 302 if (!entry.handle) {
160 result = NvResult::InvalidState; 303 gmmu->MapSparse(virtual_address, size, use_big_pages);
161 break; 304 } else {
305 auto handle{nvmap.GetHandle(entry.handle)};
306 if (!handle) {
307 return NvResult::BadValue;
308 }
309
310 VAddr cpu_address{static_cast<VAddr>(
311 handle->address +
312 (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
313
314 gmmu->Map(virtual_address, cpu_address, size, static_cast<Tegra::PTEKind>(entry.kind),
315 use_big_pages);
162 } 316 }
163 } 317 }
164 318
165 std::memcpy(output.data(), entries.data(), output.size()); 319 std::memcpy(output.data(), entries.data(), output.size());
166 return result; 320 return NvResult::Success;
167} 321}
168 322
169NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 323NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -173,79 +327,101 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
173 LOG_DEBUG(Service_NVDRV, 327 LOG_DEBUG(Service_NVDRV,
174 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" 328 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
175 ", offset={}", 329 ", offset={}",
176 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, 330 params.flags, params.handle, params.buffer_offset, params.mapping_size,
177 params.offset); 331 params.offset);
178 332
179 const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; 333 std::scoped_lock lock(mutex);
180 if (!object) {
181 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
182 std::memcpy(output.data(), &params, output.size());
183 return NvResult::InvalidState;
184 }
185
186 // The real nvservices doesn't make a distinction between handles and ids, and
187 // object can only have one handle and it will be the same as its id. Assert that this is the
188 // case to prevent unexpected behavior.
189 ASSERT(object->id == params.nvmap_handle);
190 auto& gpu = system.GPU();
191 334
192 u64 page_size{params.page_size}; 335 if (!vm.initialised) {
193 if (!page_size) { 336 return NvResult::BadValue;
194 page_size = object->align;
195 } 337 }
196 338
197 if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { 339 // Remaps a subregion of an existing mapping to a different PA
198 if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { 340 if ((params.flags & MappingFlags::Remap) != MappingFlags::None) {
199 const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; 341 try {
200 const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; 342 auto mapping{mapping_map.at(params.offset)};
201 343
202 if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { 344 if (mapping->size < params.mapping_size) {
203 LOG_CRITICAL(Service_NVDRV, 345 LOG_WARNING(Service_NVDRV,
204 "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " 346 "Cannot remap a partially mapped GPU address space region: 0x{:X}",
205 "mapping_size = {}, offset={}", 347 params.offset);
206 params.flags, params.nvmap_handle, params.buffer_offset, 348 return NvResult::BadValue;
207 params.mapping_size, params.offset);
208
209 std::memcpy(output.data(), &params, output.size());
210 return NvResult::InvalidState;
211 } 349 }
212 350
213 std::memcpy(output.data(), &params, output.size()); 351 u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
214 return NvResult::Success; 352 VAddr cpu_address{mapping->ptr + params.buffer_offset};
215 } else { 353
216 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); 354 gmmu->Map(gpu_address, cpu_address, params.mapping_size,
355 static_cast<Tegra::PTEKind>(params.kind), mapping->big_page);
217 356
218 std::memcpy(output.data(), &params, output.size()); 357 return NvResult::Success;
219 return NvResult::InvalidState; 358 } catch (const std::out_of_range&) {
359 LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}",
360 params.offset);
361 return NvResult::BadValue;
220 } 362 }
221 } 363 }
222 364
223 // We can only map objects that have already been assigned a CPU address. 365 auto handle{nvmap.GetHandle(params.handle)};
224 ASSERT(object->status == nvmap::Object::Status::Allocated); 366 if (!handle) {
225 367 return NvResult::BadValue;
226 const auto physical_address{object->addr + params.buffer_offset};
227 u64 size{params.mapping_size};
228 if (!size) {
229 size = object->size;
230 } 368 }
231 369
232 const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; 370 VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)};
233 if (is_alloc) { 371 u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
234 params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); 372
235 } else { 373 bool big_page{[&]() {
236 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); 374 if (Common::IsAligned(handle->align, vm.big_page_size)) {
237 } 375 return true;
376 } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) {
377 return false;
378 } else {
379 ASSERT(false);
380 return false;
381 }
382 }()};
383
384 if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
385 auto alloc{allocation_map.upper_bound(params.offset)};
238 386
239 auto result = NvResult::Success; 387 if (alloc-- == allocation_map.begin() ||
240 if (!params.offset) { 388 (params.offset - alloc->first) + size > alloc->second.size) {
241 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); 389 ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!");
242 result = NvResult::InvalidState; 390 return NvResult::BadValue;
391 }
392
393 const bool use_big_pages = alloc->second.big_pages && big_page;
394 gmmu->Map(params.offset, cpu_address, size, static_cast<Tegra::PTEKind>(params.kind),
395 use_big_pages);
396
397 auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true,
398 use_big_pages, alloc->second.sparse)};
399 alloc->second.mappings.push_back(mapping);
400 mapping_map[params.offset] = mapping;
243 } else { 401 } else {
244 AddBufferMap(params.offset, size, physical_address, is_alloc); 402
403 auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
404 u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
405 u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
406
407 params.offset = static_cast<u64>(allocator.Allocate(
408 static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits)))
409 << page_size_bits;
410 if (!params.offset) {
411 ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
412 return NvResult::InsufficientMemory;
413 }
414
415 gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size),
416 static_cast<Tegra::PTEKind>(params.kind), big_page);
417
418 auto mapping{
419 std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)};
420 mapping_map[params.offset] = mapping;
245 } 421 }
246 422
247 std::memcpy(output.data(), &params, output.size()); 423 std::memcpy(output.data(), &params, output.size());
248 return result; 424 return NvResult::Success;
249} 425}
250 426
251NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 427NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -254,47 +430,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
254 430
255 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 431 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
256 432
257 if (const auto size{RemoveBufferMap(params.offset)}; size) { 433 std::scoped_lock lock(mutex);
258 system.GPU().MemoryManager().Unmap(params.offset, *size); 434
259 } else { 435 if (!vm.initialised) {
260 LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); 436 return NvResult::BadValue;
437 }
438
439 try {
440 auto mapping{mapping_map.at(params.offset)};
441
442 if (!mapping->fixed) {
443 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
444 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
445
446 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
447 static_cast<u32>(mapping->size >> page_size_bits));
448 }
449
450 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
451 // Only FreeSpace can unmap them fully
452 if (mapping->sparse_alloc) {
453 gmmu->MapSparse(params.offset, mapping->size, mapping->big_page);
454 } else {
455 gmmu->Unmap(params.offset, mapping->size);
456 }
457
458 mapping_map.erase(params.offset);
459 } catch (const std::out_of_range&) {
460 LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset);
261 } 461 }
262 462
263 std::memcpy(output.data(), &params, output.size());
264 return NvResult::Success; 463 return NvResult::Success;
265} 464}
266 465
267NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 466NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
268 IoctlBindChannel params{}; 467 IoctlBindChannel params{};
269 std::memcpy(&params, input.data(), input.size()); 468 std::memcpy(&params, input.data(), input.size());
270 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); 469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
271 470
272 channel = params.fd; 471 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
472 gpu_channel_device->channel_state->memory_manager = gmmu;
273 return NvResult::Success; 473 return NvResult::Success;
274} 474}
275 475
476void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
477 params.buf_size = 2 * sizeof(VaRegion);
478
479 params.regions = std::array<VaRegion, 2>{
480 VaRegion{
481 .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS,
482 .page_size = VM::YUZU_PAGESIZE,
483 ._pad0_{},
484 .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(),
485 },
486 VaRegion{
487 .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits,
488 .page_size = vm.big_page_size,
489 ._pad0_{},
490 .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(),
491 },
492 };
493}
494
276NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 495NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
277 IoctlGetVaRegions params{}; 496 IoctlGetVaRegions params{};
278 std::memcpy(&params, input.data(), input.size()); 497 std::memcpy(&params, input.data(), input.size());
279 498
280 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 499 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
281 params.buf_size); 500 params.buf_size);
282
283 params.buf_size = 0x30;
284 501
285 params.small = IoctlVaRegion{ 502 std::scoped_lock lock(mutex);
286 .offset = 0x04000000,
287 .page_size = DEFAULT_SMALL_PAGE_SIZE,
288 .pages = 0x3fbfff,
289 };
290 503
291 params.big = IoctlVaRegion{ 504 if (!vm.initialised) {
292 .offset = 0x04000000, 505 return NvResult::BadValue;
293 .page_size = big_page_size, 506 }
294 .pages = 0x1bffff,
295 };
296 507
297 // TODO(ogniK): This probably can stay stubbed but should add support way way later 508 GetVARegionsImpl(params);
298 509
299 std::memcpy(output.data(), &params, output.size()); 510 std::memcpy(output.data(), &params, output.size());
300 return NvResult::Success; 511 return NvResult::Success;
@@ -305,62 +516,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
305 IoctlGetVaRegions params{}; 516 IoctlGetVaRegions params{};
306 std::memcpy(&params, input.data(), input.size()); 517 std::memcpy(&params, input.data(), input.size());
307 518
308 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 519 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
309 params.buf_size); 520 params.buf_size);
310
311 params.buf_size = 0x30;
312 521
313 params.small = IoctlVaRegion{ 522 std::scoped_lock lock(mutex);
314 .offset = 0x04000000,
315 .page_size = 0x1000,
316 .pages = 0x3fbfff,
317 };
318 523
319 params.big = IoctlVaRegion{ 524 if (!vm.initialised) {
320 .offset = 0x04000000, 525 return NvResult::BadValue;
321 .page_size = big_page_size, 526 }
322 .pages = 0x1bffff,
323 };
324 527
325 // TODO(ogniK): This probably can stay stubbed but should add support way way later 528 GetVARegionsImpl(params);
326 529
327 std::memcpy(output.data(), &params, output.size()); 530 std::memcpy(output.data(), &params, output.size());
328 std::memcpy(inline_output.data(), &params.small, sizeof(IoctlVaRegion)); 531 std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion));
329 std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), &params.big, sizeof(IoctlVaRegion)); 532 std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion));
330 533
331 return NvResult::Success; 534 return NvResult::Success;
332} 535}
333 536
334std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { 537Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) {
335 const auto end{buffer_mappings.upper_bound(gpu_addr)}; 538 LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id);
336 for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { 539 return nullptr;
337 if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
338 return iter->second;
339 }
340 }
341
342 return std::nullopt;
343}
344
345void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
346 bool is_allocated) {
347 buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
348}
349
350std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
351 if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
352 std::size_t size{};
353
354 if (iter->second.IsAllocated()) {
355 size = iter->second.Size();
356 }
357
358 buffer_mappings.erase(iter);
359
360 return size;
361 }
362
363 return std::nullopt;
364} 540}
365 541
366} // namespace Service::Nvidia::Devices 542} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 555843a6f..86fe71c75 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -1,35 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
7#include <bit>
8#include <list>
6#include <map> 9#include <map>
7#include <memory> 10#include <memory>
11#include <mutex>
8#include <optional> 12#include <optional>
9#include <vector> 13#include <vector>
10 14
15#include "common/address_space.h"
11#include "common/common_funcs.h" 16#include "common/common_funcs.h"
12#include "common/common_types.h" 17#include "common/common_types.h"
13#include "common/swap.h" 18#include "common/swap.h"
19#include "core/hle/service/nvdrv/core/nvmap.h"
14#include "core/hle/service/nvdrv/devices/nvdevice.h" 20#include "core/hle/service/nvdrv/devices/nvdevice.h"
15 21
16namespace Service::Nvidia::Devices { 22namespace Tegra {
23class MemoryManager;
24} // namespace Tegra
25
26namespace Service::Nvidia {
27class Module;
28}
17 29
18constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; 30namespace Service::Nvidia::NvCore {
19constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; 31class Container;
32class NvMap;
33} // namespace Service::Nvidia::NvCore
20 34
21class nvmap; 35namespace Service::Nvidia::Devices {
22 36
23enum class AddressSpaceFlags : u32 { 37enum class MappingFlags : u32 {
24 None = 0x0, 38 None = 0,
25 FixedOffset = 0x1, 39 Fixed = 1 << 0,
26 Remap = 0x100, 40 Sparse = 1 << 1,
41 Remap = 1 << 8,
27}; 42};
28DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); 43DECLARE_ENUM_FLAG_OPERATORS(MappingFlags);
29 44
30class nvhost_as_gpu final : public nvdevice { 45class nvhost_as_gpu final : public nvdevice {
31public: 46public:
32 explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); 47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
33 ~nvhost_as_gpu() override; 48 ~nvhost_as_gpu() override;
34 49
35 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 50 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -42,46 +57,17 @@ public:
42 void OnOpen(DeviceFD fd) override; 57 void OnOpen(DeviceFD fd) override;
43 void OnClose(DeviceFD fd) override; 58 void OnClose(DeviceFD fd) override;
44 59
45private: 60 Kernel::KEvent* QueryEvent(u32 event_id) override;
46 class BufferMap final { 61
47 public: 62 struct VaRegion {
48 constexpr BufferMap() = default; 63 u64 offset;
49 64 u32 page_size;
50 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) 65 u32 _pad0_;
51 : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} 66 u64 pages;
52
53 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
54 bool is_allocated_)
55 : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
56 is_allocated{is_allocated_} {}
57
58 constexpr VAddr StartAddr() const {
59 return start_addr;
60 }
61
62 constexpr VAddr EndAddr() const {
63 return end_addr;
64 }
65
66 constexpr std::size_t Size() const {
67 return end_addr - start_addr;
68 }
69
70 constexpr VAddr CpuAddr() const {
71 return cpu_addr;
72 }
73
74 constexpr bool IsAllocated() const {
75 return is_allocated;
76 }
77
78 private:
79 GPUVAddr start_addr{};
80 GPUVAddr end_addr{};
81 VAddr cpu_addr{};
82 bool is_allocated{};
83 }; 67 };
68 static_assert(sizeof(VaRegion) == 0x18);
84 69
70private:
85 struct IoctlAllocAsEx { 71 struct IoctlAllocAsEx {
86 u32_le flags{}; // usually passes 1 72 u32_le flags{}; // usually passes 1
87 s32_le as_fd{}; // ignored; passes 0 73 s32_le as_fd{}; // ignored; passes 0
@@ -96,7 +82,7 @@ private:
96 struct IoctlAllocSpace { 82 struct IoctlAllocSpace {
97 u32_le pages{}; 83 u32_le pages{};
98 u32_le page_size{}; 84 u32_le page_size{};
99 AddressSpaceFlags flags{}; 85 MappingFlags flags{};
100 INSERT_PADDING_WORDS(1); 86 INSERT_PADDING_WORDS(1);
101 union { 87 union {
102 u64_le offset; 88 u64_le offset;
@@ -113,19 +99,19 @@ private:
113 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); 99 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
114 100
115 struct IoctlRemapEntry { 101 struct IoctlRemapEntry {
116 u16_le flags{}; 102 u16 flags;
117 u16_le kind{}; 103 u16 kind;
118 u32_le nvmap_handle{}; 104 NvCore::NvMap::Handle::Id handle;
119 u32_le map_offset{}; 105 u32 handle_offset_big_pages;
120 u32_le offset{}; 106 u32 as_offset_big_pages;
121 u32_le pages{}; 107 u32 big_pages;
122 }; 108 };
123 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); 109 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
124 110
125 struct IoctlMapBufferEx { 111 struct IoctlMapBufferEx {
126 AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable 112 MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
127 u32_le kind{}; // -1 is default 113 u32_le kind{}; // -1 is default
128 u32_le nvmap_handle{}; 114 NvCore::NvMap::Handle::Id handle;
129 u32_le page_size{}; // 0 means don't care 115 u32_le page_size{}; // 0 means don't care
130 s64_le buffer_offset{}; 116 s64_le buffer_offset{};
131 u64_le mapping_size{}; 117 u64_le mapping_size{};
@@ -143,27 +129,15 @@ private:
143 }; 129 };
144 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); 130 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
145 131
146 struct IoctlVaRegion {
147 u64_le offset{};
148 u32_le page_size{};
149 INSERT_PADDING_WORDS(1);
150 u64_le pages{};
151 };
152 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
153
154 struct IoctlGetVaRegions { 132 struct IoctlGetVaRegions {
155 u64_le buf_addr{}; // (contained output user ptr on linux, ignored) 133 u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
156 u32_le buf_size{}; // forced to 2*sizeof(struct va_region) 134 u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
157 u32_le reserved{}; 135 u32_le reserved{};
158 IoctlVaRegion small{}; 136 std::array<VaRegion, 2> regions{};
159 IoctlVaRegion big{};
160 }; 137 };
161 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, 138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
162 "IoctlGetVaRegions is incorrect size"); 139 "IoctlGetVaRegions is incorrect size");
163 140
164 s32 channel{};
165 u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
166
167 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); 141 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output);
168 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 142 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
169 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); 143 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
@@ -172,18 +146,75 @@ private:
172 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); 146 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
173 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
174 148
149 void GetVARegionsImpl(IoctlGetVaRegions& params);
175 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); 150 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
176 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 151 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
177 std::vector<u8>& inline_output); 152 std::vector<u8>& inline_output);
178 153
179 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 154 void FreeMappingLocked(u64 offset);
180 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 155
181 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); 156 Module& module;
157
158 NvCore::Container& container;
159 NvCore::NvMap& nvmap;
182 160
183 std::shared_ptr<nvmap> nvmap_dev; 161 struct Mapping {
162 VAddr ptr;
163 u64 offset;
164 u64 size;
165 bool fixed;
166 bool big_page; // Only valid if fixed == false
167 bool sparse_alloc;
168
169 Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_)
170 : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_),
171 sparse_alloc(sparse_alloc_) {}
172 };
173
174 struct Allocation {
175 u64 size;
176 std::list<std::shared_ptr<Mapping>> mappings;
177 u32 page_size;
178 bool sparse;
179 bool big_pages;
180 };
184 181
185 // This is expected to be ordered, therefore we must use a map, not unordered_map 182 std::map<u64, std::shared_ptr<Mapping>>
186 std::map<GPUVAddr, BufferMap> buffer_mappings; 183 mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and
184 //!< mapping type, this is needed as what was originally a single buffer may
185 //!< have been split into multiple GPU side buffers with the remap flag.
186 std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from
187 //!< which fixed buffers can be mapped into
188 std::mutex mutex; //!< Locks all AS operations
189
190 struct VM {
191 static constexpr u32 YUZU_PAGESIZE{0x1000};
192 static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)};
193
194 static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000};
195 static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000};
196 u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
197 u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)};
198
199 static constexpr u32 VA_START_SHIFT{10};
200 static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34};
201 static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37};
202 u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT};
203 u64 va_range_split{DEFAULT_VA_SPLIT};
204 u64 va_range_end{DEFAULT_VA_RANGE};
205
206 using Allocator = Common::FlatAllocator<u32, 0, 32>;
207
208 std::unique_ptr<Allocator> big_page_allocator;
209 std::shared_ptr<Allocator>
210 small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel
211
212 bool initialised{};
213 } vm;
214 std::shared_ptr<Tegra::MemoryManager> gmmu;
215
216 // s32 channel{};
217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
187}; 218};
188 219
189} // namespace Service::Nvidia::Devices 220} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 527531f29..eee11fab8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -1,24 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
5#include <bit>
4#include <cstdlib> 6#include <cstdlib>
5#include <cstring> 7#include <cstring>
6 8
9#include <fmt/format.h>
7#include "common/assert.h" 10#include "common/assert.h"
8#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
9#include "core/core.h" 13#include "core/core.h"
10#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
11#include "core/hle/kernel/k_writable_event.h" 15#include "core/hle/service/nvdrv/core/container.h"
16#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
12#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 17#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
13#include "video_core/gpu.h" 18#include "video_core/gpu.h"
19#include "video_core/host1x/host1x.h"
14 20
15namespace Service::Nvidia::Devices { 21namespace Service::Nvidia::Devices {
16 22
17nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, 23nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
18 SyncpointManager& syncpoint_manager_) 24 NvCore::Container& core_)
19 : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ 25 : nvdevice{system_}, events_interface{events_interface_}, core{core_},
20 syncpoint_manager_} {} 26 syncpoint_manager{core_.GetSyncpointManager()} {}
21nvhost_ctrl::~nvhost_ctrl() = default; 27
28nvhost_ctrl::~nvhost_ctrl() {
29 for (auto& event : events) {
30 if (!event.registered) {
31 continue;
32 }
33 events_interface.FreeEvent(event.kevent);
34 }
35}
22 36
23NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
24 std::vector<u8>& output) { 38 std::vector<u8>& output) {
@@ -30,13 +44,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
30 case 0x1c: 44 case 0x1c:
31 return IocCtrlClearEventWait(input, output); 45 return IocCtrlClearEventWait(input, output);
32 case 0x1d: 46 case 0x1d:
33 return IocCtrlEventWait(input, output, false);
34 case 0x1e:
35 return IocCtrlEventWait(input, output, true); 47 return IocCtrlEventWait(input, output, true);
48 case 0x1e:
49 return IocCtrlEventWait(input, output, false);
36 case 0x1f: 50 case 0x1f:
37 return IocCtrlEventRegister(input, output); 51 return IocCtrlEventRegister(input, output);
38 case 0x20: 52 case 0x20:
39 return IocCtrlEventUnregister(input, output); 53 return IocCtrlEventUnregister(input, output);
54 case 0x21:
55 return IocCtrlEventUnregisterBatch(input, output);
40 } 56 }
41 break; 57 break;
42 default: 58 default:
@@ -60,6 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
60} 76}
61 77
62void nvhost_ctrl::OnOpen(DeviceFD fd) {} 78void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79
63void nvhost_ctrl::OnClose(DeviceFD fd) {} 80void nvhost_ctrl::OnClose(DeviceFD fd) {}
64 81
65NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 82NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -71,116 +88,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
71} 88}
72 89
73NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 90NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
74 bool is_async) { 91 bool is_allocation) {
75 IocCtrlEventWaitParams params{}; 92 IocCtrlEventWaitParams params{};
76 std::memcpy(&params, input.data(), sizeof(params)); 93 std::memcpy(&params, input.data(), sizeof(params));
77 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 94 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
78 params.syncpt_id, params.threshold, params.timeout, is_async); 95 params.fence.id, params.fence.value, params.timeout, is_allocation);
79 96
80 if (params.syncpt_id >= MaxSyncPoints) { 97 bool must_unmark_fail = !is_allocation;
81 return NvResult::BadParameter; 98 const u32 event_id = params.value.raw;
82 } 99 SCOPE_EXIT({
100 std::memcpy(output.data(), &params, sizeof(params));
101 if (must_unmark_fail) {
102 events[event_id].fails = 0;
103 }
104 });
83 105
84 u32 event_id = params.value & 0x00FF; 106 const u32 fence_id = static_cast<u32>(params.fence.id);
85 107
86 if (event_id >= MaxNvEvents) { 108 if (fence_id >= MaxSyncPoints) {
87 std::memcpy(output.data(), &params, sizeof(params));
88 return NvResult::BadParameter; 109 return NvResult::BadParameter;
89 } 110 }
90 111
91 if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 112 if (params.fence.value == 0) {
92 params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); 113 if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) {
93 std::memcpy(output.data(), &params, sizeof(params)); 114 LOG_WARNING(Service_NVDRV,
94 events_interface.failed[event_id] = false; 115 "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
116 params.fence.id, params.fence.value, params.timeout, is_allocation);
117 } else {
118 params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
119 }
95 return NvResult::Success; 120 return NvResult::Success;
96 } 121 }
97 122
98 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); 123 if (syncpoint_manager.IsFenceSignalled(params.fence)) {
99 syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 124 params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
100 params.value = new_value;
101 std::memcpy(output.data(), &params, sizeof(params));
102 events_interface.failed[event_id] = false;
103 return NvResult::Success; 125 return NvResult::Success;
104 } 126 }
105 127
106 auto& event = events_interface.events[event_id]; 128 if (const auto new_value = syncpoint_manager.UpdateMin(fence_id);
107 auto& gpu = system.GPU(); 129 syncpoint_manager.IsFenceSignalled(params.fence)) {
108 130 params.value.raw = new_value;
109 // This is mostly to take into account unimplemented features. As synced
110 // gpu is always synced.
111 if (!gpu.IsAsync()) {
112 event.event->GetWritableEvent().Signal();
113 return NvResult::Success;
114 }
115 const u32 current_syncpoint_value = event.fence.value;
116 const s32 diff = current_syncpoint_value - params.threshold;
117 if (diff >= 0) {
118 event.event->GetWritableEvent().Signal();
119 params.value = current_syncpoint_value;
120 std::memcpy(output.data(), &params, sizeof(params));
121 events_interface.failed[event_id] = false;
122 return NvResult::Success; 131 return NvResult::Success;
123 } 132 }
124 const u32 target_value = current_syncpoint_value - diff;
125 133
126 if (!is_async) { 134 auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
127 params.value = 0; 135 const u32 target_value = params.fence.value;
136
137 auto lock = NvEventsLock();
138
139 u32 slot = [&]() {
140 if (is_allocation) {
141 params.value.raw = 0;
142 return FindFreeNvEvent(fence_id);
143 } else {
144 return params.value.raw;
145 }
146 }();
147
148 must_unmark_fail = false;
149
150 const auto check_failing = [&]() {
151 if (events[slot].fails > 2) {
152 {
153 auto lk = system.StallProcesses();
154 host1x_syncpoint_manager.WaitHost(fence_id, target_value);
155 system.UnstallProcesses();
156 }
157 params.value.raw = target_value;
158 return true;
159 }
160 return false;
161 };
162
163 if (slot >= MaxNvEvents) {
164 return NvResult::BadParameter;
128 } 165 }
129 166
130 if (params.timeout == 0) { 167 if (params.timeout == 0) {
131 std::memcpy(output.data(), &params, sizeof(params)); 168 if (check_failing()) {
169 events[slot].fails = 0;
170 return NvResult::Success;
171 }
132 return NvResult::Timeout; 172 return NvResult::Timeout;
133 } 173 }
134 174
135 EventState status = events_interface.status[event_id]; 175 auto& event = events[slot];
136 const bool bad_parameter = status == EventState::Busy; 176
137 if (bad_parameter) { 177 if (!event.registered) {
138 std::memcpy(output.data(), &params, sizeof(params));
139 return NvResult::BadParameter; 178 return NvResult::BadParameter;
140 } 179 }
141 events_interface.SetEventStatus(event_id, EventState::Waiting); 180
142 events_interface.assigned_syncpt[event_id] = params.syncpt_id; 181 if (event.IsBeingUsed()) {
143 events_interface.assigned_value[event_id] = target_value; 182 return NvResult::BadParameter;
144 if (is_async) { 183 }
145 params.value = params.syncpt_id << 4; 184
146 } else { 185 if (check_failing()) {
147 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; 186 event.fails = 0;
148 }
149 params.value |= event_id;
150 event.event->GetWritableEvent().Clear();
151 if (events_interface.failed[event_id]) {
152 {
153 auto lk = system.StallProcesses();
154 gpu.WaitFence(params.syncpt_id, target_value);
155 system.UnstallProcesses();
156 }
157 std::memcpy(output.data(), &params, sizeof(params));
158 events_interface.failed[event_id] = false;
159 return NvResult::Success; 187 return NvResult::Success;
160 } 188 }
161 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 189
162 std::memcpy(output.data(), &params, sizeof(params)); 190 params.value.raw = 0;
191
192 event.status.store(EventState::Waiting, std::memory_order_release);
193 event.assigned_syncpt = fence_id;
194 event.assigned_value = target_value;
195 if (is_allocation) {
196 params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id));
197 params.value.event_allocated.Assign(1);
198 } else {
199 params.value.syncpoint_id.Assign(fence_id);
200 }
201 params.value.raw |= slot;
202
203 event.wait_handle =
204 host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() {
205 auto& event_ = events[slot];
206 if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) ==
207 EventState::Waiting) {
208 event_.kevent->Signal();
209 }
210 event_.status.store(EventState::Signalled, std::memory_order_release);
211 });
163 return NvResult::Timeout; 212 return NvResult::Timeout;
164} 213}
165 214
215NvResult nvhost_ctrl::FreeEvent(u32 slot) {
216 if (slot >= MaxNvEvents) {
217 return NvResult::BadParameter;
218 }
219
220 auto& event = events[slot];
221
222 if (!event.registered) {
223 return NvResult::Success;
224 }
225
226 if (event.IsBeingUsed()) {
227 return NvResult::Busy;
228 }
229
230 FreeNvEvent(slot);
231 return NvResult::Success;
232}
233
166NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 234NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
167 IocCtrlEventRegisterParams params{}; 235 IocCtrlEventRegisterParams params{};
168 std::memcpy(&params, input.data(), sizeof(params)); 236 std::memcpy(&params, input.data(), sizeof(params));
169 const u32 event_id = params.user_event_id & 0x00FF; 237 const u32 event_id = params.user_event_id;
170 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 238 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
171 if (event_id >= MaxNvEvents) { 239 if (event_id >= MaxNvEvents) {
172 return NvResult::BadParameter; 240 return NvResult::BadParameter;
173 } 241 }
174 if (events_interface.registered[event_id]) { 242
175 const auto event_state = events_interface.status[event_id]; 243 auto lock = NvEventsLock();
176 if (event_state != EventState::Free) { 244
177 LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); 245 if (events[event_id].registered) {
178 events_interface.UnregisterEvent(event_id); 246 const auto result = FreeEvent(event_id);
179 } else { 247 if (result != NvResult::Success) {
180 return NvResult::BadParameter; 248 return result;
181 } 249 }
182 } 250 }
183 events_interface.RegisterEvent(event_id); 251 CreateNvEvent(event_id);
184 return NvResult::Success; 252 return NvResult::Success;
185} 253}
186 254
@@ -190,34 +258,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
190 std::memcpy(&params, input.data(), sizeof(params)); 258 std::memcpy(&params, input.data(), sizeof(params));
191 const u32 event_id = params.user_event_id & 0x00FF; 259 const u32 event_id = params.user_event_id & 0x00FF;
192 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 260 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
193 if (event_id >= MaxNvEvents) { 261
194 return NvResult::BadParameter; 262 auto lock = NvEventsLock();
195 } 263 return FreeEvent(event_id);
196 if (!events_interface.registered[event_id]) { 264}
197 return NvResult::BadParameter; 265
266NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
267 std::vector<u8>& output) {
268 IocCtrlEventUnregisterBatchParams params{};
269 std::memcpy(&params, input.data(), sizeof(params));
270 u64 event_mask = params.user_events;
271 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
272
273 auto lock = NvEventsLock();
274 while (event_mask != 0) {
275 const u64 event_id = std::countr_zero(event_mask);
276 event_mask &= ~(1ULL << event_id);
277 const auto result = FreeEvent(static_cast<u32>(event_id));
278 if (result != NvResult::Success) {
279 return result;
280 }
198 } 281 }
199 events_interface.UnregisterEvent(event_id);
200 return NvResult::Success; 282 return NvResult::Success;
201} 283}
202 284
203NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 285NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
204 IocCtrlEventSignalParams params{}; 286 IocCtrlEventClearParams params{};
205 std::memcpy(&params, input.data(), sizeof(params)); 287 std::memcpy(&params, input.data(), sizeof(params));
206 288
207 u32 event_id = params.event_id & 0x00FF; 289 u32 event_id = params.event_id.slot;
208 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); 290 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
209 291
210 if (event_id >= MaxNvEvents) { 292 if (event_id >= MaxNvEvents) {
211 return NvResult::BadParameter; 293 return NvResult::BadParameter;
212 } 294 }
213 if (events_interface.status[event_id] == EventState::Waiting) {
214 events_interface.LiberateEvent(event_id);
215 }
216 events_interface.failed[event_id] = true;
217 295
218 syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); 296 auto lock = NvEventsLock();
297
298 auto& event = events[event_id];
299 if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) ==
300 EventState::Waiting) {
301 auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
302 host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle);
303 syncpoint_manager.UpdateMin(event.assigned_syncpt);
304 event.wait_handle = {};
305 }
306 event.fails++;
307 event.status.store(EventState::Cancelled, std::memory_order_release);
308 event.kevent->Clear();
219 309
220 return NvResult::Success; 310 return NvResult::Success;
221} 311}
222 312
313Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) {
314 const auto desired_event = SyncpointEventValue{.raw = event_id};
315
316 const bool allocated = desired_event.event_allocated.Value() != 0;
317 const u32 slot{allocated ? desired_event.partial_slot.Value()
318 : static_cast<u32>(desired_event.slot)};
319 if (slot >= MaxNvEvents) {
320 ASSERT(false);
321 return nullptr;
322 }
323
324 const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value()
325 : desired_event.syncpoint_id.Value()};
326
327 auto lock = NvEventsLock();
328
329 auto& event = events[slot];
330 if (event.registered && event.assigned_syncpt == syncpoint_id) {
331 ASSERT(event.kevent);
332 return event.kevent;
333 }
334 // Is this possible in hardware?
335 ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id);
336 return nullptr;
337}
338
339std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() {
340 return std::unique_lock<std::mutex>(events_mutex);
341}
342
343void nvhost_ctrl::CreateNvEvent(u32 event_id) {
344 auto& event = events[event_id];
345 ASSERT(!event.kevent);
346 ASSERT(!event.registered);
347 ASSERT(!event.IsBeingUsed());
348 event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id));
349 event.status = EventState::Available;
350 event.registered = true;
351 const u64 mask = 1ULL << event_id;
352 event.fails = 0;
353 events_mask |= mask;
354 event.assigned_syncpt = 0;
355}
356
357void nvhost_ctrl::FreeNvEvent(u32 event_id) {
358 auto& event = events[event_id];
359 ASSERT(event.kevent);
360 ASSERT(event.registered);
361 ASSERT(!event.IsBeingUsed());
362 events_interface.FreeEvent(event.kevent);
363 event.kevent = nullptr;
364 event.status = EventState::Available;
365 event.registered = false;
366 const u64 mask = ~(1ULL << event_id);
367 events_mask &= mask;
368}
369
370u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) {
371 u32 slot{MaxNvEvents};
372 u32 free_slot{MaxNvEvents};
373 for (u32 i = 0; i < MaxNvEvents; i++) {
374 auto& event = events[i];
375 if (event.registered) {
376 if (!event.IsBeingUsed()) {
377 slot = i;
378 if (event.assigned_syncpt == syncpoint_id) {
379 return slot;
380 }
381 }
382 } else if (free_slot == MaxNvEvents) {
383 free_slot = i;
384 }
385 }
386 if (free_slot < MaxNvEvents) {
387 CreateNvEvent(free_slot);
388 return free_slot;
389 }
390
391 if (slot < MaxNvEvents) {
392 return slot;
393 }
394
395 LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event");
396 return 0;
397}
398
223} // namespace Service::Nvidia::Devices 399} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 4fbb89b15..0b56d7070 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -1,20 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
6#include <array> 7#include <array>
7#include <vector> 8#include <vector>
9#include "common/bit_field.h"
8#include "common/common_types.h" 10#include "common/common_types.h"
9#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
13#include "video_core/host1x/syncpoint_manager.h"
14
15namespace Service::Nvidia::NvCore {
16class Container;
17class SyncpointManager;
18} // namespace Service::Nvidia::NvCore
11 19
12namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
13 21
14class nvhost_ctrl final : public nvdevice { 22class nvhost_ctrl final : public nvdevice {
15public: 23public:
16 explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, 24 explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
17 SyncpointManager& syncpoint_manager_); 25 NvCore::Container& core);
18 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
19 27
20 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -27,7 +35,70 @@ public:
27 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
28 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
29 37
38 Kernel::KEvent* QueryEvent(u32 event_id) override;
39
40 union SyncpointEventValue {
41 u32 raw;
42
43 union {
44 BitField<0, 4, u32> partial_slot;
45 BitField<4, 28, u32> syncpoint_id;
46 };
47
48 struct {
49 u16 slot;
50 union {
51 BitField<0, 12, u16> syncpoint_id_for_allocation;
52 BitField<12, 1, u16> event_allocated;
53 };
54 };
55 };
56 static_assert(sizeof(SyncpointEventValue) == sizeof(u32));
57
30private: 58private:
59 struct InternalEvent {
60 // Mask representing registered events
61
62 // Each kernel event associated to an NV event
63 Kernel::KEvent* kevent{};
64 // The status of the current NVEvent
65 std::atomic<EventState> status{};
66
67 // Tells the NVEvent that it has failed.
68 u32 fails{};
69 // When an NVEvent is waiting on GPU interrupt, this is the sync_point
70 // associated with it.
71 u32 assigned_syncpt{};
72 // This is the value of the GPU interrupt for which the NVEvent is waiting
73 // for.
74 u32 assigned_value{};
75
76 // Tells if an NVEvent is registered or not
77 bool registered{};
78
79 // Used for waiting on a syncpoint & canceling it.
80 Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{};
81
82 bool IsBeingUsed() const {
83 const auto current_status = status.load(std::memory_order_acquire);
84 return current_status == EventState::Waiting ||
85 current_status == EventState::Cancelling ||
86 current_status == EventState::Signalling;
87 }
88 };
89
90 std::unique_lock<std::mutex> NvEventsLock();
91
92 void CreateNvEvent(u32 event_id);
93
94 void FreeNvEvent(u32 event_id);
95
96 u32 FindFreeNvEvent(u32 syncpoint_id);
97
98 std::array<InternalEvent, MaxNvEvents> events{};
99 std::mutex events_mutex;
100 u64 events_mask{};
101
31 struct IocSyncptReadParams { 102 struct IocSyncptReadParams {
32 u32_le id{}; 103 u32_le id{};
33 u32_le value{}; 104 u32_le value{};
@@ -83,27 +154,18 @@ private:
83 }; 154 };
84 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 155 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
85 156
86 struct IocCtrlEventSignalParams { 157 struct IocCtrlEventClearParams {
87 u32_le event_id{}; 158 SyncpointEventValue event_id{};
88 }; 159 };
89 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 160 static_assert(sizeof(IocCtrlEventClearParams) == 4,
90 "IocCtrlEventSignalParams is incorrect size"); 161 "IocCtrlEventClearParams is incorrect size");
91 162
92 struct IocCtrlEventWaitParams { 163 struct IocCtrlEventWaitParams {
93 u32_le syncpt_id{}; 164 NvFence fence{};
94 u32_le threshold{};
95 s32_le timeout{};
96 u32_le value{};
97 };
98 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
99
100 struct IocCtrlEventWaitAsyncParams {
101 u32_le syncpt_id{};
102 u32_le threshold{};
103 u32_le timeout{}; 165 u32_le timeout{};
104 u32_le value{}; 166 SyncpointEventValue value{};
105 }; 167 };
106 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, 168 static_assert(sizeof(IocCtrlEventWaitParams) == 16,
107 "IocCtrlEventWaitAsyncParams is incorrect size"); 169 "IocCtrlEventWaitAsyncParams is incorrect size");
108 170
109 struct IocCtrlEventRegisterParams { 171 struct IocCtrlEventRegisterParams {
@@ -118,19 +180,25 @@ private:
118 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, 180 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
119 "IocCtrlEventUnregisterParams is incorrect size"); 181 "IocCtrlEventUnregisterParams is incorrect size");
120 182
121 struct IocCtrlEventKill { 183 struct IocCtrlEventUnregisterBatchParams {
122 u64_le user_events{}; 184 u64_le user_events{};
123 }; 185 };
124 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size");
125 188
126 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
127 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); 190 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
191 bool is_allocation);
128 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
129 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
194 NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output);
130 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
131 196
197 NvResult FreeEvent(u32 slot);
198
132 EventInterface& events_interface; 199 EventInterface& events_interface;
133 SyncpointManager& syncpoint_manager; 200 NvCore::Container& core;
201 NvCore::SyncpointManager& syncpoint_manager;
134}; 202};
135 203
136} // namespace Service::Nvidia::Devices 204} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 2b3b7efea..ced57dfe6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -7,11 +7,19 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
10#include "core/hle/service/nvdrv/nvdrv.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
12 13
13nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} 14nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_)
14nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 15 : nvdevice{system_}, events_interface{events_interface_} {
16 error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier");
17 unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent");
18}
19nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
20 events_interface.FreeEvent(error_notifier_event);
21 events_interface.FreeEvent(unknown_event);
22}
15 23
16NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
17 std::vector<u8>& output) { 25 std::vector<u8>& output) {
@@ -286,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u
286 return NvResult::Success; 294 return NvResult::Success;
287} 295}
288 296
297Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
298 switch (event_id) {
299 case 1:
300 return error_notifier_event;
301 case 2:
302 return unknown_event;
303 default: {
304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
305 }
306 }
307 return nullptr;
308}
309
289} // namespace Service::Nvidia::Devices 310} // namespace Service::Nvidia::Devices
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 97e9a90cb..1e8f254e2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -10,11 +10,15 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12 12
13namespace Service::Nvidia {
14class EventInterface;
15}
16
13namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
14 18
15class nvhost_ctrl_gpu final : public nvdevice { 19class nvhost_ctrl_gpu final : public nvdevice {
16public: 20public:
17 explicit nvhost_ctrl_gpu(Core::System& system_); 21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
18 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
19 23
20 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -27,6 +31,8 @@ public:
27 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
28 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
29 33
34 Kernel::KEvent* QueryEvent(u32 event_id) override;
35
30private: 36private:
31 struct IoctlGpuCharacteristics { 37 struct IoctlGpuCharacteristics {
32 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) 38 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
@@ -160,6 +166,12 @@ private:
160 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
161 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
162 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
169
170 EventInterface& events_interface;
171
172 // Events
173 Kernel::KEvent* error_notifier_event;
174 Kernel::KEvent* unknown_event;
163}; 175};
164 176
165} // namespace Service::Nvidia::Devices 177} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b98e63011..45a759fa8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -5,29 +5,46 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
8#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
9#include "core/hle/service/nvdrv/syncpoint_manager.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
10#include "core/memory.h" 13#include "core/memory.h"
14#include "video_core/control/channel_state.h"
15#include "video_core/engines/puller.h"
11#include "video_core/gpu.h" 16#include "video_core/gpu.h"
17#include "video_core/host1x/host1x.h"
12 18
13namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
14namespace { 20namespace {
15Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { 21Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) {
16 Tegra::GPU::FenceAction result{}; 22 Tegra::Engines::Puller::FenceAction result{};
17 result.op.Assign(op); 23 result.op.Assign(op);
18 result.syncpoint_id.Assign(syncpoint_id); 24 result.syncpoint_id.Assign(syncpoint_id);
19 return {result.raw}; 25 return {result.raw};
20} 26}
21} // namespace 27} // namespace
22 28
23nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 29nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
24 SyncpointManager& syncpoint_manager_) 30 NvCore::Container& core_)
25 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { 31 : nvdevice{system_}, events_interface{events_interface_}, core{core_},
26 channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); 32 syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
27 channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); 33 channel_state{system.GPU().AllocateChannel()} {
34 channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
35 sm_exception_breakpoint_int_report_event =
36 events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt");
37 sm_exception_breakpoint_pause_report_event =
38 events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause");
39 error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier");
28} 40}
29 41
30nvhost_gpu::~nvhost_gpu() = default; 42nvhost_gpu::~nvhost_gpu() {
43 events_interface.FreeEvent(sm_exception_breakpoint_int_report_event);
44 events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event);
45 events_interface.FreeEvent(error_notifier_event);
46 syncpoint_manager.FreeSyncpoint(channel_syncpoint);
47}
31 48
32NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
33 std::vector<u8>& output) { 50 std::vector<u8>& output) {
@@ -167,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
167 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 184 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
168 params.unk3); 185 params.unk3);
169 186
170 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); 187 if (channel_state->initialized) {
188 LOG_CRITICAL(Service_NVDRV, "Already allocated!");
189 return NvResult::AlreadyAllocated;
190 }
191
192 system.GPU().InitChannel(*channel_state);
171 193
172 params.fence_out = channel_fence; 194 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
173 195
174 std::memcpy(output.data(), &params, output.size()); 196 std::memcpy(output.data(), &params, output.size());
175 return NvResult::Success; 197 return NvResult::Success;
@@ -188,39 +210,37 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
188 210
189static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { 211static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
190 return { 212 return {
191 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 213 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
192 Tegra::SubmissionMode::Increasing), 214 Tegra::SubmissionMode::Increasing),
193 {fence.value}, 215 {fence.value},
194 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, 216 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
195 Tegra::SubmissionMode::Increasing), 217 Tegra::SubmissionMode::Increasing),
196 BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), 218 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id),
197 }; 219 };
198} 220}
199 221
200static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence, 222static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) {
201 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 223 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 224 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
204 Tegra::SubmissionMode::Increasing), 225 Tegra::SubmissionMode::Increasing),
205 {}}; 226 {}};
206 227
207 for (u32 count = 0; count < add_increment; ++count) { 228 for (u32 count = 0; count < 2; ++count) {
208 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, 229 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
209 Tegra::SubmissionMode::Increasing)); 230 Tegra::SubmissionMode::Increasing));
210 result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); 231 result.emplace_back(
232 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));
211 } 233 }
212 234
213 return result; 235 return result;
214} 236}
215 237
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence, 238static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) {
217 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 239 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 240 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
220 Tegra::SubmissionMode::Increasing), 241 Tegra::SubmissionMode::Increasing),
221 {}}; 242 {}};
222 const std::vector<Tegra::CommandHeader> increment{ 243 const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)};
223 BuildIncrementCommandList(fence, add_increment)};
224 244
225 result.insert(result.end(), increment.begin(), increment.end()); 245 result.insert(result.end(), increment.begin(), increment.end());
226 246
@@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
234 254
235 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
236 256
237 params.fence_out.id = channel_fence.id; 257 std::scoped_lock lock(channel_mutex);
238 258
239 if (params.flags.add_wait.Value() && 259 const auto bind_id = channel_state->bind_id;
240 !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
241 gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
242 }
243 260
244 if (params.flags.add_increment.Value() || params.flags.increment.Value()) { 261 auto& flags = params.flags;
245 const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; 262
246 params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( 263 if (flags.fence_wait.Value()) {
247 params.fence_out.id, params.AddIncrementValue() + increment_value); 264 if (flags.increment_value.Value()) {
248 } else { 265 return NvResult::BadParameter;
249 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); 266 }
267
268 if (!syncpoint_manager.IsFenceSignalled(params.fence)) {
269 gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)});
270 }
250 } 271 }
251 272
252 gpu.PushGPUEntries(std::move(entries)); 273 params.fence.id = channel_syncpoint;
274
275 u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) +
276 (flags.increment_value.Value() != 0 ? params.fence.value : 0)};
277 params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment);
278 gpu.PushGPUEntries(bind_id, std::move(entries));
253 279
254 if (params.flags.add_increment.Value()) { 280 if (flags.fence_increment.Value()) {
255 if (params.flags.suppress_wfi) { 281 if (flags.suppress_wfi.Value()) {
256 gpu.PushGPUEntries(Tegra::CommandList{ 282 gpu.PushGPUEntries(bind_id,
257 BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); 283 Tegra::CommandList{BuildIncrementCommandList(params.fence)});
258 } else { 284 } else {
259 gpu.PushGPUEntries(Tegra::CommandList{ 285 gpu.PushGPUEntries(bind_id,
260 BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); 286 Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)});
261 } 287 }
262 } 288 }
263 289
290 flags.raw = 0;
291
264 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 292 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
265 return NvResult::Success; 293 return NvResult::Success;
266} 294}
@@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect
328 return NvResult::Success; 356 return NvResult::Success;
329} 357}
330 358
359Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
360 switch (event_id) {
361 case 1:
362 return sm_exception_breakpoint_int_report_event;
363 case 2:
364 return sm_exception_breakpoint_pause_report_event;
365 case 3:
366 return error_notifier_event;
367 default: {
368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
369 }
370 }
371 return nullptr;
372}
373
331} // namespace Service::Nvidia::Devices 374} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 8a9f7775a..1e4ecd55b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -13,17 +13,31 @@
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "video_core/dma_pusher.h" 14#include "video_core/dma_pusher.h"
15 15
16namespace Tegra {
17namespace Control {
18struct ChannelState;
19}
20} // namespace Tegra
21
16namespace Service::Nvidia { 22namespace Service::Nvidia {
23
24namespace NvCore {
25class Container;
26class NvMap;
17class SyncpointManager; 27class SyncpointManager;
18} 28} // namespace NvCore
29
30class EventInterface;
31} // namespace Service::Nvidia
19 32
20namespace Service::Nvidia::Devices { 33namespace Service::Nvidia::Devices {
21 34
35class nvhost_as_gpu;
22class nvmap; 36class nvmap;
23class nvhost_gpu final : public nvdevice { 37class nvhost_gpu final : public nvdevice {
24public: 38public:
25 explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 39 explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
26 SyncpointManager& syncpoint_manager_); 40 NvCore::Container& core);
27 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
28 42
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -36,7 +50,10 @@ public:
36 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
38 52
53 Kernel::KEvent* QueryEvent(u32 event_id) override;
54
39private: 55private:
56 friend class nvhost_as_gpu;
40 enum class CtxObjects : u32_le { 57 enum class CtxObjects : u32_le {
41 Ctx2D = 0x902D, 58 Ctx2D = 0x902D,
42 Ctx3D = 0xB197, 59 Ctx3D = 0xB197,
@@ -146,17 +163,13 @@ private:
146 u32_le num_entries{}; // number of fence objects being submitted 163 u32_le num_entries{}; // number of fence objects being submitted
147 union { 164 union {
148 u32_le raw; 165 u32_le raw;
149 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 166 BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list
150 BitField<1, 1, u32_le> add_increment; // append an increment to the list 167 BitField<1, 1, u32_le> fence_increment; // append an increment to the list
151 BitField<2, 1, u32_le> new_hw_format; // mostly ignored 168 BitField<2, 1, u32_le> new_hw_format; // mostly ignored
152 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 169 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
153 BitField<8, 1, u32_le> increment; // increment the returned fence 170 BitField<8, 1, u32_le> increment_value; // increment the returned fence
154 } flags; 171 } flags;
155 NvFence fence_out{}; // returned new fence object for others to wait on 172 NvFence fence{}; // returned new fence object for others to wait on
156
157 u32 AddIncrementValue() const {
158 return flags.add_increment.Value() << 1;
159 }
160 }; 173 };
161 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), 174 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
162 "IoctlSubmitGpfifo is incorrect size"); 175 "IoctlSubmitGpfifo is incorrect size");
@@ -191,9 +204,18 @@ private:
191 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
192 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
193 206
194 std::shared_ptr<nvmap> nvmap_dev; 207 EventInterface& events_interface;
195 SyncpointManager& syncpoint_manager; 208 NvCore::Container& core;
196 NvFence channel_fence; 209 NvCore::SyncpointManager& syncpoint_manager;
210 NvCore::NvMap& nvmap;
211 std::shared_ptr<Tegra::Control::ChannelState> channel_state;
212 u32 channel_syncpoint;
213 std::mutex channel_mutex;
214
215 // Events
216 Kernel::KEvent* sm_exception_breakpoint_int_report_event;
217 Kernel::KEvent* sm_exception_breakpoint_pause_report_event;
218 Kernel::KEvent* error_notifier_event;
197}; 219};
198 220
199} // namespace Service::Nvidia::Devices 221} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index a7385fce8..1703f9cc3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -5,14 +5,14 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h"
8#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
9#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
12 13
13nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 14nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
14 SyncpointManager& syncpoint_manager_) 15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
15 : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: { 23 case 0x1: {
24 if (!fd_to_id.contains(fd)) { 24 auto& host1x_file = core.Host1xDeviceFile();
25 fd_to_id[fd] = next_id++; 25 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
26 } 27 }
27 return Submit(fd, input, output); 28 return Submit(fd, input, output);
28 } 29 }
@@ -73,8 +74,9 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {
73 74
74void nvhost_nvdec::OnClose(DeviceFD fd) { 75void nvhost_nvdec::OnClose(DeviceFD fd) {
75 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 76 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
76 const auto iter = fd_to_id.find(fd); 77 auto& host1x_file = core.Host1xDeviceFile();
77 if (iter != fd_to_id.end()) { 78 const auto iter = host1x_file.fd_to_id.find(fd);
79 if (iter != host1x_file.fd_to_id.end()) {
78 system.GPU().ClearCdmaInstance(iter->second); 80 system.GPU().ClearCdmaInstance(iter->second);
79 } 81 }
80 system.AudioCore().SetNVDECActive(false); 82 system.AudioCore().SetNVDECActive(false);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 29b3e6a36..c1b4e53e8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices {
10 10
11class nvhost_nvdec final : public nvhost_nvdec_common { 11class nvhost_nvdec final : public nvhost_nvdec_common {
12public: 12public:
13 explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
14 SyncpointManager& syncpoint_manager_);
15 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
16 15
17 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -23,9 +22,6 @@ public:
23 22
24 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
25 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
26
27private:
28 u32 next_id{};
29}; 25};
30 26
31} // namespace Service::Nvidia::Devices 27} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 8b2cd9bf1..99eede702 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -8,10 +8,12 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/nvdrv/core/container.h"
12#include "core/hle/service/nvdrv/core/nvmap.h"
13#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
11#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" 14#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
12#include "core/hle/service/nvdrv/devices/nvmap.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
14#include "core/memory.h" 15#include "core/memory.h"
16#include "video_core/host1x/host1x.h"
15#include "video_core/memory_manager.h" 17#include "video_core/memory_manager.h"
16#include "video_core/renderer_base.h" 18#include "video_core/renderer_base.h"
17 19
@@ -44,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
44} 46}
45} // Anonymous namespace 47} // Anonymous namespace
46 48
47nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 49nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
48 SyncpointManager& syncpoint_manager_) 50 NvCore::ChannelType channel_type_)
49 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} 51 : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()},
50nvhost_nvdec_common::~nvhost_nvdec_common() = default; 52 nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {
53 auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
54 if (syncpts_accumulated.empty()) {
55 channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
56 } else {
57 channel_syncpoint = syncpts_accumulated.front();
58 syncpts_accumulated.pop_front();
59 }
60}
61
62nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64}
51 65
52NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 66NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
53 IoctlSetNvmapFD params{}; 67 IoctlSetNvmapFD params{};
@@ -84,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
84 for (std::size_t i = 0; i < syncpt_increments.size(); i++) { 98 for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
85 const SyncptIncr& syncpt_incr = syncpt_increments[i]; 99 const SyncptIncr& syncpt_incr = syncpt_increments[i];
86 fence_thresholds[i] = 100 fence_thresholds[i] =
87 syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); 101 syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
88 } 102 }
89 } 103 }
90 for (const auto& cmd_buffer : command_buffers) { 104 for (const auto& cmd_buffer : command_buffers) {
91 const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); 105 const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
92 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); 106 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 107 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), 108 system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
95 cmdlist.size() * sizeof(u32)); 109 cmdlist.size() * sizeof(u32));
96 gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); 110 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
97 } 111 }
98 std::memcpy(output.data(), &params, sizeof(IoctlSubmit)); 112 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
99 // Some games expect command_buffers to be written back 113 // Some games expect command_buffers to be written back
@@ -112,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
112 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
113 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
114 128
115 if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { 129 // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
116 device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); 130 params.value = channel_syncpoint;
117 }
118 params.value = device_syncpoints[params.param];
119 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint)); 131 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
120 132
121 return NvResult::Success; 133 return NvResult::Success;
@@ -123,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
123 135
124NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 136NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
125 IoctlGetWaitbase params{}; 137 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
126 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
127 params.value = 0; // Seems to be hard coded at 0 140 params.value = 0; // Seems to be hard coded at 0
128 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase)); 141 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
@@ -136,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
136 149
137 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); 150 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
138 151
139 auto& gpu = system.GPU();
140
141 for (auto& cmd_buffer : cmd_buffer_handles) { 152 for (auto& cmd_buffer : cmd_buffer_handles) {
142 auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; 153 cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
143 if (!object) {
144 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
145 std::memcpy(output.data(), &params, output.size());
146 return NvResult::InvalidState;
147 }
148 if (object->dma_map_addr == 0) {
149 // NVDEC and VIC memory is in the 32-bit address space
150 // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
151 const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
152 object->dma_map_addr = static_cast<u32>(low_addr);
153 // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
154 ASSERT(object->dma_map_addr == low_addr);
155 }
156 if (!object->dma_map_addr) {
157 LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
158 } else {
159 cmd_buffer.map_address = object->dma_map_addr;
160 }
161 } 154 }
162 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer)); 155 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
163 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), 156 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
@@ -167,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
167} 160}
168 161
169NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 162NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
170 // This is intntionally stubbed. 163 IoctlMapBuffer params{};
171 // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame 164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
172 // addresses, and risk invalidating data before the async GPU thread is done with it 165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
166
167 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
168 for (auto& cmd_buffer : cmd_buffer_handles) {
169 nvmap.UnpinHandle(cmd_buffer.map_handle);
170 }
171
173 std::memset(output.data(), 0, output.size()); 172 std::memset(output.data(), 0, output.size());
174 LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
175 return NvResult::Success; 173 return NvResult::Success;
176} 174}
177 175
@@ -182,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
182 return NvResult::Success; 180 return NvResult::Success;
183} 181}
184 182
183Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) {
184 LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id);
185 return nullptr;
186}
187
185} // namespace Service::Nvidia::Devices 188} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 12d39946d..fe76100c8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -3,21 +3,26 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <deque>
6#include <vector> 7#include <vector>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "common/swap.h" 9#include "common/swap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
9#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
10 12
11namespace Service::Nvidia { 13namespace Service::Nvidia {
12class SyncpointManager; 14
15namespace NvCore {
16class Container;
17class NvMap;
18} // namespace NvCore
13 19
14namespace Devices { 20namespace Devices {
15class nvmap;
16 21
17class nvhost_nvdec_common : public nvdevice { 22class nvhost_nvdec_common : public nvdevice {
18public: 23public:
19 explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 24 explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core,
20 SyncpointManager& syncpoint_manager_); 25 NvCore::ChannelType channel_type);
21 ~nvhost_nvdec_common() override; 26 ~nvhost_nvdec_common() override;
22 27
23protected: 28protected:
@@ -110,11 +115,15 @@ protected:
110 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 115 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
111 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
112 117
113 std::unordered_map<DeviceFD, u32> fd_to_id{}; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119
120 u32 channel_syncpoint;
114 s32_le nvmap_fd{}; 121 s32_le nvmap_fd{};
115 u32_le submit_timeout{}; 122 u32_le submit_timeout{};
116 std::shared_ptr<nvmap> nvmap_dev; 123 NvCore::Container& core;
117 SyncpointManager& syncpoint_manager; 124 NvCore::SyncpointManager& syncpoint_manager;
125 NvCore::NvMap& nvmap;
126 NvCore::ChannelType channel_type;
118 std::array<u32, MaxSyncPoints> device_syncpoints{}; 127 std::array<u32, MaxSyncPoints> device_syncpoints{};
119}; 128};
120}; // namespace Devices 129}; // namespace Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index f58e8bada..73f97136e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -4,13 +4,14 @@
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/service/nvdrv/core/container.h"
7#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 8#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
8#include "video_core/renderer_base.h" 9#include "video_core/renderer_base.h"
9 10
10namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
11nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 12
12 SyncpointManager& syncpoint_manager_) 13nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
13 : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} 14 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {}
14 15
15nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
16 17
@@ -19,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
19 switch (command.group) { 20 switch (command.group) {
20 case 0x0: 21 case 0x0:
21 switch (command.cmd) { 22 switch (command.cmd) {
22 case 0x1: 23 case 0x1: {
23 if (!fd_to_id.contains(fd)) { 24 auto& host1x_file = core.Host1xDeviceFile();
24 fd_to_id[fd] = next_id++; 25 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
25 } 27 }
26 return Submit(fd, input, output); 28 return Submit(fd, input, output);
29 }
27 case 0x2: 30 case 0x2:
28 return GetSyncpoint(input, output); 31 return GetSyncpoint(input, output);
29 case 0x3: 32 case 0x3:
@@ -67,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
67void nvhost_vic::OnOpen(DeviceFD fd) {} 70void nvhost_vic::OnOpen(DeviceFD fd) {}
68 71
69void nvhost_vic::OnClose(DeviceFD fd) { 72void nvhost_vic::OnClose(DeviceFD fd) {
70 const auto iter = fd_to_id.find(fd); 73 auto& host1x_file = core.Host1xDeviceFile();
71 if (iter != fd_to_id.end()) { 74 const auto iter = host1x_file.fd_to_id.find(fd);
75 if (iter != host1x_file.fd_to_id.end()) {
72 system.GPU().ClearCdmaInstance(iter->second); 76 system.GPU().ClearCdmaInstance(iter->second);
73 } 77 }
74} 78}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b41b195ae..f164caafb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices {
9 9
10class nvhost_vic final : public nvhost_nvdec_common { 10class nvhost_vic final : public nvhost_nvdec_common {
11public: 11public:
12 explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
13 SyncpointManager& syncpoint_manager_);
14 ~nvhost_vic(); 13 ~nvhost_vic();
15 14
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -22,8 +21,5 @@ public:
22 21
23 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
25
26private:
27 u32 next_id{};
28}; 24};
29} // namespace Service::Nvidia::Devices 25} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index d8518149d..44388655d 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -2,19 +2,26 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <bit>
5#include <cstring> 6#include <cstring>
6 7
8#include "common/alignment.h"
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/k_process.h"
14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h" 16#include "core/hle/service/nvdrv/devices/nvmap.h"
17#include "core/memory.h"
18
19using Core::Memory::YUZU_PAGESIZE;
10 20
11namespace Service::Nvidia::Devices { 21namespace Service::Nvidia::Devices {
12 22
13nvmap::nvmap(Core::System& system_) : nvdevice{system_} { 23nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
14 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to 24 : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {}
15 // represent this.
16 CreateObject(0);
17}
18 25
19nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
20 27
@@ -62,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
62void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
63void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
64 71
65VAddr nvmap::GetObjectAddress(u32 handle) const {
66 auto object = GetObject(handle);
67 ASSERT(object);
68 ASSERT(object->status == Object::Status::Allocated);
69 return object->addr;
70}
71
72u32 nvmap::CreateObject(u32 size) {
73 // Create a new nvmap object and obtain a handle to it.
74 auto object = std::make_shared<Object>();
75 object->id = next_id++;
76 object->size = size;
77 object->status = Object::Status::Created;
78 object->refcount = 1;
79
80 const u32 handle = next_handle++;
81
82 handles.insert_or_assign(handle, std::move(object));
83
84 return handle;
85}
86
87NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
88 IocCreateParams params; 73 IocCreateParams params;
89 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
90 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
91 76
92 if (!params.size) { 77 std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
93 LOG_ERROR(Service_NVDRV, "Size is 0"); 78 auto result =
94 return NvResult::BadValue; 79 file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
80 if (result != NvResult::Success) {
81 LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
82 return result;
95 } 83 }
96 84 handle_description->orig_size = params.size; // Orig size is the unaligned size
97 params.handle = CreateObject(params.size); 85 params.handle = handle_description->id;
86 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
98 87
99 std::memcpy(output.data(), &params, sizeof(params)); 88 std::memcpy(output.data(), &params, sizeof(params));
100 return NvResult::Success; 89 return NvResult::Success;
@@ -103,63 +92,69 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
103NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
104 IocAllocParams params; 93 IocAllocParams params;
105 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
106 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
107 96
108 if (!params.handle) { 97 if (!params.handle) {
109 LOG_ERROR(Service_NVDRV, "Handle is 0"); 98 LOG_CRITICAL(Service_NVDRV, "Handle is 0");
110 return NvResult::BadValue; 99 return NvResult::BadValue;
111 } 100 }
112 101
113 if ((params.align - 1) & params.align) { 102 if ((params.align - 1) & params.align) {
114 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); 103 LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
115 return NvResult::BadValue; 104 return NvResult::BadValue;
116 } 105 }
117 106
118 const u32 min_alignment = 0x1000; 107 // Force page size alignment at a minimum
119 if (params.align < min_alignment) { 108 if (params.align < YUZU_PAGESIZE) {
120 params.align = min_alignment; 109 params.align = YUZU_PAGESIZE;
121 } 110 }
122 111
123 auto object = GetObject(params.handle); 112 auto handle_description{file.GetHandle(params.handle)};
124 if (!object) { 113 if (!handle_description) {
125 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 114 LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
126 return NvResult::BadValue; 115 return NvResult::BadValue;
127 } 116 }
128 117
129 if (object->status == Object::Status::Allocated) { 118 if (handle_description->allocated) {
130 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); 119 LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
131 return NvResult::InsufficientMemory; 120 return NvResult::InsufficientMemory;
132 } 121 }
133 122
134 object->flags = params.flags; 123 const auto result =
135 object->align = params.align; 124 handle_description->Alloc(params.flags, params.align, params.kind, params.address);
136 object->kind = params.kind; 125 if (result != NvResult::Success) {
137 object->addr = params.addr; 126 LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
138 object->status = Object::Status::Allocated; 127 return result;
139 128 }
129 ASSERT(system.CurrentProcess()
130 ->PageTable()
131 .LockForMapDeviceAddressSpace(handle_description->address, handle_description->size,
132 Kernel::KMemoryPermission::None, true)
133 .IsSuccess());
140 std::memcpy(output.data(), &params, sizeof(params)); 134 std::memcpy(output.data(), &params, sizeof(params));
141 return NvResult::Success; 135 return result;
142} 136}
143 137
144NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 138NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
145 IocGetIdParams params; 139 IocGetIdParams params;
146 std::memcpy(&params, input.data(), sizeof(params)); 140 std::memcpy(&params, input.data(), sizeof(params));
147 141
148 LOG_WARNING(Service_NVDRV, "called"); 142 LOG_DEBUG(Service_NVDRV, "called");
149 143
144 // See the comment in FromId for extra info on this function
150 if (!params.handle) { 145 if (!params.handle) {
151 LOG_ERROR(Service_NVDRV, "Handle is zero"); 146 LOG_CRITICAL(Service_NVDRV, "Error!");
152 return NvResult::BadValue; 147 return NvResult::BadValue;
153 } 148 }
154 149
155 auto object = GetObject(params.handle); 150 auto handle_description{file.GetHandle(params.handle)};
156 if (!object) { 151 if (!handle_description) {
157 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 152 LOG_CRITICAL(Service_NVDRV, "Error!");
158 return NvResult::BadValue; 153 return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
154 // handle exists or not
159 } 155 }
160 156
161 params.id = object->id; 157 params.id = handle_description->id;
162
163 std::memcpy(output.data(), &params, sizeof(params)); 158 std::memcpy(output.data(), &params, sizeof(params));
164 return NvResult::Success; 159 return NvResult::Success;
165} 160}
@@ -168,26 +163,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
168 IocFromIdParams params; 163 IocFromIdParams params;
169 std::memcpy(&params, input.data(), sizeof(params)); 164 std::memcpy(&params, input.data(), sizeof(params));
170 165
171 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 166 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
172 167
173 auto itr = std::find_if(handles.begin(), handles.end(), 168 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
174 [&](const auto& entry) { return entry.second->id == params.id; }); 169 // right permissions.
175 if (itr == handles.end()) { 170 // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and
176 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 171 // so this function just does simple validation and passes through the handle id.
172 if (!params.id) {
173 LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!");
177 return NvResult::BadValue; 174 return NvResult::BadValue;
178 } 175 }
179 176
180 auto& object = itr->second; 177 auto handle_description{file.GetHandle(params.id)};
181 if (object->status != Object::Status::Allocated) { 178 if (!handle_description) {
182 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 179 LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
183 return NvResult::BadValue; 180 return NvResult::BadValue;
184 } 181 }
185 182
186 itr->second->refcount++; 183 auto result = handle_description->Duplicate(false);
187 184 if (result != NvResult::Success) {
188 // Return the existing handle instead of creating a new one. 185 LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
189 params.handle = itr->first; 186 return result;
190 187 }
188 params.handle = handle_description->id;
191 std::memcpy(output.data(), &params, sizeof(params)); 189 std::memcpy(output.data(), &params, sizeof(params));
192 return NvResult::Success; 190 return NvResult::Success;
193} 191}
@@ -198,35 +196,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
198 IocParamParams params; 196 IocParamParams params;
199 std::memcpy(&params, input.data(), sizeof(params)); 197 std::memcpy(&params, input.data(), sizeof(params));
200 198
201 LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); 199 LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
202 200
203 auto object = GetObject(params.handle); 201 if (!params.handle) {
204 if (!object) { 202 LOG_CRITICAL(Service_NVDRV, "Invalid handle!");
205 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
206 return NvResult::BadValue; 203 return NvResult::BadValue;
207 } 204 }
208 205
209 if (object->status != Object::Status::Allocated) { 206 auto handle_description{file.GetHandle(params.handle)};
210 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 207 if (!handle_description) {
208 LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
211 return NvResult::BadValue; 209 return NvResult::BadValue;
212 } 210 }
213 211
214 switch (static_cast<ParamTypes>(params.param)) { 212 switch (params.param) {
215 case ParamTypes::Size: 213 case HandleParameterType::Size:
216 params.result = object->size; 214 params.result = static_cast<u32_le>(handle_description->orig_size);
217 break; 215 break;
218 case ParamTypes::Alignment: 216 case HandleParameterType::Alignment:
219 params.result = object->align; 217 params.result = static_cast<u32_le>(handle_description->align);
220 break; 218 break;
221 case ParamTypes::Heap: 219 case HandleParameterType::Base:
222 // TODO(Subv): Seems to be a hardcoded value? 220 params.result = static_cast<u32_le>(-22); // posix EINVAL
223 params.result = 0x40000000;
224 break; 221 break;
225 case ParamTypes::Kind: 222 case HandleParameterType::Heap:
226 params.result = object->kind; 223 if (handle_description->allocated)
224 params.result = 0x40000000;
225 else
226 params.result = 0;
227 break;
228 case HandleParameterType::Kind:
229 params.result = handle_description->kind;
230 break;
231 case HandleParameterType::IsSharedMemMapped:
232 params.result = handle_description->is_shared_mem_mapped;
227 break; 233 break;
228 default: 234 default:
229 UNIMPLEMENTED(); 235 return NvResult::BadValue;
230 } 236 }
231 237
232 std::memcpy(output.data(), &params, sizeof(params)); 238 std::memcpy(output.data(), &params, sizeof(params));
@@ -234,46 +240,31 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
234} 240}
235 241
236NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 242NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
237 // TODO(Subv): These flags are unconfirmed.
238 enum FreeFlags {
239 Freed = 0,
240 NotFreedYet = 1,
241 };
242
243 IocFreeParams params; 243 IocFreeParams params;
244 std::memcpy(&params, input.data(), sizeof(params)); 244 std::memcpy(&params, input.data(), sizeof(params));
245 245
246 LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); 246 LOG_DEBUG(Service_NVDRV, "called");
247 247
248 auto itr = handles.find(params.handle); 248 if (!params.handle) {
249 if (itr == handles.end()) { 249 LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
250 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 250 return NvResult::Success;
251 return NvResult::BadValue;
252 }
253 if (!itr->second->refcount) {
254 LOG_ERROR(
255 Service_NVDRV,
256 "There is no references to this object. The object is already freed. handle={:08X}",
257 params.handle);
258 return NvResult::BadValue;
259 } 251 }
260 252
261 itr->second->refcount--; 253 if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
262 254 if (freeInfo->can_unlock) {
263 params.size = itr->second->size; 255 ASSERT(system.CurrentProcess()
264 256 ->PageTable()
265 if (itr->second->refcount == 0) { 257 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
266 params.flags = Freed; 258 .IsSuccess());
267 // The address of the nvmap is written to the output if we're finally freeing it, otherwise 259 }
268 // 0 is written. 260 params.address = freeInfo->address;
269 params.address = itr->second->addr; 261 params.size = static_cast<u32>(freeInfo->size);
262 params.flags.raw = 0;
263 params.flags.map_uncached.Assign(freeInfo->was_uncached);
270 } else { 264 } else {
271 params.flags = NotFreedYet; 265 // This is possible when there's internel dups or other duplicates.
272 params.address = 0;
273 } 266 }
274 267
275 handles.erase(params.handle);
276
277 std::memcpy(output.data(), &params, sizeof(params)); 268 std::memcpy(output.data(), &params, sizeof(params));
278 return NvResult::Success; 269 return NvResult::Success;
279} 270}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index d5360d6e5..e9bfd0358 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -9,15 +9,23 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/core/nvmap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 13#include "core/hle/service/nvdrv/devices/nvdevice.h"
13 14
15namespace Service::Nvidia::NvCore {
16class Container;
17} // namespace Service::Nvidia::NvCore
18
14namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
15 20
16class nvmap final : public nvdevice { 21class nvmap final : public nvdevice {
17public: 22public:
18 explicit nvmap(Core::System& system_); 23 explicit nvmap(Core::System& system_, NvCore::Container& container);
19 ~nvmap() override; 24 ~nvmap() override;
20 25
26 nvmap(const nvmap&) = delete;
27 nvmap& operator=(const nvmap&) = delete;
28
21 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
22 std::vector<u8>& output) override; 30 std::vector<u8>& output) override;
23 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -28,31 +36,15 @@ public:
28 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
29 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
30 38
31 /// Returns the allocated address of an nvmap object given its handle. 39 enum class HandleParameterType : u32_le {
32 VAddr GetObjectAddress(u32 handle) const; 40 Size = 1,
33 41 Alignment = 2,
34 /// Represents an nvmap object. 42 Base = 3,
35 struct Object { 43 Heap = 4,
36 enum class Status { Created, Allocated }; 44 Kind = 5,
37 u32 id; 45 IsSharedMemMapped = 6
38 u32 size;
39 u32 flags;
40 u32 align;
41 u8 kind;
42 VAddr addr;
43 Status status;
44 u32 refcount;
45 u32 dma_map_addr;
46 }; 46 };
47 47
48 std::shared_ptr<Object> GetObject(u32 handle) const {
49 auto itr = handles.find(handle);
50 if (itr != handles.end()) {
51 return itr->second;
52 }
53 return {};
54 }
55
56private: 48private:
57 /// Id to use for the next handle that is created. 49 /// Id to use for the next handle that is created.
58 u32 next_handle = 0; 50 u32 next_handle = 0;
@@ -60,9 +52,6 @@ private:
60 /// Id to use for the next object that is created. 52 /// Id to use for the next object that is created.
61 u32 next_id = 0; 53 u32 next_id = 0;
62 54
63 /// Mapping of currently allocated handles to the objects they represent.
64 std::unordered_map<u32, std::shared_ptr<Object>> handles;
65
66 struct IocCreateParams { 55 struct IocCreateParams {
67 // Input 56 // Input
68 u32_le size{}; 57 u32_le size{};
@@ -83,11 +72,11 @@ private:
83 // Input 72 // Input
84 u32_le handle{}; 73 u32_le handle{};
85 u32_le heap_mask{}; 74 u32_le heap_mask{};
86 u32_le flags{}; 75 NvCore::NvMap::Handle::Flags flags{};
87 u32_le align{}; 76 u32_le align{};
88 u8 kind{}; 77 u8 kind{};
89 INSERT_PADDING_BYTES(7); 78 INSERT_PADDING_BYTES(7);
90 u64_le addr{}; 79 u64_le address{};
91 }; 80 };
92 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); 81 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
93 82
@@ -96,14 +85,14 @@ private:
96 INSERT_PADDING_BYTES(4); 85 INSERT_PADDING_BYTES(4);
97 u64_le address{}; 86 u64_le address{};
98 u32_le size{}; 87 u32_le size{};
99 u32_le flags{}; 88 NvCore::NvMap::Handle::Flags flags{};
100 }; 89 };
101 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); 90 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
102 91
103 struct IocParamParams { 92 struct IocParamParams {
104 // Input 93 // Input
105 u32_le handle{}; 94 u32_le handle{};
106 u32_le param{}; 95 HandleParameterType param{};
107 // Output 96 // Output
108 u32_le result{}; 97 u32_le result{};
109 }; 98 };
@@ -117,14 +106,15 @@ private:
117 }; 106 };
118 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
119 108
120 u32 CreateObject(u32 size);
121
122 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
123 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
124 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
125 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
126 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
127 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
115
116 NvCore::Container& container;
117 NvCore::NvMap& file;
128}; 118};
129 119
130} // namespace Service::Nvidia::Devices 120} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 1d00394c8..0e2f47075 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,5 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
@@ -78,11 +79,15 @@ enum class NvResult : u32 {
78 ModuleNotPresent = 0xA000E, 79 ModuleNotPresent = 0xA000E,
79}; 80};
80 81
82// obtained from
83// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47
81enum class EventState { 84enum class EventState {
82 Free = 0, 85 Available = 0,
83 Registered = 1, 86 Waiting = 1,
84 Waiting = 2, 87 Cancelling = 2,
85 Busy = 3, 88 Signalling = 3,
89 Signalled = 4,
90 Cancelled = 5,
86}; 91};
87 92
88union Ioctl { 93union Ioctl {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 756eb7453..9f4c7c99a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -1,5 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <utility> 5#include <utility>
5 6
@@ -7,7 +8,7 @@
7#include "core/core.h" 8#include "core/core.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/k_event.h" 10#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_writable_event.h" 11#include "core/hle/service/nvdrv/core/container.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 13#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
13#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 14#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -15,17 +16,31 @@
15#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 16#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
16#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 17#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
17#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 18#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
19#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
18#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" 20#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
19#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 21#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
20#include "core/hle/service/nvdrv/devices/nvmap.h" 22#include "core/hle/service/nvdrv/devices/nvmap.h"
21#include "core/hle/service/nvdrv/nvdrv.h" 23#include "core/hle/service/nvdrv/nvdrv.h"
22#include "core/hle/service/nvdrv/nvdrv_interface.h" 24#include "core/hle/service/nvdrv/nvdrv_interface.h"
23#include "core/hle/service/nvdrv/nvmemp.h" 25#include "core/hle/service/nvdrv/nvmemp.h"
24#include "core/hle/service/nvdrv/syncpoint_manager.h"
25#include "core/hle/service/nvflinger/nvflinger.h" 26#include "core/hle/service/nvflinger/nvflinger.h"
27#include "video_core/gpu.h"
26 28
27namespace Service::Nvidia { 29namespace Service::Nvidia {
28 30
31EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {}
32
33EventInterface::~EventInterface() = default;
34
35Kernel::KEvent* EventInterface::CreateEvent(std::string name) {
36 Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name));
37 return new_event;
38}
39
40void EventInterface::FreeEvent(Kernel::KEvent* event) {
41 module.service_context.CloseEvent(event);
42}
43
29void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 44void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
30 Core::System& system) { 45 Core::System& system) {
31 auto module_ = std::make_shared<Module>(system); 46 auto module_ = std::make_shared<Module>(system);
@@ -38,34 +53,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
38} 53}
39 54
40Module::Module(Core::System& system) 55Module::Module(Core::System& system)
41 : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { 56 : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} {
42 for (u32 i = 0; i < MaxNvEvents; i++) { 57 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
43 events_interface.events[i].event = 58 std::shared_ptr<Devices::nvdevice> device =
44 service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); 59 std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
45 events_interface.status[i] = EventState::Free; 60 return open_files.emplace(fd, device).first;
46 events_interface.registered[i] = false; 61 };
47 } 62 builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) {
48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system); 63 std::shared_ptr<Devices::nvdevice> device =
49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); 64 std::make_shared<Devices::nvhost_gpu>(system, events_interface, container);
50 devices["/dev/nvhost-gpu"] = 65 return open_files.emplace(fd, device).first;
51 std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); 66 };
52 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); 67 builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) {
53 devices["/dev/nvmap"] = nvmap_dev; 68 std::shared_ptr<Devices::nvdevice> device =
54 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 69 std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface);
55 devices["/dev/nvhost-ctrl"] = 70 return open_files.emplace(fd, device).first;
56 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); 71 };
57 devices["/dev/nvhost-nvdec"] = 72 builders["/dev/nvmap"] = [this, &system](DeviceFD fd) {
58 std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); 73 std::shared_ptr<Devices::nvdevice> device =
59 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); 74 std::make_shared<Devices::nvmap>(system, container);
60 devices["/dev/nvhost-vic"] = 75 return open_files.emplace(fd, device).first;
61 std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); 76 };
77 builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) {
78 std::shared_ptr<Devices::nvdevice> device =
79 std::make_shared<Devices::nvdisp_disp0>(system, container);
80 return open_files.emplace(fd, device).first;
81 };
82 builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) {
83 std::shared_ptr<Devices::nvdevice> device =
84 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container);
85 return open_files.emplace(fd, device).first;
86 };
87 builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) {
88 std::shared_ptr<Devices::nvdevice> device =
89 std::make_shared<Devices::nvhost_nvdec>(system, container);
90 return open_files.emplace(fd, device).first;
91 };
92 builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) {
93 std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system);
94 return open_files.emplace(fd, device).first;
95 };
96 builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) {
97 std::shared_ptr<Devices::nvdevice> device =
98 std::make_shared<Devices::nvhost_vic>(system, container);
99 return open_files.emplace(fd, device).first;
100 };
62} 101}
63 102
64Module::~Module() { 103Module::~Module() {}
65 for (u32 i = 0; i < MaxNvEvents; i++) {
66 service_context.CloseEvent(events_interface.events[i].event);
67 }
68}
69 104
70NvResult Module::VerifyFD(DeviceFD fd) const { 105NvResult Module::VerifyFD(DeviceFD fd) const {
71 if (fd < 0) { 106 if (fd < 0) {
@@ -82,18 +117,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
82} 117}
83 118
84DeviceFD Module::Open(const std::string& device_name) { 119DeviceFD Module::Open(const std::string& device_name) {
85 if (devices.find(device_name) == devices.end()) { 120 auto it = builders.find(device_name);
121 if (it == builders.end()) {
86 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); 122 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
87 return INVALID_NVDRV_FD; 123 return INVALID_NVDRV_FD;
88 } 124 }
89 125
90 auto device = devices[device_name];
91 const DeviceFD fd = next_fd++; 126 const DeviceFD fd = next_fd++;
127 auto& builder = it->second;
128 auto device = builder(fd)->second;
92 129
93 device->OnOpen(fd); 130 device->OnOpen(fd);
94 131
95 open_files[fd] = std::move(device);
96
97 return fd; 132 return fd;
98} 133}
99 134
@@ -168,22 +203,24 @@ NvResult Module::Close(DeviceFD fd) {
168 return NvResult::Success; 203 return NvResult::Success;
169} 204}
170 205
171void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { 206NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
172 for (u32 i = 0; i < MaxNvEvents; i++) { 207 if (fd < 0) {
173 if (events_interface.assigned_syncpt[i] == syncpoint_id && 208 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
174 events_interface.assigned_value[i] == value) { 209 return NvResult::InvalidState;
175 events_interface.LiberateEvent(i);
176 events_interface.events[i].event->GetWritableEvent().Signal();
177 }
178 } 210 }
179}
180 211
181Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { 212 const auto itr = open_files.find(fd);
182 return events_interface.events[event_id].event->GetReadableEvent();
183}
184 213
185Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { 214 if (itr == open_files.end()) {
186 return events_interface.events[event_id].event->GetWritableEvent(); 215 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
216 return NvResult::NotImplemented;
217 }
218
219 event = itr->second->QueryEvent(event_id);
220 if (!event) {
221 return NvResult::BadParameter;
222 }
223 return NvResult::Success;
187} 224}
188 225
189} // namespace Service::Nvidia 226} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index c929e5106..f3c81bd88 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -1,16 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
7#include <functional>
8#include <list>
6#include <memory> 9#include <memory>
10#include <string>
7#include <unordered_map> 11#include <unordered_map>
8#include <vector> 12#include <vector>
9 13
10#include "common/common_types.h" 14#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/container.h"
12#include "core/hle/service/nvdrv/nvdata.h" 17#include "core/hle/service/nvdrv/nvdata.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
14#include "core/hle/service/nvflinger/ui/fence.h" 18#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 19#include "core/hle/service/service.h"
16 20
@@ -28,81 +32,31 @@ class NVFlinger;
28 32
29namespace Service::Nvidia { 33namespace Service::Nvidia {
30 34
35namespace NvCore {
36class Container;
31class SyncpointManager; 37class SyncpointManager;
38} // namespace NvCore
32 39
33namespace Devices { 40namespace Devices {
34class nvdevice; 41class nvdevice;
35} 42class nvhost_ctrl;
43} // namespace Devices
36 44
37/// Represents an Nvidia event 45class Module;
38struct NvEvent {
39 Kernel::KEvent* event{};
40 NvFence fence{};
41};
42 46
43struct EventInterface { 47class EventInterface {
44 // Mask representing currently busy events 48public:
45 u64 events_mask{}; 49 explicit EventInterface(Module& module_);
46 // Each kernel event associated to an NV event 50 ~EventInterface();
47 std::array<NvEvent, MaxNvEvents> events; 51
48 // The status of the current NVEvent 52 Kernel::KEvent* CreateEvent(std::string name);
49 std::array<EventState, MaxNvEvents> status{}; 53
50 // Tells if an NVEvent is registered or not 54 void FreeEvent(Kernel::KEvent* event);
51 std::array<bool, MaxNvEvents> registered{}; 55
52 // Tells the NVEvent that it has failed. 56private:
53 std::array<bool, MaxNvEvents> failed{}; 57 Module& module;
54 // When an NVEvent is waiting on GPU interrupt, this is the sync_point 58 std::mutex guard;
55 // associated with it. 59 std::list<Devices::nvhost_ctrl*> on_signal;
56 std::array<u32, MaxNvEvents> assigned_syncpt{};
57 // This is the value of the GPU interrupt for which the NVEvent is waiting
58 // for.
59 std::array<u32, MaxNvEvents> assigned_value{};
60 // Constant to denote an unasigned syncpoint.
61 static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
62 std::optional<u32> GetFreeEvent() const {
63 u64 mask = events_mask;
64 for (u32 i = 0; i < MaxNvEvents; i++) {
65 const bool is_free = (mask & 0x1) == 0;
66 if (is_free) {
67 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
68 return {i};
69 }
70 }
71 mask = mask >> 1;
72 }
73 return std::nullopt;
74 }
75 void SetEventStatus(const u32 event_id, EventState new_status) {
76 EventState old_status = status[event_id];
77 if (old_status == new_status) {
78 return;
79 }
80 status[event_id] = new_status;
81 if (new_status == EventState::Registered) {
82 registered[event_id] = true;
83 }
84 if (new_status == EventState::Waiting || new_status == EventState::Busy) {
85 events_mask |= (1ULL << event_id);
86 }
87 }
88 void RegisterEvent(const u32 event_id) {
89 registered[event_id] = true;
90 if (status[event_id] == EventState::Free) {
91 status[event_id] = EventState::Registered;
92 }
93 }
94 void UnregisterEvent(const u32 event_id) {
95 registered[event_id] = false;
96 if (status[event_id] == EventState::Registered) {
97 status[event_id] = EventState::Free;
98 }
99 }
100 void LiberateEvent(const u32 event_id) {
101 status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
102 events_mask &= ~(1ULL << event_id);
103 assigned_syncpt[event_id] = unassigned_syncpt;
104 assigned_value[event_id] = 0;
105 }
106}; 60};
107 61
108class Module final { 62class Module final {
@@ -112,9 +66,9 @@ public:
112 66
113 /// Returns a pointer to one of the available devices, identified by its name. 67 /// Returns a pointer to one of the available devices, identified by its name.
114 template <typename T> 68 template <typename T>
115 std::shared_ptr<T> GetDevice(const std::string& name) { 69 std::shared_ptr<T> GetDevice(DeviceFD fd) {
116 auto itr = devices.find(name); 70 auto itr = open_files.find(fd);
117 if (itr == devices.end()) 71 if (itr == open_files.end())
118 return nullptr; 72 return nullptr;
119 return std::static_pointer_cast<T>(itr->second); 73 return std::static_pointer_cast<T>(itr->second);
120 } 74 }
@@ -137,28 +91,27 @@ public:
137 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
138 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
139 93
140 void SignalSyncpt(const u32 syncpoint_id, const u32 value); 94 NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
141
142 Kernel::KReadableEvent& GetEvent(u32 event_id);
143
144 Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
145 95
146private: 96private:
97 friend class EventInterface;
98 friend class Service::NVFlinger::NVFlinger;
99
147 /// Manages syncpoints on the host 100 /// Manages syncpoints on the host
148 SyncpointManager syncpoint_manager; 101 NvCore::Container container;
149 102
150 /// Id to use for the next open file descriptor. 103 /// Id to use for the next open file descriptor.
151 DeviceFD next_fd = 1; 104 DeviceFD next_fd = 1;
152 105
106 using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
153 /// Mapping of file descriptors to the devices they reference. 107 /// Mapping of file descriptors to the devices they reference.
154 std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; 108 FilesContainerType open_files;
155 109
156 /// Mapping of device node names to their implementation. 110 KernelHelpers::ServiceContext service_context;
157 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
158 111
159 EventInterface events_interface; 112 EventInterface events_interface;
160 113
161 KernelHelpers::ServiceContext service_context; 114 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
162}; 115};
163 116
164/// Registers all NVDRV services with the specified service manager. 117/// Registers all NVDRV services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index b5a980384..edbdfee43 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -1,10 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <cinttypes> 5#include <cinttypes>
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/nvdrv/nvdata.h" 11#include "core/hle/service/nvdrv/nvdata.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
@@ -12,10 +14,6 @@
12 14
13namespace Service::Nvidia { 15namespace Service::Nvidia {
14 16
15void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
16 nvdrv->SignalSyncpt(syncpoint_id, value);
17}
18
19void NVDRV::Open(Kernel::HLERequestContext& ctx) { 17void NVDRV::Open(Kernel::HLERequestContext& ctx) {
20 LOG_DEBUG(Service_NVDRV, "called"); 18 LOG_DEBUG(Service_NVDRV, "called");
21 IPC::ResponseBuilder rb{ctx, 4}; 19 IPC::ResponseBuilder rb{ctx, 4};
@@ -164,8 +162,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
164void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 162void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
165 IPC::RequestParser rp{ctx}; 163 IPC::RequestParser rp{ctx};
166 const auto fd = rp.Pop<DeviceFD>(); 164 const auto fd = rp.Pop<DeviceFD>();
167 const auto event_id = rp.Pop<u32>() & 0x00FF; 165 const auto event_id = rp.Pop<u32>();
168 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
169 166
170 if (!is_initialized) { 167 if (!is_initialized) {
171 ServiceError(ctx, NvResult::NotInitialized); 168 ServiceError(ctx, NvResult::NotInitialized);
@@ -173,24 +170,20 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
173 return; 170 return;
174 } 171 }
175 172
176 const auto nv_result = nvdrv->VerifyFD(fd); 173 Kernel::KEvent* event = nullptr;
177 if (nv_result != NvResult::Success) { 174 NvResult result = nvdrv->QueryEvent(fd, event_id, event);
178 LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
179 ServiceError(ctx, nv_result);
180 return;
181 }
182 175
183 if (event_id < MaxNvEvents) { 176 if (result == NvResult::Success) {
184 IPC::ResponseBuilder rb{ctx, 3, 1}; 177 IPC::ResponseBuilder rb{ctx, 3, 1};
185 rb.Push(ResultSuccess); 178 rb.Push(ResultSuccess);
186 auto& event = nvdrv->GetEvent(event_id); 179 auto& readable_event = event->GetReadableEvent();
187 event.Clear(); 180 rb.PushCopyObjects(readable_event);
188 rb.PushCopyObjects(event);
189 rb.PushEnum(NvResult::Success); 181 rb.PushEnum(NvResult::Success);
190 } else { 182 } else {
183 LOG_ERROR(Service_NVDRV, "Invalid event request!");
191 IPC::ResponseBuilder rb{ctx, 3}; 184 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess); 185 rb.Push(ResultSuccess);
193 rb.PushEnum(NvResult::BadParameter); 186 rb.PushEnum(result);
194 } 187 }
195} 188}
196 189
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index cbd37b52b..5ac06ee30 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -7,10 +7,6 @@
7#include "core/hle/service/nvdrv/nvdrv.h" 7#include "core/hle/service/nvdrv/nvdrv.h"
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9 9
10namespace Kernel {
11class KWritableEvent;
12}
13
14namespace Service::Nvidia { 10namespace Service::Nvidia {
15 11
16class NVDRV final : public ServiceFramework<NVDRV> { 12class NVDRV final : public ServiceFramework<NVDRV> {
@@ -18,8 +14,6 @@ public:
18 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); 14 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name);
19 ~NVDRV() override; 15 ~NVDRV() override;
20 16
21 void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value);
22
23private: 17private:
24 void Open(Kernel::HLERequestContext& ctx); 18 void Open(Kernel::HLERequestContext& ctx);
25 void Ioctl1(Kernel::HLERequestContext& ctx); 19 void Ioctl1(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
deleted file mode 100644
index a6fa943e8..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/hle/service/nvdrv/syncpoint_manager.h"
6#include "video_core/gpu.h"
7
8namespace Service::Nvidia {
9
10SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {}
11
12SyncpointManager::~SyncpointManager() = default;
13
14u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
15 syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
16 return GetSyncpointMin(syncpoint_id);
17}
18
19u32 SyncpointManager::AllocateSyncpoint() {
20 for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
21 if (!syncpoints[syncpoint_id].is_allocated) {
22 syncpoints[syncpoint_id].is_allocated = true;
23 return syncpoint_id;
24 }
25 }
26 ASSERT_MSG(false, "No more available syncpoints!");
27 return {};
28}
29
30u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
31 for (u32 index = 0; index < value; ++index) {
32 syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
33 }
34
35 return GetSyncpointMax(syncpoint_id);
36}
37
38} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
deleted file mode 100644
index 7f080f76e..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.h
+++ /dev/null
@@ -1,84 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <atomic>
8
9#include "common/common_types.h"
10#include "core/hle/service/nvdrv/nvdata.h"
11
12namespace Tegra {
13class GPU;
14}
15
16namespace Service::Nvidia {
17
18class SyncpointManager final {
19public:
20 explicit SyncpointManager(Tegra::GPU& gpu_);
21 ~SyncpointManager();
22
23 /**
24 * Returns true if the specified syncpoint is expired for the given value.
25 * @param syncpoint_id Syncpoint ID to check.
26 * @param value Value to check against the specified syncpoint.
27 * @returns True if the specified syncpoint is expired for the given value, otherwise False.
28 */
29 bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
30 return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
31 }
32
33 /**
34 * Gets the lower bound for the specified syncpoint.
35 * @param syncpoint_id Syncpoint ID to get the lower bound for.
36 * @returns The lower bound for the specified syncpoint.
37 */
38 u32 GetSyncpointMin(u32 syncpoint_id) const {
39 return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed);
40 }
41
42 /**
43 * Gets the uper bound for the specified syncpoint.
44 * @param syncpoint_id Syncpoint ID to get the upper bound for.
45 * @returns The upper bound for the specified syncpoint.
46 */
47 u32 GetSyncpointMax(u32 syncpoint_id) const {
48 return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed);
49 }
50
51 /**
52 * Refreshes the minimum value for the specified syncpoint.
53 * @param syncpoint_id Syncpoint ID to be refreshed.
54 * @returns The new syncpoint minimum value.
55 */
56 u32 RefreshSyncpoint(u32 syncpoint_id);
57
58 /**
59 * Allocates a new syncoint.
60 * @returns The syncpoint ID for the newly allocated syncpoint.
61 */
62 u32 AllocateSyncpoint();
63
64 /**
65 * Increases the maximum value for the specified syncpoint.
66 * @param syncpoint_id Syncpoint ID to be increased.
67 * @param value Value to increase the specified syncpoint by.
68 * @returns The new syncpoint maximum value.
69 */
70 u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
71
72private:
73 struct Syncpoint {
74 std::atomic<u32> min;
75 std::atomic<u32> max;
76 std::atomic<bool> is_allocated;
77 };
78
79 std::array<Syncpoint, MaxSyncPoints> syncpoints{};
80
81 Tegra::GPU& gpu;
82};
83
84} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 4b3d5efd6..1ce67c771 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -5,15 +5,18 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
8#include "core/hle/service/nvflinger/buffer_item.h" 9#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h" 10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h" 11#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h" 12#include "core/hle/service/nvflinger/producer_listener.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
12 14
13namespace Service::android { 15namespace Service::android {
14 16
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) 17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
16 : core{std::move(core_)}, slots{core->slots} {} 18 Service::Nvidia::NvCore::NvMap& nvmap_)
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
17 20
18BufferQueueConsumer::~BufferQueueConsumer() = default; 21BufferQueueConsumer::~BufferQueueConsumer() = default;
19 22
@@ -133,6 +136,8 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
133 136
134 slots[slot].buffer_state = BufferState::Free; 137 slots[slot].buffer_state = BufferState::Free;
135 138
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
136 listener = core->connected_producer_listener; 141 listener = core->connected_producer_listener;
137 142
138 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); 143 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
index b598c314f..4ec06ca13 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -13,6 +13,10 @@
13#include "core/hle/service/nvflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h" 14#include "core/hle/service/nvflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
16namespace Service::android { 20namespace Service::android {
17 21
18class BufferItem; 22class BufferItem;
@@ -21,7 +25,8 @@ class IConsumerListener;
21 25
22class BufferQueueConsumer final { 26class BufferQueueConsumer final {
23public: 27public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); 28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
29 Service::Nvidia::NvCore::NvMap& nvmap_);
25 ~BufferQueueConsumer(); 30 ~BufferQueueConsumer();
26 31
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
@@ -32,6 +37,7 @@ public:
32private: 37private:
33 std::shared_ptr<BufferQueueCore> core; 38 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots; 39 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
35}; 41};
36 42
37} // namespace Service::android 43} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 337431488..41ba44b21 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -11,10 +11,9 @@
11#include "core/hle/kernel/hle_ipc.h" 11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h" 12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h" 16#include "core/hle/service/nvdrv/core/nvmap.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h" 17#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h" 18#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h" 19#include "core/hle/service/nvflinger/consumer_listener.h"
@@ -26,8 +25,10 @@
26namespace Service::android { 25namespace Service::android {
27 26
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, 27BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_) 28 std::shared_ptr<BufferQueueCore> buffer_queue_core_,
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { 29 Service::Nvidia::NvCore::NvMap& nvmap_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
31 nvmap(nvmap_) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); 32 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32} 33}
33 34
@@ -108,7 +109,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
108 109
109 core->override_max_buffer_count = buffer_count; 110 core->override_max_buffer_count = buffer_count;
110 core->SignalDequeueCondition(); 111 core->SignalDequeueCondition();
111 buffer_wait_event->GetWritableEvent().Signal(); 112 buffer_wait_event->Signal();
112 listener = core->consumer_listener; 113 listener = core->consumer_listener;
113 } 114 }
114 115
@@ -530,6 +531,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
530 item.is_droppable = core->dequeue_buffer_cannot_block || async; 531 item.is_droppable = core->dequeue_buffer_cannot_block || async;
531 item.swap_interval = swap_interval; 532 item.swap_interval = swap_interval;
532 533
534 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
535
533 sticky_transform = sticky_transform_; 536 sticky_transform = sticky_transform_;
534 537
535 if (core->queue.empty()) { 538 if (core->queue.empty()) {
@@ -619,7 +622,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
619 slots[slot].fence = fence; 622 slots[slot].fence = fence;
620 623
621 core->SignalDequeueCondition(); 624 core->SignalDequeueCondition();
622 buffer_wait_event->GetWritableEvent().Signal(); 625 buffer_wait_event->Signal();
623} 626}
624 627
625Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { 628Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
@@ -739,6 +742,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
739 return Status::NoError; 742 return Status::NoError;
740 } 743 }
741 744
745 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
746 // Allows synchronous destruction of nvmap handles.
747 for (auto& item : core->queue) {
748 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
749 }
750 core->queue.clear();
751
742 switch (api) { 752 switch (api) {
743 case NativeWindowApi::Egl: 753 case NativeWindowApi::Egl:
744 case NativeWindowApi::Cpu: 754 case NativeWindowApi::Cpu:
@@ -749,7 +759,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
749 core->connected_producer_listener = nullptr; 759 core->connected_producer_listener = nullptr;
750 core->connected_api = NativeWindowApi::NoConnectedApi; 760 core->connected_api = NativeWindowApi::NoConnectedApi;
751 core->SignalDequeueCondition(); 761 core->SignalDequeueCondition();
752 buffer_wait_event->GetWritableEvent().Signal(); 762 buffer_wait_event->Signal();
753 listener = core->consumer_listener; 763 listener = core->consumer_listener;
754 } else { 764 } else {
755 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", 765 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
@@ -798,7 +808,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
798 } 808 }
799 809
800 core->SignalDequeueCondition(); 810 core->SignalDequeueCondition();
801 buffer_wait_event->GetWritableEvent().Signal(); 811 buffer_wait_event->Signal();
802 812
803 return Status::NoError; 813 return Status::NoError;
804} 814}
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 42d4722dc..7526bf8ec 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -24,13 +24,16 @@ namespace Kernel {
24class KernelCore; 24class KernelCore;
25class KEvent; 25class KEvent;
26class KReadableEvent; 26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel 27} // namespace Kernel
29 28
30namespace Service::KernelHelpers { 29namespace Service::KernelHelpers {
31class ServiceContext; 30class ServiceContext;
32} // namespace Service::KernelHelpers 31} // namespace Service::KernelHelpers
33 32
33namespace Service::Nvidia::NvCore {
34class NvMap;
35} // namespace Service::Nvidia::NvCore
36
34namespace Service::android { 37namespace Service::android {
35 38
36class BufferQueueCore; 39class BufferQueueCore;
@@ -39,7 +42,8 @@ class IProducerListener;
39class BufferQueueProducer final : public IBinder { 42class BufferQueueProducer final : public IBinder {
40public: 43public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, 44 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_); 45 std::shared_ptr<BufferQueueCore> buffer_queue_core_,
46 Service::Nvidia::NvCore::NvMap& nvmap_);
43 ~BufferQueueProducer(); 47 ~BufferQueueProducer();
44 48
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; 49 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
@@ -78,6 +82,8 @@ private:
78 s32 next_callback_ticket{}; 82 s32 next_callback_ticket{};
79 s32 current_callback_ticket{}; 83 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition; 84 std::condition_variable_any callback_condition;
85
86 Service::Nvidia::NvCore::NvMap& nvmap;
81}; 87};
82 88
83} // namespace Service::android 89} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 9b382bf56..c3af12c90 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -22,7 +22,10 @@
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h" 22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/vi/display/vi_display.h" 23#include "core/hle/service/vi/display/vi_display.h"
24#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
25#include "core/hle/service/vi/vi_results.h"
25#include "video_core/gpu.h" 26#include "video_core/gpu.h"
27#include "video_core/host1x/host1x.h"
28#include "video_core/host1x/syncpoint_manager.h"
26 29
27namespace Service::NVFlinger { 30namespace Service::NVFlinger {
28 31
@@ -30,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
30 33
31void NVFlinger::SplitVSync(std::stop_token stop_token) { 34void NVFlinger::SplitVSync(std::stop_token stop_token) {
32 system.RegisterHostThread(); 35 system.RegisterHostThread();
33 std::string name = "yuzu:VSyncThread"; 36 std::string name = "VSyncThread";
34 MicroProfileOnThreadCreate(name.c_str()); 37 MicroProfileOnThreadCreate(name.c_str());
35 38
36 // Cleanup 39 // Cleanup
@@ -99,6 +102,14 @@ NVFlinger::~NVFlinger() {
99 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 102 system.CoreTiming().UnscheduleEvent(single_composition_event, {});
100 } 103 }
101 104
105 ShutdownLayers();
106
107 if (nvdrv) {
108 nvdrv->Close(disp_fd);
109 }
110}
111
112void NVFlinger::ShutdownLayers() {
102 for (auto& display : displays) { 113 for (auto& display : displays) {
103 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 114 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
104 display.GetLayer(layer).Core().NotifyShutdown(); 115 display.GetLayer(layer).Core().NotifyShutdown();
@@ -108,6 +119,7 @@ NVFlinger::~NVFlinger() {
108 119
109void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 120void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
110 nvdrv = std::move(instance); 121 nvdrv = std::move(instance);
122 disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
111} 123}
112 124
113std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 125std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
@@ -126,6 +138,19 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
126 return itr->GetID(); 138 return itr->GetID();
127} 139}
128 140
141bool NVFlinger::CloseDisplay(u64 display_id) {
142 const auto lock_guard = Lock();
143 auto* const display = FindDisplay(display_id);
144
145 if (display == nullptr) {
146 return false;
147 }
148
149 display->Reset();
150
151 return true;
152}
153
129std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { 154std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
130 const auto lock_guard = Lock(); 155 const auto lock_guard = Lock();
131 auto* const display = FindDisplay(display_id); 156 auto* const display = FindDisplay(display_id);
@@ -141,7 +166,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
141 166
142void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 167void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
143 const auto buffer_id = next_buffer_queue_id++; 168 const auto buffer_id = next_buffer_queue_id++;
144 display.CreateLayer(layer_id, buffer_id); 169 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
145} 170}
146 171
147void NVFlinger::CloseLayer(u64 layer_id) { 172void NVFlinger::CloseLayer(u64 layer_id) {
@@ -163,15 +188,15 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
163 return layer->GetBinderId(); 188 return layer->GetBinderId();
164} 189}
165 190
166Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { 191ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) {
167 const auto lock_guard = Lock(); 192 const auto lock_guard = Lock();
168 auto* const display = FindDisplay(display_id); 193 auto* const display = FindDisplay(display_id);
169 194
170 if (display == nullptr) { 195 if (display == nullptr) {
171 return nullptr; 196 return VI::ResultNotFound;
172 } 197 }
173 198
174 return &display->GetVSyncEvent(); 199 return display->GetVSyncEvent();
175} 200}
176 201
177VI::Display* NVFlinger::FindDisplay(u64 display_id) { 202VI::Display* NVFlinger::FindDisplay(u64 display_id) {
@@ -261,30 +286,24 @@ void NVFlinger::Compose() {
261 return; // We are likely shutting down 286 return; // We are likely shutting down
262 } 287 }
263 288
264 auto& gpu = system.GPU();
265 const auto& multi_fence = buffer.fence;
266 guard->unlock();
267 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
268 const auto& fence = multi_fence.fences[fence_id];
269 gpu.WaitFence(fence.id, fence.value);
270 }
271 guard->lock();
272
273 MicroProfileFlip();
274
275 // Now send the buffer to the GPU for drawing. 289 // Now send the buffer to the GPU for drawing.
276 // TODO(Subv): Support more than just disp0. The display device selection is probably based 290 // TODO(Subv): Support more than just disp0. The display device selection is probably based
277 // on which display we're drawing (Default, Internal, External, etc) 291 // on which display we're drawing (Default, Internal, External, etc)
278 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 292 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
279 ASSERT(nvdisp); 293 ASSERT(nvdisp);
280 294
295 guard->unlock();
281 Common::Rectangle<int> crop_rect{ 296 Common::Rectangle<int> crop_rect{
282 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), 297 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
283 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; 298 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
284 299
285 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), 300 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
286 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), 301 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
287 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect); 302 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
303 buffer.fence.fences, buffer.fence.num_fences);
304
305 MicroProfileFlip();
306 guard->lock();
288 307
289 swap_interval = buffer.swap_interval; 308 swap_interval = buffer.swap_interval;
290 309
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 044ac6ac8..460bef976 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -11,6 +11,7 @@
11#include <vector> 11#include <vector>
12 12
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/hle/result.h"
14#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
15 16
16namespace Common { 17namespace Common {
@@ -24,7 +25,6 @@ struct EventType;
24 25
25namespace Kernel { 26namespace Kernel {
26class KReadableEvent; 27class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel 28} // namespace Kernel
29 29
30namespace Service::Nvidia { 30namespace Service::Nvidia {
@@ -48,6 +48,8 @@ public:
48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
49 ~NVFlinger(); 49 ~NVFlinger();
50 50
51 void ShutdownLayers();
52
51 /// Sets the NVDrv module instance to use to send buffers to the GPU. 53 /// Sets the NVDrv module instance to use to send buffers to the GPU.
52 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); 54 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
53 55
@@ -56,6 +58,11 @@ public:
56 /// If an invalid display name is provided, then an empty optional is returned. 58 /// If an invalid display name is provided, then an empty optional is returned.
57 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); 59 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
58 60
61 /// Closes the specified display by its ID.
62 ///
63 /// Returns false if an invalid display ID is provided.
64 [[nodiscard]] bool CloseDisplay(u64 display_id);
65
59 /// Creates a layer on the specified display and returns the layer ID. 66 /// Creates a layer on the specified display and returns the layer ID.
60 /// 67 ///
61 /// If an invalid display ID is specified, then an empty optional is returned. 68 /// If an invalid display ID is specified, then an empty optional is returned.
@@ -71,8 +78,9 @@ public:
71 78
72 /// Gets the vsync event for the specified display. 79 /// Gets the vsync event for the specified display.
73 /// 80 ///
74 /// If an invalid display ID is provided, then nullptr is returned. 81 /// If an invalid display ID is provided, then VI::ResultNotFound is returned.
75 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); 82 /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
83 [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id);
76 84
77 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 85 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
78 /// finished. 86 /// finished.
@@ -114,6 +122,7 @@ private:
114 void SplitVSync(std::stop_token stop_token); 122 void SplitVSync(std::stop_token stop_token);
115 123
116 std::shared_ptr<Nvidia::Module> nvdrv; 124 std::shared_ptr<Nvidia::Module> nvdrv;
125 s32 disp_fd;
117 126
118 std::list<VI::Display> displays; 127 std::list<VI::Display> displays;
119 128
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 2c31e9485..1ac97fe31 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -37,19 +37,19 @@ public:
37 37
38 void SignalChargerTypeChanged() { 38 void SignalChargerTypeChanged() {
39 if (should_signal && should_signal_charger_type) { 39 if (should_signal && should_signal_charger_type) {
40 state_change_event->GetWritableEvent().Signal(); 40 state_change_event->Signal();
41 } 41 }
42 } 42 }
43 43
44 void SignalPowerSupplyChanged() { 44 void SignalPowerSupplyChanged() {
45 if (should_signal && should_signal_power_supply) { 45 if (should_signal && should_signal_power_supply) {
46 state_change_event->GetWritableEvent().Signal(); 46 state_change_event->Signal();
47 } 47 }
48 } 48 }
49 49
50 void SignalBatteryVoltageStateChanged() { 50 void SignalBatteryVoltageStateChanged() {
51 if (should_signal && should_signal_battery_voltage) { 51 if (should_signal && should_signal_battery_voltage) {
52 state_change_event->GetWritableEvent().Signal(); 52 state_change_event->Signal();
53 } 53 }
54 } 54 }
55 55
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
index 65c3f135f..b1a0a5544 100644
--- a/src/core/hle/service/ptm/ts.cpp
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
15 {0, nullptr, "GetTemperatureRange"}, 15 {0, nullptr, "GetTemperatureRange"},
16 {1, &TS::GetTemperature, "GetTemperature"}, 16 {1, &TS::GetTemperature, "GetTemperature"},
17 {2, nullptr, "SetMeasurementMode"}, 17 {2, nullptr, "SetMeasurementMode"},
18 {3, nullptr, "GetTemperatureMilliC"}, 18 {3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"},
19 {4, nullptr, "OpenSession"}, 19 {4, nullptr, "OpenSession"},
20 }; 20 };
21 // clang-format on 21 // clang-format on
@@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
29 IPC::RequestParser rp{ctx}; 29 IPC::RequestParser rp{ctx};
30 const auto location{rp.PopEnum<Location>()}; 30 const auto location{rp.PopEnum<Location>()};
31 31
32 LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
33
34 const s32 temperature = location == Location::Internal ? 35 : 20; 32 const s32 temperature = location == Location::Internal ? 35 : 20;
35 33
36 IPC::ResponseBuilder rb{ctx, 3}; 34 IPC::ResponseBuilder rb{ctx, 3};
@@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
38 rb.Push(temperature); 36 rb.Push(temperature);
39} 37}
40 38
39void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) {
40 IPC::RequestParser rp{ctx};
41 const auto location{rp.PopEnum<Location>()};
42
43 const s32 temperature = location == Location::Internal ? 35000 : 20000;
44
45 IPC::ResponseBuilder rb{ctx, 3};
46 rb.Push(ResultSuccess);
47 rb.Push(temperature);
48}
49
41} // namespace Service::PTM 50} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
index 39a734ef7..39d51847e 100644
--- a/src/core/hle/service/ptm/ts.h
+++ b/src/core/hle/service/ptm/ts.h
@@ -20,6 +20,7 @@ private:
20 }; 20 };
21 21
22 void GetTemperature(Kernel::HLERequestContext& ctx); 22 void GetTemperature(Kernel::HLERequestContext& ctx);
23 void GetTemperatureMilliC(Kernel::HLERequestContext& ctx);
23}; 24};
24 25
25} // namespace Service::PTM 26} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index dadaf897f..5ab41c0c4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -99,6 +99,12 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
99ServiceFrameworkBase::~ServiceFrameworkBase() { 99ServiceFrameworkBase::~ServiceFrameworkBase() {
100 // Wait for other threads to release access before destroying 100 // Wait for other threads to release access before destroying
101 const auto guard = LockService(); 101 const auto guard = LockService();
102
103 if (named_port != nullptr) {
104 named_port->GetClientPort().Close();
105 named_port->GetServerPort().Close();
106 named_port = nullptr;
107 }
102} 108}
103 109
104void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { 110void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
@@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
113Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { 119Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
114 const auto guard = LockService(); 120 const auto guard = LockService();
115 121
116 ASSERT(!service_registered); 122 if (named_port == nullptr) {
123 ASSERT(!service_registered);
117 124
118 auto* port = Kernel::KPort::Create(kernel); 125 named_port = Kernel::KPort::Create(kernel);
119 port->Initialize(max_sessions, false, service_name); 126 named_port->Initialize(max_sessions, false, service_name);
120 port->GetServerPort().SetSessionHandler(shared_from_this());
121 127
122 service_registered = true; 128 service_registered = true;
129 }
123 130
124 return port->GetClientPort(); 131 return named_port->GetClientPort();
125} 132}
126 133
127void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { 134void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
@@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
199 switch (ctx.GetCommandType()) { 206 switch (ctx.GetCommandType()) {
200 case IPC::CommandType::Close: 207 case IPC::CommandType::Close:
201 case IPC::CommandType::TIPC_Close: { 208 case IPC::CommandType::TIPC_Close: {
202 session.Close();
203 IPC::ResponseBuilder rb{ctx, 2}; 209 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
205 result = IPC::ERR_REMOTE_PROCESS_DEAD; 211 result = IPC::ERR_REMOTE_PROCESS_DEAD;
@@ -244,6 +250,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
244 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 250 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
245 251
246 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); 252 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
253 system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler);
247 254
248 Account::InstallInterfaces(system); 255 Account::InstallInterfaces(system);
249 AM::InstallInterfaces(*sm, *nv_flinger, system); 256 AM::InstallInterfaces(*sm, *nv_flinger, system);
@@ -303,4 +310,8 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
303 310
304Services::~Services() = default; 311Services::~Services() = default;
305 312
313void Services::KillNVNFlinger() {
314 nv_flinger->ShutdownLayers();
315}
316
306} // namespace Service 317} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 5bf197c51..22e2119d7 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -20,6 +20,7 @@ class System;
20namespace Kernel { 20namespace Kernel {
21class HLERequestContext; 21class HLERequestContext;
22class KClientPort; 22class KClientPort;
23class KPort;
23class KServerSession; 24class KServerSession;
24class ServiceThread; 25class ServiceThread;
25} // namespace Kernel 26} // namespace Kernel
@@ -98,6 +99,9 @@ protected:
98 /// Identifier string used to connect to the service. 99 /// Identifier string used to connect to the service.
99 std::string service_name; 100 std::string service_name;
100 101
102 /// Port used by ManageNamedPort.
103 Kernel::KPort* named_port{};
104
101private: 105private:
102 template <typename T> 106 template <typename T>
103 friend class ServiceFramework; 107 friend class ServiceFramework;
@@ -238,6 +242,8 @@ public:
238 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); 242 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
239 ~Services(); 243 ~Services();
240 244
245 void KillNVNFlinger();
246
241private: 247private:
242 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; 248 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
243 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 249 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 2a0b812c1..d7cea6aac 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -101,6 +101,81 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
101 rb.Push(ResultSuccess); 101 rb.Push(ResultSuccess);
102} 102}
103 103
104// FIXME: implement support for the real system_settings.ini
105
106template <typename T>
107static std::vector<u8> ToBytes(const T& value) {
108 static_assert(std::is_trivially_copyable_v<T>);
109
110 const auto* begin = reinterpret_cast<const u8*>(&value);
111 const auto* end = begin + sizeof(T);
112
113 return std::vector<u8>(begin, end);
114}
115
116using Settings =
117 std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>;
118
119static Settings GetSettings() {
120 Settings ret;
121
122 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
123 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
124
125 return ret;
126}
127
128void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) {
129 LOG_DEBUG(Service_SET, "called");
130
131 // The category of the setting. This corresponds to the top-level keys of
132 // system_settings.ini.
133 const auto setting_category_buf{ctx.ReadBuffer(0)};
134 const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
135
136 // The name of the setting. This corresponds to the second-level keys of
137 // system_settings.ini.
138 const auto setting_name_buf{ctx.ReadBuffer(1)};
139 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
140
141 auto settings{GetSettings()};
142 u64 response_size{0};
143
144 if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
145 response_size = settings[setting_category][setting_name].size();
146 }
147
148 IPC::ResponseBuilder rb{ctx, 4};
149 rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess);
150 rb.Push(response_size);
151}
152
153void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
154 LOG_DEBUG(Service_SET, "called");
155
156 // The category of the setting. This corresponds to the top-level keys of
157 // system_settings.ini.
158 const auto setting_category_buf{ctx.ReadBuffer(0)};
159 const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
160
161 // The name of the setting. This corresponds to the second-level keys of
162 // system_settings.ini.
163 const auto setting_name_buf{ctx.ReadBuffer(1)};
164 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
165
166 auto settings{GetSettings()};
167 Result response{ResultUnknown};
168
169 if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
170 auto setting_value = settings[setting_category][setting_name];
171 ctx.WriteBuffer(setting_value.data(), setting_value.size());
172 response = ResultSuccess;
173 }
174
175 IPC::ResponseBuilder rb{ctx, 2};
176 rb.Push(response);
177}
178
104SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { 179SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
105 // clang-format off 180 // clang-format off
106 static const FunctionInfo functions[] = { 181 static const FunctionInfo functions[] = {
@@ -138,8 +213,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
138 {32, nullptr, "SetAccountNotificationSettings"}, 213 {32, nullptr, "SetAccountNotificationSettings"},
139 {35, nullptr, "GetVibrationMasterVolume"}, 214 {35, nullptr, "GetVibrationMasterVolume"},
140 {36, nullptr, "SetVibrationMasterVolume"}, 215 {36, nullptr, "SetVibrationMasterVolume"},
141 {37, nullptr, "GetSettingsItemValueSize"}, 216 {37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
142 {38, nullptr, "GetSettingsItemValue"}, 217 {38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"},
143 {39, nullptr, "GetTvSettings"}, 218 {39, nullptr, "GetTvSettings"},
144 {40, nullptr, "SetTvSettings"}, 219 {40, nullptr, "SetTvSettings"},
145 {41, nullptr, "GetEdid"}, 220 {41, nullptr, "GetEdid"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index ac97772b7..258ef8c57 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -23,6 +23,8 @@ private:
23 BasicBlack = 1, 23 BasicBlack = 1,
24 }; 24 };
25 25
26 void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx);
27 void GetSettingsItemValue(Kernel::HLERequestContext& ctx);
26 void GetFirmwareVersion(Kernel::HLERequestContext& ctx); 28 void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
27 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); 29 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
28 void GetColorSetId(Kernel::HLERequestContext& ctx); 30 void GetColorSetId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 246c94623..84720094f 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6);
23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); 23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
24 24
25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} 25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
26ServiceManager::~ServiceManager() = default; 26
27ServiceManager::~ServiceManager() {
28 for (auto& [name, port] : service_ports) {
29 port->GetClientPort().Close();
30 port->GetServerPort().Close();
31 }
32}
27 33
28void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 34void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
29 controller_interface->InvokeRequest(context); 35 controller_interface->InvokeRequest(context);
@@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
43 return self.sm_interface->CreatePort(); 49 return self.sm_interface->CreatePort();
44} 50}
45 51
52void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) {
53 self.sm_interface->AcceptSession(server_port);
54}
55
46Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 56Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
47 Kernel::SessionRequestHandlerPtr handler) { 57 Kernel::SessionRequestHandlerPtr handler) {
48 58
@@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
53 return ERR_ALREADY_REGISTERED; 63 return ERR_ALREADY_REGISTERED;
54 } 64 }
55 65
56 registered_services.emplace(std::move(name), handler); 66 auto* port = Kernel::KPort::Create(kernel);
67 port->Initialize(ServerSessionCountMax, false, name);
68
69 service_ports.emplace(name, port);
70 registered_services.emplace(name, handler);
57 71
58 return ResultSuccess; 72 return ResultSuccess;
59} 73}
@@ -68,25 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) {
68 } 82 }
69 83
70 registered_services.erase(iter); 84 registered_services.erase(iter);
85 service_ports.erase(name);
86
71 return ResultSuccess; 87 return ResultSuccess;
72} 88}
73 89
74ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { 90ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
75 CASCADE_CODE(ValidateServiceName(name)); 91 CASCADE_CODE(ValidateServiceName(name));
76 auto it = registered_services.find(name); 92 auto it = service_ports.find(name);
77 if (it == registered_services.end()) { 93 if (it == service_ports.end()) {
78 LOG_ERROR(Service_SM, "Server is not registered! service={}", name); 94 LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
79 return ERR_SERVICE_NOT_REGISTERED; 95 return ERR_SERVICE_NOT_REGISTERED;
80 } 96 }
81 97
82 auto* port = Kernel::KPort::Create(kernel); 98 return it->second;
83 SCOPE_EXIT({ port->Close(); });
84
85 port->Initialize(ServerSessionCountMax, false, name);
86 auto handler = it->second;
87 port->GetServerPort().SetSessionHandler(std::move(handler));
88
89 return port;
90} 99}
91 100
92/** 101/**
@@ -145,22 +154,20 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
145 154
146 // Find the named port. 155 // Find the named port.
147 auto port_result = service_manager.GetServicePort(name); 156 auto port_result = service_manager.GetServicePort(name);
148 if (port_result.Failed()) { 157 auto service = service_manager.GetService<Kernel::SessionRequestHandler>(name);
158 if (port_result.Failed() || !service) {
149 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); 159 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
150 return port_result.Code(); 160 return port_result.Code();
151 } 161 }
152 auto& port = port_result.Unwrap(); 162 auto& port = port_result.Unwrap();
153 SCOPE_EXIT({ port->GetClientPort().Close(); });
154
155 kernel.RegisterServerObject(&port->GetServerPort());
156 163
157 // Create a new session. 164 // Create a new session.
158 Kernel::KClientSession* session{}; 165 Kernel::KClientSession* session{};
159 if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); 166 if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
160 result.IsError()) {
161 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 167 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
162 return result; 168 return result;
163 } 169 }
170 service->AcceptSession(&port->GetServerPort());
164 171
165 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); 172 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
166 173
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 878decc6f..02a5dde9e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -51,6 +51,7 @@ private:
51class ServiceManager { 51class ServiceManager {
52public: 52public:
53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); 53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
54 static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port);
54 55
55 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
56 ~ServiceManager(); 57 ~ServiceManager();
@@ -78,6 +79,7 @@ private:
78 79
79 /// Map of registered services, retrieved using GetServicePort. 80 /// Map of registered services, retrieved using GetServicePort.
80 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; 81 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
82 std::unordered_map<std::string, Kernel::KPort*> service_ports;
81 83
82 /// Kernel context 84 /// Kernel context
83 Kernel::KernelCore& kernel; 85 Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 2a4bd64ab..69e0fe808 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -15,9 +15,9 @@
15namespace Service::SM { 15namespace Service::SM {
16 16
17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { 17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
18 ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); 18 ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain");
19 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); 19 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
20 ctx.Session()->ConvertToDomain(); 20 ctx.GetManager()->ConvertToDomainOnRequestEnd();
21 21
22 IPC::ResponseBuilder rb{ctx, 3}; 22 IPC::ResponseBuilder rb{ctx, 3};
23 rb.Push(ResultSuccess); 23 rb.Push(ResultSuccess);
@@ -27,23 +27,35 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
27void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { 27void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
28 LOG_DEBUG(Service, "called"); 28 LOG_DEBUG(Service, "called");
29 29
30 auto& parent_session = *ctx.Session()->GetParent(); 30 auto& process = *ctx.GetThread().GetOwnerProcess();
31 auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); 31 auto session_manager = ctx.GetManager();
32 auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager();
33 32
34 // Create a session. 33 // FIXME: this is duplicated from the SVC, it should just call it instead
35 Kernel::KClientSession* session{}; 34 // once this is a proper process
36 const Result result = parent_port.CreateSession(std::addressof(session), session_manager); 35
37 if (result.IsError()) { 36 // Reserve a new session from the process resource limit.
38 LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); 37 Kernel::KScopedResourceReservation session_reservation(&process,
39 IPC::ResponseBuilder rb{ctx, 2}; 38 Kernel::LimitableResource::Sessions);
40 rb.Push(result); 39 ASSERT(session_reservation.Succeeded());
41 } 40
41 // Create the session.
42 Kernel::KSession* session = Kernel::KSession::Create(system.Kernel());
43 ASSERT(session != nullptr);
44
45 // Initialize the session.
46 session->Initialize(nullptr, "");
47
48 // Commit the session reservation.
49 session_reservation.Commit();
50
51 // Register with manager.
52 session_manager->SessionHandler().RegisterSession(&session->GetServerSession(),
53 session_manager);
42 54
43 // We succeeded. 55 // We succeeded.
44 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 56 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
45 rb.Push(ResultSuccess); 57 rb.Push(ResultSuccess);
46 rb.PushMoveObjects(session); 58 rb.PushMoveObjects(session->GetClientSession());
47} 59}
48 60
49void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { 61void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index cc679cc81..9e94a462f 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -929,7 +929,7 @@ BSD::BSD(Core::System& system_, const char* name)
929 proxy_packet_received = room_member->BindOnProxyPacketReceived( 929 proxy_packet_received = room_member->BindOnProxyPacketReceived(
930 [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); 930 [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
931 } else { 931 } else {
932 LOG_ERROR(Service, "Network isn't initalized"); 932 LOG_ERROR(Service, "Network isn't initialized");
933 } 933 }
934} 934}
935 935
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
index a649bed3a..cafc04ee7 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/kernel/k_writable_event.h" 4#include "core/hle/kernel/k_event.h"
5#include "core/hle/service/time/errors.h" 5#include "core/hle/service/time/errors.h"
6#include "core/hle/service/time/system_clock_context_update_callback.h" 6#include "core/hle/service/time/system_clock_context_update_callback.h"
7 7
@@ -20,13 +20,13 @@ bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& valu
20} 20}
21 21
22void SystemClockContextUpdateCallback::RegisterOperationEvent( 22void SystemClockContextUpdateCallback::RegisterOperationEvent(
23 std::shared_ptr<Kernel::KWritableEvent>&& writable_event) { 23 std::shared_ptr<Kernel::KEvent>&& event) {
24 operation_event_list.emplace_back(std::move(writable_event)); 24 operation_event_list.emplace_back(std::move(event));
25} 25}
26 26
27void SystemClockContextUpdateCallback::BroadcastOperationEvent() { 27void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
28 for (const auto& writable_event : operation_event_list) { 28 for (const auto& event : operation_event_list) {
29 writable_event->Signal(); 29 event->Signal();
30 } 30 }
31} 31}
32 32
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
index 9c6caf196..bf657acd9 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -9,7 +9,7 @@
9#include "core/hle/service/time/clock_types.h" 9#include "core/hle/service/time/clock_types.h"
10 10
11namespace Kernel { 11namespace Kernel {
12class KWritableEvent; 12class KEvent;
13} 13}
14 14
15namespace Service::Time::Clock { 15namespace Service::Time::Clock {
@@ -24,7 +24,7 @@ public:
24 24
25 bool NeedUpdate(const SystemClockContext& value) const; 25 bool NeedUpdate(const SystemClockContext& value) const;
26 26
27 void RegisterOperationEvent(std::shared_ptr<Kernel::KWritableEvent>&& writable_event); 27 void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event);
28 28
29 void BroadcastOperationEvent(); 29 void BroadcastOperationEvent();
30 30
@@ -37,7 +37,7 @@ protected:
37 37
38private: 38private:
39 bool has_context{}; 39 bool has_context{};
40 std::vector<std::shared_ptr<Kernel::KWritableEvent>> operation_event_list; 40 std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list;
41}; 41};
42 42
43} // namespace Service::Time::Clock 43} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b34febb50..8ef74f1f0 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -10,8 +10,8 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
12#include "core/hle/kernel/k_readable_event.h" 12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/kernel/k_writable_event.h"
14#include "core/hle/service/kernel_helpers.h" 13#include "core/hle/service/kernel_helpers.h"
14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvflinger/buffer_item_consumer.h" 15#include "core/hle/service/nvflinger/buffer_item_consumer.h"
16#include "core/hle/service/nvflinger/buffer_queue_consumer.h" 16#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_core.h" 17#include "core/hle/service/nvflinger/buffer_queue_core.h"
@@ -19,6 +19,7 @@
19#include "core/hle/service/nvflinger/hos_binder_driver_server.h" 19#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
20#include "core/hle/service/vi/display/vi_display.h" 20#include "core/hle/service/vi/display/vi_display.h"
21#include "core/hle/service/vi/layer/vi_layer.h" 21#include "core/hle/service/vi/layer/vi_layer.h"
22#include "core/hle/service/vi/vi_results.h"
22 23
23namespace Service::VI { 24namespace Service::VI {
24 25
@@ -28,11 +29,13 @@ struct BufferQueue {
28 std::unique_ptr<android::BufferQueueConsumer> consumer; 29 std::unique_ptr<android::BufferQueueConsumer> consumer;
29}; 30};
30 31
31static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { 32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
33 Service::Nvidia::NvCore::NvMap& nvmap) {
32 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); 34 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
33 return {buffer_queue_core, 35 return {
34 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core), 36 buffer_queue_core,
35 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; 37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)};
36} 39}
37 40
38Display::Display(u64 id, std::string name_, 41Display::Display(u64 id, std::string name_,
@@ -55,18 +58,29 @@ const Layer& Display::GetLayer(std::size_t index) const {
55 return *layers.at(index); 58 return *layers.at(index);
56} 59}
57 60
58Kernel::KReadableEvent& Display::GetVSyncEvent() { 61ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() {
59 return vsync_event->GetReadableEvent(); 62 if (got_vsync_event) {
63 return ResultPermissionDenied;
64 }
65
66 got_vsync_event = true;
67
68 return GetVSyncEventUnchecked();
69}
70
71Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
72 return &vsync_event->GetReadableEvent();
60} 73}
61 74
62void Display::SignalVSyncEvent() { 75void Display::SignalVSyncEvent() {
63 vsync_event->GetWritableEvent().Signal(); 76 vsync_event->Signal();
64} 77}
65 78
66void Display::CreateLayer(u64 layer_id, u32 binder_id) { 79void Display::CreateLayer(u64 layer_id, u32 binder_id,
80 Service::Nvidia::NvCore::Container& nv_core) {
67 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 81 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
68 82
69 auto [core, producer, consumer] = CreateBufferQueue(service_context); 83 auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
70 84
71 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); 85 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
72 buffer_item_consumer->Connect(false); 86 buffer_item_consumer->Connect(false);
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 3838bb599..0b65a65da 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -9,6 +9,7 @@
9 9
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/result.h"
12 13
13namespace Kernel { 14namespace Kernel {
14class KEvent; 15class KEvent;
@@ -26,6 +27,11 @@ namespace Service::NVFlinger {
26class HosBinderDriverServer; 27class HosBinderDriverServer;
27} 28}
28 29
30namespace Service::Nvidia::NvCore {
31class Container;
32class NvMap;
33} // namespace Service::Nvidia::NvCore
34
29namespace Service::VI { 35namespace Service::VI {
30 36
31class Layer; 37class Layer;
@@ -73,8 +79,16 @@ public:
73 return layers.size(); 79 return layers.size();
74 } 80 }
75 81
76 /// Gets the readable vsync event. 82 /**
77 Kernel::KReadableEvent& GetVSyncEvent(); 83 * Gets the internal vsync event.
84 *
85 * @returns The internal Vsync event if it has not yet been retrieved,
86 * VI::ResultPermissionDenied otherwise.
87 */
88 [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent();
89
90 /// Gets the internal vsync event.
91 Kernel::KReadableEvent* GetVSyncEventUnchecked();
78 92
79 /// Signals the internal vsync event. 93 /// Signals the internal vsync event.
80 void SignalVSyncEvent(); 94 void SignalVSyncEvent();
@@ -84,7 +98,7 @@ public:
84 /// @param layer_id The ID to assign to the created layer. 98 /// @param layer_id The ID to assign to the created layer.
85 /// @param binder_id The ID assigned to the buffer queue. 99 /// @param binder_id The ID assigned to the buffer queue.
86 /// 100 ///
87 void CreateLayer(u64 layer_id, u32 binder_id); 101 void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
88 102
89 /// Closes and removes a layer from this display with the given ID. 103 /// Closes and removes a layer from this display with the given ID.
90 /// 104 ///
@@ -92,6 +106,12 @@ public:
92 /// 106 ///
93 void CloseLayer(u64 layer_id); 107 void CloseLayer(u64 layer_id);
94 108
109 /// Resets the display for a new connection.
110 void Reset() {
111 layers.clear();
112 got_vsync_event = false;
113 }
114
95 /// Attempts to find a layer with the given ID. 115 /// Attempts to find a layer with the given ID.
96 /// 116 ///
97 /// @param layer_id The layer ID. 117 /// @param layer_id The layer ID.
@@ -118,6 +138,7 @@ private:
118 138
119 std::vector<std::unique_ptr<Layer>> layers; 139 std::vector<std::unique_ptr<Layer>> layers;
120 Kernel::KEvent* vsync_event{}; 140 Kernel::KEvent* vsync_event{};
141 bool got_vsync_event{false};
121}; 142};
122 143
123} // namespace Service::VI 144} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 546879648..bb283e74e 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -29,16 +29,12 @@
29#include "core/hle/service/service.h" 29#include "core/hle/service/service.h"
30#include "core/hle/service/vi/vi.h" 30#include "core/hle/service/vi/vi.h"
31#include "core/hle/service/vi/vi_m.h" 31#include "core/hle/service/vi/vi_m.h"
32#include "core/hle/service/vi/vi_results.h"
32#include "core/hle/service/vi/vi_s.h" 33#include "core/hle/service/vi/vi_s.h"
33#include "core/hle/service/vi/vi_u.h" 34#include "core/hle/service/vi/vi_u.h"
34 35
35namespace Service::VI { 36namespace Service::VI {
36 37
37constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1};
38constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
39constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6};
40constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7};
41
42struct DisplayInfo { 38struct DisplayInfo {
43 /// The name of this particular display. 39 /// The name of this particular display.
44 char display_name[0x40]{"Default"}; 40 char display_name[0x40]{"Default"};
@@ -62,6 +58,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
62class NativeWindow final { 58class NativeWindow final {
63public: 59public:
64 constexpr explicit NativeWindow(u32 id_) : id{id_} {} 60 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
61 constexpr explicit NativeWindow(const NativeWindow& other) = default;
65 62
66private: 63private:
67 const u32 magic = 2; 64 const u32 magic = 2;
@@ -327,10 +324,10 @@ private:
327 IPC::RequestParser rp{ctx}; 324 IPC::RequestParser rp{ctx};
328 const u64 display = rp.Pop<u64>(); 325 const u64 display = rp.Pop<u64>();
329 326
330 LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); 327 const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
331 328
332 IPC::ResponseBuilder rb{ctx, 2}; 329 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(ResultSuccess); 330 rb.Push(rc);
334 } 331 }
335 332
336 void CreateManagedLayer(Kernel::HLERequestContext& ctx) { 333 void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
@@ -348,7 +345,7 @@ private:
348 if (!layer_id) { 345 if (!layer_id) {
349 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); 346 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
350 IPC::ResponseBuilder rb{ctx, 2}; 347 IPC::ResponseBuilder rb{ctx, 2};
351 rb.Push(ERR_NOT_FOUND); 348 rb.Push(ResultNotFound);
352 return; 349 return;
353 } 350 }
354 351
@@ -498,7 +495,7 @@ private:
498 if (!display_id) { 495 if (!display_id) {
499 LOG_ERROR(Service_VI, "Display not found! display_name={}", name); 496 LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
500 IPC::ResponseBuilder rb{ctx, 2}; 497 IPC::ResponseBuilder rb{ctx, 2};
501 rb.Push(ERR_NOT_FOUND); 498 rb.Push(ResultNotFound);
502 return; 499 return;
503 } 500 }
504 501
@@ -511,10 +508,10 @@ private:
511 IPC::RequestParser rp{ctx}; 508 IPC::RequestParser rp{ctx};
512 const u64 display_id = rp.Pop<u64>(); 509 const u64 display_id = rp.Pop<u64>();
513 510
514 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 511 const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
515 512
516 IPC::ResponseBuilder rb{ctx, 2}; 513 IPC::ResponseBuilder rb{ctx, 2};
517 rb.Push(ResultSuccess); 514 rb.Push(rc);
518 } 515 }
519 516
520 // This literally does nothing internally in the actual service itself, 517 // This literally does nothing internally in the actual service itself,
@@ -554,14 +551,14 @@ private:
554 551
555 if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { 552 if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
556 LOG_ERROR(Service_VI, "Invalid scaling mode provided."); 553 LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
557 rb.Push(ERR_OPERATION_FAILED); 554 rb.Push(ResultOperationFailed);
558 return; 555 return;
559 } 556 }
560 557
561 if (scaling_mode != NintendoScaleMode::ScaleToWindow && 558 if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
562 scaling_mode != NintendoScaleMode::PreserveAspectRatio) { 559 scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
563 LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); 560 LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
564 rb.Push(ERR_UNSUPPORTED); 561 rb.Push(ResultNotSupported);
565 return; 562 return;
566 } 563 }
567 564
@@ -594,7 +591,7 @@ private:
594 if (!display_id) { 591 if (!display_id) {
595 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); 592 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
596 IPC::ResponseBuilder rb{ctx, 2}; 593 IPC::ResponseBuilder rb{ctx, 2};
597 rb.Push(ERR_NOT_FOUND); 594 rb.Push(ResultNotFound);
598 return; 595 return;
599 } 596 }
600 597
@@ -602,7 +599,7 @@ private:
602 if (!buffer_queue_id) { 599 if (!buffer_queue_id) {
603 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); 600 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
604 IPC::ResponseBuilder rb{ctx, 2}; 601 IPC::ResponseBuilder rb{ctx, 2};
605 rb.Push(ERR_NOT_FOUND); 602 rb.Push(ResultNotFound);
606 return; 603 return;
607 } 604 }
608 605
@@ -640,7 +637,7 @@ private:
640 if (!layer_id) { 637 if (!layer_id) {
641 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); 638 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
642 IPC::ResponseBuilder rb{ctx, 2}; 639 IPC::ResponseBuilder rb{ctx, 2};
643 rb.Push(ERR_NOT_FOUND); 640 rb.Push(ResultNotFound);
644 return; 641 return;
645 } 642 }
646 643
@@ -648,7 +645,7 @@ private:
648 if (!buffer_queue_id) { 645 if (!buffer_queue_id) {
649 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 646 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
650 IPC::ResponseBuilder rb{ctx, 2}; 647 IPC::ResponseBuilder rb{ctx, 2};
651 rb.Push(ERR_NOT_FOUND); 648 rb.Push(ResultNotFound);
652 return; 649 return;
653 } 650 }
654 651
@@ -675,19 +672,23 @@ private:
675 IPC::RequestParser rp{ctx}; 672 IPC::RequestParser rp{ctx};
676 const u64 display_id = rp.Pop<u64>(); 673 const u64 display_id = rp.Pop<u64>();
677 674
678 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 675 LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
679 676
680 const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); 677 const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
681 if (!vsync_event) { 678 if (vsync_event.Failed()) {
682 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); 679 const auto result = vsync_event.Code();
680 if (result == ResultNotFound) {
681 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
682 }
683
683 IPC::ResponseBuilder rb{ctx, 2}; 684 IPC::ResponseBuilder rb{ctx, 2};
684 rb.Push(ERR_NOT_FOUND); 685 rb.Push(result);
685 return; 686 return;
686 } 687 }
687 688
688 IPC::ResponseBuilder rb{ctx, 2, 1}; 689 IPC::ResponseBuilder rb{ctx, 2, 1};
689 rb.Push(ResultSuccess); 690 rb.Push(ResultSuccess);
690 rb.PushCopyObjects(vsync_event); 691 rb.PushCopyObjects(*vsync_event);
691 } 692 }
692 693
693 void ConvertScalingMode(Kernel::HLERequestContext& ctx) { 694 void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
@@ -764,7 +765,7 @@ private:
764 return ConvertedScaleMode::PreserveAspectRatio; 765 return ConvertedScaleMode::PreserveAspectRatio;
765 default: 766 default:
766 LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); 767 LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
767 return ERR_OPERATION_FAILED; 768 return ResultOperationFailed;
768 } 769 }
769 } 770 }
770 771
@@ -794,7 +795,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
794 if (!IsValidServiceAccess(permission, policy)) { 795 if (!IsValidServiceAccess(permission, policy)) {
795 LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); 796 LOG_ERROR(Service_VI, "Permission denied for policy {}", policy);
796 IPC::ResponseBuilder rb{ctx, 2}; 797 IPC::ResponseBuilder rb{ctx, 2};
797 rb.Push(ERR_PERMISSION_DENIED); 798 rb.Push(ResultPermissionDenied);
798 return; 799 return;
799 } 800 }
800 801
diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h
new file mode 100644
index 000000000..22bac799f
--- /dev/null
+++ b/src/core/hle/service/vi/vi_results.h
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::VI {
9
10constexpr Result ResultOperationFailed{ErrorModule::VI, 1};
11constexpr Result ResultPermissionDenied{ErrorModule::VI, 5};
12constexpr Result ResultNotSupported{ErrorModule::VI, 6};
13constexpr Result ResultNotFound{ErrorModule::VI, 7};
14
15} // namespace Service::VI
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index cdf38a2a4..447fbffaa 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -364,7 +364,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
364 std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); 364 std::vector<WSAPOLLFD> host_pollfds(pollfds.size());
365 std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { 365 std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) {
366 WSAPOLLFD result; 366 WSAPOLLFD result;
367 result.fd = fd.socket->fd; 367 result.fd = fd.socket->GetFD();
368 result.events = TranslatePollEvents(fd.events); 368 result.events = TranslatePollEvents(fd.events);
369 result.revents = 0; 369 result.revents = 0;
370 return result; 370 return result;
@@ -430,12 +430,12 @@ std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
430 return {AcceptResult{}, GetAndLogLastError()}; 430 return {AcceptResult{}, GetAndLogLastError()};
431 } 431 }
432 432
433 AcceptResult result;
434 result.socket = std::make_unique<Socket>();
435 result.socket->fd = new_socket;
436
437 ASSERT(addrlen == sizeof(sockaddr_in)); 433 ASSERT(addrlen == sizeof(sockaddr_in));
438 result.sockaddr_in = TranslateToSockAddrIn(addr); 434
435 AcceptResult result{
436 .socket = std::make_unique<Socket>(new_socket),
437 .sockaddr_in = TranslateToSockAddrIn(addr),
438 };
439 439
440 return {std::move(result), Errno::SUCCESS}; 440 return {std::move(result), Errno::SUCCESS};
441} 441}
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 0f0a66160..057fd3661 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -188,7 +188,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
188std::optional<NetworkInterface> GetSelectedNetworkInterface() { 188std::optional<NetworkInterface> GetSelectedNetworkInterface() {
189 const auto& selected_network_interface = Settings::values.network_interface.GetValue(); 189 const auto& selected_network_interface = Settings::values.network_interface.GetValue();
190 const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); 190 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
191 if (network_interfaces.size() == 0) { 191 if (network_interfaces.empty()) {
192 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); 192 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
193 return std::nullopt; 193 return std::nullopt;
194 } 194 }
@@ -206,4 +206,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
206 return *res; 206 return *res;
207} 207}
208 208
209void SelectFirstNetworkInterface() {
210 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
211
212 if (network_interfaces.empty()) {
213 return;
214 }
215
216 Settings::values.network_interface.SetValue(network_interfaces[0].name);
217}
218
209} // namespace Network 219} // namespace Network
diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h
index 9b98b6b42..175e61b1f 100644
--- a/src/core/internal_network/network_interface.h
+++ b/src/core/internal_network/network_interface.h
@@ -24,5 +24,6 @@ struct NetworkInterface {
24 24
25std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); 25std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
26std::optional<NetworkInterface> GetSelectedNetworkInterface(); 26std::optional<NetworkInterface> GetSelectedNetworkInterface();
27void SelectFirstNetworkInterface();
27 28
28} // namespace Network 29} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 0c746bd82..7d5d37bbc 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/zstd_compression.h"
9#include "core/internal_network/network.h" 10#include "core/internal_network/network.h"
10#include "core/internal_network/network_interface.h" 11#include "core/internal_network/network_interface.h"
11#include "core/internal_network/socket_proxy.h" 12#include "core/internal_network/socket_proxy.h"
@@ -32,8 +33,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
32 return; 33 return;
33 } 34 }
34 35
36 auto decompressed = packet;
37 decompressed.data = Common::Compression::DecompressDataZSTD(packet.data);
38
35 std::lock_guard guard(packets_mutex); 39 std::lock_guard guard(packets_mutex);
36 received_packets.push(packet); 40 received_packets.push(decompressed);
37} 41}
38 42
39template <typename T> 43template <typename T>
@@ -185,6 +189,8 @@ std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flag
185void ProxySocket::SendPacket(ProxyPacket& packet) { 189void ProxySocket::SendPacket(ProxyPacket& packet) {
186 if (auto room_member = room_network.GetRoomMember().lock()) { 190 if (auto room_member = room_network.GetRoomMember().lock()) {
187 if (room_member->IsConnected()) { 191 if (room_member->IsConnected()) {
192 packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
193 packet.data.size());
188 room_member->SendProxyPacket(packet); 194 room_member->SendProxyPacket(packet);
189 } 195 }
190 } 196 }
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index a70429b19..2e328c645 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -32,6 +32,10 @@ public:
32 std::unique_ptr<SocketBase> socket; 32 std::unique_ptr<SocketBase> socket;
33 SockAddrIn sockaddr_in; 33 SockAddrIn sockaddr_in;
34 }; 34 };
35
36 SocketBase() = default;
37 explicit SocketBase(SOCKET fd_) : fd{fd_} {}
38
35 virtual ~SocketBase() = default; 39 virtual ~SocketBase() = default;
36 40
37 virtual SocketBase& operator=(const SocketBase&) = delete; 41 virtual SocketBase& operator=(const SocketBase&) = delete;
@@ -89,12 +93,19 @@ public:
89 93
90 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; 94 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
91 95
96 [[nodiscard]] SOCKET GetFD() const {
97 return fd;
98 }
99
100protected:
92 SOCKET fd = INVALID_SOCKET; 101 SOCKET fd = INVALID_SOCKET;
93}; 102};
94 103
95class Socket : public SocketBase { 104class Socket : public SocketBase {
96public: 105public:
97 Socket() = default; 106 Socket() = default;
107 explicit Socket(SOCKET fd_) : SocketBase{fd_} {}
108
98 ~Socket() override; 109 ~Socket() override;
99 110
100 Socket(const Socket&) = delete; 111 Socket(const Socket&) = delete;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 104d16efa..f24474ed8 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -244,6 +244,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
244 244
245std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, 245std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
246 u64 program_id, std::size_t program_index) { 246 u64 program_id, std::size_t program_index) {
247 if (!file) {
248 return nullptr;
249 }
250
247 FileType type = IdentifyFile(file); 251 FileType type = IdentifyFile(file);
248 const FileType filename_type = GuessFromFilename(file->GetName()); 252 const FileType filename_type = GuessFromFilename(file->GetName());
249 253
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 34ad7cadd..3ca80c8ff 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -65,7 +65,7 @@ struct Memory::Impl {
65 return {}; 65 return {};
66 } 66 }
67 67
68 return system.DeviceMemory().GetPointer(paddr) + vaddr; 68 return system.DeviceMemory().GetPointer<u8>(paddr) + vaddr;
69 } 69 }
70 70
71 [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { 71 [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
@@ -75,7 +75,7 @@ struct Memory::Impl {
75 return {}; 75 return {};
76 } 76 }
77 77
78 return system.DeviceMemory().GetPointer(paddr) + vaddr; 78 return system.DeviceMemory().GetPointer<u8>(paddr) + vaddr;
79 } 79 }
80 80
81 u8 Read8(const VAddr addr) { 81 u8 Read8(const VAddr addr) {
@@ -233,18 +233,17 @@ struct Memory::Impl {
233 current_vaddr, src_addr, size); 233 current_vaddr, src_addr, size);
234 std::memset(dest_buffer, 0, copy_amount); 234 std::memset(dest_buffer, 0, copy_amount);
235 }, 235 },
236 [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { 236 [&](const std::size_t copy_amount, const u8* const src_ptr) {
237 std::memcpy(dest_buffer, src_ptr, copy_amount); 237 std::memcpy(dest_buffer, src_ptr, copy_amount);
238 }, 238 },
239 [&system = system, &dest_buffer](const VAddr current_vaddr, 239 [&](const VAddr current_vaddr, const std::size_t copy_amount,
240 const std::size_t copy_amount, 240 const u8* const host_ptr) {
241 const u8* const host_ptr) {
242 if constexpr (!UNSAFE) { 241 if constexpr (!UNSAFE) {
243 system.GPU().FlushRegion(current_vaddr, copy_amount); 242 system.GPU().FlushRegion(current_vaddr, copy_amount);
244 } 243 }
245 std::memcpy(dest_buffer, host_ptr, copy_amount); 244 std::memcpy(dest_buffer, host_ptr, copy_amount);
246 }, 245 },
247 [&dest_buffer](const std::size_t copy_amount) { 246 [&](const std::size_t copy_amount) {
248 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 247 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
249 }); 248 });
250 } 249 }
@@ -267,17 +266,16 @@ struct Memory::Impl {
267 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 266 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
268 current_vaddr, dest_addr, size); 267 current_vaddr, dest_addr, size);
269 }, 268 },
270 [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { 269 [&](const std::size_t copy_amount, u8* const dest_ptr) {
271 std::memcpy(dest_ptr, src_buffer, copy_amount); 270 std::memcpy(dest_ptr, src_buffer, copy_amount);
272 }, 271 },
273 [&system = system, &src_buffer](const VAddr current_vaddr, 272 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
274 const std::size_t copy_amount, u8* const host_ptr) {
275 if constexpr (!UNSAFE) { 273 if constexpr (!UNSAFE) {
276 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 274 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
277 } 275 }
278 std::memcpy(host_ptr, src_buffer, copy_amount); 276 std::memcpy(host_ptr, src_buffer, copy_amount);
279 }, 277 },
280 [&src_buffer](const std::size_t copy_amount) { 278 [&](const std::size_t copy_amount) {
281 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; 279 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
282 }); 280 });
283 } 281 }
@@ -301,8 +299,7 @@ struct Memory::Impl {
301 [](const std::size_t copy_amount, u8* const dest_ptr) { 299 [](const std::size_t copy_amount, u8* const dest_ptr) {
302 std::memset(dest_ptr, 0, copy_amount); 300 std::memset(dest_ptr, 0, copy_amount);
303 }, 301 },
304 [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, 302 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
305 u8* const host_ptr) {
306 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 303 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
307 std::memset(host_ptr, 0, copy_amount); 304 std::memset(host_ptr, 0, copy_amount);
308 }, 305 },
@@ -313,22 +310,20 @@ struct Memory::Impl {
313 const std::size_t size) { 310 const std::size_t size) {
314 WalkBlock( 311 WalkBlock(
315 process, dest_addr, size, 312 process, dest_addr, size,
316 [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, 313 [&](const std::size_t copy_amount, const VAddr current_vaddr) {
317 const VAddr current_vaddr) {
318 LOG_ERROR(HW_Memory, 314 LOG_ERROR(HW_Memory,
319 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 315 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
320 current_vaddr, src_addr, size); 316 current_vaddr, src_addr, size);
321 ZeroBlock(process, dest_addr, copy_amount); 317 ZeroBlock(process, dest_addr, copy_amount);
322 }, 318 },
323 [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { 319 [&](const std::size_t copy_amount, const u8* const src_ptr) {
324 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount); 320 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
325 }, 321 },
326 [this, &system = system, &process, &dest_addr]( 322 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
327 const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
328 system.GPU().FlushRegion(current_vaddr, copy_amount); 323 system.GPU().FlushRegion(current_vaddr, copy_amount);
329 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); 324 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
330 }, 325 },
331 [&dest_addr, &src_addr](const std::size_t copy_amount) { 326 [&](const std::size_t copy_amount) {
332 dest_addr += static_cast<VAddr>(copy_amount); 327 dest_addr += static_cast<VAddr>(copy_amount);
333 src_addr += static_cast<VAddr>(copy_amount); 328 src_addr += static_cast<VAddr>(copy_amount);
334 }); 329 });
@@ -499,7 +494,7 @@ struct Memory::Impl {
499 } else { 494 } else {
500 while (base != end) { 495 while (base != end) {
501 page_table.pointers[base].Store( 496 page_table.pointers[base].Store(
502 system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); 497 system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS), type);
503 page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); 498 page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS);
504 499
505 ASSERT_MSG(page_table.pointers[base].Pointer(), 500 ASSERT_MSG(page_table.pointers[base].Pointer(),
@@ -551,6 +546,11 @@ struct Memory::Impl {
551 []() {}); 546 []() {});
552 } 547 }
553 548
549 [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const {
550 return GetPointerImpl(
551 vaddr, []() {}, []() {});
552 }
553
554 /** 554 /**
555 * Reads a particular data type out of memory at the given virtual address. 555 * Reads a particular data type out of memory at the given virtual address.
556 * 556 *
@@ -570,7 +570,7 @@ struct Memory::Impl {
570 [vaddr]() { 570 [vaddr]() {
571 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); 571 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
572 }, 572 },
573 [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); 573 [&]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
574 if (ptr) { 574 if (ptr) {
575 std::memcpy(&result, ptr, sizeof(T)); 575 std::memcpy(&result, ptr, sizeof(T));
576 } 576 }
@@ -594,7 +594,7 @@ struct Memory::Impl {
594 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, 594 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
595 vaddr, static_cast<u64>(data)); 595 vaddr, static_cast<u64>(data));
596 }, 596 },
597 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); 597 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
598 if (ptr) { 598 if (ptr) {
599 std::memcpy(ptr, &data, sizeof(T)); 599 std::memcpy(ptr, &data, sizeof(T));
600 } 600 }
@@ -608,7 +608,7 @@ struct Memory::Impl {
608 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", 608 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
609 sizeof(T) * 8, vaddr, static_cast<u64>(data)); 609 sizeof(T) * 8, vaddr, static_cast<u64>(data));
610 }, 610 },
611 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); 611 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
612 if (ptr) { 612 if (ptr) {
613 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); 613 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
614 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 614 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
@@ -623,7 +623,7 @@ struct Memory::Impl {
623 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", 623 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
624 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0])); 624 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
625 }, 625 },
626 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); 626 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
627 if (ptr) { 627 if (ptr) {
628 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); 628 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
629 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 629 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
@@ -686,6 +686,10 @@ u8* Memory::GetPointer(VAddr vaddr) {
686 return impl->GetPointer(vaddr); 686 return impl->GetPointer(vaddr);
687} 687}
688 688
689u8* Memory::GetPointerSilent(VAddr vaddr) {
690 return impl->GetPointerSilent(vaddr);
691}
692
689const u8* Memory::GetPointer(VAddr vaddr) const { 693const u8* Memory::GetPointer(VAddr vaddr) const {
690 return impl->GetPointer(vaddr); 694 return impl->GetPointer(vaddr);
691} 695}
diff --git a/src/core/memory.h b/src/core/memory.h
index a11ff8766..81eac448b 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -114,6 +114,7 @@ public:
114 * If the address is not valid, nullptr will be returned. 114 * If the address is not valid, nullptr will be returned.
115 */ 115 */
116 u8* GetPointer(VAddr vaddr); 116 u8* GetPointer(VAddr vaddr);
117 u8* GetPointerSilent(VAddr vaddr);
117 118
118 template <typename T> 119 template <typename T>
119 T* GetPointer(VAddr vaddr) { 120 T* GetPointer(VAddr vaddr) {
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
index 1efdbc1f7..2d9731f19 100644
--- a/src/dedicated_room/CMakeLists.txt
+++ b/src/dedicated_room/CMakeLists.txt
@@ -23,5 +23,5 @@ endif()
23target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 23target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
24 24
25if(UNIX AND NOT APPLE) 25if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 26 install(TARGETS yuzu-room)
27endif() 27endif()
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
index 7b6deba41..359891883 100644
--- a/src/dedicated_room/yuzu_room.cpp
+++ b/src/dedicated_room/yuzu_room.cpp
@@ -76,7 +76,18 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1";
76static constexpr char token_delimiter{':'}; 76static constexpr char token_delimiter{':'};
77 77
78static void PadToken(std::string& token) { 78static void PadToken(std::string& token) {
79 while (token.size() % 4 != 0) { 79 std::size_t outlen = 0;
80
81 std::array<unsigned char, 512> output{};
82 std::array<unsigned char, 2048> roundtrip{};
83 for (size_t i = 0; i < 3; i++) {
84 mbedtls_base64_decode(output.data(), output.size(), &outlen,
85 reinterpret_cast<const unsigned char*>(token.c_str()),
86 token.length());
87 mbedtls_base64_encode(roundtrip.data(), roundtrip.size(), &outlen, output.data(), outlen);
88 if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) {
89 break;
90 }
80 token.push_back('='); 91 token.push_back('=');
81 } 92 }
82} 93}
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 4b91b88ce..cc6f0ffc0 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(input_common STATIC
18 drivers/touch_screen.h 18 drivers/touch_screen.h
19 drivers/udp_client.cpp 19 drivers/udp_client.cpp
20 drivers/udp_client.h 20 drivers/udp_client.h
21 drivers/virtual_amiibo.cpp
22 drivers/virtual_amiibo.h
21 helpers/stick_from_buttons.cpp 23 helpers/stick_from_buttons.cpp
22 helpers/stick_from_buttons.h 24 helpers/stick_from_buttons.h
23 helpers/touch_from_buttons.cpp 25 helpers/touch_from_buttons.cpp
@@ -37,21 +39,14 @@ add_library(input_common STATIC
37if (MSVC) 39if (MSVC)
38 target_compile_options(input_common PRIVATE 40 target_compile_options(input_common PRIVATE
39 /W4 41 /W4
40 /WX
41 42
42 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 43 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
43 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
44 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
45 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 44 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
45 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
46 ) 46 )
47else() 47else()
48 target_compile_options(input_common PRIVATE 48 target_compile_options(input_common PRIVATE
49 -Werror
50 -Werror=conversion 49 -Werror=conversion
51 -Werror=ignored-qualifiers
52 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
53 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
54 -Werror=unused-variable
55 ) 50 )
56endif() 51endif()
57 52
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 27a0ffb0d..826fa2109 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -90,7 +90,7 @@ GCAdapter::~GCAdapter() {
90 90
91void GCAdapter::AdapterInputThread(std::stop_token stop_token) { 91void GCAdapter::AdapterInputThread(std::stop_token stop_token) {
92 LOG_DEBUG(Input, "Input thread started"); 92 LOG_DEBUG(Input, "Input thread started");
93 Common::SetCurrentThreadName("yuzu:input:GCAdapter"); 93 Common::SetCurrentThreadName("GCAdapter");
94 s32 payload_size{}; 94 s32 payload_size{};
95 AdapterPayload adapter_payload{}; 95 AdapterPayload adapter_payload{};
96 96
@@ -214,7 +214,7 @@ void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_
214} 214}
215 215
216void GCAdapter::AdapterScanThread(std::stop_token stop_token) { 216void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter"); 217 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 218 usb_adapter_handle = nullptr;
219 pads = {}; 219 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 220 while (!stop_token.stop_requested() && !Setup()) {
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetRumble( 327Common::Input::VibrationError GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble(
338 return Common::Input::VibrationError::None; 338 return Common::Input::VibrationError::None;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
342 return rumble_enabled;
343}
344
341void GCAdapter::UpdateVibrations() { 345void GCAdapter::UpdateVibrations() {
342 // Use 8 states to keep the switching between on/off fast enough for 346 // Use 8 states to keep the switching between on/off fast enough for
343 // a human to feel different vibration strenght 347 // a human to feel different vibration strenght
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 8682da847..7f81767f7 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,9 +25,11 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetRumble( 28 Common::Input::VibrationError SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
32
31 /// Used for automapping features 33 /// Used for automapping features
32 std::vector<Common::ParamPackage> GetInputDevices() const override; 34 std::vector<Common::ParamPackage> GetInputDevices() const override;
33 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; 35 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 4909fa8d7..98c3157a8 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -37,7 +37,7 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_))
37} 37}
38 38
39void Mouse::UpdateThread(std::stop_token stop_token) { 39void Mouse::UpdateThread(std::stop_token stop_token) {
40 Common::SetCurrentThreadName("yuzu:input:Mouse"); 40 Common::SetCurrentThreadName("Mouse");
41 constexpr int update_time = 10; 41 constexpr int update_time = 10;
42 while (!stop_token.stop_requested()) { 42 while (!stop_token.stop_requested()) {
43 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 43 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 5cc1ccbd9..45ce588f0 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,8 +40,8 @@ public:
40 void EnableMotion() { 40 void EnableMotion() {
41 if (sdl_controller) { 41 if (sdl_controller) {
42 SDL_GameController* controller = sdl_controller.get(); 42 SDL_GameController* controller = sdl_controller.get();
43 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); 43 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
44 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); 44 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
45 if (has_accel) { 45 if (has_accel) {
46 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); 46 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
47 } 47 }
@@ -114,6 +114,20 @@ public:
114 } 114 }
115 return false; 115 return false;
116 } 116 }
117
118 void EnableVibration(bool is_enabled) {
119 has_vibration = is_enabled;
120 is_vibration_tested = true;
121 }
122
123 bool HasVibration() const {
124 return has_vibration;
125 }
126
127 bool IsVibrationTested() const {
128 return is_vibration_tested;
129 }
130
117 /** 131 /**
118 * The Pad identifier of the joystick 132 * The Pad identifier of the joystick
119 */ 133 */
@@ -236,6 +250,8 @@ private:
236 u64 last_motion_update{}; 250 u64 last_motion_update{};
237 bool has_gyro{false}; 251 bool has_gyro{false};
238 bool has_accel{false}; 252 bool has_accel{false};
253 bool has_vibration{false};
254 bool is_vibration_tested{false};
239 BasicMotion motion; 255 BasicMotion motion;
240}; 256};
241 257
@@ -436,7 +452,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
436 initialized = true; 452 initialized = true;
437 if (start_thread) { 453 if (start_thread) {
438 poll_thread = std::thread([this] { 454 poll_thread = std::thread([this] {
439 Common::SetCurrentThreadName("yuzu:input:SDL"); 455 Common::SetCurrentThreadName("SDL_MainLoop");
440 using namespace std::chrono_literals; 456 using namespace std::chrono_literals;
441 while (initialized) { 457 while (initialized) {
442 SDL_PumpEvents(); 458 SDL_PumpEvents();
@@ -444,7 +460,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
444 } 460 }
445 }); 461 });
446 vibration_thread = std::thread([this] { 462 vibration_thread = std::thread([this] {
447 Common::SetCurrentThreadName("yuzu:input:SDL_Vibration"); 463 Common::SetCurrentThreadName("SDL_Vibration");
448 using namespace std::chrono_literals; 464 using namespace std::chrono_literals;
449 while (initialized) { 465 while (initialized) {
450 SendVibrations(); 466 SendVibrations();
@@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
517 return devices; 533 return devices;
518} 534}
519 535
520Common::Input::VibrationError SDLDriver::SetRumble( 536Common::Input::VibrationError SDLDriver::SetVibration(
521 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 537 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
522 const auto joystick = 538 const auto joystick =
523 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 539 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble(
546 .type = Common::Input::VibrationAmplificationType::Exponential, 562 .type = Common::Input::VibrationAmplificationType::Exponential,
547 }; 563 };
548 564
549 if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
550 if (!joystick->RumblePlay(new_vibration)) {
551 return Common::Input::VibrationError::Unknown;
552 }
553 return Common::Input::VibrationError::None;
554 }
555
556 vibration_queue.Push(VibrationRequest{ 565 vibration_queue.Push(VibrationRequest{
557 .identifier = identifier, 566 .identifier = identifier,
558 .vibration = new_vibration, 567 .vibration = new_vibration,
@@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble(
561 return Common::Input::VibrationError::None; 570 return Common::Input::VibrationError::None;
562} 571}
563 572
573bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
574 const auto joystick =
575 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
576
577 constexpr Common::Input::VibrationStatus test_vibration{
578 .low_amplitude = 1,
579 .low_frequency = 160.0f,
580 .high_amplitude = 1,
581 .high_frequency = 320.0f,
582 .type = Common::Input::VibrationAmplificationType::Exponential,
583 };
584
585 constexpr Common::Input::VibrationStatus zero_vibration{
586 .low_amplitude = 0,
587 .low_frequency = 160.0f,
588 .high_amplitude = 0,
589 .high_frequency = 320.0f,
590 .type = Common::Input::VibrationAmplificationType::Exponential,
591 };
592
593 if (joystick->IsVibrationTested()) {
594 return joystick->HasVibration();
595 }
596
597 // First vibration might fail
598 joystick->RumblePlay(test_vibration);
599
600 // Wait for about 15ms to ensure the controller is ready for the stop command
601 std::this_thread::sleep_for(std::chrono::milliseconds(15));
602
603 if (!joystick->RumblePlay(zero_vibration)) {
604 joystick->EnableVibration(false);
605 return false;
606 }
607
608 joystick->EnableVibration(true);
609 return true;
610}
611
564void SDLDriver::SendVibrations() { 612void SDLDriver::SendVibrations() {
565 while (!vibration_queue.Empty()) { 613 while (!vibration_queue.Empty()) {
566 VibrationRequest request; 614 VibrationRequest request;
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index fc3a44572..d1b4471cf 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -61,9 +61,11 @@ public:
61 61
62 bool IsStickInverted(const Common::ParamPackage& params) override; 62 bool IsStickInverted(const Common::ParamPackage& params) override;
63 63
64 Common::Input::VibrationError SetRumble( 64 Common::Input::VibrationError SetVibration(
65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
66 66
67 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
68
67private: 69private:
68 struct VibrationRequest { 70 struct VibrationRequest {
69 PadIdentifier identifier; 71 PadIdentifier identifier;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
new file mode 100644
index 000000000..0cd5129da
--- /dev/null
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <cstring>
5#include <fmt/format.h>
6
7#include "common/fs/file.h"
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "common/settings.h"
12#include "input_common/drivers/virtual_amiibo.h"
13
14namespace InputCommon {
15constexpr PadIdentifier identifier = {
16 .guid = Common::UUID{},
17 .port = 0,
18 .pad = 0,
19};
20
21VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
22
23VirtualAmiibo::~VirtualAmiibo() = default;
24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_;
29
30 if (polling_mode == Common::Input::PollingMode::NFC) {
31 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo;
33 }
34 } else {
35 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo();
37 }
38 }
39
40 return Common::Input::PollingError::None;
41}
42
43Common::Input::NfcState VirtualAmiibo::SupportsNfc(
44 [[maybe_unused]] const PadIdentifier& identifier_) const {
45 return Common::Input::NfcState::Success;
46}
47
48Common::Input::NfcState VirtualAmiibo::WriteNfcData(
49 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
50 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
51 Common::FS::FileType::BinaryFile};
52
53 if (!amiibo_file.IsOpen()) {
54 LOG_ERROR(Core, "Amiibo is already on use");
55 return Common::Input::NfcState::WriteFailed;
56 }
57
58 if (!amiibo_file.Write(data)) {
59 LOG_ERROR(Service_NFP, "Error writting to file");
60 return Common::Input::NfcState::WriteFailed;
61 }
62
63 return Common::Input::NfcState::Success;
64}
65
66VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
67 return state;
68}
69
70VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
71 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
72 Common::FS::FileType::BinaryFile};
73
74 if (state != State::WaitingForAmiibo) {
75 return Info::WrongDeviceState;
76 }
77
78 if (!amiibo_file.IsOpen()) {
79 return Info::UnableToLoad;
80 }
81
82 amiibo_data.resize(amiibo_size);
83
84 if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
85 return Info::NotAnAmiibo;
86 }
87
88 file_path = filename;
89 state = State::AmiiboIsOpen;
90 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
91 return Info::Success;
92}
93
94VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
95 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
96 : State::Initialized;
97 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
98 return Info::Success;
99}
100
101} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
new file mode 100644
index 000000000..9eac07544
--- /dev/null
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8#include <vector>
9
10#include "common/common_types.h"
11#include "input_common/input_engine.h"
12
13namespace Common::FS {
14class IOFile;
15}
16
17namespace InputCommon {
18
19class VirtualAmiibo final : public InputEngine {
20public:
21 enum class State {
22 Initialized,
23 WaitingForAmiibo,
24 AmiiboIsOpen,
25 };
26
27 enum class Info {
28 Success,
29 UnableToLoad,
30 NotAnAmiibo,
31 WrongDeviceState,
32 Unknown,
33 };
34
35 explicit VirtualAmiibo(std::string input_engine_);
36 ~VirtualAmiibo() override;
37
38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43
44 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
45 const std::vector<u8>& data) override;
46
47 State GetCurrentState() const;
48
49 Info LoadAmiibo(const std::string& amiibo_file);
50 Info CloseAmiibo();
51
52private:
53 static constexpr std::size_t amiibo_size = 0x21C;
54 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
55
56 std::string file_path{};
57 State state{State::Initialized};
58 std::vector<u8> amiibo_data;
59 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
60};
61} // namespace InputCommon
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 6ede0e4b0..61cfd0911 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -102,6 +102,17 @@ void InputEngine::SetCamera(const PadIdentifier& identifier,
102 TriggerOnCameraChange(identifier, value); 102 TriggerOnCameraChange(identifier, value);
103} 103}
104 104
105void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) {
106 {
107 std::scoped_lock lock{mutex};
108 ControllerData& controller = controller_list.at(identifier);
109 if (!configuring) {
110 controller.nfc = value;
111 }
112 }
113 TriggerOnNfcChange(identifier, value);
114}
115
105bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { 116bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
106 std::scoped_lock lock{mutex}; 117 std::scoped_lock lock{mutex};
107 const auto controller_iter = controller_list.find(identifier); 118 const auto controller_iter = controller_list.find(identifier);
@@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi
189 return controller.camera; 200 return controller.camera;
190} 201}
191 202
203Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const {
204 std::scoped_lock lock{mutex};
205 const auto controller_iter = controller_list.find(identifier);
206 if (controller_iter == controller_list.cend()) {
207 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
208 identifier.pad, identifier.port);
209 return {};
210 }
211 const ControllerData& controller = controller_iter->second;
212 return controller.nfc;
213}
214
192void InputEngine::ResetButtonState() { 215void InputEngine::ResetButtonState() {
193 for (const auto& controller : controller_list) { 216 for (const auto& controller : controller_list) {
194 for (const auto& button : controller.second.buttons) { 217 for (const auto& button : controller.second.buttons) {
@@ -355,6 +378,20 @@ void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
355 } 378 }
356} 379}
357 380
381void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier,
382 [[maybe_unused]] const Common::Input::NfcStatus& value) {
383 std::scoped_lock lock{mutex_callback};
384 for (const auto& poller_pair : callback_list) {
385 const InputIdentifier& poller = poller_pair.second;
386 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) {
387 continue;
388 }
389 if (poller.callback.on_change) {
390 poller.callback.on_change();
391 }
392 }
393}
394
358bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, 395bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
359 const PadIdentifier& identifier, EngineInputType type, 396 const PadIdentifier& identifier, EngineInputType type,
360 int index) const { 397 int index) const {
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index f6b3c4610..d4c264a8e 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -42,6 +42,7 @@ enum class EngineInputType {
42 Camera, 42 Camera,
43 HatButton, 43 HatButton,
44 Motion, 44 Motion,
45 Nfc,
45}; 46};
46 47
47namespace std { 48namespace std {
@@ -107,12 +108,17 @@ public:
107 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {}
108 109
109 // Sets rumble to a controller 110 // Sets rumble to a controller
110 virtual Common::Input::VibrationError SetRumble( 111 virtual Common::Input::VibrationError SetVibration(
111 [[maybe_unused]] const PadIdentifier& identifier, 112 [[maybe_unused]] const PadIdentifier& identifier,
112 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
113 return Common::Input::VibrationError::NotSupported; 114 return Common::Input::VibrationError::NotSupported;
114 } 115 }
115 116
117 // Returns true if device supports vibrations
118 virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
119 return false;
120 }
121
116 // Sets polling mode to a controller 122 // Sets polling mode to a controller
117 virtual Common::Input::PollingError SetPollingMode( 123 virtual Common::Input::PollingError SetPollingMode(
118 [[maybe_unused]] const PadIdentifier& identifier, 124 [[maybe_unused]] const PadIdentifier& identifier,
@@ -127,6 +133,18 @@ public:
127 return Common::Input::CameraError::NotSupported; 133 return Common::Input::CameraError::NotSupported;
128 } 134 }
129 135
136 // Request nfc data from a controller
137 virtual Common::Input::NfcState SupportsNfc(
138 [[maybe_unused]] const PadIdentifier& identifier) const {
139 return Common::Input::NfcState::NotSupported;
140 }
141
142 // Writes data to an nfc tag
143 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
144 [[maybe_unused]] const std::vector<u8>& data) {
145 return Common::Input::NfcState::NotSupported;
146 }
147
130 // Returns the engine name 148 // Returns the engine name
131 [[nodiscard]] const std::string& GetEngineName() const; 149 [[nodiscard]] const std::string& GetEngineName() const;
132 150
@@ -183,6 +201,7 @@ public:
183 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 201 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
184 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 202 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
185 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; 203 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
204 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
186 205
187 int SetCallback(InputIdentifier input_identifier); 206 int SetCallback(InputIdentifier input_identifier);
188 void SetMappingCallback(MappingCallback callback); 207 void SetMappingCallback(MappingCallback callback);
@@ -195,6 +214,7 @@ protected:
195 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 214 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
196 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 215 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
197 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); 216 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
217 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
198 218
199 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { 219 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
200 return "Unknown"; 220 return "Unknown";
@@ -208,6 +228,7 @@ private:
208 std::unordered_map<int, BasicMotion> motions; 228 std::unordered_map<int, BasicMotion> motions;
209 Common::Input::BatteryLevel battery{}; 229 Common::Input::BatteryLevel battery{};
210 Common::Input::CameraStatus camera{}; 230 Common::Input::CameraStatus camera{};
231 Common::Input::NfcStatus nfc{};
211 }; 232 };
212 233
213 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); 234 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
@@ -218,6 +239,7 @@ private:
218 const BasicMotion& value); 239 const BasicMotion& value);
219 void TriggerOnCameraChange(const PadIdentifier& identifier, 240 void TriggerOnCameraChange(const PadIdentifier& identifier,
220 const Common::Input::CameraStatus& value); 241 const Common::Input::CameraStatus& value);
242 void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
221 243
222 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, 244 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
223 const PadIdentifier& identifier, EngineInputType type, 245 const PadIdentifier& identifier, EngineInputType type,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index ffb9b945e..4ac182147 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -691,9 +691,56 @@ public:
691 } 691 }
692 692
693 void OnChange() { 693 void OnChange() {
694 const auto camera_status = GetStatus();
695
694 const Common::Input::CallbackStatus status{ 696 const Common::Input::CallbackStatus status{
695 .type = Common::Input::InputType::IrSensor, 697 .type = Common::Input::InputType::IrSensor,
696 .camera_status = GetStatus(), 698 .camera_status = camera_status.format,
699 .raw_data = camera_status.data,
700 };
701
702 TriggerOnChange(status);
703 }
704
705private:
706 const PadIdentifier identifier;
707 int callback_key;
708 InputEngine* input_engine;
709};
710
711class InputFromNfc final : public Common::Input::InputDevice {
712public:
713 explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_)
714 : identifier(identifier_), input_engine(input_engine_) {
715 UpdateCallback engine_callback{[this]() { OnChange(); }};
716 const InputIdentifier input_identifier{
717 .identifier = identifier,
718 .type = EngineInputType::Nfc,
719 .index = 0,
720 .callback = engine_callback,
721 };
722 callback_key = input_engine->SetCallback(input_identifier);
723 }
724
725 ~InputFromNfc() override {
726 input_engine->DeleteCallback(callback_key);
727 }
728
729 Common::Input::NfcStatus GetStatus() const {
730 return input_engine->GetNfc(identifier);
731 }
732
733 void ForceUpdate() override {
734 OnChange();
735 }
736
737 void OnChange() {
738 const auto nfc_status = GetStatus();
739
740 const Common::Input::CallbackStatus status{
741 .type = Common::Input::InputType::Nfc,
742 .nfc_status = nfc_status.state,
743 .raw_data = nfc_status.data,
697 }; 744 };
698 745
699 TriggerOnChange(status); 746 TriggerOnChange(status);
@@ -716,7 +763,11 @@ public:
716 763
717 Common::Input::VibrationError SetVibration( 764 Common::Input::VibrationError SetVibration(
718 const Common::Input::VibrationStatus& vibration_status) override { 765 const Common::Input::VibrationStatus& vibration_status) override {
719 return input_engine->SetRumble(identifier, vibration_status); 766 return input_engine->SetVibration(identifier, vibration_status);
767 }
768
769 bool IsVibrationEnabled() override {
770 return input_engine->IsVibrationEnabled(identifier);
720 } 771 }
721 772
722 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 773 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
@@ -727,6 +778,14 @@ public:
727 return input_engine->SetCameraFormat(identifier, camera_format); 778 return input_engine->SetCameraFormat(identifier, camera_format);
728 } 779 }
729 780
781 Common::Input::NfcState SupportsNfc() const override {
782 return input_engine->SupportsNfc(identifier);
783 }
784
785 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
786 return input_engine->WriteNfcData(identifier, data);
787 }
788
730private: 789private:
731 const PadIdentifier identifier; 790 const PadIdentifier identifier;
732 InputEngine* input_engine; 791 InputEngine* input_engine;
@@ -742,8 +801,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
742 801
743 const auto button_id = params.Get("button", 0); 802 const auto button_id = params.Get("button", 0);
744 const auto keyboard_key = params.Get("code", 0); 803 const auto keyboard_key = params.Get("code", 0);
745 const auto toggle = params.Get("toggle", false); 804 const auto toggle = params.Get("toggle", false) != 0;
746 const auto inverted = params.Get("inverted", false); 805 const auto inverted = params.Get("inverted", false) != 0;
747 input_engine->PreSetController(identifier); 806 input_engine->PreSetController(identifier);
748 input_engine->PreSetButton(identifier, button_id); 807 input_engine->PreSetButton(identifier, button_id);
749 input_engine->PreSetButton(identifier, keyboard_key); 808 input_engine->PreSetButton(identifier, keyboard_key);
@@ -765,8 +824,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
765 824
766 const auto button_id = params.Get("hat", 0); 825 const auto button_id = params.Get("hat", 0);
767 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 826 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
768 const auto toggle = params.Get("toggle", false); 827 const auto toggle = params.Get("toggle", false) != 0;
769 const auto inverted = params.Get("inverted", false); 828 const auto inverted = params.Get("inverted", false) != 0;
770 829
771 input_engine->PreSetController(identifier); 830 input_engine->PreSetController(identifier);
772 input_engine->PreSetHatButton(identifier, button_id); 831 input_engine->PreSetHatButton(identifier, button_id);
@@ -824,7 +883,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
824 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), 883 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
825 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), 884 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
826 .inverted = params.Get("invert", "+") == "-", 885 .inverted = params.Get("invert", "+") == "-",
827 .toggle = static_cast<bool>(params.Get("toggle", false)), 886 .toggle = params.Get("toggle", false) != 0,
828 }; 887 };
829 input_engine->PreSetController(identifier); 888 input_engine->PreSetController(identifier);
830 input_engine->PreSetAxis(identifier, axis); 889 input_engine->PreSetAxis(identifier, axis);
@@ -840,8 +899,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
840 }; 899 };
841 900
842 const auto button = params.Get("button", 0); 901 const auto button = params.Get("button", 0);
843 const auto toggle = params.Get("toggle", false); 902 const auto toggle = params.Get("toggle", false) != 0;
844 const auto inverted = params.Get("inverted", false); 903 const auto inverted = params.Get("inverted", false) != 0;
845 904
846 const auto axis = params.Get("axis", 0); 905 const auto axis = params.Get("axis", 0);
847 const Common::Input::AnalogProperties properties = { 906 const Common::Input::AnalogProperties properties = {
@@ -871,8 +930,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
871 }; 930 };
872 931
873 const auto button = params.Get("button", 0); 932 const auto button = params.Get("button", 0);
874 const auto toggle = params.Get("toggle", false); 933 const auto toggle = params.Get("toggle", false) != 0;
875 const auto inverted = params.Get("inverted", false); 934 const auto inverted = params.Get("inverted", false) != 0;
876 935
877 const auto axis_x = params.Get("axis_x", 0); 936 const auto axis_x = params.Get("axis_x", 0);
878 const Common::Input::AnalogProperties properties_x = { 937 const Common::Input::AnalogProperties properties_x = {
@@ -978,6 +1037,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
978 return std::make_unique<InputFromCamera>(identifier, input_engine.get()); 1037 return std::make_unique<InputFromCamera>(identifier, input_engine.get());
979} 1038}
980 1039
1040std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateNfcDevice(
1041 const Common::ParamPackage& params) {
1042 const PadIdentifier identifier = {
1043 .guid = Common::UUID{params.Get("guid", "")},
1044 .port = static_cast<std::size_t>(params.Get("port", 0)),
1045 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
1046 };
1047
1048 input_engine->PreSetController(identifier);
1049 return std::make_unique<InputFromNfc>(identifier, input_engine.get());
1050}
1051
981InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) 1052InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
982 : input_engine(std::move(input_engine_)) {} 1053 : input_engine(std::move(input_engine_)) {}
983 1054
@@ -989,6 +1060,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
989 if (params.Has("camera")) { 1060 if (params.Has("camera")) {
990 return CreateCameraDevice(params); 1061 return CreateCameraDevice(params);
991 } 1062 }
1063 if (params.Has("nfc")) {
1064 return CreateNfcDevice(params);
1065 }
992 if (params.Has("button") && params.Has("axis")) { 1066 if (params.Has("button") && params.Has("axis")) {
993 return CreateTriggerDevice(params); 1067 return CreateTriggerDevice(params);
994 } 1068 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index 4410a8415..d7db13ce4 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -222,6 +222,16 @@ private:
222 std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice( 222 std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
223 const Common::ParamPackage& params); 223 const Common::ParamPackage& params);
224 224
225 /**
226 * Creates a nfc device from the parameters given.
227 * @param params contains parameters for creating the device:
228 * - "guid": text string for identifying controllers
229 * - "port": port of the connected device
230 * - "pad": slot of the connected controller
231 * @returns a unique input device with the parameters specified
232 */
233 std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params);
234
225 std::shared_ptr<InputEngine> input_engine; 235 std::shared_ptr<InputEngine> input_engine;
226}; 236};
227} // namespace InputCommon 237} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 75a57b9fc..b2064ef95 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -11,6 +11,7 @@
11#include "input_common/drivers/tas_input.h" 11#include "input_common/drivers/tas_input.h"
12#include "input_common/drivers/touch_screen.h" 12#include "input_common/drivers/touch_screen.h"
13#include "input_common/drivers/udp_client.h" 13#include "input_common/drivers/udp_client.h"
14#include "input_common/drivers/virtual_amiibo.h"
14#include "input_common/helpers/stick_from_buttons.h" 15#include "input_common/helpers/stick_from_buttons.h"
15#include "input_common/helpers/touch_from_buttons.h" 16#include "input_common/helpers/touch_from_buttons.h"
16#include "input_common/input_engine.h" 17#include "input_common/input_engine.h"
@@ -87,6 +88,15 @@ struct InputSubsystem::Impl {
87 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(), 88 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
88 camera_output_factory); 89 camera_output_factory);
89 90
91 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
92 virtual_amiibo->SetMappingCallback(mapping_callback);
93 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
94 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
95 Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(),
96 virtual_amiibo_input_factory);
97 Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(),
98 virtual_amiibo_output_factory);
99
90#ifdef HAVE_SDL2 100#ifdef HAVE_SDL2
91 sdl = std::make_shared<SDLDriver>("sdl"); 101 sdl = std::make_shared<SDLDriver>("sdl");
92 sdl->SetMappingCallback(mapping_callback); 102 sdl->SetMappingCallback(mapping_callback);
@@ -327,6 +337,7 @@ struct InputSubsystem::Impl {
327 std::shared_ptr<TasInput::Tas> tas_input; 337 std::shared_ptr<TasInput::Tas> tas_input;
328 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 338 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
329 std::shared_ptr<Camera> camera; 339 std::shared_ptr<Camera> camera;
340 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
330 341
331 std::shared_ptr<InputFactory> keyboard_factory; 342 std::shared_ptr<InputFactory> keyboard_factory;
332 std::shared_ptr<InputFactory> mouse_factory; 343 std::shared_ptr<InputFactory> mouse_factory;
@@ -335,6 +346,7 @@ struct InputSubsystem::Impl {
335 std::shared_ptr<InputFactory> udp_client_input_factory; 346 std::shared_ptr<InputFactory> udp_client_input_factory;
336 std::shared_ptr<InputFactory> tas_input_factory; 347 std::shared_ptr<InputFactory> tas_input_factory;
337 std::shared_ptr<InputFactory> camera_input_factory; 348 std::shared_ptr<InputFactory> camera_input_factory;
349 std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
338 350
339 std::shared_ptr<OutputFactory> keyboard_output_factory; 351 std::shared_ptr<OutputFactory> keyboard_output_factory;
340 std::shared_ptr<OutputFactory> mouse_output_factory; 352 std::shared_ptr<OutputFactory> mouse_output_factory;
@@ -342,6 +354,7 @@ struct InputSubsystem::Impl {
342 std::shared_ptr<OutputFactory> udp_client_output_factory; 354 std::shared_ptr<OutputFactory> udp_client_output_factory;
343 std::shared_ptr<OutputFactory> tas_output_factory; 355 std::shared_ptr<OutputFactory> tas_output_factory;
344 std::shared_ptr<OutputFactory> camera_output_factory; 356 std::shared_ptr<OutputFactory> camera_output_factory;
357 std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
345 358
346#ifdef HAVE_SDL2 359#ifdef HAVE_SDL2
347 std::shared_ptr<SDLDriver> sdl; 360 std::shared_ptr<SDLDriver> sdl;
@@ -402,6 +415,14 @@ const Camera* InputSubsystem::GetCamera() const {
402 return impl->camera.get(); 415 return impl->camera.get();
403} 416}
404 417
418VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
419 return impl->virtual_amiibo.get();
420}
421
422const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
423 return impl->virtual_amiibo.get();
424}
425
405std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 426std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
406 return impl->GetInputDevices(); 427 return impl->GetInputDevices();
407} 428}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 9a969e747..ced252383 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -33,6 +33,7 @@ class Camera;
33class Keyboard; 33class Keyboard;
34class Mouse; 34class Mouse;
35class TouchScreen; 35class TouchScreen;
36class VirtualAmiibo;
36struct MappingData; 37struct MappingData;
37} // namespace InputCommon 38} // namespace InputCommon
38 39
@@ -101,6 +102,12 @@ public:
101 /// Retrieves the underlying camera input device. 102 /// Retrieves the underlying camera input device.
102 [[nodiscard]] const Camera* GetCamera() const; 103 [[nodiscard]] const Camera* GetCamera() const;
103 104
105 /// Retrieves the underlying virtual amiibo input device.
106 [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();
107
108 /// Retrieves the underlying virtual amiibo input device.
109 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
110
104 /** 111 /**
105 * Returns all available input devices that this Factory can create a new device with. 112 * Returns all available input devices that this Factory can create a new device with.
106 * Each returned ParamPackage should have a `display` field used for display, a `engine` field 113 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
diff --git a/src/network/network.cpp b/src/network/network.cpp
index 0841e4134..6652a186b 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -15,7 +15,7 @@ RoomNetwork::RoomNetwork() {
15 15
16bool RoomNetwork::Init() { 16bool RoomNetwork::Init() {
17 if (enet_initialize() != 0) { 17 if (enet_initialize() != 0) {
18 LOG_ERROR(Network, "Error initalizing ENet"); 18 LOG_ERROR(Network, "Error initializing ENet");
19 return false; 19 return false;
20 } 20 }
21 m_room = std::make_shared<Room>(); 21 m_room = std::make_shared<Room>();
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 8c63b255b..dc5dbce7f 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -212,6 +212,12 @@ public:
212 void HandleProxyPacket(const ENetEvent* event); 212 void HandleProxyPacket(const ENetEvent* event);
213 213
214 /** 214 /**
215 * Broadcasts this packet to all members except the sender.
216 * @param event The ENet event containing the data
217 */
218 void HandleLdnPacket(const ENetEvent* event);
219
220 /**
215 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 221 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
216 * @param event The ENet event that was received. 222 * @param event The ENet event that was received.
217 */ 223 */
@@ -247,6 +253,9 @@ void Room::RoomImpl::ServerLoop() {
247 case IdProxyPacket: 253 case IdProxyPacket:
248 HandleProxyPacket(&event); 254 HandleProxyPacket(&event);
249 break; 255 break;
256 case IdLdnPacket:
257 HandleLdnPacket(&event);
258 break;
250 case IdChatMessage: 259 case IdChatMessage:
251 HandleChatPacket(&event); 260 HandleChatPacket(&event);
252 break; 261 break;
@@ -861,6 +870,60 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
861 enet_host_flush(server); 870 enet_host_flush(server);
862} 871}
863 872
873void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) {
874 Packet in_packet;
875 in_packet.Append(event->packet->data, event->packet->dataLength);
876
877 in_packet.IgnoreBytes(sizeof(u8)); // Message type
878
879 in_packet.IgnoreBytes(sizeof(u8)); // LAN packet type
880 in_packet.IgnoreBytes(sizeof(IPv4Address)); // Local IP
881
882 IPv4Address remote_ip;
883 in_packet.Read(remote_ip); // Remote IP
884
885 bool broadcast;
886 in_packet.Read(broadcast); // Broadcast
887
888 Packet out_packet;
889 out_packet.Append(event->packet->data, event->packet->dataLength);
890 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
891 ENET_PACKET_FLAG_RELIABLE);
892
893 const auto& destination_address = remote_ip;
894 if (broadcast) { // Send the data to everyone except the sender
895 std::lock_guard lock(member_mutex);
896 bool sent_packet = false;
897 for (const auto& member : members) {
898 if (member.peer != event->peer) {
899 sent_packet = true;
900 enet_peer_send(member.peer, 0, enet_packet);
901 }
902 }
903
904 if (!sent_packet) {
905 enet_packet_destroy(enet_packet);
906 }
907 } else {
908 std::lock_guard lock(member_mutex);
909 auto member = std::find_if(members.begin(), members.end(),
910 [destination_address](const Member& member_entry) -> bool {
911 return member_entry.fake_ip == destination_address;
912 });
913 if (member != members.end()) {
914 enet_peer_send(member->peer, 0, enet_packet);
915 } else {
916 LOG_ERROR(Network,
917 "Attempting to send to unknown IP address: "
918 "{}.{}.{}.{}",
919 destination_address[0], destination_address[1], destination_address[2],
920 destination_address[3]);
921 enet_packet_destroy(enet_packet);
922 }
923 }
924 enet_host_flush(server);
925}
926
864void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { 927void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
865 Packet in_packet; 928 Packet in_packet;
866 in_packet.Append(event->packet->data, event->packet->dataLength); 929 in_packet.Append(event->packet->data, event->packet->dataLength);
diff --git a/src/network/room.h b/src/network/room.h
index c2a4b1a70..edbd3ecfb 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -40,6 +40,7 @@ enum RoomMessageTypes : u8 {
40 IdRoomInformation, 40 IdRoomInformation,
41 IdSetGameInfo, 41 IdSetGameInfo,
42 IdProxyPacket, 42 IdProxyPacket,
43 IdLdnPacket,
43 IdChatMessage, 44 IdChatMessage,
44 IdNameCollision, 45 IdNameCollision,
45 IdIpCollision, 46 IdIpCollision,
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index 06818af78..b94cb24ad 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -58,6 +58,7 @@ public:
58 58
59 private: 59 private:
60 CallbackSet<ProxyPacket> callback_set_proxy_packet; 60 CallbackSet<ProxyPacket> callback_set_proxy_packet;
61 CallbackSet<LDNPacket> callback_set_ldn_packet;
61 CallbackSet<ChatEntry> callback_set_chat_messages; 62 CallbackSet<ChatEntry> callback_set_chat_messages;
62 CallbackSet<StatusMessageEntry> callback_set_status_messages; 63 CallbackSet<StatusMessageEntry> callback_set_status_messages;
63 CallbackSet<RoomInformation> callback_set_room_information; 64 CallbackSet<RoomInformation> callback_set_room_information;
@@ -108,6 +109,12 @@ public:
108 void HandleProxyPackets(const ENetEvent* event); 109 void HandleProxyPackets(const ENetEvent* event);
109 110
110 /** 111 /**
112 * Extracts an LdnPacket from a received ENet packet.
113 * @param event The ENet event that was received.
114 */
115 void HandleLdnPackets(const ENetEvent* event);
116
117 /**
111 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 118 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
112 * @param event The ENet event that was received. 119 * @param event The ENet event that was received.
113 */ 120 */
@@ -166,6 +173,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
166 case IdProxyPacket: 173 case IdProxyPacket:
167 HandleProxyPackets(&event); 174 HandleProxyPackets(&event);
168 break; 175 break;
176 case IdLdnPacket:
177 HandleLdnPackets(&event);
178 break;
169 case IdChatMessage: 179 case IdChatMessage:
170 HandleChatPacket(&event); 180 HandleChatPacket(&event);
171 break; 181 break;
@@ -372,6 +382,27 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
372 Invoke<ProxyPacket>(proxy_packet); 382 Invoke<ProxyPacket>(proxy_packet);
373} 383}
374 384
385void RoomMember::RoomMemberImpl::HandleLdnPackets(const ENetEvent* event) {
386 LDNPacket ldn_packet{};
387 Packet packet;
388 packet.Append(event->packet->data, event->packet->dataLength);
389
390 // Ignore the first byte, which is the message id.
391 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
392
393 u8 packet_type;
394 packet.Read(packet_type);
395 ldn_packet.type = static_cast<LDNPacketType>(packet_type);
396
397 packet.Read(ldn_packet.local_ip);
398 packet.Read(ldn_packet.remote_ip);
399 packet.Read(ldn_packet.broadcast);
400
401 packet.Read(ldn_packet.data);
402
403 Invoke<LDNPacket>(ldn_packet);
404}
405
375void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { 406void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
376 Packet packet; 407 Packet packet;
377 packet.Append(event->packet->data, event->packet->dataLength); 408 packet.Append(event->packet->data, event->packet->dataLength);
@@ -450,6 +481,11 @@ RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl
450} 481}
451 482
452template <> 483template <>
484RoomMember::RoomMemberImpl::CallbackSet<LDNPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
485 return callback_set_ldn_packet;
486}
487
488template <>
453RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& 489RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
454RoomMember::RoomMemberImpl::Callbacks::Get() { 490RoomMember::RoomMemberImpl::Callbacks::Get() {
455 return callback_set_state; 491 return callback_set_state;
@@ -607,6 +643,21 @@ void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {
607 room_member_impl->Send(std::move(packet)); 643 room_member_impl->Send(std::move(packet));
608} 644}
609 645
646void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) {
647 Packet packet;
648 packet.Write(static_cast<u8>(IdLdnPacket));
649
650 packet.Write(static_cast<u8>(ldn_packet.type));
651
652 packet.Write(ldn_packet.local_ip);
653 packet.Write(ldn_packet.remote_ip);
654 packet.Write(ldn_packet.broadcast);
655
656 packet.Write(ldn_packet.data);
657
658 room_member_impl->Send(std::move(packet));
659}
660
610void RoomMember::SendChatMessage(const std::string& message) { 661void RoomMember::SendChatMessage(const std::string& message) {
611 Packet packet; 662 Packet packet;
612 packet.Write(static_cast<u8>(IdChatMessage)); 663 packet.Write(static_cast<u8>(IdChatMessage));
@@ -663,6 +714,11 @@ RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived(
663 return room_member_impl->Bind(callback); 714 return room_member_impl->Bind(callback);
664} 715}
665 716
717RoomMember::CallbackHandle<LDNPacket> RoomMember::BindOnLdnPacketReceived(
718 std::function<void(const LDNPacket&)> callback) {
719 return room_member_impl->Bind(std::move(callback));
720}
721
666RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( 722RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
667 std::function<void(const RoomInformation&)> callback) { 723 std::function<void(const RoomInformation&)> callback) {
668 return room_member_impl->Bind(callback); 724 return room_member_impl->Bind(callback);
@@ -699,6 +755,7 @@ void RoomMember::Leave() {
699} 755}
700 756
701template void RoomMember::Unbind(CallbackHandle<ProxyPacket>); 757template void RoomMember::Unbind(CallbackHandle<ProxyPacket>);
758template void RoomMember::Unbind(CallbackHandle<LDNPacket>);
702template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); 759template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
703template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); 760template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);
704template void RoomMember::Unbind(CallbackHandle<RoomInformation>); 761template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
diff --git a/src/network/room_member.h b/src/network/room_member.h
index f578f7f6a..0d6417294 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -17,7 +17,24 @@ namespace Network {
17using AnnounceMultiplayerRoom::GameInfo; 17using AnnounceMultiplayerRoom::GameInfo;
18using AnnounceMultiplayerRoom::RoomInformation; 18using AnnounceMultiplayerRoom::RoomInformation;
19 19
20/// Information about the received WiFi packets. 20enum class LDNPacketType : u8 {
21 Scan,
22 ScanResp,
23 Connect,
24 SyncNetwork,
25 Disconnect,
26 DestroyNetwork,
27};
28
29struct LDNPacket {
30 LDNPacketType type;
31 IPv4Address local_ip;
32 IPv4Address remote_ip;
33 bool broadcast;
34 std::vector<u8> data;
35};
36
37/// Information about the received proxy packets.
21struct ProxyPacket { 38struct ProxyPacket {
22 SockAddrIn local_endpoint; 39 SockAddrIn local_endpoint;
23 SockAddrIn remote_endpoint; 40 SockAddrIn remote_endpoint;
@@ -152,6 +169,12 @@ public:
152 void SendProxyPacket(const ProxyPacket& packet); 169 void SendProxyPacket(const ProxyPacket& packet);
153 170
154 /** 171 /**
172 * Sends an LDN packet to the room.
173 * @param packet The WiFi packet to send.
174 */
175 void SendLdnPacket(const LDNPacket& packet);
176
177 /**
155 * Sends a chat message to the room. 178 * Sends a chat message to the room.
156 * @param message The contents of the message. 179 * @param message The contents of the message.
157 */ 180 */
@@ -205,6 +228,16 @@ public:
205 std::function<void(const ProxyPacket&)> callback); 228 std::function<void(const ProxyPacket&)> callback);
206 229
207 /** 230 /**
231 * Binds a function to an event that will be triggered every time an LDNPacket is received.
232 * The function wil be called everytime the event is triggered.
233 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
234 * @param callback The function to call
235 * @return A handle used for removing the function from the registered list
236 */
237 CallbackHandle<LDNPacket> BindOnLdnPacketReceived(
238 std::function<void(const LDNPacket&)> callback);
239
240 /**
208 * Binds a function to an event that will be triggered every time the RoomInformation changes. 241 * Binds a function to an event that will be triggered every time the RoomInformation changes.
209 * The function wil be called every time the event is triggered. 242 * The function wil be called every time the event is triggered.
210 * The callback function must not bind or unbind a function. Doing so will cause a deadlock 243 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 189e5cb17..545d69c7e 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -242,24 +242,14 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
242if (MSVC) 242if (MSVC)
243 target_compile_options(shader_recompiler PRIVATE 243 target_compile_options(shader_recompiler PRIVATE
244 /W4 244 /W4
245 /WX 245
246 /we4018 # 'expression' : signed/unsigned mismatch 246 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
247 /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
248 /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
249 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 247 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
250 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
251 /we4305 # 'context' : truncation from 'type1' to 'type2'
252 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 248 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
253 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
254 ) 249 )
255else() 250else()
256 target_compile_options(shader_recompiler PRIVATE 251 target_compile_options(shader_recompiler PRIVATE
257 -Werror
258 -Werror=conversion 252 -Werror=conversion
259 -Werror=ignored-qualifiers
260 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
261 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
262 -Werror=unused-variable
263 253
264 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. 254 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
265 # And this in turns limits the size of a std::array. 255 # And this in turns limits the size of a std::array.
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 1419207e8..3b0176bf6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -175,7 +175,7 @@ bool IsReference(IR::Inst& inst) {
175} 175}
176 176
177void PrecolorInst(IR::Inst& phi) { 177void PrecolorInst(IR::Inst& phi) {
178 // Insert phi moves before references to avoid overwritting other phis 178 // Insert phi moves before references to avoid overwriting other phis
179 const size_t num_args{phi.NumArgs()}; 179 const size_t num_args{phi.NumArgs()};
180 for (size_t i = 0; i < num_args; ++i) { 180 for (size_t i = 0; i < num_args; ++i) {
181 IR::Block& phi_block{*phi.PhiBlock(i)}; 181 IR::Block& phi_block{*phi.PhiBlock(i)};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index ee0b0f53d..0a7d42dda 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -13,9 +13,6 @@ namespace Shader::Backend::GLASM {
13namespace { 13namespace {
14void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset, 14void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
15 std::string_view size) { 15 std::string_view size) {
16 if (!binding.IsImmediate()) {
17 throw NotImplementedException("Indirect constant buffer loading");
18 }
19 const Register ret{ctx.reg_alloc.Define(inst)}; 16 const Register ret{ctx.reg_alloc.Define(inst)};
20 if (offset.type == Type::U32) { 17 if (offset.type == Type::U32) {
21 // Avoid reading arrays out of bounds, matching hardware's behavior 18 // Avoid reading arrays out of bounds, matching hardware's behavior
@@ -24,7 +21,27 @@ void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU
24 return; 21 return;
25 } 22 }
26 } 23 }
27 ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); 24
25 if (binding.IsImmediate()) {
26 ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
27 return;
28 }
29
30 const ScalarU32 idx{ctx.reg_alloc.Consume(binding)};
31 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
32 ctx.Add("SEQ.S.CC RC.x,{},{};"
33 "IF NE.x;"
34 "LDC.{} {},c{}[{}];",
35 idx, i, size, ret, i, offset);
36
37 if (i != Info::MAX_INDIRECT_CBUFS - 1) {
38 ctx.Add("ELSE;");
39 }
40 }
41
42 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
43 ctx.Add("ENDIF;");
44 }
28} 45}
29 46
30bool IsInputArray(Stage stage) { 47bool IsInputArray(Stage stage) {
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 7094d8e42..1f4ffdd62 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -5,10 +5,6 @@
5#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 5#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
6#include "shader_recompiler/frontend/ir/value.h" 6#include "shader_recompiler/frontend/ir/value.h"
7 7
8#ifdef _MSC_VER
9#pragma warning(disable : 4100)
10#endif
11
12namespace Shader::Backend::GLASM { 8namespace Shader::Backend::GLASM {
13 9
14#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) 10#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 76c18e488..e8a4390f6 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -101,7 +101,7 @@ bool IsReference(IR::Inst& inst) {
101} 101}
102 102
103void PrecolorInst(IR::Inst& phi) { 103void PrecolorInst(IR::Inst& phi) {
104 // Insert phi moves before references to avoid overwritting other phis 104 // Insert phi moves before references to avoid overwriting other phis
105 const size_t num_args{phi.NumArgs()}; 105 const size_t num_args{phi.NumArgs()};
106 for (size_t i = 0; i < num_args; ++i) { 106 for (size_t i = 0; i < num_args; ++i) {
107 IR::Block& phi_block{*phi.PhiBlock(i)}; 107 IR::Block& phi_block{*phi.PhiBlock(i)};
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index b03a8ba1e..9f1ed95a4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -7,10 +7,6 @@
7#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 7#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
8#include "shader_recompiler/frontend/ir/value.h" 8#include "shader_recompiler/frontend/ir/value.h"
9 9
10#ifdef _MSC_VER
11#pragma warning(disable : 4100)
12#endif
13
14namespace Shader::Backend::GLSL { 10namespace Shader::Backend::GLSL {
15 11
16void EmitGetRegister(EmitContext& ctx) { 12void EmitGetRegister(EmitContext& ctx) {
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 468782eb1..84417980b 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
325 phi_args.emplace_back(predecessor, value); 325 phi_args.emplace_back(predecessor, value);
326} 326}
327 327
328void Inst::ErasePhiOperand(size_t index) {
329 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
330 phi_args.erase(operand_it);
331}
332
333void Inst::OrderPhiArgs() { 328void Inst::OrderPhiArgs() {
334 if (op != Opcode::Phi) { 329 if (op != Opcode::Phi) {
335 throw LogicError("{} is not a Phi instruction", op); 330 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 1a2e4ccb6..6a673ca05 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -178,13 +178,9 @@ public:
178 178
179 /// Get a pointer to the block of a phi argument. 179 /// Get a pointer to the block of a phi argument.
180 [[nodiscard]] Block* PhiBlock(size_t index) const; 180 [[nodiscard]] Block* PhiBlock(size_t index) const;
181
182 /// Add phi operand to a phi instruction. 181 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 182 void AddPhiOperand(Block* predecessor, const Value& value);
184 183
185 // Erase the phi operand at the given index.
186 void ErasePhiOperand(size_t index);
187
188 /// Orders the Phi arguments from farthest away to nearest. 184 /// Orders the Phi arguments from farthest away to nearest.
189 void OrderPhiArgs(); 185 void OrderPhiArgs();
190 186
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 578bc8c1b..ce42475d4 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -964,9 +964,9 @@ private:
964 demote_endif_node.type = Type::EndIf; 964 demote_endif_node.type = Type::EndIf;
965 demote_endif_node.data.end_if.merge = return_block_it->data.block; 965 demote_endif_node.data.end_if.merge = return_block_it->data.block;
966 966
967 asl.insert(return_block_it, demote_endif_node); 967 const auto next_it_1 = asl.insert(return_block_it, demote_endif_node);
968 asl.insert(return_block_it, demote_node); 968 const auto next_it_2 = asl.insert(next_it_1, demote_node);
969 asl.insert(return_block_it, demote_if_node); 969 asl.insert(next_it_2, demote_if_node);
970 } 970 }
971 971
972 ObjectPool<Statement>& stmt_pool; 972 ObjectPool<Statement>& stmt_pool;
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index dafb9deee..b7162f719 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -137,28 +137,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) {
137} 137}
138 138
139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( 139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
140 const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) { 140 const VaryingState& state, std::queue<IR::Attribute> unused_generics,
141 const std::map<IR::Attribute, IR::Attribute>& previous_stage_mapping) {
141 std::map<IR::Attribute, IR::Attribute> mapping; 142 std::map<IR::Attribute, IR::Attribute> mapping;
143 auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr,
144 size_t count) {
145 if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) {
146 for (size_t i = 0; i < count; ++i) {
147 mapping.insert({attr + i, previous_stage_mapping.at(attr + i)});
148 }
149 } else {
150 for (size_t i = 0; i < count; ++i) {
151 mapping.insert({attr + i, unused_generics.front() + i});
152 }
153 unused_generics.pop();
154 }
155 };
142 for (size_t index = 0; index < 4; ++index) { 156 for (size_t index = 0; index < 4; ++index) {
143 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; 157 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
144 if (state.AnyComponent(attr)) { 158 if (state.AnyComponent(attr)) {
145 for (size_t i = 0; i < 4; ++i) { 159 update_mapping(attr, 4);
146 mapping.insert({attr + i, ununsed_generics.front() + i});
147 }
148 ununsed_generics.pop();
149 } 160 }
150 } 161 }
151 if (state[IR::Attribute::FogCoordinate]) { 162 if (state[IR::Attribute::FogCoordinate]) {
152 mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); 163 update_mapping(IR::Attribute::FogCoordinate, 1);
153 ununsed_generics.pop();
154 } 164 }
155 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { 165 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
156 auto attr = IR::Attribute::FixedFncTexture0S + index * 4; 166 auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
157 if (state.AnyComponent(attr)) { 167 if (state.AnyComponent(attr)) {
158 for (size_t i = 0; i < 4; ++i) { 168 update_mapping(attr, 4);
159 mapping.insert({attr + i, ununsed_generics.front() + i});
160 }
161 ununsed_generics.pop();
162 } 169 }
163 } 170 }
164 return mapping; 171 return mapping;
@@ -267,21 +274,22 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
267void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { 274void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
268 auto& stores = program.info.stores; 275 auto& stores = program.info.stores;
269 if (stores.Legacy()) { 276 if (stores.Legacy()) {
270 std::queue<IR::Attribute> ununsed_output_generics{}; 277 std::queue<IR::Attribute> unused_output_generics{};
271 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 278 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
272 if (!stores.Generic(index)) { 279 if (!stores.Generic(index)) {
273 ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); 280 unused_output_generics.push(IR::Attribute::Generic0X + index * 4);
274 } 281 }
275 } 282 }
276 auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); 283 program.info.legacy_stores_mapping =
284 GenerateLegacyToGenericMappings(stores, unused_output_generics, {});
277 for (IR::Block* const block : program.post_order_blocks) { 285 for (IR::Block* const block : program.post_order_blocks) {
278 for (IR::Inst& inst : block->Instructions()) { 286 for (IR::Inst& inst : block->Instructions()) {
279 switch (inst.GetOpcode()) { 287 switch (inst.GetOpcode()) {
280 case IR::Opcode::SetAttribute: { 288 case IR::Opcode::SetAttribute: {
281 const auto attr = inst.Arg(0).Attribute(); 289 const auto attr = inst.Arg(0).Attribute();
282 if (IsLegacyAttribute(attr)) { 290 if (IsLegacyAttribute(attr)) {
283 stores.Set(mappings[attr], true); 291 stores.Set(program.info.legacy_stores_mapping[attr], true);
284 inst.SetArg(0, Shader::IR::Value(mappings[attr])); 292 inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr]));
285 } 293 }
286 break; 294 break;
287 } 295 }
@@ -294,15 +302,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
294 302
295 auto& loads = program.info.loads; 303 auto& loads = program.info.loads;
296 if (loads.Legacy()) { 304 if (loads.Legacy()) {
297 std::queue<IR::Attribute> ununsed_input_generics{}; 305 std::queue<IR::Attribute> unused_input_generics{};
298 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 306 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
299 const AttributeType input_type{runtime_info.generic_input_types[index]}; 307 const AttributeType input_type{runtime_info.generic_input_types[index]};
300 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || 308 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
301 input_type == AttributeType::Disabled) { 309 input_type == AttributeType::Disabled) {
302 ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); 310 unused_input_generics.push(IR::Attribute::Generic0X + index * 4);
303 } 311 }
304 } 312 }
305 auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); 313 auto mappings = GenerateLegacyToGenericMappings(
314 loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping);
306 for (IR::Block* const block : program.post_order_blocks) { 315 for (IR::Block* const block : program.post_order_blocks) {
307 for (IR::Inst& inst : block->Instructions()) { 316 for (IR::Inst& inst : block->Instructions()) {
308 switch (inst.GetOpcode()) { 317 switch (inst.GetOpcode()) {
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 9a7d47344..1bd8afd6f 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -1,104 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5
6#include <boost/container/small_vector.hpp>
7
8#include "shader_recompiler/frontend/ir/basic_block.h" 4#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/value.h" 5#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/ir_opt/passes.h" 6#include "shader_recompiler/ir_opt/passes.h"
11 7
12namespace Shader::Optimization { 8namespace Shader::Optimization {
13namespace { 9
14template <bool TEST_USES> 10void DeadCodeEliminationPass(IR::Program& program) {
15void DeadInstElimination(IR::Block* const block) {
16 // We iterate over the instructions in reverse order. 11 // We iterate over the instructions in reverse order.
17 // This is because removing an instruction reduces the number of uses for earlier instructions. 12 // This is because removing an instruction reduces the number of uses for earlier instructions.
18 auto it{block->end()}; 13 for (IR::Block* const block : program.post_order_blocks) {
19 while (it != block->begin()) { 14 auto it{block->end()};
20 --it; 15 while (it != block->begin()) {
21 if constexpr (TEST_USES) { 16 --it;
22 if (it->HasUses() || it->MayHaveSideEffects()) { 17 if (!it->HasUses() && !it->MayHaveSideEffects()) {
23 continue; 18 it->Invalidate();
24 } 19 it = block->Instructions().erase(it);
25 }
26 it->Invalidate();
27 it = block->Instructions().erase(it);
28 }
29}
30
31void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
32 for (IR::Block* const block : program.blocks) {
33 for (IR::Inst& phi : *block) {
34 if (!IR::IsPhi(phi)) {
35 continue;
36 }
37 for (size_t i = 0; i < phi.NumArgs(); ++i) {
38 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
39 continue;
40 }
41 // Phi operand at this index is an unreachable block
42 phi.ErasePhiOperand(i);
43 --i;
44 }
45 }
46 }
47}
48
49void DeadBranchElimination(IR::Program& program) {
50 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
51 const auto begin_it{program.syntax_list.begin()};
52 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
53 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
54 continue;
55 }
56 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
57 const IR::U1 cond{cond_ref->Arg(0)};
58 if (!cond.IsImmediate()) {
59 continue;
60 }
61 if (cond.U1()) {
62 continue;
63 }
64 // False immediate condition. Remove condition ref, erase the entire branch.
65 cond_ref->Invalidate();
66 // Account for nested if-statements within the if(false) branch
67 u32 nested_ifs{1u};
68 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
69 node_it = program.syntax_list.erase(node_it);
70 switch (node_it->type) {
71 case IR::AbstractSyntaxNode::Type::If:
72 ++nested_ifs;
73 break;
74 case IR::AbstractSyntaxNode::Type::EndIf:
75 --nested_ifs;
76 break;
77 case IR::AbstractSyntaxNode::Type::Block: {
78 IR::Block* const block{node_it->data.block};
79 DeadInstElimination<false>(block);
80 dead_blocks.push_back(block);
81 break;
82 }
83 default:
84 break;
85 } 20 }
86 } 21 }
87 // Erase EndIf node of the if(false) branch
88 node_it = program.syntax_list.erase(node_it);
89 // Account for loop increment
90 --node_it;
91 }
92 if (!dead_blocks.empty()) {
93 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
94 }
95}
96} // namespace
97
98void DeadCodeEliminationPass(IR::Program& program) {
99 DeadBranchElimination(program);
100 for (IR::Block* const block : program.post_order_blocks) {
101 DeadInstElimination<true>(block);
102 } 22 }
103} 23}
104 24
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 597112ba4..e8be58357 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -19,8 +19,10 @@ namespace {
19struct ConstBufferAddr { 19struct ConstBufferAddr {
20 u32 index; 20 u32 index;
21 u32 offset; 21 u32 offset;
22 u32 shift_left;
22 u32 secondary_index; 23 u32 secondary_index;
23 u32 secondary_offset; 24 u32 secondary_offset;
25 u32 secondary_shift_left;
24 IR::U32 dynamic_offset; 26 IR::U32 dynamic_offset;
25 u32 count; 27 u32 count;
26 bool has_secondary; 28 bool has_secondary;
@@ -172,19 +174,41 @@ bool IsTextureInstruction(const IR::Inst& inst) {
172 return IndexedInstruction(inst) != IR::Opcode::Void; 174 return IndexedInstruction(inst) != IR::Opcode::Void;
173} 175}
174 176
175std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst); 177std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
176 178
177std::optional<ConstBufferAddr> Track(const IR::Value& value) { 179std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
178 return IR::BreadthFirstSearch(value, TryGetConstBuffer); 180 return IR::BreadthFirstSearch(
181 value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
179} 182}
180 183
181std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) { 184std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
185 const IR::Inst* inst = value.InstRecursive();
186 if (inst->GetOpcode() != IR::Opcode::GetCbufU32) {
187 return std::nullopt;
188 }
189 const IR::Value index{inst->Arg(0)};
190 const IR::Value offset{inst->Arg(1)};
191 if (!index.IsImmediate()) {
192 return std::nullopt;
193 }
194 if (!offset.IsImmediate()) {
195 return std::nullopt;
196 }
197 const auto index_number = index.U32();
198 if (index_number != 1) {
199 return std::nullopt;
200 }
201 const auto offset_number = offset.U32();
202 return env.ReadCbufValue(index_number, offset_number);
203}
204
205std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
182 switch (inst->GetOpcode()) { 206 switch (inst->GetOpcode()) {
183 default: 207 default:
184 return std::nullopt; 208 return std::nullopt;
185 case IR::Opcode::BitwiseOr32: { 209 case IR::Opcode::BitwiseOr32: {
186 std::optional lhs{Track(inst->Arg(0))}; 210 std::optional lhs{Track(inst->Arg(0), env)};
187 std::optional rhs{Track(inst->Arg(1))}; 211 std::optional rhs{Track(inst->Arg(1), env)};
188 if (!lhs || !rhs) { 212 if (!lhs || !rhs) {
189 return std::nullopt; 213 return std::nullopt;
190 } 214 }
@@ -194,19 +218,62 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
194 if (lhs->count > 1 || rhs->count > 1) { 218 if (lhs->count > 1 || rhs->count > 1) {
195 return std::nullopt; 219 return std::nullopt;
196 } 220 }
197 if (lhs->index > rhs->index || lhs->offset > rhs->offset) { 221 if (lhs->shift_left > 0 || lhs->index > rhs->index || lhs->offset > rhs->offset) {
198 std::swap(lhs, rhs); 222 std::swap(lhs, rhs);
199 } 223 }
200 return ConstBufferAddr{ 224 return ConstBufferAddr{
201 .index = lhs->index, 225 .index = lhs->index,
202 .offset = lhs->offset, 226 .offset = lhs->offset,
227 .shift_left = lhs->shift_left,
203 .secondary_index = rhs->index, 228 .secondary_index = rhs->index,
204 .secondary_offset = rhs->offset, 229 .secondary_offset = rhs->offset,
230 .secondary_shift_left = rhs->shift_left,
205 .dynamic_offset = {}, 231 .dynamic_offset = {},
206 .count = 1, 232 .count = 1,
207 .has_secondary = true, 233 .has_secondary = true,
208 }; 234 };
209 } 235 }
236 case IR::Opcode::ShiftLeftLogical32: {
237 const IR::Value shift{inst->Arg(1)};
238 if (!shift.IsImmediate()) {
239 return std::nullopt;
240 }
241 std::optional lhs{Track(inst->Arg(0), env)};
242 if (lhs) {
243 lhs->shift_left = shift.U32();
244 }
245 return lhs;
246 break;
247 }
248 case IR::Opcode::BitwiseAnd32: {
249 IR::Value op1{inst->Arg(0)};
250 IR::Value op2{inst->Arg(1)};
251 if (op1.IsImmediate()) {
252 std::swap(op1, op2);
253 }
254 if (!op2.IsImmediate() && !op1.IsImmediate()) {
255 do {
256 auto try_index = TryGetConstant(op1, env);
257 if (try_index) {
258 op1 = op2;
259 op2 = IR::Value{*try_index};
260 break;
261 }
262 auto try_index_2 = TryGetConstant(op2, env);
263 if (try_index_2) {
264 op2 = IR::Value{*try_index_2};
265 break;
266 }
267 return std::nullopt;
268 } while (false);
269 }
270 std::optional lhs{Track(op1, env)};
271 if (lhs) {
272 lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
273 }
274 return lhs;
275 break;
276 }
210 case IR::Opcode::GetCbufU32x2: 277 case IR::Opcode::GetCbufU32x2:
211 case IR::Opcode::GetCbufU32: 278 case IR::Opcode::GetCbufU32:
212 break; 279 break;
@@ -222,8 +289,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
222 return ConstBufferAddr{ 289 return ConstBufferAddr{
223 .index = index.U32(), 290 .index = index.U32(),
224 .offset = offset.U32(), 291 .offset = offset.U32(),
292 .shift_left = 0,
225 .secondary_index = 0, 293 .secondary_index = 0,
226 .secondary_offset = 0, 294 .secondary_offset = 0,
295 .secondary_shift_left = 0,
227 .dynamic_offset = {}, 296 .dynamic_offset = {},
228 .count = 1, 297 .count = 1,
229 .has_secondary = false, 298 .has_secondary = false,
@@ -247,8 +316,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
247 return ConstBufferAddr{ 316 return ConstBufferAddr{
248 .index = index.U32(), 317 .index = index.U32(),
249 .offset = base_offset, 318 .offset = base_offset,
319 .shift_left = 0,
250 .secondary_index = 0, 320 .secondary_index = 0,
251 .secondary_offset = 0, 321 .secondary_offset = 0,
322 .secondary_shift_left = 0,
252 .dynamic_offset = dynamic_offset, 323 .dynamic_offset = dynamic_offset,
253 .count = 8, 324 .count = 8,
254 .has_secondary = false, 325 .has_secondary = false,
@@ -258,7 +329,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
258TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { 329TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
259 ConstBufferAddr addr; 330 ConstBufferAddr addr;
260 if (IsBindless(inst)) { 331 if (IsBindless(inst)) {
261 const std::optional<ConstBufferAddr> track_addr{Track(inst.Arg(0))}; 332 const std::optional<ConstBufferAddr> track_addr{Track(inst.Arg(0), env)};
262 if (!track_addr) { 333 if (!track_addr) {
263 throw NotImplementedException("Failed to track bindless texture constant buffer"); 334 throw NotImplementedException("Failed to track bindless texture constant buffer");
264 } 335 }
@@ -267,8 +338,10 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
267 addr = ConstBufferAddr{ 338 addr = ConstBufferAddr{
268 .index = env.TextureBoundBuffer(), 339 .index = env.TextureBoundBuffer(),
269 .offset = inst.Arg(0).U32(), 340 .offset = inst.Arg(0).U32(),
341 .shift_left = 0,
270 .secondary_index = 0, 342 .secondary_index = 0,
271 .secondary_offset = 0, 343 .secondary_offset = 0,
344 .secondary_shift_left = 0,
272 .dynamic_offset = {}, 345 .dynamic_offset = {},
273 .count = 1, 346 .count = 1,
274 .has_secondary = false, 347 .has_secondary = false,
@@ -284,8 +357,9 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
284TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 357TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
285 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 358 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
286 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 359 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
287 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)}; 360 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
288 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)}; 361 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
362 << cbuf.secondary_shift_left};
289 return env.ReadTextureType(lhs_raw | rhs_raw); 363 return env.ReadTextureType(lhs_raw | rhs_raw);
290} 364}
291 365
@@ -487,8 +561,10 @@ void TexturePass(Environment& env, IR::Program& program) {
487 .has_secondary = cbuf.has_secondary, 561 .has_secondary = cbuf.has_secondary,
488 .cbuf_index = cbuf.index, 562 .cbuf_index = cbuf.index,
489 .cbuf_offset = cbuf.offset, 563 .cbuf_offset = cbuf.offset,
564 .shift_left = cbuf.shift_left,
490 .secondary_cbuf_index = cbuf.secondary_index, 565 .secondary_cbuf_index = cbuf.secondary_index,
491 .secondary_cbuf_offset = cbuf.secondary_offset, 566 .secondary_cbuf_offset = cbuf.secondary_offset,
567 .secondary_shift_left = cbuf.secondary_shift_left,
492 .count = cbuf.count, 568 .count = cbuf.count,
493 .size_shift = DESCRIPTOR_SIZE_SHIFT, 569 .size_shift = DESCRIPTOR_SIZE_SHIFT,
494 }); 570 });
@@ -499,8 +575,10 @@ void TexturePass(Environment& env, IR::Program& program) {
499 .has_secondary = cbuf.has_secondary, 575 .has_secondary = cbuf.has_secondary,
500 .cbuf_index = cbuf.index, 576 .cbuf_index = cbuf.index,
501 .cbuf_offset = cbuf.offset, 577 .cbuf_offset = cbuf.offset,
578 .shift_left = cbuf.shift_left,
502 .secondary_cbuf_index = cbuf.secondary_index, 579 .secondary_cbuf_index = cbuf.secondary_index,
503 .secondary_cbuf_offset = cbuf.secondary_offset, 580 .secondary_cbuf_offset = cbuf.secondary_offset,
581 .secondary_shift_left = cbuf.secondary_shift_left,
504 .count = cbuf.count, 582 .count = cbuf.count,
505 .size_shift = DESCRIPTOR_SIZE_SHIFT, 583 .size_shift = DESCRIPTOR_SIZE_SHIFT,
506 }); 584 });
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index dcb5ab158..549b81ef7 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <map>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
9 10
@@ -60,6 +61,7 @@ struct TransformFeedbackVarying {
60struct RuntimeInfo { 61struct RuntimeInfo {
61 std::array<AttributeType, 32> generic_input_types{}; 62 std::array<AttributeType, 32> generic_input_types{};
62 VaryingState previous_stage_stores; 63 VaryingState previous_stage_stores;
64 std::map<IR::Attribute, IR::Attribute> previous_stage_legacy_stores_mapping;
63 65
64 bool convert_depth_mode{}; 66 bool convert_depth_mode{};
65 bool force_early_z{}; 67 bool force_early_z{};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index e4b5ba567..a479e105e 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
8#include <map>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "shader_recompiler/frontend/ir/type.h" 11#include "shader_recompiler/frontend/ir/type.h"
@@ -61,8 +62,10 @@ struct TextureBufferDescriptor {
61 bool has_secondary; 62 bool has_secondary;
62 u32 cbuf_index; 63 u32 cbuf_index;
63 u32 cbuf_offset; 64 u32 cbuf_offset;
65 u32 shift_left;
64 u32 secondary_cbuf_index; 66 u32 secondary_cbuf_index;
65 u32 secondary_cbuf_offset; 67 u32 secondary_cbuf_offset;
68 u32 secondary_shift_left;
66 u32 count; 69 u32 count;
67 u32 size_shift; 70 u32 size_shift;
68}; 71};
@@ -85,8 +88,10 @@ struct TextureDescriptor {
85 bool has_secondary; 88 bool has_secondary;
86 u32 cbuf_index; 89 u32 cbuf_index;
87 u32 cbuf_offset; 90 u32 cbuf_offset;
91 u32 shift_left;
88 u32 secondary_cbuf_index; 92 u32 secondary_cbuf_index;
89 u32 secondary_cbuf_offset; 93 u32 secondary_cbuf_offset;
94 u32 secondary_shift_left;
90 u32 count; 95 u32 count;
91 u32 size_shift; 96 u32 size_shift;
92}; 97};
@@ -123,6 +128,8 @@ struct Info {
123 VaryingState stores; 128 VaryingState stores;
124 VaryingState passthrough; 129 VaryingState passthrough;
125 130
131 std::map<IR::Attribute, IR::Attribute> legacy_stores_mapping;
132
126 bool loads_indexed_attributes{}; 133 bool loads_indexed_attributes{};
127 134
128 std::array<bool, 8> stores_frag_color{}; 135 std::array<bool, 8> stores_frag_color{};
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 7c432a63c..284b2ae66 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -40,9 +40,6 @@ struct ScopeInit final {
40 core_timing.SetMulticore(true); 40 core_timing.SetMulticore(true);
41 core_timing.Initialize([]() {}); 41 core_timing.Initialize([]() {});
42 } 42 }
43 ~ScopeInit() {
44 core_timing.Shutdown();
45 }
46 43
47 Core::Timing::CoreTiming core_timing; 44 Core::Timing::CoreTiming core_timing;
48}; 45};
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 71121e42a..f7236afab 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -44,7 +44,7 @@ public:
44 44
45 [[nodiscard]] unsigned Count() const noexcept { 45 [[nodiscard]] unsigned Count() const noexcept {
46 unsigned count = 0; 46 unsigned count = 0;
47 for (const auto [index, value] : page_table) { 47 for (const auto& [index, value] : page_table) {
48 count += value; 48 count += value;
49 } 49 }
50 return count; 50 return count;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 5b3808351..106991969 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -4,7 +4,7 @@
4add_subdirectory(host_shaders) 4add_subdirectory(host_shaders)
5 5
6if(LIBVA_FOUND) 6if(LIBVA_FOUND)
7 set_source_files_properties(command_classes/codecs/codec.cpp 7 set_source_files_properties(host1x/codecs/codec.cpp
8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) 8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) 9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
10endif() 10endif()
@@ -15,26 +15,14 @@ add_library(video_core STATIC
15 buffer_cache/buffer_cache.h 15 buffer_cache/buffer_cache.h
16 cdma_pusher.cpp 16 cdma_pusher.cpp
17 cdma_pusher.h 17 cdma_pusher.h
18 command_classes/codecs/codec.cpp
19 command_classes/codecs/codec.h
20 command_classes/codecs/h264.cpp
21 command_classes/codecs/h264.h
22 command_classes/codecs/vp8.cpp
23 command_classes/codecs/vp8.h
24 command_classes/codecs/vp9.cpp
25 command_classes/codecs/vp9.h
26 command_classes/codecs/vp9_types.h
27 command_classes/host1x.cpp
28 command_classes/host1x.h
29 command_classes/nvdec.cpp
30 command_classes/nvdec.h
31 command_classes/nvdec_common.h
32 command_classes/sync_manager.cpp
33 command_classes/sync_manager.h
34 command_classes/vic.cpp
35 command_classes/vic.h
36 compatible_formats.cpp 18 compatible_formats.cpp
37 compatible_formats.h 19 compatible_formats.h
20 control/channel_state.cpp
21 control/channel_state.h
22 control/channel_state_cache.cpp
23 control/channel_state_cache.h
24 control/scheduler.cpp
25 control/scheduler.h
38 delayed_destruction_ring.h 26 delayed_destruction_ring.h
39 dirty_flags.cpp 27 dirty_flags.cpp
40 dirty_flags.h 28 dirty_flags.h
@@ -54,7 +42,31 @@ add_library(video_core STATIC
54 engines/maxwell_3d.h 42 engines/maxwell_3d.h
55 engines/maxwell_dma.cpp 43 engines/maxwell_dma.cpp
56 engines/maxwell_dma.h 44 engines/maxwell_dma.h
45 engines/puller.cpp
46 engines/puller.h
57 framebuffer_config.h 47 framebuffer_config.h
48 host1x/codecs/codec.cpp
49 host1x/codecs/codec.h
50 host1x/codecs/h264.cpp
51 host1x/codecs/h264.h
52 host1x/codecs/vp8.cpp
53 host1x/codecs/vp8.h
54 host1x/codecs/vp9.cpp
55 host1x/codecs/vp9.h
56 host1x/codecs/vp9_types.h
57 host1x/control.cpp
58 host1x/control.h
59 host1x/host1x.cpp
60 host1x/host1x.h
61 host1x/nvdec.cpp
62 host1x/nvdec.h
63 host1x/nvdec_common.h
64 host1x/sync_manager.cpp
65 host1x/sync_manager.h
66 host1x/syncpoint_manager.cpp
67 host1x/syncpoint_manager.h
68 host1x/vic.cpp
69 host1x/vic.h
58 macro/macro.cpp 70 macro/macro.cpp
59 macro/macro.h 71 macro/macro.h
60 macro/macro_hle.cpp 72 macro/macro_hle.cpp
@@ -70,6 +82,7 @@ add_library(video_core STATIC
70 gpu_thread.h 82 gpu_thread.h
71 memory_manager.cpp 83 memory_manager.cpp
72 memory_manager.h 84 memory_manager.h
85 pte_kind.h
73 query_cache.h 86 query_cache.h
74 rasterizer_accelerated.cpp 87 rasterizer_accelerated.cpp
75 rasterizer_accelerated.h 88 rasterizer_accelerated.h
@@ -195,6 +208,7 @@ add_library(video_core STATIC
195 texture_cache/render_targets.h 208 texture_cache/render_targets.h
196 texture_cache/samples_helper.h 209 texture_cache/samples_helper.h
197 texture_cache/slot_vector.h 210 texture_cache/slot_vector.h
211 texture_cache/texture_cache.cpp
198 texture_cache/texture_cache.h 212 texture_cache/texture_cache.h
199 texture_cache/texture_cache_base.h 213 texture_cache/texture_cache_base.h
200 texture_cache/types.h 214 texture_cache/types.h
@@ -265,14 +279,8 @@ if (MSVC)
265else() 279else()
266 target_compile_options(video_core PRIVATE 280 target_compile_options(video_core PRIVATE
267 -Werror=conversion 281 -Werror=conversion
268 -Wno-error=sign-conversion
269 -Werror=pessimizing-move
270 -Werror=redundant-move
271 -Werror=type-limits
272 282
273 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 283 -Wno-sign-conversion
274 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
275 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
276 ) 284 )
277endif() 285endif()
278 286
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f015dae56..2ba33543c 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -5,7 +5,6 @@
5 5
6#include <algorithm> 6#include <algorithm>
7#include <array> 7#include <array>
8#include <deque>
9#include <memory> 8#include <memory>
10#include <mutex> 9#include <mutex>
11#include <numeric> 10#include <numeric>
@@ -23,6 +22,7 @@
23#include "common/settings.h" 22#include "common/settings.h"
24#include "core/memory.h" 23#include "core/memory.h"
25#include "video_core/buffer_cache/buffer_base.h" 24#include "video_core/buffer_cache/buffer_base.h"
25#include "video_core/control/channel_state_cache.h"
26#include "video_core/delayed_destruction_ring.h" 26#include "video_core/delayed_destruction_ring.h"
27#include "video_core/dirty_flags.h" 27#include "video_core/dirty_flags.h"
28#include "video_core/engines/kepler_compute.h" 28#include "video_core/engines/kepler_compute.h"
@@ -56,7 +56,7 @@ using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFE
56using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>; 56using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
57 57
58template <typename P> 58template <typename P>
59class BufferCache { 59class BufferCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
60 60
61 // Page size for caching purposes. 61 // Page size for caching purposes.
62 // This is unrelated to the CPU page size and it can be changed as it seems optimal. 62 // This is unrelated to the CPU page size and it can be changed as it seems optimal.
@@ -116,10 +116,7 @@ public:
116 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB); 116 static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
117 117
118 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, 118 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
119 Tegra::Engines::Maxwell3D& maxwell3d_, 119 Core::Memory::Memory& cpu_memory_, Runtime& runtime_);
120 Tegra::Engines::KeplerCompute& kepler_compute_,
121 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
122 Runtime& runtime_);
123 120
124 void TickFrame(); 121 void TickFrame();
125 122
@@ -129,7 +126,7 @@ public:
129 126
130 void DownloadMemory(VAddr cpu_addr, u64 size); 127 void DownloadMemory(VAddr cpu_addr, u64 size);
131 128
132 bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<u8> inlined_buffer); 129 bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
133 130
134 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size); 131 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
135 132
@@ -353,7 +350,7 @@ private:
353 350
354 void NotifyBufferDeletion(); 351 void NotifyBufferDeletion();
355 352
356 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; 353 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, bool is_written = false) const;
357 354
358 [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size, 355 [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
359 PixelFormat format); 356 PixelFormat format);
@@ -367,9 +364,6 @@ private:
367 void ClearDownload(IntervalType subtract_interval); 364 void ClearDownload(IntervalType subtract_interval);
368 365
369 VideoCore::RasterizerInterface& rasterizer; 366 VideoCore::RasterizerInterface& rasterizer;
370 Tegra::Engines::Maxwell3D& maxwell3d;
371 Tegra::Engines::KeplerCompute& kepler_compute;
372 Tegra::MemoryManager& gpu_memory;
373 Core::Memory::Memory& cpu_memory; 367 Core::Memory::Memory& cpu_memory;
374 368
375 SlotVector<Buffer> slot_buffers; 369 SlotVector<Buffer> slot_buffers;
@@ -444,12 +438,8 @@ private:
444 438
445template <class P> 439template <class P>
446BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, 440BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
447 Tegra::Engines::Maxwell3D& maxwell3d_, 441 Core::Memory::Memory& cpu_memory_, Runtime& runtime_)
448 Tegra::Engines::KeplerCompute& kepler_compute_, 442 : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_} {
449 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
450 Runtime& runtime_)
451 : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
452 kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} {
453 // Ensure the first slot is used for the null buffer 443 // Ensure the first slot is used for the null buffer
454 void(slot_buffers.insert(runtime, NullBufferParams{})); 444 void(slot_buffers.insert(runtime, NullBufferParams{}));
455 common_ranges.clear(); 445 common_ranges.clear();
@@ -552,8 +542,8 @@ void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
552 542
553template <class P> 543template <class P>
554bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 544bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
555 const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); 545 const std::optional<VAddr> cpu_src_address = gpu_memory->GpuToCpuAddress(src_address);
556 const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); 546 const std::optional<VAddr> cpu_dest_address = gpu_memory->GpuToCpuAddress(dest_address);
557 if (!cpu_src_address || !cpu_dest_address) { 547 if (!cpu_src_address || !cpu_dest_address) {
558 return false; 548 return false;
559 } 549 }
@@ -611,7 +601,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
611 601
612template <class P> 602template <class P>
613bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { 603bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
614 const std::optional<VAddr> cpu_dst_address = gpu_memory.GpuToCpuAddress(dst_address); 604 const std::optional<VAddr> cpu_dst_address = gpu_memory->GpuToCpuAddress(dst_address);
615 if (!cpu_dst_address) { 605 if (!cpu_dst_address) {
616 return false; 606 return false;
617 } 607 }
@@ -635,7 +625,7 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
635template <class P> 625template <class P>
636void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, 626void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
637 u32 size) { 627 u32 size) {
638 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 628 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
639 const Binding binding{ 629 const Binding binding{
640 .cpu_addr = *cpu_addr, 630 .cpu_addr = *cpu_addr,
641 .size = size, 631 .size = size,
@@ -673,7 +663,7 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
673 if (is_indexed) { 663 if (is_indexed) {
674 BindHostIndexBuffer(); 664 BindHostIndexBuffer();
675 } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { 665 } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
676 const auto& regs = maxwell3d.regs; 666 const auto& regs = maxwell3d->regs;
677 if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { 667 if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
678 runtime.BindQuadArrayIndexBuffer(regs.vertex_buffer.first, regs.vertex_buffer.count); 668 runtime.BindQuadArrayIndexBuffer(regs.vertex_buffer.first, regs.vertex_buffer.count);
679 } 669 }
@@ -733,9 +723,9 @@ void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
733 enabled_storage_buffers[stage] |= 1U << ssbo_index; 723 enabled_storage_buffers[stage] |= 1U << ssbo_index;
734 written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; 724 written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index;
735 725
736 const auto& cbufs = maxwell3d.state.shader_stages[stage]; 726 const auto& cbufs = maxwell3d->state.shader_stages[stage];
737 const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; 727 const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
738 storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr); 728 storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, is_written);
739} 729}
740 730
741template <class P> 731template <class P>
@@ -770,12 +760,12 @@ void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index,
770 enabled_compute_storage_buffers |= 1U << ssbo_index; 760 enabled_compute_storage_buffers |= 1U << ssbo_index;
771 written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index; 761 written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index;
772 762
773 const auto& launch_desc = kepler_compute.launch_description; 763 const auto& launch_desc = kepler_compute->launch_description;
774 ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0); 764 ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0);
775 765
776 const auto& cbufs = launch_desc.const_buffer_config; 766 const auto& cbufs = launch_desc.const_buffer_config;
777 const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset; 767 const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset;
778 compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr); 768 compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr, is_written);
779} 769}
780 770
781template <class P> 771template <class P>
@@ -836,6 +826,19 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
836 const bool is_accuracy_normal = 826 const bool is_accuracy_normal =
837 Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal; 827 Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal;
838 828
829 auto it = committed_ranges.begin();
830 while (it != committed_ranges.end()) {
831 auto& current_intervals = *it;
832 auto next_it = std::next(it);
833 while (next_it != committed_ranges.end()) {
834 for (auto& interval : *next_it) {
835 current_intervals.subtract(interval);
836 }
837 next_it++;
838 }
839 it++;
840 }
841
839 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; 842 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
840 u64 total_size_bytes = 0; 843 u64 total_size_bytes = 0;
841 u64 largest_copy = 0; 844 u64 largest_copy = 0;
@@ -991,19 +994,19 @@ void BufferCache<P>::BindHostIndexBuffer() {
991 const u32 size = index_buffer.size; 994 const u32 size = index_buffer.size;
992 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); 995 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
993 if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { 996 if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
994 const u32 new_offset = offset + maxwell3d.regs.index_array.first * 997 const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
995 maxwell3d.regs.index_array.FormatSizeInBytes(); 998 maxwell3d->regs.index_buffer.FormatSizeInBytes();
996 runtime.BindIndexBuffer(buffer, new_offset, size); 999 runtime.BindIndexBuffer(buffer, new_offset, size);
997 } else { 1000 } else {
998 runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, 1001 runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
999 maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, 1002 maxwell3d->regs.index_buffer.first,
1000 buffer, offset, size); 1003 maxwell3d->regs.index_buffer.count, buffer, offset, size);
1001 } 1004 }
1002} 1005}
1003 1006
1004template <class P> 1007template <class P>
1005void BufferCache<P>::BindHostVertexBuffers() { 1008void BufferCache<P>::BindHostVertexBuffers() {
1006 auto& flags = maxwell3d.dirty.flags; 1009 auto& flags = maxwell3d->dirty.flags;
1007 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 1010 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
1008 const Binding& binding = vertex_buffers[index]; 1011 const Binding& binding = vertex_buffers[index];
1009 Buffer& buffer = slot_buffers[binding.buffer_id]; 1012 Buffer& buffer = slot_buffers[binding.buffer_id];
@@ -1014,7 +1017,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
1014 } 1017 }
1015 flags[Dirty::VertexBuffer0 + index] = false; 1018 flags[Dirty::VertexBuffer0 + index] = false;
1016 1019
1017 const u32 stride = maxwell3d.regs.vertex_array[index].stride; 1020 const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
1018 const u32 offset = buffer.Offset(binding.cpu_addr); 1021 const u32 offset = buffer.Offset(binding.cpu_addr);
1019 runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); 1022 runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride);
1020 } 1023 }
@@ -1154,7 +1157,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
1154 1157
1155template <class P> 1158template <class P>
1156void BufferCache<P>::BindHostTransformFeedbackBuffers() { 1159void BufferCache<P>::BindHostTransformFeedbackBuffers() {
1157 if (maxwell3d.regs.tfb_enabled == 0) { 1160 if (maxwell3d->regs.transform_feedback_enabled == 0) {
1158 return; 1161 return;
1159 } 1162 }
1160 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { 1163 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
@@ -1239,16 +1242,19 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
1239 1242
1240template <class P> 1243template <class P>
1241void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { 1244void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
1242 if (is_indexed) { 1245 do {
1243 UpdateIndexBuffer(); 1246 has_deleted_buffers = false;
1244 } 1247 if (is_indexed) {
1245 UpdateVertexBuffers(); 1248 UpdateIndexBuffer();
1246 UpdateTransformFeedbackBuffers(); 1249 }
1247 for (size_t stage = 0; stage < NUM_STAGES; ++stage) { 1250 UpdateVertexBuffers();
1248 UpdateUniformBuffers(stage); 1251 UpdateTransformFeedbackBuffers();
1249 UpdateStorageBuffers(stage); 1252 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
1250 UpdateTextureBuffers(stage); 1253 UpdateUniformBuffers(stage);
1251 } 1254 UpdateStorageBuffers(stage);
1255 UpdateTextureBuffers(stage);
1256 }
1257 } while (has_deleted_buffers);
1252} 1258}
1253 1259
1254template <class P> 1260template <class P>
@@ -1262,8 +1268,8 @@ template <class P>
1262void BufferCache<P>::UpdateIndexBuffer() { 1268void BufferCache<P>::UpdateIndexBuffer() {
1263 // We have to check for the dirty flags and index count 1269 // We have to check for the dirty flags and index count
1264 // The index count is currently changed without updating the dirty flags 1270 // The index count is currently changed without updating the dirty flags
1265 const auto& index_array = maxwell3d.regs.index_array; 1271 const auto& index_array = maxwell3d->regs.index_buffer;
1266 auto& flags = maxwell3d.dirty.flags; 1272 auto& flags = maxwell3d->dirty.flags;
1267 if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { 1273 if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) {
1268 return; 1274 return;
1269 } 1275 }
@@ -1272,7 +1278,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
1272 1278
1273 const GPUVAddr gpu_addr_begin = index_array.StartAddress(); 1279 const GPUVAddr gpu_addr_begin = index_array.StartAddress();
1274 const GPUVAddr gpu_addr_end = index_array.EndAddress(); 1280 const GPUVAddr gpu_addr_end = index_array.EndAddress();
1275 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1281 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
1276 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1282 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1277 const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); 1283 const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes();
1278 const u32 size = std::min(address_size, draw_size); 1284 const u32 size = std::min(address_size, draw_size);
@@ -1289,8 +1295,8 @@ void BufferCache<P>::UpdateIndexBuffer() {
1289 1295
1290template <class P> 1296template <class P>
1291void BufferCache<P>::UpdateVertexBuffers() { 1297void BufferCache<P>::UpdateVertexBuffers() {
1292 auto& flags = maxwell3d.dirty.flags; 1298 auto& flags = maxwell3d->dirty.flags;
1293 if (!maxwell3d.dirty.flags[Dirty::VertexBuffers]) { 1299 if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
1294 return; 1300 return;
1295 } 1301 }
1296 flags[Dirty::VertexBuffers] = false; 1302 flags[Dirty::VertexBuffers] = false;
@@ -1302,33 +1308,25 @@ void BufferCache<P>::UpdateVertexBuffers() {
1302 1308
1303template <class P> 1309template <class P>
1304void BufferCache<P>::UpdateVertexBuffer(u32 index) { 1310void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1305 if (!maxwell3d.dirty.flags[Dirty::VertexBuffer0 + index]) { 1311 if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) {
1306 return; 1312 return;
1307 } 1313 }
1308 const auto& array = maxwell3d.regs.vertex_array[index]; 1314 const auto& array = maxwell3d->regs.vertex_streams[index];
1309 const auto& limit = maxwell3d.regs.vertex_array_limit[index]; 1315 const auto& limit = maxwell3d->regs.vertex_stream_limits[index];
1310 const GPUVAddr gpu_addr_begin = array.StartAddress(); 1316 const GPUVAddr gpu_addr_begin = array.Address();
1311 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; 1317 const GPUVAddr gpu_addr_end = limit.Address() + 1;
1312 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1318 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
1313 u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1319 u32 address_size = static_cast<u32>(
1314 if (address_size >= 64_MiB) { 1320 std::min(gpu_addr_end - gpu_addr_begin, static_cast<u64>(std::numeric_limits<u32>::max())));
1315 // Reported vertex buffer size is very large, cap to mapped buffer size 1321 if (array.enable == 0 || address_size == 0 || !cpu_addr) {
1316 GPUVAddr submapped_addr_end = gpu_addr_begin;
1317
1318 const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)};
1319 if (ranges.size() > 0) {
1320 const auto& [addr, size] = *ranges.begin();
1321 submapped_addr_end = addr + size;
1322 }
1323
1324 address_size =
1325 std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin));
1326 }
1327 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1328 if (array.enable == 0 || size == 0 || !cpu_addr) {
1329 vertex_buffers[index] = NULL_BINDING; 1322 vertex_buffers[index] = NULL_BINDING;
1330 return; 1323 return;
1331 } 1324 }
1325 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
1326 address_size =
1327 static_cast<u32>(gpu_memory->MaxContinousRange(gpu_addr_begin, address_size));
1328 }
1329 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1332 vertex_buffers[index] = Binding{ 1330 vertex_buffers[index] = Binding{
1333 .cpu_addr = *cpu_addr, 1331 .cpu_addr = *cpu_addr,
1334 .size = size, 1332 .size = size,
@@ -1382,7 +1380,7 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
1382 1380
1383template <class P> 1381template <class P>
1384void BufferCache<P>::UpdateTransformFeedbackBuffers() { 1382void BufferCache<P>::UpdateTransformFeedbackBuffers() {
1385 if (maxwell3d.regs.tfb_enabled == 0) { 1383 if (maxwell3d->regs.transform_feedback_enabled == 0) {
1386 return; 1384 return;
1387 } 1385 }
1388 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { 1386 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
@@ -1392,11 +1390,11 @@ void BufferCache<P>::UpdateTransformFeedbackBuffers() {
1392 1390
1393template <class P> 1391template <class P>
1394void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) { 1392void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
1395 const auto& binding = maxwell3d.regs.tfb_bindings[index]; 1393 const auto& binding = maxwell3d->regs.transform_feedback.buffers[index];
1396 const GPUVAddr gpu_addr = binding.Address() + binding.buffer_offset; 1394 const GPUVAddr gpu_addr = binding.Address() + binding.start_offset;
1397 const u32 size = binding.buffer_size; 1395 const u32 size = binding.size;
1398 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 1396 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1399 if (binding.buffer_enable == 0 || size == 0 || !cpu_addr) { 1397 if (binding.enable == 0 || size == 0 || !cpu_addr) {
1400 transform_feedback_buffers[index] = NULL_BINDING; 1398 transform_feedback_buffers[index] = NULL_BINDING;
1401 return; 1399 return;
1402 } 1400 }
@@ -1414,10 +1412,10 @@ void BufferCache<P>::UpdateComputeUniformBuffers() {
1414 ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) { 1412 ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
1415 Binding& binding = compute_uniform_buffers[index]; 1413 Binding& binding = compute_uniform_buffers[index];
1416 binding = NULL_BINDING; 1414 binding = NULL_BINDING;
1417 const auto& launch_desc = kepler_compute.launch_description; 1415 const auto& launch_desc = kepler_compute->launch_description;
1418 if (((launch_desc.const_buffer_enable_mask >> index) & 1) != 0) { 1416 if (((launch_desc.const_buffer_enable_mask >> index) & 1) != 0) {
1419 const auto& cbuf = launch_desc.const_buffer_config[index]; 1417 const auto& cbuf = launch_desc.const_buffer_config[index];
1420 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(cbuf.Address()); 1418 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(cbuf.Address());
1421 if (cpu_addr) { 1419 if (cpu_addr) {
1422 binding.cpu_addr = *cpu_addr; 1420 binding.cpu_addr = *cpu_addr;
1423 binding.size = cbuf.size; 1421 binding.size = cbuf.size;
@@ -1567,6 +1565,8 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1567 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); 1565 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
1568 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1566 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1569 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1567 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
1568 auto& new_buffer = slot_buffers[new_buffer_id];
1569 runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0);
1570 for (const BufferId overlap_id : overlap.ids) { 1570 for (const BufferId overlap_id : overlap.ids) {
1571 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1571 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1572 } 1572 }
@@ -1695,7 +1695,7 @@ void BufferCache<P>::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes,
1695 1695
1696template <class P> 1696template <class P>
1697bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, 1697bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
1698 std::span<u8> inlined_buffer) { 1698 std::span<const u8> inlined_buffer) {
1699 const bool is_dirty = IsRegionRegistered(dest_address, copy_size); 1699 const bool is_dirty = IsRegionRegistered(dest_address, copy_size);
1700 if (!is_dirty) { 1700 if (!is_dirty) {
1701 return false; 1701 return false;
@@ -1831,7 +1831,7 @@ void BufferCache<P>::NotifyBufferDeletion() {
1831 dirty_uniform_buffers.fill(~u32{0}); 1831 dirty_uniform_buffers.fill(~u32{0});
1832 uniform_buffer_binding_sizes.fill({}); 1832 uniform_buffer_binding_sizes.fill({});
1833 } 1833 }
1834 auto& flags = maxwell3d.dirty.flags; 1834 auto& flags = maxwell3d->dirty.flags;
1835 flags[Dirty::IndexBuffer] = true; 1835 flags[Dirty::IndexBuffer] = true;
1836 flags[Dirty::VertexBuffers] = true; 1836 flags[Dirty::VertexBuffers] = true;
1837 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 1837 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
@@ -1841,16 +1841,18 @@ void BufferCache<P>::NotifyBufferDeletion() {
1841} 1841}
1842 1842
1843template <class P> 1843template <class P>
1844typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr) const { 1844typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr,
1845 const GPUVAddr gpu_addr = gpu_memory.Read<u64>(ssbo_addr); 1845 bool is_written) const {
1846 const u32 size = gpu_memory.Read<u32>(ssbo_addr + 8); 1846 const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr);
1847 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 1847 const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8);
1848 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1848 if (!cpu_addr || size == 0) { 1849 if (!cpu_addr || size == 0) {
1849 return NULL_BINDING; 1850 return NULL_BINDING;
1850 } 1851 }
1852 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
1851 const Binding binding{ 1853 const Binding binding{
1852 .cpu_addr = *cpu_addr, 1854 .cpu_addr = *cpu_addr,
1853 .size = size, 1855 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr),
1854 .buffer_id = BufferId{}, 1856 .buffer_id = BufferId{},
1855 }; 1857 };
1856 return binding; 1858 return binding;
@@ -1859,7 +1861,7 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
1859template <class P> 1861template <class P>
1860typename BufferCache<P>::TextureBufferBinding BufferCache<P>::GetTextureBufferBinding( 1862typename BufferCache<P>::TextureBufferBinding BufferCache<P>::GetTextureBufferBinding(
1861 GPUVAddr gpu_addr, u32 size, PixelFormat format) { 1863 GPUVAddr gpu_addr, u32 size, PixelFormat format) {
1862 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 1864 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1863 TextureBufferBinding binding; 1865 TextureBufferBinding binding;
1864 if (!cpu_addr || size == 0) { 1866 if (!cpu_addr || size == 0) {
1865 binding.cpu_addr = 0; 1867 binding.cpu_addr = 0;
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index 8e890a85e..28a2d2090 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -2,20 +2,22 @@
2// SPDX-License-Identifier: MIT 2// SPDX-License-Identifier: MIT
3 3
4#include <bit> 4#include <bit>
5#include "command_classes/host1x.h"
6#include "command_classes/nvdec.h"
7#include "command_classes/vic.h"
8#include "video_core/cdma_pusher.h" 5#include "video_core/cdma_pusher.h"
9#include "video_core/command_classes/sync_manager.h"
10#include "video_core/engines/maxwell_3d.h" 6#include "video_core/engines/maxwell_3d.h"
11#include "video_core/gpu.h" 7#include "video_core/host1x/control.h"
8#include "video_core/host1x/host1x.h"
9#include "video_core/host1x/nvdec.h"
10#include "video_core/host1x/nvdec_common.h"
11#include "video_core/host1x/sync_manager.h"
12#include "video_core/host1x/vic.h"
13#include "video_core/memory_manager.h"
12 14
13namespace Tegra { 15namespace Tegra {
14CDmaPusher::CDmaPusher(GPU& gpu_) 16CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_)
15 : gpu{gpu_}, nvdec_processor(std::make_shared<Nvdec>(gpu)), 17 : host1x{host1x_}, nvdec_processor(std::make_shared<Host1x::Nvdec>(host1x)),
16 vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)), 18 vic_processor(std::make_unique<Host1x::Vic>(host1x, nvdec_processor)),
17 host1x_processor(std::make_unique<Host1x>(gpu)), 19 host1x_processor(std::make_unique<Host1x::Control>(host1x)),
18 sync_manager(std::make_unique<SyncptIncrManager>(gpu)) {} 20 sync_manager(std::make_unique<Host1x::SyncptIncrManager>(host1x)) {}
19 21
20CDmaPusher::~CDmaPusher() = default; 22CDmaPusher::~CDmaPusher() = default;
21 23
@@ -109,16 +111,17 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
109 case ThiMethod::SetMethod1: 111 case ThiMethod::SetMethod1:
110 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", 112 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
111 static_cast<u32>(vic_thi_state.method_0), data); 113 static_cast<u32>(vic_thi_state.method_0), data);
112 vic_processor->ProcessMethod(static_cast<Vic::Method>(vic_thi_state.method_0), data); 114 vic_processor->ProcessMethod(static_cast<Host1x::Vic::Method>(vic_thi_state.method_0),
115 data);
113 break; 116 break;
114 default: 117 default:
115 break; 118 break;
116 } 119 }
117 break; 120 break;
118 case ChClassId::Host1x: 121 case ChClassId::Control:
119 // This device is mainly for syncpoint synchronization 122 // This device is mainly for syncpoint synchronization
120 LOG_DEBUG(Service_NVDRV, "Host1X Class Method"); 123 LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
121 host1x_processor->ProcessMethod(static_cast<Host1x::Method>(offset), data); 124 host1x_processor->ProcessMethod(static_cast<Host1x::Control::Method>(offset), data);
122 break; 125 break;
123 default: 126 default:
124 UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class)); 127 UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index d6ffef95f..83112dfce 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -12,11 +12,13 @@
12 12
13namespace Tegra { 13namespace Tegra {
14 14
15class GPU; 15namespace Host1x {
16class Control;
16class Host1x; 17class Host1x;
17class Nvdec; 18class Nvdec;
18class SyncptIncrManager; 19class SyncptIncrManager;
19class Vic; 20class Vic;
21} // namespace Host1x
20 22
21enum class ChSubmissionMode : u32 { 23enum class ChSubmissionMode : u32 {
22 SetClass = 0, 24 SetClass = 0,
@@ -30,7 +32,7 @@ enum class ChSubmissionMode : u32 {
30 32
31enum class ChClassId : u32 { 33enum class ChClassId : u32 {
32 NoClass = 0x0, 34 NoClass = 0x0,
33 Host1x = 0x1, 35 Control = 0x1,
34 VideoEncodeMpeg = 0x20, 36 VideoEncodeMpeg = 0x20,
35 VideoEncodeNvEnc = 0x21, 37 VideoEncodeNvEnc = 0x21,
36 VideoStreamingVi = 0x30, 38 VideoStreamingVi = 0x30,
@@ -88,7 +90,7 @@ enum class ThiMethod : u32 {
88 90
89class CDmaPusher { 91class CDmaPusher {
90public: 92public:
91 explicit CDmaPusher(GPU& gpu_); 93 explicit CDmaPusher(Host1x::Host1x& host1x);
92 ~CDmaPusher(); 94 ~CDmaPusher();
93 95
94 /// Process the command entry 96 /// Process the command entry
@@ -101,11 +103,11 @@ private:
101 /// Write arguments value to the ThiRegisters member at the specified offset 103 /// Write arguments value to the ThiRegisters member at the specified offset
102 void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument); 104 void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument);
103 105
104 GPU& gpu; 106 Host1x::Host1x& host1x;
105 std::shared_ptr<Tegra::Nvdec> nvdec_processor; 107 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
106 std::unique_ptr<Tegra::Vic> vic_processor; 108 std::unique_ptr<Tegra::Host1x::Vic> vic_processor;
107 std::unique_ptr<Tegra::Host1x> host1x_processor; 109 std::unique_ptr<Tegra::Host1x::Control> host1x_processor;
108 std::unique_ptr<SyncptIncrManager> sync_manager; 110 std::unique_ptr<Host1x::SyncptIncrManager> sync_manager;
109 ChClassId current_class{}; 111 ChClassId current_class{};
110 ThiRegisters vic_thi_state{}; 112 ThiRegisters vic_thi_state{};
111 ThiRegisters nvdec_thi_state{}; 113 ThiRegisters nvdec_thi_state{};
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp
deleted file mode 100644
index 11855fe10..000000000
--- a/src/video_core/command_classes/host1x.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "video_core/command_classes/host1x.h"
6#include "video_core/gpu.h"
7
8Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {}
9
10Tegra::Host1x::~Host1x() = default;
11
12void Tegra::Host1x::ProcessMethod(Method method, u32 argument) {
13 switch (method) {
14 case Method::LoadSyncptPayload32:
15 syncpoint_value = argument;
16 break;
17 case Method::WaitSyncpt:
18 case Method::WaitSyncpt32:
19 Execute(argument);
20 break;
21 default:
22 UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method));
23 break;
24 }
25}
26
27void Tegra::Host1x::Execute(u32 data) {
28 gpu.WaitFence(data, syncpoint_value);
29}
diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp
new file mode 100644
index 000000000..cdecc3a91
--- /dev/null
+++ b/src/video_core/control/channel_state.cpp
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/assert.h"
5#include "video_core/control/channel_state.h"
6#include "video_core/dma_pusher.h"
7#include "video_core/engines/fermi_2d.h"
8#include "video_core/engines/kepler_compute.h"
9#include "video_core/engines/kepler_memory.h"
10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/engines/maxwell_dma.h"
12#include "video_core/engines/puller.h"
13#include "video_core/memory_manager.h"
14
15namespace Tegra::Control {
16
17ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {}
18
19void ChannelState::Init(Core::System& system, GPU& gpu) {
20 ASSERT(memory_manager);
21 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, gpu, *memory_manager, *this);
22 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, *memory_manager);
23 fermi_2d = std::make_unique<Engines::Fermi2D>();
24 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, *memory_manager);
25 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
26 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
27 initialized = true;
28}
29
30void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
31 dma_pusher->BindRasterizer(rasterizer);
32 memory_manager->BindRasterizer(rasterizer);
33 maxwell_3d->BindRasterizer(rasterizer);
34 fermi_2d->BindRasterizer(rasterizer);
35 kepler_memory->BindRasterizer(rasterizer);
36 kepler_compute->BindRasterizer(rasterizer);
37 maxwell_dma->BindRasterizer(rasterizer);
38}
39
40} // namespace Tegra::Control
diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h
new file mode 100644
index 000000000..3a7b9872c
--- /dev/null
+++ b/src/video_core/control/channel_state.h
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace VideoCore {
15class RasterizerInterface;
16}
17
18namespace Tegra {
19
20class GPU;
21
22namespace Engines {
23class Puller;
24class Fermi2D;
25class Maxwell3D;
26class MaxwellDMA;
27class KeplerCompute;
28class KeplerMemory;
29} // namespace Engines
30
31class MemoryManager;
32class DmaPusher;
33
34namespace Control {
35
36struct ChannelState {
37 explicit ChannelState(s32 bind_id);
38 ChannelState(const ChannelState& state) = delete;
39 ChannelState& operator=(const ChannelState&) = delete;
40 ChannelState(ChannelState&& other) noexcept = default;
41 ChannelState& operator=(ChannelState&& other) noexcept = default;
42
43 void Init(Core::System& system, GPU& gpu);
44
45 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
46
47 s32 bind_id = -1;
48 /// 3D engine
49 std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
50 /// 2D engine
51 std::unique_ptr<Engines::Fermi2D> fermi_2d;
52 /// Compute engine
53 std::unique_ptr<Engines::KeplerCompute> kepler_compute;
54 /// DMA engine
55 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
56 /// Inline memory engine
57 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
58
59 std::shared_ptr<MemoryManager> memory_manager;
60
61 std::unique_ptr<DmaPusher> dma_pusher;
62
63 bool initialized{};
64};
65
66} // namespace Control
67
68} // namespace Tegra
diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp
new file mode 100644
index 000000000..4ebeb6356
--- /dev/null
+++ b/src/video_core/control/channel_state_cache.cpp
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "video_core/control/channel_state_cache.inc"
5
6namespace VideoCommon {
7
8ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
9 : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
10 gpu_memory{*channel_state.memory_manager} {}
11
12template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
13
14} // namespace VideoCommon
diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h
new file mode 100644
index 000000000..584a0c26c
--- /dev/null
+++ b/src/video_core/control/channel_state_cache.h
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <deque>
7#include <limits>
8#include <mutex>
9#include <optional>
10#include <unordered_map>
11#include <vector>
12
13#include "common/common_types.h"
14
15namespace Tegra {
16
17namespace Engines {
18class Maxwell3D;
19class KeplerCompute;
20} // namespace Engines
21
22class MemoryManager;
23
24namespace Control {
25struct ChannelState;
26}
27
28} // namespace Tegra
29
30namespace VideoCommon {
31
32class ChannelInfo {
33public:
34 ChannelInfo() = delete;
35 explicit ChannelInfo(Tegra::Control::ChannelState& state);
36 ChannelInfo(const ChannelInfo& state) = delete;
37 ChannelInfo& operator=(const ChannelInfo&) = delete;
38 ChannelInfo(ChannelInfo&& other) = default;
39 ChannelInfo& operator=(ChannelInfo&& other) = default;
40
41 Tegra::Engines::Maxwell3D& maxwell3d;
42 Tegra::Engines::KeplerCompute& kepler_compute;
43 Tegra::MemoryManager& gpu_memory;
44};
45
46template <class P>
47class ChannelSetupCaches {
48public:
49 /// Operations for seting the channel of execution.
50 virtual ~ChannelSetupCaches();
51
52 /// Create channel state.
53 virtual void CreateChannel(Tegra::Control::ChannelState& channel);
54
55 /// Bind a channel for execution.
56 void BindToChannel(s32 id);
57
58 /// Erase channel's state.
59 void EraseChannel(s32 id);
60
61 Tegra::MemoryManager* GetFromID(size_t id) const {
62 std::unique_lock<std::mutex> lk(config_mutex);
63 const auto ref = address_spaces.find(id);
64 return ref->second.gpu_memory;
65 }
66
67 std::optional<size_t> getStorageID(size_t id) const {
68 std::unique_lock<std::mutex> lk(config_mutex);
69 const auto ref = address_spaces.find(id);
70 if (ref == address_spaces.end()) {
71 return std::nullopt;
72 }
73 return ref->second.storage_id;
74 }
75
76protected:
77 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()};
78
79 P* channel_state;
80 size_t current_channel_id{UNSET_CHANNEL};
81 size_t current_address_space{};
82 Tegra::Engines::Maxwell3D* maxwell3d;
83 Tegra::Engines::KeplerCompute* kepler_compute;
84 Tegra::MemoryManager* gpu_memory;
85
86 std::deque<P> channel_storage;
87 std::deque<size_t> free_channel_ids;
88 std::unordered_map<s32, size_t> channel_map;
89 std::vector<size_t> active_channel_ids;
90 struct AddresSpaceRef {
91 size_t ref_count;
92 size_t storage_id;
93 Tegra::MemoryManager* gpu_memory;
94 };
95 std::unordered_map<size_t, AddresSpaceRef> address_spaces;
96 mutable std::mutex config_mutex;
97
98 virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {}
99};
100
101} // namespace VideoCommon
diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc
new file mode 100644
index 000000000..460313893
--- /dev/null
+++ b/src/video_core/control/channel_state_cache.inc
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <algorithm>
5
6#include "video_core/control/channel_state.h"
7#include "video_core/control/channel_state_cache.h"
8#include "video_core/engines/kepler_compute.h"
9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/memory_manager.h"
11
12namespace VideoCommon {
13
14template <class P>
15ChannelSetupCaches<P>::~ChannelSetupCaches() = default;
16
17template <class P>
18void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
19 std::unique_lock<std::mutex> lk(config_mutex);
20 ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0);
21 auto new_id = [this, &channel]() {
22 if (!free_channel_ids.empty()) {
23 auto id = free_channel_ids.front();
24 free_channel_ids.pop_front();
25 new (&channel_storage[id]) P(channel);
26 return id;
27 }
28 channel_storage.emplace_back(channel);
29 return channel_storage.size() - 1;
30 }();
31 channel_map.emplace(channel.bind_id, new_id);
32 if (current_channel_id != UNSET_CHANNEL) {
33 channel_state = &channel_storage[current_channel_id];
34 }
35 active_channel_ids.push_back(new_id);
36 auto as_it = address_spaces.find(channel.memory_manager->GetID());
37 if (as_it != address_spaces.end()) {
38 as_it->second.ref_count++;
39 return;
40 }
41 AddresSpaceRef new_gpu_mem_ref{
42 .ref_count = 1,
43 .storage_id = address_spaces.size(),
44 .gpu_memory = channel.memory_manager.get(),
45 };
46 address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
47 OnGPUASRegister(channel.memory_manager->GetID());
48}
49
50/// Bind a channel for execution.
51template <class P>
52void ChannelSetupCaches<P>::BindToChannel(s32 id) {
53 std::unique_lock<std::mutex> lk(config_mutex);
54 auto it = channel_map.find(id);
55 ASSERT(it != channel_map.end() && id >= 0);
56 current_channel_id = it->second;
57 channel_state = &channel_storage[current_channel_id];
58 maxwell3d = &channel_state->maxwell3d;
59 kepler_compute = &channel_state->kepler_compute;
60 gpu_memory = &channel_state->gpu_memory;
61 current_address_space = gpu_memory->GetID();
62}
63
64/// Erase channel's channel_state.
65template <class P>
66void ChannelSetupCaches<P>::EraseChannel(s32 id) {
67 std::unique_lock<std::mutex> lk(config_mutex);
68 const auto it = channel_map.find(id);
69 ASSERT(it != channel_map.end() && id >= 0);
70 const auto this_id = it->second;
71 free_channel_ids.push_back(this_id);
72 channel_map.erase(it);
73 if (this_id == current_channel_id) {
74 current_channel_id = UNSET_CHANNEL;
75 channel_state = nullptr;
76 maxwell3d = nullptr;
77 kepler_compute = nullptr;
78 gpu_memory = nullptr;
79 } else if (current_channel_id != UNSET_CHANNEL) {
80 channel_state = &channel_storage[current_channel_id];
81 }
82 active_channel_ids.erase(
83 std::find(active_channel_ids.begin(), active_channel_ids.end(), this_id));
84}
85
86} // namespace VideoCommon
diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp
new file mode 100644
index 000000000..f7cbe204e
--- /dev/null
+++ b/src/video_core/control/scheduler.cpp
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "common/assert.h"
7#include "video_core/control/channel_state.h"
8#include "video_core/control/scheduler.h"
9#include "video_core/gpu.h"
10
11namespace Tegra::Control {
12Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
13
14Scheduler::~Scheduler() = default;
15
16void Scheduler::Push(s32 channel, CommandList&& entries) {
17 std::unique_lock lk(scheduling_guard);
18 auto it = channels.find(channel);
19 ASSERT(it != channels.end());
20 auto channel_state = it->second;
21 gpu.BindChannel(channel_state->bind_id);
22 channel_state->dma_pusher->Push(std::move(entries));
23 channel_state->dma_pusher->DispatchCalls();
24}
25
26void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
27 s32 channel = new_channel->bind_id;
28 std::unique_lock lk(scheduling_guard);
29 channels.emplace(channel, new_channel);
30}
31
32} // namespace Tegra::Control
diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h
new file mode 100644
index 000000000..44addf61c
--- /dev/null
+++ b/src/video_core/control/scheduler.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <unordered_map>
9
10#include "video_core/dma_pusher.h"
11
12namespace Tegra {
13
14class GPU;
15
16namespace Control {
17
18struct ChannelState;
19
20class Scheduler {
21public:
22 explicit Scheduler(GPU& gpu_);
23 ~Scheduler();
24
25 void Push(s32 channel, CommandList&& entries);
26
27 void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
28
29private:
30 std::unordered_map<s32, std::shared_ptr<ChannelState>> channels;
31 std::mutex scheduling_guard;
32 GPU& gpu;
33};
34
35} // namespace Control
36
37} // namespace Tegra
diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp
index 9dc4341f0..c2ecc12f5 100644
--- a/src/video_core/dirty_flags.cpp
+++ b/src/video_core/dirty_flags.cpp
@@ -17,21 +17,23 @@ using Tegra::Engines::Maxwell3D;
17void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) { 17void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) {
18 static constexpr std::size_t num_array = 3; 18 static constexpr std::size_t num_array = 3;
19 for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) { 19 for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) {
20 const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); 20 const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]);
21 const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]); 21 const std::size_t limit_offset =
22 OFF(vertex_stream_limits) + i * NUM(vertex_stream_limits[0]);
22 23
23 FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers); 24 FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
24 FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers); 25 FillBlock(tables, limit_offset, NUM(vertex_stream_limits), VertexBuffer0 + i,
26 VertexBuffers);
25 } 27 }
26} 28}
27 29
28void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) { 30void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) {
29 FillBlock(tables[0], OFF(index_array), NUM(index_array), IndexBuffer); 31 FillBlock(tables[0], OFF(index_buffer), NUM(index_buffer), IndexBuffer);
30} 32}
31 33
32void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) { 34void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) {
33 FillBlock(tables[0], OFF(tic), NUM(tic), Descriptors); 35 FillBlock(tables[0], OFF(tex_header), NUM(tex_header), Descriptors);
34 FillBlock(tables[0], OFF(tsc), NUM(tsc), Descriptors); 36 FillBlock(tables[0], OFF(tex_sampler), NUM(tex_sampler), Descriptors);
35} 37}
36 38
37void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { 39void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
@@ -42,7 +44,7 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
42 FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt); 44 FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
43 } 45 }
44 FillBlock(tables[1], begin, num, RenderTargets); 46 FillBlock(tables[1], begin, num, RenderTargets);
45 FillBlock(tables[0], OFF(render_area), NUM(render_area), RenderTargets); 47 FillBlock(tables[0], OFF(surface_clip), NUM(surface_clip), RenderTargets);
46 48
47 tables[0][OFF(rt_control)] = RenderTargets; 49 tables[0][OFF(rt_control)] = RenderTargets;
48 tables[1][OFF(rt_control)] = RenderTargetControl; 50 tables[1][OFF(rt_control)] = RenderTargetControl;
@@ -52,15 +54,15 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
52 const u8 flag = zeta_flags[i]; 54 const u8 flag = zeta_flags[i];
53 auto& table = tables[i]; 55 auto& table = tables[i];
54 table[OFF(zeta_enable)] = flag; 56 table[OFF(zeta_enable)] = flag;
55 table[OFF(zeta_width)] = flag; 57 table[OFF(zeta_size.width)] = flag;
56 table[OFF(zeta_height)] = flag; 58 table[OFF(zeta_size.height)] = flag;
57 FillBlock(table, OFF(zeta), NUM(zeta), flag); 59 FillBlock(table, OFF(zeta), NUM(zeta), flag);
58 } 60 }
59} 61}
60 62
61void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) { 63void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) {
62 FillBlock(tables[0], OFF(shader_config[0]), 64 FillBlock(tables[0], OFF(pipelines), NUM(pipelines[0]) * Maxwell3D::Regs::MaxShaderProgram,
63 NUM(shader_config[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders); 65 Shaders);
64} 66}
65} // Anonymous namespace 67} // Anonymous namespace
66 68
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 29b8582ab..9835e3ac1 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -12,7 +12,10 @@
12 12
13namespace Tegra { 13namespace Tegra {
14 14
15DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_) : gpu{gpu_}, system{system_} {} 15DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
16 Control::ChannelState& channel_state_)
17 : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_,
18 *this, channel_state_} {}
16 19
17DmaPusher::~DmaPusher() = default; 20DmaPusher::~DmaPusher() = default;
18 21
@@ -21,8 +24,6 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128,
21void DmaPusher::DispatchCalls() { 24void DmaPusher::DispatchCalls() {
22 MICROPROFILE_SCOPE(DispatchCalls); 25 MICROPROFILE_SCOPE(DispatchCalls);
23 26
24 gpu.SyncGuestHost();
25
26 dma_pushbuffer_subindex = 0; 27 dma_pushbuffer_subindex = 0;
27 28
28 dma_state.is_last_call = true; 29 dma_state.is_last_call = true;
@@ -33,7 +34,6 @@ void DmaPusher::DispatchCalls() {
33 } 34 }
34 } 35 }
35 gpu.FlushCommands(); 36 gpu.FlushCommands();
36 gpu.SyncGuestHost();
37 gpu.OnCommandListEnd(); 37 gpu.OnCommandListEnd();
38} 38}
39 39
@@ -76,11 +76,11 @@ bool DmaPusher::Step() {
76 // Push buffer non-empty, read a word 76 // Push buffer non-empty, read a word
77 command_headers.resize(command_list_header.size); 77 command_headers.resize(command_list_header.size);
78 if (Settings::IsGPULevelHigh()) { 78 if (Settings::IsGPULevelHigh()) {
79 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), 79 memory_manager.ReadBlock(dma_get, command_headers.data(),
80 command_list_header.size * sizeof(u32)); 80 command_list_header.size * sizeof(u32));
81 } else { 81 } else {
82 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(),
83 command_list_header.size * sizeof(u32)); 83 command_list_header.size * sizeof(u32));
84 } 84 }
85 } 85 }
86 for (std::size_t index = 0; index < command_headers.size();) { 86 for (std::size_t index = 0; index < command_headers.size();) {
@@ -154,7 +154,7 @@ void DmaPusher::SetState(const CommandHeader& command_header) {
154 154
155void DmaPusher::CallMethod(u32 argument) const { 155void DmaPusher::CallMethod(u32 argument) const {
156 if (dma_state.method < non_puller_methods) { 156 if (dma_state.method < non_puller_methods) {
157 gpu.CallMethod(GPU::MethodCall{ 157 puller.CallPullerMethod(Engines::Puller::MethodCall{
158 dma_state.method, 158 dma_state.method,
159 argument, 159 argument,
160 dma_state.subchannel, 160 dma_state.subchannel,
@@ -168,12 +168,16 @@ void DmaPusher::CallMethod(u32 argument) const {
168 168
169void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { 169void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
170 if (dma_state.method < non_puller_methods) { 170 if (dma_state.method < non_puller_methods) {
171 gpu.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, 171 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
172 dma_state.method_count); 172 dma_state.method_count);
173 } else { 173 } else {
174 subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, 174 subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start,
175 num_methods, dma_state.method_count); 175 num_methods, dma_state.method_count);
176 } 176 }
177} 177}
178 178
179void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
180 puller.BindRasterizer(rasterizer);
181}
182
179} // namespace Tegra 183} // namespace Tegra
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 872fd146a..938f0f11c 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -10,6 +10,7 @@
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/engine_interface.h" 12#include "video_core/engines/engine_interface.h"
13#include "video_core/engines/puller.h"
13 14
14namespace Core { 15namespace Core {
15class System; 16class System;
@@ -17,7 +18,12 @@ class System;
17 18
18namespace Tegra { 19namespace Tegra {
19 20
21namespace Control {
22struct ChannelState;
23}
24
20class GPU; 25class GPU;
26class MemoryManager;
21 27
22enum class SubmissionMode : u32 { 28enum class SubmissionMode : u32 {
23 IncreasingOld = 0, 29 IncreasingOld = 0,
@@ -31,24 +37,32 @@ enum class SubmissionMode : u32 {
31// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence 37// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
32// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. 38// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
33// So the values you see in docs might be multiplied by 4. 39// So the values you see in docs might be multiplied by 4.
40// Register documentation:
41// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/cla26f.h
42//
43// Register Description (approx):
44// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/manuals/volta/gv100/dev_pbdma.ref.txt
34enum class BufferMethods : u32 { 45enum class BufferMethods : u32 {
35 BindObject = 0x0, 46 BindObject = 0x0,
47 Illegal = 0x1,
36 Nop = 0x2, 48 Nop = 0x2,
37 SemaphoreAddressHigh = 0x4, 49 SemaphoreAddressHigh = 0x4,
38 SemaphoreAddressLow = 0x5, 50 SemaphoreAddressLow = 0x5,
39 SemaphoreSequence = 0x6, 51 SemaphoreSequencePayload = 0x6,
40 SemaphoreTrigger = 0x7, 52 SemaphoreOperation = 0x7,
41 NotifyIntr = 0x8, 53 NonStallInterrupt = 0x8,
42 WrcacheFlush = 0x9, 54 WrcacheFlush = 0x9,
43 Unk28 = 0xA, 55 MemOpA = 0xA,
44 UnkCacheFlush = 0xB, 56 MemOpB = 0xB,
57 MemOpC = 0xC,
58 MemOpD = 0xD,
45 RefCnt = 0x14, 59 RefCnt = 0x14,
46 SemaphoreAcquire = 0x1A, 60 SemaphoreAcquire = 0x1A,
47 SemaphoreRelease = 0x1B, 61 SemaphoreRelease = 0x1B,
48 FenceValue = 0x1C, 62 SyncpointPayload = 0x1C,
49 FenceAction = 0x1D, 63 SyncpointOperation = 0x1D,
50 WaitForInterrupt = 0x1E, 64 WaitForIdle = 0x1E,
51 Unk7c = 0x1F, 65 CRCCheck = 0x1F,
52 Yield = 0x20, 66 Yield = 0x20,
53 NonPullerMethods = 0x40, 67 NonPullerMethods = 0x40,
54}; 68};
@@ -102,7 +116,8 @@ struct CommandList final {
102 */ 116 */
103class DmaPusher final { 117class DmaPusher final {
104public: 118public:
105 explicit DmaPusher(Core::System& system_, GPU& gpu_); 119 explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
120 Control::ChannelState& channel_state_);
106 ~DmaPusher(); 121 ~DmaPusher();
107 122
108 void Push(CommandList&& entries) { 123 void Push(CommandList&& entries) {
@@ -115,6 +130,8 @@ public:
115 subchannels[subchannel_id] = engine; 130 subchannels[subchannel_id] = engine;
116 } 131 }
117 132
133 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
134
118private: 135private:
119 static constexpr u32 non_puller_methods = 0x40; 136 static constexpr u32 non_puller_methods = 0x40;
120 static constexpr u32 max_subchannels = 8; 137 static constexpr u32 max_subchannels = 8;
@@ -148,6 +165,8 @@ private:
148 165
149 GPU& gpu; 166 GPU& gpu;
150 Core::System& system; 167 Core::System& system;
168 MemoryManager& memory_manager;
169 mutable Engines::Puller puller;
151}; 170};
152 171
153} // namespace Tegra 172} // namespace Tegra
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index 6ff5b1eca..a34819234 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cstring> 4#include <cstring>
5 5
6#include "common/algorithm.h"
6#include "common/assert.h" 7#include "common/assert.h"
7#include "video_core/engines/engine_upload.h" 8#include "video_core/engines/engine_upload.h"
8#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
@@ -34,21 +35,48 @@ void State::ProcessData(const u32 data, const bool is_last_call) {
34 if (!is_last_call) { 35 if (!is_last_call) {
35 return; 36 return;
36 } 37 }
38 ProcessData(inner_buffer);
39}
40
41void State::ProcessData(const u32* data, size_t num_data) {
42 std::span<const u8> read_buffer(reinterpret_cast<const u8*>(data), num_data * sizeof(u32));
43 ProcessData(read_buffer);
44}
45
46void State::ProcessData(std::span<const u8> read_buffer) {
37 const GPUVAddr address{regs.dest.Address()}; 47 const GPUVAddr address{regs.dest.Address()};
38 if (is_linear) { 48 if (is_linear) {
39 rasterizer->AccelerateInlineToMemory(address, copy_size, inner_buffer); 49 if (regs.line_count == 1) {
50 rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer);
51 } else {
52 for (u32 line = 0; line < regs.line_count; ++line) {
53 const GPUVAddr dest_line = address + static_cast<size_t>(line) * regs.dest.pitch;
54 memory_manager.WriteBlockUnsafe(
55 dest_line, read_buffer.data() + static_cast<size_t>(line) * regs.line_length_in,
56 regs.line_length_in);
57 }
58 memory_manager.InvalidateRegion(address, regs.dest.pitch * regs.line_count);
59 }
40 } else { 60 } else {
41 UNIMPLEMENTED_IF(regs.dest.z != 0); 61 u32 width = regs.dest.width;
42 UNIMPLEMENTED_IF(regs.dest.depth != 1); 62 u32 x_elements = regs.line_length_in;
43 UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 0); 63 u32 x_offset = regs.dest.x;
44 UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 0); 64 const u32 bpp_shift = Common::FoldRight(
65 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
66 width, x_elements, x_offset, static_cast<u32>(address));
67 width >>= bpp_shift;
68 x_elements >>= bpp_shift;
69 x_offset >>= bpp_shift;
70 const u32 bytes_per_pixel = 1U << bpp_shift;
45 const std::size_t dst_size = Tegra::Texture::CalculateSize( 71 const std::size_t dst_size = Tegra::Texture::CalculateSize(
46 true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 0); 72 true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
73 regs.dest.BlockHeight(), regs.dest.BlockDepth());
47 tmp_buffer.resize(dst_size); 74 tmp_buffer.resize(dst_size);
48 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); 75 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
49 Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y, 76 Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width,
50 regs.dest.BlockHeight(), copy_size, inner_buffer.data(), 77 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
51 tmp_buffer.data()); 78 x_elements, regs.line_count, regs.dest.BlockHeight(),
79 regs.dest.BlockDepth(), regs.line_length_in);
52 memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); 80 memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size);
53 } 81 }
54} 82}
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index 94ff3314a..f08f6e36a 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
6#include <vector> 7#include <vector>
7#include "common/bit_field.h" 8#include "common/bit_field.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
@@ -33,7 +34,7 @@ struct Registers {
33 u32 width; 34 u32 width;
34 u32 height; 35 u32 height;
35 u32 depth; 36 u32 depth;
36 u32 z; 37 u32 layer;
37 u32 x; 38 u32 x;
38 u32 y; 39 u32 y;
39 40
@@ -62,11 +63,14 @@ public:
62 63
63 void ProcessExec(bool is_linear_); 64 void ProcessExec(bool is_linear_);
64 void ProcessData(u32 data, bool is_last_call); 65 void ProcessData(u32 data, bool is_last_call);
66 void ProcessData(const u32* data, size_t num_data);
65 67
66 /// Binds a rasterizer to this engine. 68 /// Binds a rasterizer to this engine.
67 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 69 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
68 70
69private: 71private:
72 void ProcessData(std::span<const u8> read_buffer);
73
70 u32 write_offset = 0; 74 u32 write_offset = 0;
71 u32 copy_size = 0; 75 u32 copy_size = 0;
72 std::vector<u8> inner_buffer; 76 std::vector<u8> inner_buffer;
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 5db254d94..7c50bdbe0 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -36,8 +36,6 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
36 } 36 }
37 case KEPLER_COMPUTE_REG_INDEX(data_upload): { 37 case KEPLER_COMPUTE_REG_INDEX(data_upload): {
38 upload_state.ProcessData(method_argument, is_last_call); 38 upload_state.ProcessData(method_argument, is_last_call);
39 if (is_last_call) {
40 }
41 break; 39 break;
42 } 40 }
43 case KEPLER_COMPUTE_REG_INDEX(launch): 41 case KEPLER_COMPUTE_REG_INDEX(launch):
@@ -50,8 +48,15 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
50 48
51void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 49void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
52 u32 methods_pending) { 50 u32 methods_pending) {
53 for (std::size_t i = 0; i < amount; i++) { 51 switch (method) {
54 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 52 case KEPLER_COMPUTE_REG_INDEX(data_upload):
53 upload_state.ProcessData(base_start, static_cast<size_t>(amount));
54 return;
55 default:
56 for (std::size_t i = 0; i < amount; i++) {
57 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
58 }
59 break;
55 } 60 }
56} 61}
57 62
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index e2b029542..a3fbab1e5 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -33,8 +33,6 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call
33 } 33 }
34 case KEPLERMEMORY_REG_INDEX(data): { 34 case KEPLERMEMORY_REG_INDEX(data): {
35 upload_state.ProcessData(method_argument, is_last_call); 35 upload_state.ProcessData(method_argument, is_last_call);
36 if (is_last_call) {
37 }
38 break; 36 break;
39 } 37 }
40 } 38 }
@@ -42,8 +40,15 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call
42 40
43void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 41void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
44 u32 methods_pending) { 42 u32 methods_pending) {
45 for (std::size_t i = 0; i < amount; i++) { 43 switch (method) {
46 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 44 case KEPLERMEMORY_REG_INDEX(data):
45 upload_state.ProcessData(base_start, static_cast<size_t>(amount));
46 return;
47 default:
48 for (std::size_t i = 0; i < amount; i++) {
49 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
50 }
51 break;
47 } 52 }
48} 53}
49 54
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 3a4646289..f9794dfe4 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -56,37 +56,37 @@ void Maxwell3D::InitializeRegisterDefaults() {
56 56
57 // Doom and Bomberman seems to use the uninitialized registers and just enable blend 57 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
58 // so initialize blend registers with sane values 58 // so initialize blend registers with sane values
59 regs.blend.equation_rgb = Regs::Blend::Equation::Add; 59 regs.blend.color_op = Regs::Blend::Equation::Add_D3D;
60 regs.blend.factor_source_rgb = Regs::Blend::Factor::One; 60 regs.blend.color_source = Regs::Blend::Factor::One_D3D;
61 regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero; 61 regs.blend.color_dest = Regs::Blend::Factor::Zero_D3D;
62 regs.blend.equation_a = Regs::Blend::Equation::Add; 62 regs.blend.alpha_op = Regs::Blend::Equation::Add_D3D;
63 regs.blend.factor_source_a = Regs::Blend::Factor::One; 63 regs.blend.alpha_source = Regs::Blend::Factor::One_D3D;
64 regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; 64 regs.blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
65 for (auto& blend : regs.independent_blend) { 65 for (auto& blend : regs.blend_per_target) {
66 blend.equation_rgb = Regs::Blend::Equation::Add; 66 blend.color_op = Regs::Blend::Equation::Add_D3D;
67 blend.factor_source_rgb = Regs::Blend::Factor::One; 67 blend.color_source = Regs::Blend::Factor::One_D3D;
68 blend.factor_dest_rgb = Regs::Blend::Factor::Zero; 68 blend.color_dest = Regs::Blend::Factor::Zero_D3D;
69 blend.equation_a = Regs::Blend::Equation::Add; 69 blend.alpha_op = Regs::Blend::Equation::Add_D3D;
70 blend.factor_source_a = Regs::Blend::Factor::One; 70 blend.alpha_source = Regs::Blend::Factor::One_D3D;
71 blend.factor_dest_a = Regs::Blend::Factor::Zero; 71 blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
72 } 72 }
73 regs.stencil_front_op_fail = Regs::StencilOp::Keep; 73 regs.stencil_front_op.fail = Regs::StencilOp::Op::Keep_D3D;
74 regs.stencil_front_op_zfail = Regs::StencilOp::Keep; 74 regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D;
75 regs.stencil_front_op_zpass = Regs::StencilOp::Keep; 75 regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D;
76 regs.stencil_front_func_func = Regs::ComparisonOp::Always; 76 regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL;
77 regs.stencil_front_func_mask = 0xFFFFFFFF; 77 regs.stencil_front_func_mask = 0xFFFFFFFF;
78 regs.stencil_front_mask = 0xFFFFFFFF; 78 regs.stencil_front_mask = 0xFFFFFFFF;
79 regs.stencil_two_side_enable = 1; 79 regs.stencil_two_side_enable = 1;
80 regs.stencil_back_op_fail = Regs::StencilOp::Keep; 80 regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D;
81 regs.stencil_back_op_zfail = Regs::StencilOp::Keep; 81 regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D;
82 regs.stencil_back_op_zpass = Regs::StencilOp::Keep; 82 regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D;
83 regs.stencil_back_func_func = Regs::ComparisonOp::Always; 83 regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL;
84 regs.stencil_back_func_mask = 0xFFFFFFFF; 84 regs.stencil_back_func_mask = 0xFFFFFFFF;
85 regs.stencil_back_mask = 0xFFFFFFFF; 85 regs.stencil_back_mask = 0xFFFFFFFF;
86 86
87 regs.depth_test_func = Regs::ComparisonOp::Always; 87 regs.depth_test_func = Regs::ComparisonOp::Always_GL;
88 regs.front_face = Regs::FrontFace::CounterClockWise; 88 regs.gl_front_face = Regs::FrontFace::CounterClockWise;
89 regs.cull_face = Regs::CullFace::Back; 89 regs.gl_cull_face = Regs::CullFace::Back;
90 90
91 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a 91 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
92 // register carrying a default value. Assume it's OpenGL's default (1). 92 // register carrying a default value. Assume it's OpenGL's default (1).
@@ -107,20 +107,25 @@ void Maxwell3D::InitializeRegisterDefaults() {
107 107
108 // NVN games expect these values to be enabled at boot 108 // NVN games expect these values to be enabled at boot
109 regs.rasterize_enable = 1; 109 regs.rasterize_enable = 1;
110 regs.rt_separate_frag_data = 1; 110 regs.color_target_mrt_enable = 1;
111 regs.framebuffer_srgb = 1; 111 regs.framebuffer_srgb = 1;
112 regs.line_width_aliased = 1.0f; 112 regs.line_width_aliased = 1.0f;
113 regs.line_width_smooth = 1.0f; 113 regs.line_width_smooth = 1.0f;
114 regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; 114 regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise;
115 regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill; 115 regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill;
116 regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill; 116 regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
117 117
118 shadow_state = regs; 118 shadow_state = regs;
119 119
120 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; 120 draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
121 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; 121 draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
122 mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; 122 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
123 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; 123 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
124 draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
125 draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
124} 129}
125 130
126void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 131void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -173,77 +178,77 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
173 case MAXWELL3D_REG_INDEX(shadow_ram_control): 178 case MAXWELL3D_REG_INDEX(shadow_ram_control):
174 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument); 179 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
175 return; 180 return;
176 case MAXWELL3D_REG_INDEX(macros.upload_address): 181 case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
177 return macro_engine->ClearCode(regs.macros.upload_address); 182 return macro_engine->ClearCode(regs.load_mme.instruction_ptr);
178 case MAXWELL3D_REG_INDEX(macros.data): 183 case MAXWELL3D_REG_INDEX(load_mme.instruction):
179 return macro_engine->AddCode(regs.macros.upload_address, argument); 184 return macro_engine->AddCode(regs.load_mme.instruction_ptr, argument);
180 case MAXWELL3D_REG_INDEX(macros.bind): 185 case MAXWELL3D_REG_INDEX(load_mme.start_address):
181 return ProcessMacroBind(argument); 186 return ProcessMacroBind(argument);
182 case MAXWELL3D_REG_INDEX(firmware[4]): 187 case MAXWELL3D_REG_INDEX(falcon[4]):
183 return ProcessFirmwareCall4(); 188 return ProcessFirmwareCall4();
184 case MAXWELL3D_REG_INDEX(const_buffer.cb_data): 189 case MAXWELL3D_REG_INDEX(const_buffer.buffer):
185 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: 190 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
186 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: 191 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
187 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: 192 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
188 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: 193 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
189 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: 194 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
190 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: 195 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
191 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: 196 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
192 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: 197 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
193 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: 198 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
194 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: 199 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
195 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: 200 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
196 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: 201 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
197 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: 202 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
198 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: 203 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
199 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: 204 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
200 return ProcessCBData(argument); 205 return ProcessCBData(argument);
201 case MAXWELL3D_REG_INDEX(cb_bind[0]): 206 case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
202 return ProcessCBBind(0); 207 return ProcessCBBind(0);
203 case MAXWELL3D_REG_INDEX(cb_bind[1]): 208 case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
204 return ProcessCBBind(1); 209 return ProcessCBBind(1);
205 case MAXWELL3D_REG_INDEX(cb_bind[2]): 210 case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
206 return ProcessCBBind(2); 211 return ProcessCBBind(2);
207 case MAXWELL3D_REG_INDEX(cb_bind[3]): 212 case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
208 return ProcessCBBind(3); 213 return ProcessCBBind(3);
209 case MAXWELL3D_REG_INDEX(cb_bind[4]): 214 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
210 return ProcessCBBind(4); 215 return ProcessCBBind(4);
211 case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): 216 case MAXWELL3D_REG_INDEX(index_buffer32_first):
212 return DrawArrays(); 217 regs.index_buffer.count = regs.index_buffer32_first.count;
213 case MAXWELL3D_REG_INDEX(small_index): 218 regs.index_buffer.first = regs.index_buffer32_first.first;
214 regs.index_array.count = regs.small_index.count;
215 regs.index_array.first = regs.small_index.first;
216 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 219 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
217 return DrawArrays(); 220 return ProcessDraw();
218 case MAXWELL3D_REG_INDEX(small_index_2): 221 case MAXWELL3D_REG_INDEX(index_buffer16_first):
219 regs.index_array.count = regs.small_index_2.count; 222 regs.index_buffer.count = regs.index_buffer16_first.count;
220 regs.index_array.first = regs.small_index_2.first; 223 regs.index_buffer.first = regs.index_buffer16_first.first;
221 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 224 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
222 return DrawArrays(); 225 return ProcessDraw();
226 case MAXWELL3D_REG_INDEX(index_buffer8_first):
227 regs.index_buffer.count = regs.index_buffer8_first.count;
228 regs.index_buffer.first = regs.index_buffer8_first.first;
229 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
230 return ProcessDraw();
223 case MAXWELL3D_REG_INDEX(topology_override): 231 case MAXWELL3D_REG_INDEX(topology_override):
224 use_topology_override = true; 232 use_topology_override = true;
225 return; 233 return;
226 case MAXWELL3D_REG_INDEX(clear_buffers): 234 case MAXWELL3D_REG_INDEX(clear_surface):
227 return ProcessClearBuffers(); 235 return ProcessClearBuffers();
228 case MAXWELL3D_REG_INDEX(query.query_get): 236 case MAXWELL3D_REG_INDEX(report_semaphore.query):
229 return ProcessQueryGet(); 237 return ProcessQueryGet();
230 case MAXWELL3D_REG_INDEX(condition.mode): 238 case MAXWELL3D_REG_INDEX(render_enable.mode):
231 return ProcessQueryCondition(); 239 return ProcessQueryCondition();
232 case MAXWELL3D_REG_INDEX(counter_reset): 240 case MAXWELL3D_REG_INDEX(clear_report_value):
233 return ProcessCounterReset(); 241 return ProcessCounterReset();
234 case MAXWELL3D_REG_INDEX(sync_info): 242 case MAXWELL3D_REG_INDEX(sync_info):
235 return ProcessSyncPoint(); 243 return ProcessSyncPoint();
236 case MAXWELL3D_REG_INDEX(exec_upload): 244 case MAXWELL3D_REG_INDEX(launch_dma):
237 return upload_state.ProcessExec(regs.exec_upload.linear != 0); 245 return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() ==
238 case MAXWELL3D_REG_INDEX(data_upload): 246 Regs::LaunchDMA::Layout::Pitch);
247 case MAXWELL3D_REG_INDEX(inline_data):
239 upload_state.ProcessData(argument, is_last_call); 248 upload_state.ProcessData(argument, is_last_call);
240 if (is_last_call) {
241 }
242 return; 249 return;
243 case MAXWELL3D_REG_INDEX(fragment_barrier): 250 case MAXWELL3D_REG_INDEX(fragment_barrier):
244 return rasterizer->FragmentBarrier(); 251 return rasterizer->FragmentBarrier();
245 case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
246 return rasterizer->TiledCacheBarrier();
247 } 252 }
248} 253}
249 254
@@ -257,14 +262,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
257 262
258 // Execute the current macro. 263 // Execute the current macro.
259 macro_engine->Execute(macro_positions[entry], parameters); 264 macro_engine->Execute(macro_positions[entry], parameters);
260 if (mme_draw.current_mode != MMEDrawMode::Undefined) { 265
261 FlushMMEInlineDraw(); 266 ProcessDeferredDraw();
262 }
263} 267}
264 268
265void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 269void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
266 // It is an error to write to a register other than the current macro's ARG register before it 270 // It is an error to write to a register other than the current macro's ARG register before
267 // has finished execution. 271 // it has finished execution.
268 if (executing_macro != 0) { 272 if (executing_macro != 0) {
269 ASSERT(method == executing_macro + 1); 273 ASSERT(method == executing_macro + 1);
270 } 274 }
@@ -279,9 +283,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
279 ASSERT_MSG(method < Regs::NUM_REGS, 283 ASSERT_MSG(method < Regs::NUM_REGS,
280 "Invalid Maxwell3D register, increase the size of the Regs structure"); 284 "Invalid Maxwell3D register, increase the size of the Regs structure");
281 285
282 const u32 argument = ProcessShadowRam(method, method_argument); 286 if (draw_command[method]) {
283 ProcessDirtyRegisters(method, argument); 287 regs.reg_array[method] = method_argument;
284 ProcessMethodCall(method, argument, method_argument, is_last_call); 288 deferred_draw_method.push_back(method);
289 auto u32_to_u8 = [&](const u32 argument) {
290 inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
291 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
292 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
293 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
294 };
295 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
296 u32_to_u8(method_argument);
297 } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
298 u32_to_u8(regs.inline_index_2x16.even);
299 u32_to_u8(regs.inline_index_2x16.odd);
300 } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
301 u32_to_u8(regs.inline_index_4x8.index0);
302 u32_to_u8(regs.inline_index_4x8.index1);
303 u32_to_u8(regs.inline_index_4x8.index2);
304 u32_to_u8(regs.inline_index_4x8.index3);
305 }
306 } else {
307 ProcessDeferredDraw();
308
309 const u32 argument = ProcessShadowRam(method, method_argument);
310 ProcessDirtyRegisters(method, argument);
311 ProcessMethodCall(method, argument, method_argument, is_last_call);
312 }
285} 313}
286 314
287void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 315void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -293,24 +321,27 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
293 return; 321 return;
294 } 322 }
295 switch (method) { 323 switch (method) {
296 case MAXWELL3D_REG_INDEX(const_buffer.cb_data): 324 case MAXWELL3D_REG_INDEX(const_buffer.buffer):
297 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: 325 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
298 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: 326 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
299 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: 327 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
300 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: 328 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
301 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: 329 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
302 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: 330 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
303 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: 331 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
304 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: 332 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
305 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: 333 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
306 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: 334 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
307 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: 335 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
308 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: 336 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
309 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: 337 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
310 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: 338 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
311 case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: 339 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
312 ProcessCBMultiData(base_start, amount); 340 ProcessCBMultiData(base_start, amount);
313 break; 341 break;
342 case MAXWELL3D_REG_INDEX(inline_data):
343 upload_state.ProcessData(base_start, static_cast<size_t>(amount));
344 return;
314 default: 345 default:
315 for (std::size_t i = 0; i < amount; i++) { 346 for (std::size_t i = 0; i < amount; i++) {
316 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 347 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
@@ -319,54 +350,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
319 } 350 }
320} 351}
321 352
322void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
323 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
324 if (mme_draw.gl_begin_consume) {
325 mme_draw.current_mode = expected_mode;
326 mme_draw.current_count = count;
327 mme_draw.instance_count = 1;
328 mme_draw.gl_begin_consume = false;
329 mme_draw.gl_end_count = 0;
330 }
331 return;
332 } else {
333 if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
334 mme_draw.instance_mode && mme_draw.gl_begin_consume) {
335 mme_draw.instance_count++;
336 mme_draw.gl_begin_consume = false;
337 return;
338 } else {
339 FlushMMEInlineDraw();
340 }
341 }
342 // Tail call in case it needs to retry.
343 StepInstance(expected_mode, count);
344}
345
346void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
347 if (mme_inline[method]) {
348 regs.reg_array[method] = method_argument;
349 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
350 method == MAXWELL3D_REG_INDEX(index_array.count)) {
351 const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
352 ? MMEDrawMode::Array
353 : MMEDrawMode::Indexed;
354 StepInstance(expected_mode, method_argument);
355 } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) {
356 mme_draw.instance_mode =
357 (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0);
358 mme_draw.gl_begin_consume = true;
359 } else {
360 mme_draw.gl_end_count++;
361 }
362 } else {
363 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
364 FlushMMEInlineDraw();
365 }
366 CallMethod(method, method_argument, true);
367 }
368}
369
370void Maxwell3D::ProcessTopologyOverride() { 353void Maxwell3D::ProcessTopologyOverride() {
371 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; 354 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
372 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; 355 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
@@ -396,46 +379,12 @@ void Maxwell3D::ProcessTopologyOverride() {
396 } 379 }
397} 380}
398 381
399void Maxwell3D::FlushMMEInlineDraw() {
400 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
401 regs.vertex_buffer.count);
402 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
403 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
404
405 // Both instance configuration registers can not be set at the same time.
406 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
407 "Illegal combination of instancing parameters");
408
409 ProcessTopologyOverride();
410
411 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
412 if (ShouldExecute()) {
413 rasterizer->Draw(is_indexed, true);
414 }
415
416 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
417 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
418 // it's possible that it is incorrect and that there is some other register used to specify the
419 // drawing mode.
420 if (is_indexed) {
421 regs.index_array.count = 0;
422 } else {
423 regs.vertex_buffer.count = 0;
424 }
425 mme_draw.current_mode = MMEDrawMode::Undefined;
426 mme_draw.current_count = 0;
427 mme_draw.instance_count = 0;
428 mme_draw.instance_mode = false;
429 mme_draw.gl_begin_consume = false;
430 mme_draw.gl_end_count = 0;
431}
432
433void Maxwell3D::ProcessMacroUpload(u32 data) { 382void Maxwell3D::ProcessMacroUpload(u32 data) {
434 macro_engine->AddCode(regs.macros.upload_address++, data); 383 macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
435} 384}
436 385
437void Maxwell3D::ProcessMacroBind(u32 data) { 386void Maxwell3D::ProcessMacroBind(u32 data) {
438 macro_positions[regs.macros.entry++] = data; 387 macro_positions[regs.load_mme.start_address_ptr++] = data;
439} 388}
440 389
441void Maxwell3D::ProcessFirmwareCall4() { 390void Maxwell3D::ProcessFirmwareCall4() {
@@ -443,22 +392,14 @@ void Maxwell3D::ProcessFirmwareCall4() {
443 392
444 // Firmware call 4 is a blob that changes some registers depending on its parameters. 393 // Firmware call 4 is a blob that changes some registers depending on its parameters.
445 // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. 394 // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
446 regs.reg_array[0xd00] = 1; 395 regs.shadow_scratch[0] = 1;
447} 396}
448 397
449void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { 398void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
450 struct LongQueryResult { 399 const GPUVAddr sequence_address{regs.report_semaphore.Address()};
451 u64_le value;
452 u64_le timestamp;
453 };
454 static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size");
455 const GPUVAddr sequence_address{regs.query.QueryAddress()};
456 if (long_query) { 400 if (long_query) {
457 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast 401 memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
458 // GPU, this command may actually take a while to complete in real hardware due to GPU 402 memory_manager.Write<u64>(sequence_address, payload);
459 // wait queues.
460 LongQueryResult query_result{payload, system.GPU().GetTicks()};
461 memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
462 } else { 403 } else {
463 memory_manager.Write<u32>(sequence_address, static_cast<u32>(payload)); 404 memory_manager.Write<u32>(sequence_address, static_cast<u32>(payload));
464 } 405 }
@@ -466,31 +407,45 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
466 407
467void Maxwell3D::ProcessQueryGet() { 408void Maxwell3D::ProcessQueryGet() {
468 // TODO(Subv): Support the other query units. 409 // TODO(Subv): Support the other query units.
469 if (regs.query.query_get.unit != Regs::QueryUnit::Crop) { 410 if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) {
470 LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented"); 411 LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented");
471 } 412 }
472 413
473 switch (regs.query.query_get.operation) { 414 switch (regs.report_semaphore.query.operation) {
474 case Regs::QueryOperation::Release: 415 case Regs::ReportSemaphore::Operation::Release:
475 if (regs.query.query_get.fence == 1) { 416 if (regs.report_semaphore.query.short_query != 0) {
476 rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); 417 const GPUVAddr sequence_address{regs.report_semaphore.Address()};
418 const u32 payload = regs.report_semaphore.payload;
419 std::function<void()> operation([this, sequence_address, payload] {
420 memory_manager.Write<u32>(sequence_address, payload);
421 });
422 rasterizer->SignalFence(std::move(operation));
477 } else { 423 } else {
478 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); 424 struct LongQueryResult {
425 u64_le value;
426 u64_le timestamp;
427 };
428 const GPUVAddr sequence_address{regs.report_semaphore.Address()};
429 const u32 payload = regs.report_semaphore.payload;
430 [this, sequence_address, payload] {
431 memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
432 memory_manager.Write<u64>(sequence_address, payload);
433 }();
479 } 434 }
480 break; 435 break;
481 case Regs::QueryOperation::Acquire: 436 case Regs::ReportSemaphore::Operation::Acquire:
482 // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that 437 // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
483 // matches the current payload. 438 // matches the current payload.
484 UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE"); 439 UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
485 break; 440 break;
486 case Regs::QueryOperation::Counter: 441 case Regs::ReportSemaphore::Operation::ReportOnly:
487 if (const std::optional<u64> result = GetQueryResult()) { 442 if (const std::optional<u64> result = GetQueryResult()) {
488 // If the query returns an empty optional it means it's cached and deferred. 443 // If the query returns an empty optional it means it's cached and deferred.
489 // In this case we have a non-empty result, so we stamp it immediately. 444 // In this case we have a non-empty result, so we stamp it immediately.
490 StampQueryResult(*result, regs.query.query_get.short_query == 0); 445 StampQueryResult(*result, regs.report_semaphore.query.short_query == 0);
491 } 446 }
492 break; 447 break;
493 case Regs::QueryOperation::Trap: 448 case Regs::ReportSemaphore::Operation::Trap:
494 UNIMPLEMENTED_MSG("Unimplemented query operation TRAP"); 449 UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
495 break; 450 break;
496 default: 451 default:
@@ -500,31 +455,31 @@ void Maxwell3D::ProcessQueryGet() {
500} 455}
501 456
502void Maxwell3D::ProcessQueryCondition() { 457void Maxwell3D::ProcessQueryCondition() {
503 const GPUVAddr condition_address{regs.condition.Address()}; 458 const GPUVAddr condition_address{regs.render_enable.Address()};
504 switch (regs.condition.mode) { 459 switch (regs.render_enable.mode) {
505 case Regs::ConditionMode::Always: { 460 case Regs::RenderEnable::Mode::True: {
506 execute_on = true; 461 execute_on = true;
507 break; 462 break;
508 } 463 }
509 case Regs::ConditionMode::Never: { 464 case Regs::RenderEnable::Mode::False: {
510 execute_on = false; 465 execute_on = false;
511 break; 466 break;
512 } 467 }
513 case Regs::ConditionMode::ResNonZero: { 468 case Regs::RenderEnable::Mode::Conditional: {
514 Regs::QueryCompare cmp; 469 Regs::ReportSemaphore::Compare cmp;
515 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 470 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
516 execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; 471 execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
517 break; 472 break;
518 } 473 }
519 case Regs::ConditionMode::Equal: { 474 case Regs::RenderEnable::Mode::IfEqual: {
520 Regs::QueryCompare cmp; 475 Regs::ReportSemaphore::Compare cmp;
521 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 476 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
522 execute_on = 477 execute_on =
523 cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; 478 cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
524 break; 479 break;
525 } 480 }
526 case Regs::ConditionMode::NotEqual: { 481 case Regs::RenderEnable::Mode::IfNotEqual: {
527 Regs::QueryCompare cmp; 482 Regs::ReportSemaphore::Compare cmp;
528 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 483 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
529 execute_on = 484 execute_on =
530 cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; 485 cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
@@ -539,108 +494,73 @@ void Maxwell3D::ProcessQueryCondition() {
539} 494}
540 495
541void Maxwell3D::ProcessCounterReset() { 496void Maxwell3D::ProcessCounterReset() {
542 switch (regs.counter_reset) { 497 switch (regs.clear_report_value) {
543 case Regs::CounterReset::SampleCnt: 498 case Regs::ClearReport::ZPassPixelCount:
544 rasterizer->ResetCounter(QueryType::SamplesPassed); 499 rasterizer->ResetCounter(QueryType::SamplesPassed);
545 break; 500 break;
546 default: 501 default:
547 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.counter_reset); 502 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value);
548 break; 503 break;
549 } 504 }
550} 505}
551 506
552void Maxwell3D::ProcessSyncPoint() { 507void Maxwell3D::ProcessSyncPoint() {
553 const u32 sync_point = regs.sync_info.sync_point.Value(); 508 const u32 sync_point = regs.sync_info.sync_point.Value();
554 const u32 increment = regs.sync_info.increment.Value(); 509 const u32 cache_flush = regs.sync_info.clean_l2.Value();
555 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); 510 if (cache_flush != 0) {
556 if (increment) { 511 rasterizer->InvalidateGPUCache();
557 rasterizer->SignalSyncPoint(sync_point);
558 }
559}
560
561void Maxwell3D::DrawArrays() {
562 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
563 regs.vertex_buffer.count);
564 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
565
566 // Both instance configuration registers can not be set at the same time.
567 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
568 "Illegal combination of instancing parameters");
569
570 ProcessTopologyOverride();
571
572 if (regs.draw.instance_next) {
573 // Increment the current instance *before* drawing.
574 state.current_instance += 1;
575 } else if (!regs.draw.instance_cont) {
576 // Reset the current instance to 0.
577 state.current_instance = 0;
578 }
579
580 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
581 if (ShouldExecute()) {
582 rasterizer->Draw(is_indexed, false);
583 }
584
585 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
586 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
587 // it's possible that it is incorrect and that there is some other register used to specify the
588 // drawing mode.
589 if (is_indexed) {
590 regs.index_array.count = 0;
591 } else {
592 regs.vertex_buffer.count = 0;
593 } 512 }
513 rasterizer->SignalSyncPoint(sync_point);
594} 514}
595 515
596std::optional<u64> Maxwell3D::GetQueryResult() { 516std::optional<u64> Maxwell3D::GetQueryResult() {
597 switch (regs.query.query_get.select) { 517 switch (regs.report_semaphore.query.report) {
598 case Regs::QuerySelect::Payload: 518 case Regs::ReportSemaphore::Report::Payload:
599 return regs.query.query_sequence; 519 return regs.report_semaphore.payload;
600 case Regs::QuerySelect::SamplesPassed: 520 case Regs::ReportSemaphore::Report::ZPassPixelCount64:
601 // Deferred. 521 // Deferred.
602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, 522 rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
603 system.GPU().GetTicks()); 523 system.GPU().GetTicks());
604 return std::nullopt; 524 return std::nullopt;
605 default: 525 default:
606 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 526 LOG_DEBUG(HW_GPU, "Unimplemented query report type {}",
607 regs.query.query_get.select.Value()); 527 regs.report_semaphore.query.report.Value());
608 return 1; 528 return 1;
609 } 529 }
610} 530}
611 531
612void Maxwell3D::ProcessCBBind(size_t stage_index) { 532void Maxwell3D::ProcessCBBind(size_t stage_index) {
613 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. 533 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
614 const auto& bind_data = regs.cb_bind[stage_index]; 534 const auto& bind_data = regs.bind_groups[stage_index];
615 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.index]; 535 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
616 buffer.enabled = bind_data.valid.Value() != 0; 536 buffer.enabled = bind_data.valid.Value() != 0;
617 buffer.address = regs.const_buffer.BufferAddress(); 537 buffer.address = regs.const_buffer.Address();
618 buffer.size = regs.const_buffer.cb_size; 538 buffer.size = regs.const_buffer.size;
619 539
620 const bool is_enabled = bind_data.valid.Value() != 0; 540 const bool is_enabled = bind_data.valid.Value() != 0;
621 if (!is_enabled) { 541 if (!is_enabled) {
622 rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.index); 542 rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.shader_slot);
623 return; 543 return;
624 } 544 }
625 const GPUVAddr gpu_addr = regs.const_buffer.BufferAddress(); 545 const GPUVAddr gpu_addr = regs.const_buffer.Address();
626 const u32 size = regs.const_buffer.cb_size; 546 const u32 size = regs.const_buffer.size;
627 rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); 547 rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.shader_slot, gpu_addr, size);
628} 548}
629 549
630void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) { 550void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
631 // Write the input value to the current const buffer at the current position. 551 // Write the input value to the current const buffer at the current position.
632 const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); 552 const GPUVAddr buffer_address = regs.const_buffer.Address();
633 ASSERT(buffer_address != 0); 553 ASSERT(buffer_address != 0);
634 554
635 // Don't allow writing past the end of the buffer. 555 // Don't allow writing past the end of the buffer.
636 ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); 556 ASSERT(regs.const_buffer.offset <= regs.const_buffer.size);
637 557
638 const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos}; 558 const GPUVAddr address{buffer_address + regs.const_buffer.offset};
639 const size_t copy_size = amount * sizeof(u32); 559 const size_t copy_size = amount * sizeof(u32);
640 memory_manager.WriteBlock(address, start_base, copy_size); 560 memory_manager.WriteBlock(address, start_base, copy_size);
641 561
642 // Increment the current buffer position. 562 // Increment the current buffer position.
643 regs.const_buffer.cb_pos += static_cast<u32>(copy_size); 563 regs.const_buffer.offset += static_cast<u32>(copy_size);
644} 564}
645 565
646void Maxwell3D::ProcessCBData(u32 value) { 566void Maxwell3D::ProcessCBData(u32 value) {
@@ -648,7 +568,8 @@ void Maxwell3D::ProcessCBData(u32 value) {
648} 568}
649 569
650Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 570Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
651 const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)}; 571 const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
572 tic_index * sizeof(Texture::TICEntry)};
652 573
653 Texture::TICEntry tic_entry; 574 Texture::TICEntry tic_entry;
654 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); 575 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
@@ -657,7 +578,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
657} 578}
658 579
659Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { 580Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
660 const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)}; 581 const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() +
582 tsc_index * sizeof(Texture::TSCEntry)};
661 583
662 Texture::TSCEntry tsc_entry; 584 Texture::TSCEntry tsc_entry;
663 memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); 585 memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
@@ -673,4 +595,90 @@ void Maxwell3D::ProcessClearBuffers() {
673 rasterizer->Clear(); 595 rasterizer->Clear();
674} 596}
675 597
598void Maxwell3D::ProcessDraw(u32 instance_count) {
599 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
600 regs.vertex_buffer.count);
601
602 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
603
604 // Both instance configuration registers can not be set at the same time.
605 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
606 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
607 "Illegal combination of instancing parameters");
608
609 ProcessTopologyOverride();
610
611 const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
612 if (ShouldExecute()) {
613 rasterizer->Draw(is_indexed, instance_count);
614 }
615
616 if (is_indexed) {
617 regs.index_buffer.count = 0;
618 } else {
619 regs.vertex_buffer.count = 0;
620 }
621}
622
623void Maxwell3D::ProcessDeferredDraw() {
624 if (deferred_draw_method.empty()) {
625 return;
626 }
627
628 enum class DrawMode {
629 Undefined,
630 General,
631 Instance,
632 };
633 DrawMode draw_mode{DrawMode::Undefined};
634 u32 instance_count = 1;
635
636 u32 index = 0;
637 u32 method = 0;
638 u32 method_count = static_cast<u32>(deferred_draw_method.size());
639 for (; index < method_count &&
640 (method = deferred_draw_method[index]) != MAXWELL3D_REG_INDEX(draw.begin);
641 ++index)
642 ;
643
644 if (MAXWELL3D_REG_INDEX(draw.begin) != method) {
645 return;
646 }
647
648 // The minimum number of methods for drawing must be greater than or equal to
649 // 3[draw.begin->vertex(index)count(first)->draw.end] to avoid errors in index mode drawing
650 if ((method_count - index) < 3) {
651 return;
652 }
653 draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
654 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
655 ? DrawMode::Instance
656 : DrawMode::General;
657
658 // Drawing will only begin with draw.begin or index_buffer method, other methods directly
659 // clear
660 if (draw_mode == DrawMode::Undefined) {
661 deferred_draw_method.clear();
662 return;
663 }
664
665 if (draw_mode == DrawMode::Instance) {
666 ASSERT_MSG(deferred_draw_method.size() % 4 == 0, "Instance mode method size error");
667 instance_count = static_cast<u32>(method_count - index) / 4;
668 } else {
669 method = deferred_draw_method[index + 1];
670 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
671 MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
672 MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
673 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
674 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
675 }
676 }
677
678 ProcessDraw(instance_count);
679
680 deferred_draw_method.clear();
681 inline_index_draw_indexes.clear();
682}
683
676} // namespace Tegra::Engines 684} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 5f9eb208c..a948fcb14 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -39,12 +39,15 @@ namespace Tegra::Engines {
39 39
40/** 40/**
41 * This Engine is known as GF100_3D. Documentation can be found in: 41 * This Engine is known as GF100_3D. Documentation can be found in:
42 * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/3d/clb197.h
42 * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml 43 * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml
43 * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h 44 * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h
45 *
46 * Note: nVidia have confirmed that their open docs have had parts redacted, so this list is
47 * currently incomplete, and the gaps are still worth exploring.
44 */ 48 */
45 49
46#define MAXWELL3D_REG_INDEX(field_name) \ 50#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Maxwell3D::Regs, field_name) / sizeof(u32))
47 (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32))
48 51
49class Maxwell3D final : public EngineInterface { 52class Maxwell3D final : public EngineInterface {
50public: 53public:
@@ -55,7 +58,6 @@ public:
55 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 58 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
56 59
57 /// Register structure of the Maxwell3D engine. 60 /// Register structure of the Maxwell3D engine.
58 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
59 struct Regs { 61 struct Regs {
60 static constexpr std::size_t NUM_REGS = 0xE00; 62 static constexpr std::size_t NUM_REGS = 0xE00;
61 63
@@ -74,90 +76,515 @@ public:
74 static constexpr std::size_t MaxConstBuffers = 18; 76 static constexpr std::size_t MaxConstBuffers = 18;
75 static constexpr std::size_t MaxConstBufferSize = 0x10000; 77 static constexpr std::size_t MaxConstBufferSize = 0x10000;
76 78
77 enum class QueryOperation : u32 { 79 struct ID {
78 Release = 0, 80 union {
79 Acquire = 1, 81 BitField<0, 16, u32> cls;
80 Counter = 2, 82 BitField<16, 5, u32> engine;
81 Trap = 3, 83 };
82 }; 84 };
83 85
84 enum class QueryUnit : u32 { 86 struct LoadMME {
85 VFetch = 1, 87 u32 instruction_ptr;
86 VP = 2, 88 u32 instruction;
87 Rast = 4, 89 u32 start_address_ptr;
88 StrmOut = 5, 90 u32 start_address;
89 GP = 6,
90 ZCull = 7,
91 Prop = 10,
92 Crop = 15,
93 }; 91 };
94 92
95 enum class QuerySelect : u32 { 93 struct Notify {
96 Payload = 0, 94 u32 address_high;
97 TimeElapsed = 2, 95 u32 address_low;
98 TransformFeedbackPrimitivesGenerated = 11, 96 u32 type;
99 PrimitivesGenerated = 18, 97
100 SamplesPassed = 21, 98 GPUVAddr Address() const {
101 TransformFeedbackUnknown = 26, 99 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
100 address_low);
101 }
102 }; 102 };
103 103
104 struct QueryCompare { 104 struct PeerSemaphore {
105 u32 initial_sequence; 105 u32 address_high;
106 u32 initial_mode; 106 u32 address_low;
107 u32 unknown1; 107
108 u32 unknown2; 108 GPUVAddr Address() const {
109 u32 current_sequence; 109 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
110 u32 current_mode; 110 address_low);
111 }
111 }; 112 };
112 113
113 enum class QuerySyncCondition : u32 { 114 struct GlobalRender {
114 NotEqual = 0, 115 enum class Mode : u32 {
115 GreaterThan = 1, 116 False = 0,
117 True = 1,
118 Conditional = 2,
119 IfEqual = 3,
120 IfNotEqual = 4,
121 };
122 u32 offset_high;
123 u32 offset_low;
124 Mode mode;
125
126 GPUVAddr Address() const {
127 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(offset_high) << 32) |
128 offset_low);
129 }
116 }; 130 };
117 131
118 enum class ConditionMode : u32 { 132 enum class ReductionOp : u32 {
119 Never = 0, 133 RedAdd = 0,
120 Always = 1, 134 RedMin = 1,
121 ResNonZero = 2, 135 RedMax = 2,
122 Equal = 3, 136 RedInc = 3,
123 NotEqual = 4, 137 RedDec = 4,
138 RedAnd = 5,
139 RedOr = 6,
140 RedXor = 7,
124 }; 141 };
125 142
126 enum class ShaderProgram : u32 { 143 struct LaunchDMA {
127 VertexA = 0, 144 enum class Layout : u32 {
128 VertexB = 1, 145 Blocklinear = 0,
129 TesselationControl = 2, 146 Pitch = 1,
130 TesselationEval = 3, 147 };
131 Geometry = 4, 148
132 Fragment = 5, 149 enum class CompletionType : u32 {
150 FlushDisable = 0,
151 FlushOnly = 1,
152 Release = 2,
153 };
154
155 union {
156 BitField<0, 1, Layout> memory_layout;
157 BitField<4, 2, CompletionType> completion_type;
158 BitField<8, 2, u32> interrupt_type;
159 BitField<12, 1, u32> sem_size;
160 BitField<1, 1, u32> reduction_enable;
161 BitField<13, 3, ReductionOp> reduction_op;
162 BitField<2, 2, u32> reduction_format;
163 BitField<6, 1, u32> sysmembar_disable;
164 };
165 };
166
167 struct I2M {
168 u32 address_high;
169 u32 address_low;
170 u32 payload;
171 INSERT_PADDING_BYTES_NOINIT(0x8);
172 u32 nop0;
173 u32 nop1;
174 u32 nop2;
175 u32 nop3;
176 };
177
178 struct OpportunisticEarlyZ {
179 BitField<0, 5, u32> threshold;
180
181 u32 Threshold() const {
182 switch (threshold) {
183 case 0x0:
184 return 0;
185 case 0x1F:
186 return 0x1F;
187 default:
188 // Thresholds begin at 0x10 (1 << 4)
189 // Threshold is in the range 0x1 to 0x13
190 return 1 << (4 + threshold.Value() - 1);
191 }
192 }
193 };
194
195 struct GeometryShaderDmFifo {
196 union {
197 BitField<0, 13, u32> raster_on;
198 BitField<16, 13, u32> raster_off;
199 BitField<31, 1, u32> spill_enabled;
200 };
201 };
202
203 struct L2CacheControl {
204 enum class EvictPolicy : u32 {
205 First = 0,
206 Normal = 1,
207 Last = 2,
208 };
209
210 union {
211 BitField<4, 2, EvictPolicy> policy;
212 };
213 };
214
215 struct InvalidateShaderCache {
216 union {
217 BitField<0, 1, u32> instruction;
218 BitField<4, 1, u32> data;
219 BitField<12, 1, u32> constant;
220 BitField<1, 1, u32> locks;
221 BitField<2, 1, u32> flush_data;
222 };
223 };
224
225 struct SyncInfo {
226 enum class Condition : u32 {
227 StreamOutWritesDone = 0,
228 RopWritesDone = 1,
229 };
230
231 union {
232 BitField<0, 16, u32> sync_point;
233 BitField<16, 1, u32> clean_l2;
234 BitField<20, 1, Condition> condition;
235 };
236 };
237
238 struct SurfaceClipBlockId {
239 union {
240 BitField<0, 4, u32> block_width;
241 BitField<4, 4, u32> block_height;
242 BitField<8, 4, u32> block_depth;
243 };
244 };
245
246 struct DecompressSurface {
247 union {
248 BitField<0, 3, u32> mrt_select;
249 BitField<4, 16, u32> rt_array_index;
250 };
251 };
252
253 struct ZCullRopBypass {
254 union {
255 BitField<0, 1, u32> enable;
256 BitField<4, 1, u32> no_stall;
257 BitField<8, 1, u32> cull_everything;
258 BitField<12, 4, u32> threshold;
259 };
260 };
261
262 struct ZCullSubregion {
263 union {
264 BitField<0, 1, u32> enable;
265 BitField<4, 24, u32> normalized_aliquots;
266 };
267 };
268
269 struct RasterBoundingBox {
270 enum class Mode : u32 {
271 BoundingBox = 0,
272 FullViewport = 1,
273 };
274
275 union {
276 BitField<0, 1, Mode> mode;
277 BitField<4, 8, u32> pad;
278 };
279 };
280
281 struct IteratedBlendOptimization {
282 enum class Noop : u32 {
283 Never = 0,
284 SourceRGBA0000 = 1,
285 SourceAlpha = 2,
286 SourceRGBA0001 = 3,
287 };
288
289 union {
290 BitField<0, 1, Noop> noop;
291 };
292 };
293
294 struct ZCullSubregionAllocation {
295 enum class Format : u32 {
296 Z_16x16x2_4x4 = 0,
297 ZS_16x16_4x4 = 1,
298 Z_16x16_4x2 = 2,
299 Z_16x16_2x4 = 3,
300 Z_16x8_4x4 = 4,
301 Z_8x8_4x2 = 5,
302 Z_8x8_2x4 = 6,
303 Z_16x16_4x8 = 7,
304 Z_4x8_2x2 = 8,
305 ZS_16x8_4x2 = 9,
306 ZS_16x8_2x4 = 10,
307 ZS_8x8_2x2 = 11,
308 Z_4x8_1x1 = 12,
309 None = 15,
310 };
311
312 union {
313 BitField<0, 8, u32> id;
314 BitField<8, 16, u32> aliquots;
315 BitField<24, 4, Format> format;
316 };
317 };
318
319 enum class ZCullSubregionAlgorithm : u32 {
320 Static = 0,
321 Adaptive = 1,
322 };
323
324 struct PixelShaderOutputSampleMaskUsage {
325 union {
326 BitField<0, 1, u32> enable;
327 BitField<1, 1, u32> qualify_by_aa;
328 };
329 };
330
331 struct L1Configuration {
332 enum class AddressableMemory : u32 {
333 Size16Kb = 0,
334 Size48Kb = 3,
335 };
336 union {
337 BitField<0, 3, AddressableMemory> direct_addressable_memory;
338 };
339 };
340
341 struct SPAVersion {
342 union {
343 BitField<0, 8, u32> minor;
344 BitField<8, 8, u32> major;
345 };
346 };
347
348 struct SnapGrid {
349 enum class Location : u32 {
350 Pixel2x2 = 1,
351 Pixel4x4 = 2,
352 Pixel8x8 = 3,
353 Pixel16x16 = 4,
354 Pixel32x32 = 5,
355 Pixel64x64 = 6,
356 Pixel128x128 = 7,
357 Pixel256x256 = 8,
358 };
359
360 enum class Mode : u32 {
361 RTNE = 0,
362 Tesla = 1,
363 };
364
365 struct {
366 union {
367 BitField<0, 4, Location> location;
368 BitField<8, 1, Mode> rounding_mode;
369 };
370 } line;
371
372 struct {
373 union {
374 BitField<0, 4, Location> location;
375 BitField<8, 1, Mode> rounding_mode;
376 };
377 } non_line;
378 };
379
380 struct Tessellation {
381 enum class DomainType : u32 {
382 Isolines = 0,
383 Triangles = 1,
384 Quads = 2,
385 };
386
387 enum class Spacing : u32 {
388 Integer = 0,
389 FractionalOdd = 1,
390 FractionalEven = 2,
391 };
392
393 enum class OutputPrimitives : u32 {
394 Points = 0,
395 Lines = 1,
396 Triangles_CW = 2,
397 Triangles_CCW = 3,
398 };
399
400 struct Parameters {
401 union {
402 BitField<0, 2, DomainType> domain_type;
403 BitField<4, 2, Spacing> spacing;
404 BitField<8, 2, OutputPrimitives> output_primitives;
405 };
406 } params;
407
408 struct LOD {
409 std::array<f32, 4> outer;
410 std::array<f32, 2> inner;
411 } lod;
412
413 std::array<u32, 9> reserved;
414 };
415
416 struct SubTilingPerf {
417 struct {
418 union {
419 BitField<0, 8, u32> spm_triangle_register_file_per;
420 BitField<8, 8, u32> spm_pixel_output_buffer_per;
421 BitField<16, 8, u32> spm_triangle_ram_per;
422 BitField<24, 8, u32> max_quads_per;
423 };
424 } knob_a;
425
426 struct {
427 union {
428 BitField<0, 8, u32> max_primitives_per;
429 };
430 } knob_b;
431
432 u32 knob_c;
433 };
434
435 struct ZCullSubregionReport {
436 enum class ReportType : u32 {
437 DepthTest = 0,
438 DepthTestNoAccept = 1,
439 DepthTestLateZ = 2,
440 StencilTest = 3,
441 };
442
443 union {
444 BitField<0, 1, u32> enabled;
445 BitField<4, 8, u32> subregion_id;
446 } to_report;
447
448 union {
449 BitField<0, 1, u32> enabled;
450 BitField<4, 3, ReportType> type;
451 } report_type;
452 };
453
454 struct BalancedPrimitiveWorkload {
455 union {
456 BitField<0, 1, u32> unpartitioned_mode;
457 BitField<4, 1, u32> timesliced_mode;
458 };
459 };
460
461 struct TransformFeedback {
462 struct Buffer {
463 u32 enable;
464 u32 address_high;
465 u32 address_low;
466 s32 size;
467 s32 start_offset;
468 INSERT_PADDING_BYTES_NOINIT(0xC);
469
470 GPUVAddr Address() const {
471 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
472 address_low);
473 }
474 };
475 static_assert(sizeof(Buffer) == 0x20);
476
477 struct Control {
478 u32 stream;
479 u32 varying_count;
480 u32 stride;
481 INSERT_PADDING_BYTES_NOINIT(0x4);
482 };
483 static_assert(sizeof(Control) == 0x10);
484
485 std::array<TransformFeedback::Buffer, NumTransformFeedbackBuffers> buffers;
486
487 INSERT_PADDING_BYTES_NOINIT(0x300);
488
489 std::array<TransformFeedback::Control, NumTransformFeedbackBuffers> controls;
490 };
491
492 struct HybridAntiAliasControl {
493 enum class Centroid : u32 {
494 PerFragment = 0,
495 PerPass = 1,
496 };
497 union {
498 BitField<0, 4, u32> passes;
499 BitField<4, 1, Centroid> centroid;
500 BitField<5, 1, u32> passes_extended;
501 };
502 };
503
504 struct ShaderLocalMemory {
505 u32 base_address;
506 INSERT_PADDING_BYTES_NOINIT(0x10);
507 u32 address_high;
508 u32 address_low;
509 u32 size_high;
510 u32 size_low;
511 u32 default_size_per_warp;
512
513 GPUVAddr Address() const {
514 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
515 address_low);
516 }
517
518 u64 Size() const {
519 return (static_cast<u64>(size_high) << 32) | size_low;
520 }
521 };
522
523 struct ZCullRegion {
524 u32 width;
525 u32 height;
526 u32 depth;
527 u32 offset;
528 INSERT_PADDING_BYTES_NOINIT(0xC);
529 u32 fetch_streams_once;
530 union {
531 BitField<0, 16, u32> start_aliquot;
532 BitField<16, 16, u32> aliquot_count;
533 } location;
534 u32 aliquots_per_layer;
535 u32 storage_address_high;
536 u32 storage_address_low;
537 u32 storage_limit_address_high;
538 u32 storage_limit_address_low;
539
540 GPUVAddr StorageAddress() const {
541 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(storage_address_high) << 32) |
542 storage_address_low);
543 }
544 GPUVAddr StorageLimitAddress() const {
545 return static_cast<GPUVAddr>(
546 (static_cast<GPUVAddr>(storage_limit_address_high) << 32) |
547 storage_limit_address_low);
548 }
549 };
550
551 struct ZetaReadOnly {
552 union {
553 BitField<0, 1, u32> enable_z;
554 BitField<4, 1, u32> enable_stencil;
555 };
133 }; 556 };
134 557
135 struct VertexAttribute { 558 struct VertexAttribute {
136 enum class Size : u32 { 559 enum class Size : u32 {
137 Invalid = 0x0, 560 Invalid = 0x0,
138 Size_32_32_32_32 = 0x01, 561 Size_R32_G32_B32_A32 = 0x01,
139 Size_32_32_32 = 0x02, 562 Size_R32_G32_B32 = 0x02,
140 Size_16_16_16_16 = 0x03, 563 Size_R16_G16_B16_A16 = 0x03,
141 Size_32_32 = 0x04, 564 Size_R32_G32 = 0x04,
142 Size_16_16_16 = 0x05, 565 Size_R16_G16_B16 = 0x05,
143 Size_8_8_8_8 = 0x0a, 566 Size_R8_G8_B8_A8 = 0x0A,
144 Size_16_16 = 0x0f, 567 Size_R16_G16 = 0x0F,
145 Size_32 = 0x12, 568 Size_R32 = 0x12,
146 Size_8_8_8 = 0x13, 569 Size_R8_G8_B8 = 0x13,
147 Size_8_8 = 0x18, 570 Size_R8_G8 = 0x18,
148 Size_16 = 0x1b, 571 Size_R16 = 0x1B,
149 Size_8 = 0x1d, 572 Size_R8 = 0x1D,
150 Size_10_10_10_2 = 0x30, 573 Size_A2_B10_G10_R10 = 0x30,
151 Size_11_11_10 = 0x31, 574 Size_B10_G11_R11 = 0x31,
575 Size_G8_R8 = 0x32,
576 Size_X8_B8_G8_R8 = 0x33,
577 Size_A8 = 0x34,
152 }; 578 };
153 579
154 enum class Type : u32 { 580 enum class Type : u32 {
155 SignedNorm = 1, 581 UnusedEnumDoNotUseBecauseItWillGoAway = 0,
156 UnsignedNorm = 2, 582 SNorm = 1,
157 SignedInt = 3, 583 UNorm = 2,
158 UnsignedInt = 4, 584 SInt = 3,
159 UnsignedScaled = 5, 585 UInt = 4,
160 SignedScaled = 6, 586 UScaled = 5,
587 SScaled = 6,
161 Float = 7, 588 Float = 7,
162 }; 589 };
163 590
@@ -173,33 +600,36 @@ public:
173 600
174 u32 ComponentCount() const { 601 u32 ComponentCount() const {
175 switch (size) { 602 switch (size) {
176 case Size::Size_32_32_32_32: 603 case Size::Size_R32_G32_B32_A32:
177 return 4; 604 return 4;
178 case Size::Size_32_32_32: 605 case Size::Size_R32_G32_B32:
179 return 3; 606 return 3;
180 case Size::Size_16_16_16_16: 607 case Size::Size_R16_G16_B16_A16:
181 return 4; 608 return 4;
182 case Size::Size_32_32: 609 case Size::Size_R32_G32:
183 return 2; 610 return 2;
184 case Size::Size_16_16_16: 611 case Size::Size_R16_G16_B16:
185 return 3; 612 return 3;
186 case Size::Size_8_8_8_8: 613 case Size::Size_R8_G8_B8_A8:
614 case Size::Size_X8_B8_G8_R8:
187 return 4; 615 return 4;
188 case Size::Size_16_16: 616 case Size::Size_R16_G16:
189 return 2; 617 return 2;
190 case Size::Size_32: 618 case Size::Size_R32:
191 return 1; 619 return 1;
192 case Size::Size_8_8_8: 620 case Size::Size_R8_G8_B8:
193 return 3; 621 return 3;
194 case Size::Size_8_8: 622 case Size::Size_R8_G8:
623 case Size::Size_G8_R8:
195 return 2; 624 return 2;
196 case Size::Size_16: 625 case Size::Size_R16:
197 return 1; 626 return 1;
198 case Size::Size_8: 627 case Size::Size_R8:
628 case Size::Size_A8:
199 return 1; 629 return 1;
200 case Size::Size_10_10_10_2: 630 case Size::Size_A2_B10_G10_R10:
201 return 4; 631 return 4;
202 case Size::Size_11_11_10: 632 case Size::Size_B10_G11_R11:
203 return 3; 633 return 3;
204 default: 634 default:
205 ASSERT(false); 635 ASSERT(false);
@@ -209,33 +639,36 @@ public:
209 639
210 u32 SizeInBytes() const { 640 u32 SizeInBytes() const {
211 switch (size) { 641 switch (size) {
212 case Size::Size_32_32_32_32: 642 case Size::Size_R32_G32_B32_A32:
213 return 16; 643 return 16;
214 case Size::Size_32_32_32: 644 case Size::Size_R32_G32_B32:
215 return 12; 645 return 12;
216 case Size::Size_16_16_16_16: 646 case Size::Size_R16_G16_B16_A16:
217 return 8; 647 return 8;
218 case Size::Size_32_32: 648 case Size::Size_R32_G32:
219 return 8; 649 return 8;
220 case Size::Size_16_16_16: 650 case Size::Size_R16_G16_B16:
221 return 6; 651 return 6;
222 case Size::Size_8_8_8_8: 652 case Size::Size_R8_G8_B8_A8:
653 case Size::Size_X8_B8_G8_R8:
223 return 4; 654 return 4;
224 case Size::Size_16_16: 655 case Size::Size_R16_G16:
225 return 4; 656 return 4;
226 case Size::Size_32: 657 case Size::Size_R32:
227 return 4; 658 return 4;
228 case Size::Size_8_8_8: 659 case Size::Size_R8_G8_B8:
229 return 3; 660 return 3;
230 case Size::Size_8_8: 661 case Size::Size_R8_G8:
662 case Size::Size_G8_R8:
231 return 2; 663 return 2;
232 case Size::Size_16: 664 case Size::Size_R16:
233 return 2; 665 return 2;
234 case Size::Size_8: 666 case Size::Size_R8:
667 case Size::Size_A8:
235 return 1; 668 return 1;
236 case Size::Size_10_10_10_2: 669 case Size::Size_A2_B10_G10_R10:
237 return 4; 670 return 4;
238 case Size::Size_11_11_10: 671 case Size::Size_B10_G11_R11:
239 return 4; 672 return 4;
240 default: 673 default:
241 ASSERT(false); 674 ASSERT(false);
@@ -245,34 +678,36 @@ public:
245 678
246 std::string SizeString() const { 679 std::string SizeString() const {
247 switch (size) { 680 switch (size) {
248 case Size::Size_32_32_32_32: 681 case Size::Size_R32_G32_B32_A32:
249 return "32_32_32_32"; 682 return "32_32_32_32";
250 case Size::Size_32_32_32: 683 case Size::Size_R32_G32_B32:
251 return "32_32_32"; 684 return "32_32_32";
252 case Size::Size_16_16_16_16: 685 case Size::Size_R16_G16_B16_A16:
253 return "16_16_16_16"; 686 return "16_16_16_16";
254 case Size::Size_32_32: 687 case Size::Size_R32_G32:
255 return "32_32"; 688 return "32_32";
256 case Size::Size_16_16_16: 689 case Size::Size_R16_G16_B16:
257 return "16_16_16"; 690 return "16_16_16";
258 case Size::Size_8_8_8_8: 691 case Size::Size_R8_G8_B8_A8:
259 return "8_8_8_8"; 692 return "8_8_8_8";
260 case Size::Size_16_16: 693 case Size::Size_R16_G16:
261 return "16_16"; 694 return "16_16";
262 case Size::Size_32: 695 case Size::Size_R32:
263 return "32"; 696 return "32";
264 case Size::Size_8_8_8: 697 case Size::Size_R8_G8_B8:
265 return "8_8_8"; 698 return "8_8_8";
266 case Size::Size_8_8: 699 case Size::Size_R8_G8:
700 case Size::Size_G8_R8:
267 return "8_8"; 701 return "8_8";
268 case Size::Size_16: 702 case Size::Size_R16:
269 return "16"; 703 return "16";
270 case Size::Size_8: 704 case Size::Size_R8:
705 case Size::Size_A8:
271 return "8"; 706 return "8";
272 case Size::Size_10_10_10_2: 707 case Size::Size_A2_B10_G10_R10:
273 return "10_10_10_2"; 708 return "2_10_10_10";
274 case Size::Size_11_11_10: 709 case Size::Size_B10_G11_R11:
275 return "11_11_10"; 710 return "10_11_12";
276 default: 711 default:
277 ASSERT(false); 712 ASSERT(false);
278 return {}; 713 return {};
@@ -281,17 +716,19 @@ public:
281 716
282 std::string TypeString() const { 717 std::string TypeString() const {
283 switch (type) { 718 switch (type) {
284 case Type::SignedNorm: 719 case Type::UnusedEnumDoNotUseBecauseItWillGoAway:
720 return "Unused";
721 case Type::SNorm:
285 return "SNORM"; 722 return "SNORM";
286 case Type::UnsignedNorm: 723 case Type::UNorm:
287 return "UNORM"; 724 return "UNORM";
288 case Type::SignedInt: 725 case Type::SInt:
289 return "SINT"; 726 return "SINT";
290 case Type::UnsignedInt: 727 case Type::UInt:
291 return "UINT"; 728 return "UINT";
292 case Type::UnsignedScaled: 729 case Type::UScaled:
293 return "USCALED"; 730 return "USCALED";
294 case Type::SignedScaled: 731 case Type::SScaled:
295 return "SSCALED"; 732 return "SSCALED";
296 case Type::Float: 733 case Type::Float:
297 return "FLOAT"; 734 return "FLOAT";
@@ -301,7 +738,7 @@ public:
301 } 738 }
302 739
303 bool IsNormalized() const { 740 bool IsNormalized() const {
304 return (type == Type::SignedNorm) || (type == Type::UnsignedNorm); 741 return (type == Type::SNorm) || (type == Type::UNorm);
305 } 742 }
306 743
307 bool IsValid() const { 744 bool IsValid() const {
@@ -312,6 +749,7 @@ public:
312 return hex < other.hex; 749 return hex < other.hex;
313 } 750 }
314 }; 751 };
752 static_assert(sizeof(VertexAttribute) == 0x4);
315 753
316 struct MsaaSampleLocation { 754 struct MsaaSampleLocation {
317 union { 755 union {
@@ -342,9 +780,96 @@ public:
342 } 780 }
343 }; 781 };
344 782
345 enum class DepthMode : u32 { 783 struct MultisampleCoverageToColor {
346 MinusOneToOne = 0, 784 union {
347 ZeroToOne = 1, 785 BitField<0, 1, u32> enable;
786 BitField<4, 3, u32> target;
787 };
788 };
789
790 struct DecompressZetaSurface {
791 union {
792 BitField<0, 1, u32> z_enable;
793 BitField<4, 1, u32> stencil_enable;
794 };
795 };
796
797 struct ZetaSparse {
798 enum class UnmappedCompare : u32 {
799 Unmapped = 0,
800 FailAlways = 1,
801 };
802 union {
803 BitField<0, 1, u32> enable;
804 BitField<1, 1, UnmappedCompare> unmapped_compare;
805 };
806 };
807
808 struct RtControl {
809 union {
810 BitField<0, 4, u32> count;
811 BitField<4, 3, u32> target0;
812 BitField<7, 3, u32> target1;
813 BitField<10, 3, u32> target2;
814 BitField<13, 3, u32> target3;
815 BitField<16, 3, u32> target4;
816 BitField<19, 3, u32> target5;
817 BitField<22, 3, u32> target6;
818 BitField<25, 3, u32> target7;
819 };
820
821 u32 Map(std::size_t index) const {
822 const std::array<u32, NumRenderTargets> maps{target0, target1, target2, target3,
823 target4, target5, target6, target7};
824 ASSERT(index < maps.size());
825 return maps[index];
826 }
827 };
828
829 struct CompressionThresholdSamples {
830 u32 samples;
831
832 u32 Samples() {
833 if (samples == 0) {
834 return 0;
835 }
836 return 1 << (samples - 1);
837 }
838 };
839
840 struct PixelShaderInterlockControl {
841 enum class TileMode : u32 {
842 NoConflictDetect = 0,
843 DetectSampleConflict = 1,
844 DetectPixelConflict = 2,
845 };
846 enum class TileSize : u32 {
847 Size_16x16 = 0,
848 Size_8x8 = 1,
849 };
850 enum class FragmentOrder : u32 {
851 FragmentOrdered = 0,
852 FragmentUnordered = 1,
853 };
854 union {
855 BitField<0, 2, TileMode> tile_mode;
856 BitField<2, 1, TileSize> tile_size;
857 BitField<3, 1, FragmentOrder> fragment_order;
858 };
859 };
860
861 struct ZetaSize {
862 enum class DimensionControl : u32 {
863 DepthDefinesArray = 0,
864 ArraySizeOne = 1,
865 };
866
867 u32 width;
868 u32 height;
869 union {
870 BitField<0, 16, u32> depth;
871 BitField<16, 1, DimensionControl> dim_control;
872 };
348 }; 873 };
349 874
350 enum class PrimitiveTopology : u32 { 875 enum class PrimitiveTopology : u32 {
@@ -358,15 +883,21 @@ public:
358 Quads = 0x7, 883 Quads = 0x7,
359 QuadStrip = 0x8, 884 QuadStrip = 0x8,
360 Polygon = 0x9, 885 Polygon = 0x9,
361 LinesAdjacency = 0xa, 886 LinesAdjacency = 0xA,
362 LineStripAdjacency = 0xb, 887 LineStripAdjacency = 0xB,
363 TrianglesAdjacency = 0xc, 888 TrianglesAdjacency = 0xC,
364 TriangleStripAdjacency = 0xd, 889 TriangleStripAdjacency = 0xD,
365 Patches = 0xe, 890 Patches = 0xE,
891 };
892
893 struct VertexArray {
894 union {
895 BitField<0, 16, u32> start;
896 BitField<16, 12, u32> count;
897 BitField<28, 3, PrimitiveTopology> topology;
898 };
366 }; 899 };
367 900
368 // Constants as from NVC0_3D_UNK1970_D3D
369 // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598
370 enum class PrimitiveTopologyOverride : u32 { 901 enum class PrimitiveTopologyOverride : u32 {
371 None = 0x0, 902 None = 0x0,
372 Points = 0x1, 903 Points = 0x1,
@@ -374,11 +905,32 @@ public:
374 LineStrip = 0x3, 905 LineStrip = 0x3,
375 Triangles = 0x4, 906 Triangles = 0x4,
376 TriangleStrip = 0x5, 907 TriangleStrip = 0x5,
377 LinesAdjacency = 0xa, 908 LinesAdjacency = 0xA,
378 LineStripAdjacency = 0xb, 909 LineStripAdjacency = 0xB,
379 TrianglesAdjacency = 0xc, 910 TrianglesAdjacency = 0xC,
380 TriangleStripAdjacency = 0xd, 911 TriangleStripAdjacency = 0xD,
381 Patches = 0xe, 912 Patches = 0xE,
913
914 LegacyPoints = 0x1001,
915 LegacyIndexedLines = 0x1002,
916 LegacyIndexedTriangles = 0x1003,
917 LegacyLines = 0x100F,
918 LegacyLineStrip = 0x1010,
919 LegacyIndexedLineStrip = 0x1011,
920 LegacyTriangles = 0x1012,
921 LegacyTriangleStrip = 0x1013,
922 LegacyIndexedTriangleStrip = 0x1014,
923 LegacyTriangleFan = 0x1015,
924 LegacyIndexedTriangleFan = 0x1016,
925 LegacyTriangleFanImm = 0x1017,
926 LegacyLinesImm = 0x1018,
927 LegacyIndexedTriangles2 = 0x101A,
928 LegacyIndexedLines2 = 0x101B,
929 };
930
931 enum class DepthMode : u32 {
932 MinusOneToOne = 0,
933 ZeroToOne = 1,
382 }; 934 };
383 935
384 enum class IndexFormat : u32 { 936 enum class IndexFormat : u32 {
@@ -388,183 +940,143 @@ public:
388 }; 940 };
389 941
390 enum class ComparisonOp : u32 { 942 enum class ComparisonOp : u32 {
391 // These values are used by Nouveau and most games, they correspond to the OpenGL token 943 Never_D3D = 1,
392 // values for these operations. 944 Less_D3D = 2,
393 Never = 0x200, 945 Equal_D3D = 3,
394 Less = 0x201, 946 LessEqual_D3D = 4,
395 Equal = 0x202, 947 Greater_D3D = 5,
396 LessEqual = 0x203, 948 NotEqual_D3D = 6,
397 Greater = 0x204, 949 GreaterEqual_D3D = 7,
398 NotEqual = 0x205, 950 Always_D3D = 8,
399 GreaterEqual = 0x206, 951
400 Always = 0x207, 952 Never_GL = 0x200,
401 953 Less_GL = 0x201,
402 // These values are used by some games, they seem to be NV04 values. 954 Equal_GL = 0x202,
403 NeverOld = 1, 955 LessEqual_GL = 0x203,
404 LessOld = 2, 956 Greater_GL = 0x204,
405 EqualOld = 3, 957 NotEqual_GL = 0x205,
406 LessEqualOld = 4, 958 GreaterEqual_GL = 0x206,
407 GreaterOld = 5, 959 Always_GL = 0x207,
408 NotEqualOld = 6, 960 };
409 GreaterEqualOld = 7, 961
410 AlwaysOld = 8, 962 enum class ClearReport : u32 {
411 }; 963 ZPassPixelCount = 0x01,
412 964 ZCullStats = 0x02,
413 enum class LogicOperation : u32 { 965 StreamingPrimitvesNeededMinusSucceeded = 0x03,
414 Clear = 0x1500, 966 AlphaBetaClocks = 0x04,
415 And = 0x1501, 967 StreamingPrimitivesSucceeded = 0x10,
416 AndReverse = 0x1502, 968 StreamingPrimitivesNeeded = 0x11,
417 Copy = 0x1503, 969 VerticesGenerated = 0x12,
418 AndInverted = 0x1504, 970 PrimitivesGenerated = 0x13,
419 NoOp = 0x1505, 971 VertexShaderInvocations = 0x15,
420 Xor = 0x1506, 972 TessellationInitInvocations = 0x16,
421 Or = 0x1507, 973 TessellationShaderInvocations = 0x17,
422 Nor = 0x1508, 974 TessellationShaderPrimitivesGenerated = 0x18,
423 Equiv = 0x1509, 975 GeometryShaderInvocations = 0x1A,
424 Invert = 0x150A, 976 GeometryShaderPrimitivesGenerated = 0x1B,
425 OrReverse = 0x150B, 977 ClipperInvocations = 0x1C,
426 CopyInverted = 0x150C, 978 ClipperPrimitivesGenerated = 0x1D,
427 OrInverted = 0x150D, 979 PixelShaderInvocations = 0x1E,
428 Nand = 0x150E, 980 VtgPrimitivesOut = 0x1F,
429 Set = 0x150F,
430 };
431
432 enum class StencilOp : u32 {
433 Keep = 1,
434 Zero = 2,
435 Replace = 3,
436 Incr = 4,
437 Decr = 5,
438 Invert = 6,
439 IncrWrap = 7,
440 DecrWrap = 8,
441 KeepOGL = 0x1E00,
442 ZeroOGL = 0,
443 ReplaceOGL = 0x1E01,
444 IncrOGL = 0x1E02,
445 DecrOGL = 0x1E03,
446 InvertOGL = 0x150A,
447 IncrWrapOGL = 0x8507,
448 DecrWrapOGL = 0x8508,
449 };
450
451 enum class CounterReset : u32 {
452 SampleCnt = 0x01,
453 Unk02 = 0x02,
454 Unk03 = 0x03,
455 Unk04 = 0x04,
456 EmittedPrimitives = 0x10, // Not tested
457 Unk11 = 0x11,
458 Unk12 = 0x12,
459 Unk13 = 0x13,
460 Unk15 = 0x15,
461 Unk16 = 0x16,
462 Unk17 = 0x17,
463 Unk18 = 0x18,
464 Unk1A = 0x1A,
465 Unk1B = 0x1B,
466 Unk1C = 0x1C,
467 Unk1D = 0x1D,
468 Unk1E = 0x1E,
469 GeneratedPrimitives = 0x1F,
470 }; 981 };
471 982
472 enum class FrontFace : u32 { 983 enum class FrontFace : u32 {
473 ClockWise = 0x0900, 984 ClockWise = 0x900,
474 CounterClockWise = 0x0901, 985 CounterClockWise = 0x901,
475 }; 986 };
476 987
477 enum class CullFace : u32 { 988 enum class CullFace : u32 {
478 Front = 0x0404, 989 Front = 0x404,
479 Back = 0x0405, 990 Back = 0x405,
480 FrontAndBack = 0x0408, 991 FrontAndBack = 0x408,
481 }; 992 };
482 993
483 struct Blend { 994 struct Blend {
484 enum class Equation : u32 { 995 enum class Equation : u32 {
485 Add = 1, 996 Add_D3D = 1,
486 Subtract = 2, 997 Subtract_D3D = 2,
487 ReverseSubtract = 3, 998 ReverseSubtract_D3D = 3,
488 Min = 4, 999 Min_D3D = 4,
489 Max = 5, 1000 Max_D3D = 5,
490 1001
491 // These values are used by Nouveau and some games. 1002 Add_GL = 0x8006,
492 AddGL = 0x8006, 1003 Min_GL = 0x8007,
493 MinGL = 0x8007, 1004 Max_GL = 0x8008,
494 MaxGL = 0x8008, 1005 Subtract_GL = 0x800A,
495 SubtractGL = 0x800a, 1006 ReverseSubtract_GL = 0x800B
496 ReverseSubtractGL = 0x800b
497 }; 1007 };
498 1008
499 enum class Factor : u32 { 1009 enum class Factor : u32 {
500 Zero = 0x1, 1010 Zero_D3D = 0x1,
501 One = 0x2, 1011 One_D3D = 0x2,
502 SourceColor = 0x3, 1012 SourceColor_D3D = 0x3,
503 OneMinusSourceColor = 0x4, 1013 OneMinusSourceColor_D3D = 0x4,
504 SourceAlpha = 0x5, 1014 SourceAlpha_D3D = 0x5,
505 OneMinusSourceAlpha = 0x6, 1015 OneMinusSourceAlpha_D3D = 0x6,
506 DestAlpha = 0x7, 1016 DestAlpha_D3D = 0x7,
507 OneMinusDestAlpha = 0x8, 1017 OneMinusDestAlpha_D3D = 0x8,
508 DestColor = 0x9, 1018 DestColor_D3D = 0x9,
509 OneMinusDestColor = 0xa, 1019 OneMinusDestColor_D3D = 0xA,
510 SourceAlphaSaturate = 0xb, 1020 SourceAlphaSaturate_D3D = 0xB,
511 Source1Color = 0x10, 1021 BothSourceAlpha_D3D = 0xC,
512 OneMinusSource1Color = 0x11, 1022 OneMinusBothSourceAlpha_D3D = 0xD,
513 Source1Alpha = 0x12, 1023 BlendFactor_D3D = 0xE,
514 OneMinusSource1Alpha = 0x13, 1024 OneMinusBlendFactor_D3D = 0xF,
515 ConstantColor = 0x61, 1025 Source1Color_D3D = 0x10,
516 OneMinusConstantColor = 0x62, 1026 OneMinusSource1Color_D3D = 0x11,
517 ConstantAlpha = 0x63, 1027 Source1Alpha_D3D = 0x12,
518 OneMinusConstantAlpha = 0x64, 1028 OneMinusSource1Alpha_D3D = 0x13,
519 1029
520 // These values are used by Nouveau and some games. 1030 Zero_GL = 0x4000,
521 ZeroGL = 0x4000, 1031 One_GL = 0x4001,
522 OneGL = 0x4001, 1032 SourceColor_GL = 0x4300,
523 SourceColorGL = 0x4300, 1033 OneMinusSourceColor_GL = 0x4301,
524 OneMinusSourceColorGL = 0x4301, 1034 SourceAlpha_GL = 0x4302,
525 SourceAlphaGL = 0x4302, 1035 OneMinusSourceAlpha_GL = 0x4303,
526 OneMinusSourceAlphaGL = 0x4303, 1036 DestAlpha_GL = 0x4304,
527 DestAlphaGL = 0x4304, 1037 OneMinusDestAlpha_GL = 0x4305,
528 OneMinusDestAlphaGL = 0x4305, 1038 DestColor_GL = 0x4306,
529 DestColorGL = 0x4306, 1039 OneMinusDestColor_GL = 0x4307,
530 OneMinusDestColorGL = 0x4307, 1040 SourceAlphaSaturate_GL = 0x4308,
531 SourceAlphaSaturateGL = 0x4308, 1041 ConstantColor_GL = 0xC001,
532 ConstantColorGL = 0xc001, 1042 OneMinusConstantColor_GL = 0xC002,
533 OneMinusConstantColorGL = 0xc002, 1043 ConstantAlpha_GL = 0xC003,
534 ConstantAlphaGL = 0xc003, 1044 OneMinusConstantAlpha_GL = 0xC004,
535 OneMinusConstantAlphaGL = 0xc004, 1045 Source1Color_GL = 0xC900,
536 Source1ColorGL = 0xc900, 1046 OneMinusSource1Color_GL = 0xC901,
537 OneMinusSource1ColorGL = 0xc901, 1047 Source1Alpha_GL = 0xC902,
538 Source1AlphaGL = 0xc902, 1048 OneMinusSource1Alpha_GL = 0xC903,
539 OneMinusSource1AlphaGL = 0xc903,
540 }; 1049 };
541 1050
542 u32 separate_alpha; 1051 u32 separate_alpha;
543 Equation equation_rgb; 1052 Equation color_op;
544 Factor factor_source_rgb; 1053 Factor color_source;
545 Factor factor_dest_rgb; 1054 Factor color_dest;
546 Equation equation_a; 1055 Equation alpha_op;
547 Factor factor_source_a; 1056 Factor alpha_source;
548 Factor factor_dest_a; 1057 u32 enable_global_color_key;
549 INSERT_PADDING_WORDS_NOINIT(1); 1058 Factor alpha_dest;
1059
1060 u32 single_rop_control_enable;
1061 u32 enable[NumRenderTargets];
550 }; 1062 };
551 1063
552 enum class TessellationPrimitive : u32 { 1064 struct BlendPerTarget {
553 Isolines = 0, 1065 u32 separate_alpha;
554 Triangles = 1, 1066 Blend::Equation color_op;
555 Quads = 2, 1067 Blend::Factor color_source;
556 }; 1068 Blend::Factor color_dest;
557 1069 Blend::Equation alpha_op;
558 enum class TessellationSpacing : u32 { 1070 Blend::Factor alpha_source;
559 Equal = 0, 1071 Blend::Factor alpha_dest;
560 FractionalOdd = 1, 1072 INSERT_PADDING_BYTES_NOINIT(0x4);
561 FractionalEven = 2,
562 }; 1073 };
1074 static_assert(sizeof(BlendPerTarget) == 0x20);
563 1075
564 enum class PolygonMode : u32 { 1076 enum class PolygonMode : u32 {
565 Point = 0x1b00, 1077 Point = 0x1B00,
566 Line = 0x1b01, 1078 Line = 0x1B01,
567 Fill = 0x1b02, 1079 Fill = 0x1B02,
568 }; 1080 };
569 1081
570 enum class ShadowRamControl : u32 { 1082 enum class ShadowRamControl : u32 {
@@ -589,18 +1101,22 @@ public:
589 NegativeW = 7, 1101 NegativeW = 7,
590 }; 1102 };
591 1103
592 enum class SamplerIndex : u32 { 1104 enum class SamplerBinding : u32 {
593 Independently = 0, 1105 Independently = 0,
594 ViaHeaderIndex = 1, 1106 ViaHeaderBinding = 1,
595 }; 1107 };
596 1108
597 struct TileMode { 1109 struct TileMode {
1110 enum class DimensionControl : u32 {
1111 DepthDefinesArray = 0,
1112 DepthDefinesDepth = 1,
1113 };
598 union { 1114 union {
599 BitField<0, 4, u32> block_width; 1115 BitField<0, 4, u32> block_width;
600 BitField<4, 4, u32> block_height; 1116 BitField<4, 4, u32> block_height;
601 BitField<8, 4, u32> block_depth; 1117 BitField<8, 4, u32> block_depth;
602 BitField<12, 1, u32> is_pitch_linear; 1118 BitField<12, 1, u32> is_pitch_linear;
603 BitField<16, 1, u32> is_3d; 1119 BitField<16, 1, DimensionControl> dim_control;
604 }; 1120 };
605 }; 1121 };
606 static_assert(sizeof(TileMode) == 4); 1122 static_assert(sizeof(TileMode) == 4);
@@ -616,23 +1132,25 @@ public:
616 BitField<0, 16, u32> depth; 1132 BitField<0, 16, u32> depth;
617 BitField<16, 1, u32> volume; 1133 BitField<16, 1, u32> volume;
618 }; 1134 };
619 u32 layer_stride; 1135 u32 array_pitch;
620 u32 base_layer; 1136 u32 base_layer;
621 INSERT_PADDING_WORDS_NOINIT(7); 1137 u32 mark_ieee_clean;
1138 INSERT_PADDING_BYTES_NOINIT(0x18);
622 1139
623 GPUVAddr Address() const { 1140 GPUVAddr Address() const {
624 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1141 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
625 address_low); 1142 address_low);
626 } 1143 }
627 }; 1144 };
1145 static_assert(sizeof(RenderTargetConfig) == 0x40);
628 1146
629 struct ColorMask { 1147 struct ColorMask {
630 union { 1148 union {
631 u32 raw; 1149 u32 raw;
632 BitField<0, 4, u32> R; 1150 BitField<0, 1, u32> R;
633 BitField<4, 4, u32> G; 1151 BitField<4, 1, u32> G;
634 BitField<8, 4, u32> B; 1152 BitField<8, 1, u32> B;
635 BitField<12, 4, u32> A; 1153 BitField<12, 1, u32> A;
636 }; 1154 };
637 }; 1155 };
638 1156
@@ -643,6 +1161,7 @@ public:
643 f32 translate_x; 1161 f32 translate_x;
644 f32 translate_y; 1162 f32 translate_y;
645 f32 translate_z; 1163 f32 translate_z;
1164
646 union { 1165 union {
647 u32 raw; 1166 u32 raw;
648 BitField<0, 3, ViewportSwizzle> x; 1167 BitField<0, 3, ViewportSwizzle> x;
@@ -650,7 +1169,11 @@ public:
650 BitField<8, 3, ViewportSwizzle> z; 1169 BitField<8, 3, ViewportSwizzle> z;
651 BitField<12, 3, ViewportSwizzle> w; 1170 BitField<12, 3, ViewportSwizzle> w;
652 } swizzle; 1171 } swizzle;
653 INSERT_PADDING_WORDS_NOINIT(1); 1172
1173 union {
1174 BitField<0, 5, u32> x;
1175 BitField<8, 5, u32> y;
1176 } snap_grid_precision;
654 1177
655 Common::Rectangle<f32> GetRect() const { 1178 Common::Rectangle<f32> GetRect() const {
656 return { 1179 return {
@@ -677,21 +1200,14 @@ public:
677 return translate_y + std::fabs(scale_y) - GetY(); 1200 return translate_y + std::fabs(scale_y) - GetY();
678 } 1201 }
679 }; 1202 };
1203 static_assert(sizeof(ViewportTransform) == 0x20);
680 1204
681 struct ScissorTest { 1205 struct Viewport {
682 u32 enable; 1206 enum class PixelCenter : u32 {
683 union { 1207 HalfIntegers = 0,
684 BitField<0, 16, u32> min_x; 1208 Integers = 1,
685 BitField<16, 16, u32> max_x;
686 };
687 union {
688 BitField<0, 16, u32> min_y;
689 BitField<16, 16, u32> max_y;
690 }; 1209 };
691 u32 fill;
692 };
693 1210
694 struct ViewPort {
695 union { 1211 union {
696 BitField<0, 16, u32> x; 1212 BitField<0, 16, u32> x;
697 BitField<16, 16, u32> width; 1213 BitField<16, 16, u32> width;
@@ -703,726 +1219,1816 @@ public:
703 float depth_range_near; 1219 float depth_range_near;
704 float depth_range_far; 1220 float depth_range_far;
705 }; 1221 };
1222 static_assert(sizeof(Viewport) == 0x10);
706 1223
707 struct TransformFeedbackBinding { 1224 struct Window {
708 u32 buffer_enable; 1225 union {
709 u32 address_high; 1226 BitField<0, 16, u32> x_min;
710 u32 address_low; 1227 BitField<16, 16, u32> x_max;
711 s32 buffer_size; 1228 };
712 s32 buffer_offset; 1229 union {
713 INSERT_PADDING_WORDS_NOINIT(3); 1230 BitField<0, 16, u32> y_min;
714 1231 BitField<16, 16, u32> y_max;
715 GPUVAddr Address() const { 1232 };
716 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
717 address_low);
718 }
719 }; 1233 };
720 static_assert(sizeof(TransformFeedbackBinding) == 32); 1234 static_assert(sizeof(Window) == 0x8);
721 1235
722 struct TransformFeedbackLayout { 1236 struct ClipIdExtent {
723 u32 stream; 1237 union {
724 u32 varying_count; 1238 BitField<0, 16, u32> x;
725 u32 stride; 1239 BitField<16, 16, u32> width;
726 INSERT_PADDING_WORDS_NOINIT(1); 1240 };
1241 union {
1242 BitField<0, 16, u32> y;
1243 BitField<16, 16, u32> height;
1244 };
1245 };
1246 static_assert(sizeof(ClipIdExtent) == 0x8);
1247
1248 enum class VisibleCallLimit : u32 {
1249 Limit0 = 0,
1250 Limit1 = 1,
1251 Limit2 = 2,
1252 Limit4 = 3,
1253 Limit8 = 4,
1254 Limit16 = 5,
1255 Limit32 = 6,
1256 Limit64 = 7,
1257 Limit128 = 8,
1258 None = 15,
727 }; 1259 };
728 static_assert(sizeof(TransformFeedbackLayout) == 16);
729
730 bool IsShaderConfigEnabled(std::size_t index) const {
731 // The VertexB is always enabled.
732 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
733 return true;
734 }
735 return shader_config[index].enable != 0;
736 }
737
738 bool IsShaderConfigEnabled(Regs::ShaderProgram type) const {
739 return IsShaderConfigEnabled(static_cast<std::size_t>(type));
740 }
741
742 union {
743 struct {
744 INSERT_PADDING_WORDS_NOINIT(0x44);
745
746 u32 wait_for_idle;
747 1260
748 struct { 1261 struct StatisticsCounter {
749 u32 upload_address; 1262 union {
750 u32 data; 1263 BitField<0, 1, u32> da_vertices;
751 u32 entry; 1264 BitField<1, 1, u32> da_primitives;
752 u32 bind; 1265 BitField<2, 1, u32> vs_invocations;
753 } macros; 1266 BitField<3, 1, u32> gs_invocations;
1267 BitField<4, 1, u32> gs_primitives;
1268 BitField<5, 1, u32> streaming_primitives_succeeded;
1269 BitField<6, 1, u32> streaming_primitives_needed;
1270 BitField<7, 1, u32> clipper_invocations;
1271 BitField<8, 1, u32> clipper_primitives;
1272 BitField<9, 1, u32> ps_invocations;
1273 BitField<11, 1, u32> ti_invocations;
1274 BitField<12, 1, u32> ts_invocations;
1275 BitField<13, 1, u32> ts_primitives;
1276 BitField<14, 1, u32> total_streaming_primitives_needed_succeeded;
1277 BitField<10, 1, u32> vtg_primitives_out;
1278 BitField<15, 1, u32> alpha_beta_clocks;
1279 };
1280 };
754 1281
755 ShadowRamControl shadow_ram_control; 1282 struct ClearRect {
1283 union {
1284 BitField<0, 16, u32> x_min;
1285 BitField<16, 16, u32> x_max;
1286 };
1287 union {
1288 BitField<0, 16, u32> y_min;
1289 BitField<16, 16, u32> y_max;
1290 };
1291 };
756 1292
757 INSERT_PADDING_WORDS_NOINIT(0x16); 1293 struct VertexBuffer {
1294 u32 first;
1295 u32 count;
1296 };
758 1297
759 Upload::Registers upload; 1298 struct InvalidateShaderCacheNoWFI {
760 struct { 1299 union {
761 union { 1300 BitField<0, 1, u32> instruction;
762 BitField<0, 1, u32> linear; 1301 BitField<4, 1, u32> global_data;
763 }; 1302 BitField<12, 1, u32> constant;
764 } exec_upload; 1303 };
1304 };
765 1305
766 u32 data_upload; 1306 struct ZCullSerialization {
1307 enum class Applied : u32 {
1308 Always = 0,
1309 LateZ = 1,
1310 OutOfGamutZ = 2,
1311 LateZOrOutOfGamutZ = 3,
1312 };
1313 union {
1314 BitField<0, 1, u32> enable;
1315 BitField<4, 2, Applied> applied;
1316 };
1317 };
767 1318
768 INSERT_PADDING_WORDS_NOINIT(0x16); 1319 struct ZCullDirFormat {
1320 enum class Zdir : u32 {
1321 Less = 0,
1322 Greater = 1,
1323 };
1324 enum class Zformat : u32 {
1325 MSB = 0,
1326 FP = 1,
1327 Ztrick = 2,
1328 Zf32 = 3,
1329 };
769 1330
770 u32 force_early_fragment_tests; 1331 union {
1332 BitField<0, 16, Zdir> dir;
1333 BitField<16, 16, Zformat> format;
1334 };
1335 };
771 1336
772 INSERT_PADDING_WORDS_NOINIT(0x2D); 1337 struct IteratedBlend {
1338 union {
1339 BitField<0, 1, u32> enable;
1340 BitField<1, 1, u32> enable_alpha;
1341 };
1342 u32 pass_count;
1343 };
773 1344
774 struct { 1345 struct ZCullCriterion {
775 union { 1346 enum class Sfunc : u32 {
776 BitField<0, 16, u32> sync_point; 1347 Never = 0,
777 BitField<16, 1, u32> unknown; 1348 Less = 1,
778 BitField<20, 1, u32> increment; 1349 Equal = 2,
779 }; 1350 LessOrEqual = 3,
780 } sync_info; 1351 Greater = 4,
1352 NotEqual = 5,
1353 GreaterOrEqual = 6,
1354 Always = 7,
1355 };
781 1356
782 INSERT_PADDING_WORDS_NOINIT(0x15); 1357 union {
1358 BitField<0, 8, Sfunc> sfunc;
1359 BitField<8, 1, u32> no_invalidate;
1360 BitField<9, 1, u32> force_match;
1361 BitField<16, 8, u32> sref;
1362 BitField<24, 8, u32> smask;
1363 };
1364 };
783 1365
784 union { 1366 struct LoadIteratedBlend {
785 BitField<0, 2, TessellationPrimitive> prim; 1367 enum class Test : u32 {
786 BitField<4, 2, TessellationSpacing> spacing; 1368 False = 0,
787 BitField<8, 1, u32> cw; 1369 True = 1,
788 BitField<9, 1, u32> connected; 1370 Equal = 2,
789 } tess_mode; 1371 NotEqual = 3,
1372 LessThan = 4,
1373 LessOrEqual = 5,
1374 Greater = 6,
1375 GreaterOrEqual = 7,
1376 };
1377 enum class Operation : u32 {
1378 AddProducts = 0,
1379 SubProducts = 1,
1380 Min = 2,
1381 Max = 3,
1382 Reciprocal = 4,
1383 Add = 5,
1384 Sub = 6,
1385 };
1386 enum class OperandA : u32 {
1387 SrcRGB = 0,
1388 DstRGB = 1,
1389 SrcAAA = 2,
1390 DstAAA = 3,
1391 Temp0_RGB = 4,
1392 Temp1_RGB = 5,
1393 Temp2_RGB = 6,
1394 PBR_RGB = 7,
1395 };
1396 enum class OperandB : u32 {
1397 Zero = 0,
1398 One = 1,
1399 SrcRGB = 2,
1400 SrcAAA = 3,
1401 OneMinusSrcAAA = 4,
1402 DstRGB = 5,
1403 DstAAA = 6,
1404 OneMinusDstAAA = 7,
1405 Temp0_RGB = 9,
1406 Temp1_RGB = 10,
1407 Temp2_RGB = 11,
1408 PBR_RGB = 12,
1409 ConstRGB = 13,
1410 ZeroATimesB = 14,
1411 };
1412 enum class Swizzle : u32 {
1413 RGB = 0,
1414 GBR = 1,
1415 RRR = 2,
1416 GGG = 3,
1417 BBB = 4,
1418 RToA = 5,
1419 };
1420 enum class WriteMask : u32 {
1421 RGB = 0,
1422 ROnly = 1,
1423 GOnly = 2,
1424 BOnly = 3,
1425 };
1426 enum class Pass : u32 {
1427 Temp0 = 0,
1428 Temp1 = 1,
1429 Temp2 = 2,
1430 None = 3,
1431 };
790 1432
791 std::array<f32, 4> tess_level_outer; 1433 u32 instruction_ptr;
792 std::array<f32, 2> tess_level_inner; 1434 union {
1435 BitField<0, 3, Test> test;
1436 BitField<3, 3, Operation> operation;
1437 BitField<6, 3, u32> const_input;
1438 BitField<9, 3, OperandA> operand_a;
1439 BitField<12, 4, OperandB> operand_b;
1440 BitField<16, 3, OperandA> operand_c;
1441 BitField<19, 4, OperandB> operand_d;
1442 BitField<23, 3, Swizzle> output_swizzle;
1443 BitField<26, 2, WriteMask> output_mask;
1444 BitField<28, 2, Pass> output_pass;
1445 BitField<31, 1, u32> test_enabled;
1446 };
1447 };
793 1448
794 INSERT_PADDING_WORDS_NOINIT(0x10); 1449 struct ScissorTest {
1450 u32 enable;
1451 union {
1452 BitField<0, 16, u32> min_x;
1453 BitField<16, 16, u32> max_x;
1454 };
1455 union {
1456 BitField<0, 16, u32> min_y;
1457 BitField<16, 16, u32> max_y;
1458 };
1459 INSERT_PADDING_BYTES_NOINIT(0x4);
1460 };
1461 static_assert(sizeof(ScissorTest) == 0x10);
795 1462
796 u32 rasterize_enable; 1463 struct VPCPerf {
1464 union {
1465 BitField<0, 8, u32> culled_small_lines;
1466 BitField<8, 8, u32> culled_small_triangles;
1467 BitField<16, 8, u32> nonculled_lines_and_points;
1468 BitField<24, 8, u32> nonculled_triangles;
1469 };
1470 };
797 1471
798 std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings; 1472 struct ConstantColorRendering {
1473 u32 enabled;
1474 u32 red;
1475 u32 green;
1476 u32 blue;
1477 u32 alpha;
1478 };
799 1479
800 INSERT_PADDING_WORDS_NOINIT(0xC0); 1480 struct VertexStreamSubstitute {
1481 u32 address_high;
1482 u32 address_low;
801 1483
802 std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts; 1484 GPUVAddr Address() const {
1485 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1486 address_low);
1487 }
1488 };
803 1489
804 INSERT_PADDING_WORDS_NOINIT(0x1); 1490 struct VTGWarpWatermarks {
1491 union {
1492 BitField<0, 16, u32> low;
1493 BitField<16, 16, u32> high;
1494 };
1495 };
805 1496
806 u32 tfb_enabled; 1497 struct SampleMask {
1498 struct Target {
1499 union {
1500 BitField<0, 1, u32> raster_out;
1501 BitField<4, 1, u32> color_target;
1502 };
1503 u32 target;
1504 };
1505 struct Pos {
1506 u32 x0_y0;
1507 u32 x1_y0;
1508 u32 x0_y1;
1509 u32 x1_y1;
1510 };
1511 };
807 1512
808 INSERT_PADDING_WORDS_NOINIT(0x2E); 1513 enum class NonMultisampledZ : u32 {
1514 PerSample = 0,
1515 PixelCenter = 1,
1516 };
809 1517
810 std::array<RenderTargetConfig, NumRenderTargets> rt; 1518 enum class TIRMode : u32 {
1519 Disabled = 0,
1520 RasterNTargetM = 1,
1521 };
811 1522
812 std::array<ViewportTransform, NumViewports> viewport_transform; 1523 enum class AntiAliasRaster : u32 {
1524 Mode1x1 = 0,
1525 Mode2x2 = 2,
1526 Mode4x2_D3D = 4,
1527 Mode2x1_D3D = 5,
1528 Mode4x4 = 6,
1529 };
813 1530
814 std::array<ViewPort, NumViewports> viewports; 1531 struct SurfaceClipIDMemory {
1532 u32 address_high;
1533 u32 address_low;
815 1534
816 INSERT_PADDING_WORDS_NOINIT(0x1D); 1535 GPUVAddr Address() const {
1536 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1537 address_low);
1538 }
1539 };
817 1540
818 struct { 1541 struct TIRModulation {
819 u32 first; 1542 enum class Component : u32 {
820 u32 count; 1543 None = 0,
821 } vertex_buffer; 1544 RGB = 1,
1545 AlphaOnly = 2,
1546 RGBA = 3,
1547 };
1548 enum class Function : u32 {
1549 Linear = 0,
1550 Table = 1,
1551 };
1552 Component component;
1553 Function function;
1554 };
822 1555
823 DepthMode depth_mode; 1556 struct Zeta {
1557 u32 address_high;
1558 u32 address_low;
1559 Tegra::DepthFormat format;
1560 TileMode tile_mode;
1561 u32 array_pitch;
824 1562
825 float clear_color[4]; 1563 GPUVAddr Address() const {
826 float clear_depth; 1564 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1565 address_low);
1566 }
1567 };
827 1568
828 INSERT_PADDING_WORDS_NOINIT(0x3); 1569 struct SurfaceClip {
1570 union {
1571 BitField<0, 16, u32> x;
1572 BitField<16, 16, u32> width;
1573 };
1574 union {
1575 BitField<0, 16, u32> y;
1576 BitField<16, 16, u32> height;
1577 };
1578 };
829 1579
830 s32 clear_stencil; 1580 enum class L2CacheControlPolicy : u32 {
1581 First = 0,
1582 Normal = 1,
1583 Last = 2,
1584 };
831 1585
832 INSERT_PADDING_WORDS_NOINIT(0x2); 1586 struct L2CacheVAFRequests {
1587 union {
1588 BitField<0, 1, u32> system_memory_volatile;
1589 BitField<4, 2, L2CacheControlPolicy> policy;
1590 };
1591 };
833 1592
834 PolygonMode polygon_mode_front; 1593 enum class ViewportMulticast : u32 {
835 PolygonMode polygon_mode_back; 1594 ViewportOrder = 0,
1595 PrimitiveOrder = 1,
1596 };
836 1597
837 INSERT_PADDING_WORDS_NOINIT(0x3); 1598 struct TIRModulationCoeff {
1599 union {
1600 BitField<0, 8, u32> table_v0;
1601 BitField<8, 8, u32> table_v1;
1602 BitField<16, 8, u32> table_v2;
1603 BitField<24, 8, u32> table_v3;
1604 };
1605 };
1606 static_assert(sizeof(TIRModulationCoeff) == 0x4);
838 1607
839 u32 polygon_offset_point_enable; 1608 struct ReduceColorThreshold {
840 u32 polygon_offset_line_enable; 1609 union {
841 u32 polygon_offset_fill_enable; 1610 BitField<0, 8, u32> all_hit_once;
1611 BitField<16, 8, u32> all_covered;
1612 };
1613 };
842 1614
843 u32 patch_vertices; 1615 struct ClearControl {
1616 union {
1617 BitField<0, 1, u32> respect_stencil_mask;
1618 BitField<4, 1, u32> use_clear_rect;
1619 BitField<8, 1, u32> use_scissor;
1620 BitField<12, 1, u32> use_viewport_clip0;
1621 };
1622 };
844 1623
845 INSERT_PADDING_WORDS_NOINIT(0x4); 1624 struct L2CacheRopNonInterlockedReads {
1625 union {
1626 BitField<4, 2, L2CacheControlPolicy> policy;
1627 };
1628 };
846 1629
847 u32 fragment_barrier; 1630 struct VertexOutputAttributeSkipMasks {
1631 struct Attributes {
1632 union {
1633 BitField<0, 1, u32> attribute0_comp0;
1634 BitField<1, 1, u32> attribute0_comp1;
1635 BitField<2, 1, u32> attribute0_comp2;
1636 BitField<3, 1, u32> attribute0_comp3;
1637 BitField<4, 1, u32> attribute1_comp0;
1638 BitField<5, 1, u32> attribute1_comp1;
1639 BitField<6, 1, u32> attribute1_comp2;
1640 BitField<7, 1, u32> attribute1_comp3;
1641 BitField<8, 1, u32> attribute2_comp0;
1642 BitField<9, 1, u32> attribute2_comp1;
1643 BitField<10, 1, u32> attribute2_comp2;
1644 BitField<11, 1, u32> attribute2_comp3;
1645 BitField<12, 1, u32> attribute3_comp0;
1646 BitField<13, 1, u32> attribute3_comp1;
1647 BitField<14, 1, u32> attribute3_comp2;
1648 BitField<15, 1, u32> attribute3_comp3;
1649 BitField<16, 1, u32> attribute4_comp0;
1650 BitField<17, 1, u32> attribute4_comp1;
1651 BitField<18, 1, u32> attribute4_comp2;
1652 BitField<19, 1, u32> attribute4_comp3;
1653 BitField<20, 1, u32> attribute5_comp0;
1654 BitField<21, 1, u32> attribute5_comp1;
1655 BitField<22, 1, u32> attribute5_comp2;
1656 BitField<23, 1, u32> attribute5_comp3;
1657 BitField<24, 1, u32> attribute6_comp0;
1658 BitField<25, 1, u32> attribute6_comp1;
1659 BitField<26, 1, u32> attribute6_comp2;
1660 BitField<27, 1, u32> attribute6_comp3;
1661 BitField<28, 1, u32> attribute7_comp0;
1662 BitField<29, 1, u32> attribute7_comp1;
1663 BitField<30, 1, u32> attribute7_comp2;
1664 BitField<31, 1, u32> attribute7_comp3;
1665 };
1666 };
848 1667
849 INSERT_PADDING_WORDS_NOINIT(0x7); 1668 std::array<Attributes, 2> a;
1669 std::array<Attributes, 2> b;
1670 };
850 1671
851 std::array<ScissorTest, NumViewports> scissor_test; 1672 struct TIRControl {
1673 union {
1674 BitField<0, 1, u32> z_pass_pixel_count_use_raster_samples;
1675 BitField<4, 1, u32> alpha_coverage_use_raster_samples;
1676 BitField<1, 1, u32> reduce_coverage;
1677 };
1678 };
852 1679
853 INSERT_PADDING_WORDS_NOINIT(0x15); 1680 enum class FillViaTriangleMode : u32 {
1681 Disabled = 0,
1682 FillAll = 1,
1683 FillBoundingBox = 2,
1684 };
854 1685
855 s32 stencil_back_func_ref; 1686 struct PsTicketDispenserValue {
856 u32 stencil_back_mask; 1687 union {
857 u32 stencil_back_func_mask; 1688 BitField<0, 8, u32> index;
1689 BitField<8, 16, u32> value;
1690 };
1691 };
858 1692
859 INSERT_PADDING_WORDS_NOINIT(0x5); 1693 struct RegisterWatermarks {
1694 union {
1695 BitField<0, 16, u32> low;
1696 BitField<16, 16, u32> high;
1697 };
1698 };
860 1699
861 u32 invalidate_texture_data_cache; 1700 enum class InvalidateCacheLines : u32 {
1701 All = 0,
1702 One = 1,
1703 };
862 1704
863 INSERT_PADDING_WORDS_NOINIT(0x1); 1705 struct InvalidateTextureDataCacheNoWfi {
1706 union {
1707 BitField<0, 1, InvalidateCacheLines> lines;
1708 BitField<4, 22, u32> tag;
1709 };
1710 };
864 1711
865 u32 tiled_cache_barrier; 1712 struct ZCullRegionEnable {
1713 union {
1714 BitField<0, 1, u32> enable_z;
1715 BitField<4, 1, u32> enable_stencil;
1716 BitField<1, 1, u32> rect_clear;
1717 BitField<2, 1, u32> use_rt_array_index;
1718 BitField<5, 16, u32> rt_array_index;
1719 BitField<3, 1, u32> make_conservative;
1720 };
1721 };
866 1722
867 INSERT_PADDING_WORDS_NOINIT(0x4); 1723 enum class FillMode : u32 {
1724 Point = 1,
1725 Wireframe = 2,
1726 Solid = 3,
1727 };
868 1728
869 u32 color_mask_common; 1729 enum class ShadeMode : u32 {
1730 Flat = 0x1,
1731 Gouraud = 0x2,
1732 GL_Flat = 0x1D00,
1733 GL_Smooth = 0x1D01,
1734 };
870 1735
871 INSERT_PADDING_WORDS_NOINIT(0x2); 1736 enum class AlphaToCoverageDither : u32 {
1737 Footprint_1x1 = 0,
1738 Footprint_2x2 = 1,
1739 Footprint_1x1_Virtual = 2,
1740 };
872 1741
873 f32 depth_bounds[2]; 1742 struct InlineIndex4x8 {
1743 union {
1744 BitField<0, 30, u32> count;
1745 BitField<30, 2, u32> start;
1746 };
1747 union {
1748 BitField<0, 8, u32> index0;
1749 BitField<8, 8, u32> index1;
1750 BitField<16, 8, u32> index2;
1751 BitField<24, 8, u32> index3;
1752 };
1753 };
874 1754
875 INSERT_PADDING_WORDS_NOINIT(0x2); 1755 enum class D3DCullMode : u32 {
1756 None = 0,
1757 CW = 1,
1758 CCW = 2,
1759 };
876 1760
877 u32 rt_separate_frag_data; 1761 struct BlendColor {
1762 f32 r;
1763 f32 g;
1764 f32 b;
1765 f32 a;
1766 };
878 1767
879 INSERT_PADDING_WORDS_NOINIT(0x1); 1768 struct StencilOp {
1769 enum class Op : u32 {
1770 Keep_D3D = 1,
1771 Zero_D3D = 2,
1772 Replace_D3D = 3,
1773 IncrSaturate_D3D = 4,
1774 DecrSaturate_D3D = 5,
1775 Invert_D3D = 6,
1776 Incr_D3D = 7,
1777 Decr_D3D = 8,
1778
1779 Keep_GL = 0x1E00,
1780 Zero_GL = 0,
1781 Replace_GL = 0x1E01,
1782 IncrSaturate_GL = 0x1E02,
1783 DecrSaturate_GL = 0x1E03,
1784 Invert_GL = 0x150A,
1785 Incr_GL = 0x8507,
1786 Decr_GL = 0x8508,
1787 };
880 1788
881 u32 multisample_raster_enable; 1789 Op fail;
882 u32 multisample_raster_samples; 1790 Op zfail;
883 std::array<u32, 4> multisample_sample_mask; 1791 Op zpass;
1792 ComparisonOp func;
1793 };
884 1794
885 INSERT_PADDING_WORDS_NOINIT(0x5); 1795 struct PsSaturate {
1796 // Opposite of DepthMode
1797 enum class Depth : u32 {
1798 ZeroToOne = 0,
1799 MinusOneToOne = 1,
1800 };
886 1801
887 struct { 1802 union {
888 u32 address_high; 1803 BitField<0, 1, u32> output0_enable;
889 u32 address_low; 1804 BitField<1, 1, Depth> output0_range;
890 Tegra::DepthFormat format; 1805 BitField<4, 1, u32> output1_enable;
891 TileMode tile_mode; 1806 BitField<5, 1, Depth> output1_range;
892 u32 layer_stride; 1807 BitField<8, 1, u32> output2_enable;
1808 BitField<9, 1, Depth> output2_range;
1809 BitField<12, 1, u32> output3_enable;
1810 BitField<13, 1, Depth> output3_range;
1811 BitField<16, 1, u32> output4_enable;
1812 BitField<17, 1, Depth> output4_range;
1813 BitField<20, 1, u32> output5_enable;
1814 BitField<21, 1, Depth> output5_range;
1815 BitField<24, 1, u32> output6_enable;
1816 BitField<25, 1, Depth> output6_range;
1817 BitField<28, 1, u32> output7_enable;
1818 BitField<29, 1, Depth> output7_range;
1819 };
893 1820
894 GPUVAddr Address() const { 1821 bool AnyEnabled() const {
895 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1822 return output0_enable || output1_enable || output2_enable || output3_enable ||
896 address_low); 1823 output4_enable || output5_enable || output6_enable || output7_enable;
897 } 1824 }
898 } zeta; 1825 };
899 1826
900 struct { 1827 struct WindowOrigin {
901 union { 1828 enum class Mode : u32 {
902 BitField<0, 16, u32> x; 1829 UpperLeft = 0,
903 BitField<16, 16, u32> width; 1830 LowerLeft = 1,
904 }; 1831 };
905 union { 1832 union {
906 BitField<0, 16, u32> y; 1833 BitField<0, 1, Mode> mode;
907 BitField<16, 16, u32> height; 1834 BitField<4, 1, u32> flip_y;
908 }; 1835 };
909 } render_area; 1836 };
910 1837
911 INSERT_PADDING_WORDS_NOINIT(0x3F); 1838 struct IteratedBlendConstants {
1839 u32 r;
1840 u32 g;
1841 u32 b;
1842 INSERT_PADDING_BYTES_NOINIT(0x4);
1843 };
1844 static_assert(sizeof(IteratedBlendConstants) == 0x10);
912 1845
1846 struct UserClip {
1847 struct Enable {
913 union { 1848 union {
914 BitField<0, 4, u32> stencil; 1849 u32 raw;
915 BitField<4, 4, u32> unknown; 1850 BitField<0, 1, u32> plane0;
916 BitField<8, 4, u32> scissor; 1851 BitField<1, 1, u32> plane1;
917 BitField<12, 4, u32> viewport; 1852 BitField<2, 1, u32> plane2;
918 } clear_flags; 1853 BitField<3, 1, u32> plane3;
919 1854 BitField<4, 1, u32> plane4;
920 INSERT_PADDING_WORDS_NOINIT(0x10); 1855 BitField<5, 1, u32> plane5;
921 1856 BitField<6, 1, u32> plane6;
922 u32 fill_rectangle; 1857 BitField<7, 1, u32> plane7;
923 1858 };
924 INSERT_PADDING_WORDS_NOINIT(0x2);
925
926 u32 conservative_raster_enable;
927
928 INSERT_PADDING_WORDS_NOINIT(0x5);
929
930 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
931 1859
932 std::array<MsaaSampleLocation, 4> multisample_sample_locations; 1860 bool AnyEnabled() const {
1861 return plane0 || plane1 || plane2 || plane3 || plane4 || plane5 || plane6 ||
1862 plane7;
1863 }
1864 };
933 1865
934 INSERT_PADDING_WORDS_NOINIT(0x2); 1866 struct Op {
1867 enum class ClipOrCull : u32 {
1868 Clip = 0,
1869 Cull = 1,
1870 };
935 1871
936 union { 1872 union {
937 BitField<0, 1, u32> enable; 1873 u32 raw;
938 BitField<4, 3, u32> target; 1874 BitField<0, 1, ClipOrCull> plane0;
939 } multisample_coverage_to_color; 1875 BitField<4, 1, ClipOrCull> plane1;
940 1876 BitField<8, 1, ClipOrCull> plane2;
941 INSERT_PADDING_WORDS_NOINIT(0x8); 1877 BitField<12, 1, ClipOrCull> plane3;
942 1878 BitField<16, 1, ClipOrCull> plane4;
943 struct { 1879 BitField<20, 1, ClipOrCull> plane5;
944 union { 1880 BitField<24, 1, ClipOrCull> plane6;
945 BitField<0, 4, u32> count; 1881 BitField<28, 1, ClipOrCull> plane7;
946 BitField<4, 3, u32> map_0;
947 BitField<7, 3, u32> map_1;
948 BitField<10, 3, u32> map_2;
949 BitField<13, 3, u32> map_3;
950 BitField<16, 3, u32> map_4;
951 BitField<19, 3, u32> map_5;
952 BitField<22, 3, u32> map_6;
953 BitField<25, 3, u32> map_7;
954 };
955
956 u32 Map(std::size_t index) const {
957 const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3,
958 map_4, map_5, map_6, map_7};
959 ASSERT(index < maps.size());
960 return maps[index];
961 }
962 } rt_control;
963
964 INSERT_PADDING_WORDS_NOINIT(0x2);
965
966 u32 zeta_width;
967 u32 zeta_height;
968 union {
969 BitField<0, 16, u32> zeta_depth;
970 BitField<16, 1, u32> zeta_volume;
971 }; 1882 };
1883 };
1884 };
972 1885
973 SamplerIndex sampler_index; 1886 struct AntiAliasAlphaControl {
1887 union {
1888 BitField<0, 1, u32> alpha_to_coverage;
1889 BitField<4, 1, u32> alpha_to_one;
1890 };
1891 };
974 1892
975 INSERT_PADDING_WORDS_NOINIT(0x2); 1893 struct RenderEnable {
1894 enum class Override : u32 {
1895 UseRenderEnable = 0,
1896 AlwaysRender = 1,
1897 NeverRender = 2,
1898 };
976 1899
977 std::array<u32, 8> gp_passthrough_mask; 1900 enum class Mode : u32 {
1901 False = 0,
1902 True = 1,
1903 Conditional = 2,
1904 IfEqual = 3,
1905 IfNotEqual = 4,
1906 };
978 1907
979 INSERT_PADDING_WORDS_NOINIT(0x1B); 1908 u32 address_high;
1909 u32 address_low;
1910 Mode mode;
980 1911
981 u32 depth_test_enable; 1912 GPUVAddr Address() const {
1913 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1914 address_low);
1915 }
1916 };
982 1917
983 INSERT_PADDING_WORDS_NOINIT(0x5); 1918 struct TexSampler {
1919 u32 address_high;
1920 u32 address_low;
1921 u32 limit;
984 1922
985 u32 independent_blend_enable; 1923 GPUVAddr Address() const {
1924 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1925 address_low);
1926 }
1927 };
986 1928
987 u32 depth_write_enabled; 1929 struct TexHeader {
1930 u32 address_high;
1931 u32 address_low;
1932 u32 limit;
988 1933
989 u32 alpha_test_enabled; 1934 GPUVAddr Address() const {
1935 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1936 address_low);
1937 }
1938 };
990 1939
991 INSERT_PADDING_WORDS_NOINIT(0x6); 1940 enum class ZCullRegionFormat : u32 {
1941 Z_4x4 = 0,
1942 ZS_4x4 = 1,
1943 Z_4x2 = 2,
1944 Z_2x4 = 3,
1945 Z_16x8_4x4 = 4,
1946 Z_8x8_4x2 = 5,
1947 Z_8x8_2x4 = 6,
1948 Z_16x16_4x8 = 7,
1949 Z_4x8_2x2 = 8,
1950 ZS_16x8_4x2 = 9,
1951 ZS_16x8_2x4 = 10,
1952 ZS_8x8_2x2 = 11,
1953 Z_4x8_1x1 = 12,
1954 };
1955
1956 struct RtLayer {
1957 enum class Control {
1958 LayerSelectsLayer = 0,
1959 GeometryShaderSelectsLayer = 1,
1960 };
1961
1962 union {
1963 BitField<0, 16, u32> layer;
1964 BitField<16, 1, u32> control;
1965 };
1966 };
992 1967
993 u32 d3d_cull_mode; 1968 struct InlineIndex2x16 {
1969 union {
1970 BitField<0, 31, u32> count;
1971 BitField<31, 1, u32> start_odd;
1972 };
1973 union {
1974 BitField<0, 16, u32> even;
1975 BitField<16, 16, u32> odd;
1976 };
1977 };
994 1978
995 ComparisonOp depth_test_func; 1979 struct VertexGlobalBaseOffset {
996 float alpha_test_ref; 1980 u32 address_high;
997 ComparisonOp alpha_test_func; 1981 u32 address_low;
998 u32 draw_tfb_stride;
999 struct {
1000 float r;
1001 float g;
1002 float b;
1003 float a;
1004 } blend_color;
1005 1982
1006 INSERT_PADDING_WORDS_NOINIT(0x4); 1983 GPUVAddr Address() const {
1984 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
1985 address_low);
1986 }
1987 };
1007 1988
1008 struct { 1989 struct ZCullRegionPixelOffset {
1009 u32 separate_alpha; 1990 u32 width;
1010 Blend::Equation equation_rgb; 1991 u32 height;
1011 Blend::Factor factor_source_rgb; 1992 };
1012 Blend::Factor factor_dest_rgb;
1013 Blend::Equation equation_a;
1014 Blend::Factor factor_source_a;
1015 INSERT_PADDING_WORDS_NOINIT(1);
1016 Blend::Factor factor_dest_a;
1017 1993
1018 u32 enable_common; 1994 struct PointSprite {
1019 u32 enable[NumRenderTargets]; 1995 enum class RMode : u32 {
1020 } blend; 1996 Zero = 0,
1997 FromR = 1,
1998 FromS = 2,
1999 };
2000 enum class Origin : u32 {
2001 Bottom = 0,
2002 Top = 1,
2003 };
2004 enum class Texture : u32 {
2005 Passthrough = 0,
2006 Generate = 1,
2007 };
1021 2008
1022 u32 stencil_enable; 2009 union {
1023 StencilOp stencil_front_op_fail; 2010 BitField<0, 2, RMode> rmode;
1024 StencilOp stencil_front_op_zfail; 2011 BitField<2, 1, Origin> origin;
1025 StencilOp stencil_front_op_zpass; 2012 BitField<3, 1, Texture> texture0;
1026 ComparisonOp stencil_front_func_func; 2013 BitField<4, 1, Texture> texture1;
1027 s32 stencil_front_func_ref; 2014 BitField<5, 1, Texture> texture2;
1028 u32 stencil_front_func_mask; 2015 BitField<6, 1, Texture> texture3;
1029 u32 stencil_front_mask; 2016 BitField<7, 1, Texture> texture4;
2017 BitField<8, 1, Texture> texture5;
2018 BitField<9, 1, Texture> texture6;
2019 BitField<10, 1, Texture> texture7;
2020 BitField<11, 1, Texture> texture8;
2021 BitField<12, 1, Texture> texture9;
2022 };
2023 };
1030 2024
1031 INSERT_PADDING_WORDS_NOINIT(0x2); 2025 struct ProgramRegion {
2026 u32 address_high;
2027 u32 address_low;
1032 2028
1033 u32 frag_color_clamp; 2029 GPUVAddr Address() const {
2030 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
2031 address_low);
2032 }
2033 };
1034 2034
1035 union { 2035 struct DefaultAttributes {
1036 BitField<0, 1, u32> y_negate; 2036 enum class Diffuse : u32 {
1037 BitField<4, 1, u32> triangle_rast_flip; 2037 Vector_0001 = 0,
1038 } screen_y_control; 2038 Vector_1111 = 1,
2039 };
2040 enum class Specular : u32 {
2041 Vector_0000 = 0,
2042 Vector_0001 = 1,
2043 };
2044 enum class Vector : u32 {
2045 Vector_0000 = 0,
2046 Vector_0001 = 1,
2047 };
2048 enum class FixedFncTexture : u32 {
2049 Vector_0000 = 0,
2050 Vector_0001 = 1,
2051 };
2052 enum class DX9Color0 : u32 {
2053 Vector_0000 = 0,
2054 Vector_1111 = 1,
2055 };
2056 enum class DX9Color1To15 : u32 {
2057 Vector_0000 = 0,
2058 Vector_0001 = 1,
2059 };
1039 2060
1040 float line_width_smooth; 2061 union {
1041 float line_width_aliased; 2062 BitField<0, 1, Diffuse> color_front_diffuse;
2063 BitField<1, 1, Specular> color_front_specular;
2064 BitField<2, 1, Vector> generic_vector;
2065 BitField<3, 1, FixedFncTexture> fixed_fnc_texture;
2066 BitField<4, 1, DX9Color0> dx9_color0;
2067 BitField<5, 1, DX9Color1To15> dx9_color1_to_15;
2068 };
2069 };
1042 2070
1043 INSERT_PADDING_WORDS_NOINIT(0x1B); 2071 struct Draw {
2072 enum class PrimitiveId : u32 {
2073 First = 0,
2074 Unchanged = 1,
2075 };
2076 enum class InstanceId : u32 {
2077 First = 0,
2078 Subsequent = 1,
2079 Unchanged = 2,
2080 };
2081 enum class SplitMode : u32 {
2082 NormalBeginNormal = 0,
2083 NormalBeginOpen = 1,
2084 OpenBeginOpen = 2,
2085 OpenBeginNormal = 3,
2086 };
1044 2087
1045 u32 invalidate_sampler_cache_no_wfi; 2088 u32 end;
1046 u32 invalidate_texture_header_cache_no_wfi; 2089 union {
2090 u32 begin;
2091 BitField<0, 16, PrimitiveTopology> topology;
2092 BitField<24, 1, PrimitiveId> primitive_id;
2093 BitField<26, 2, InstanceId> instance_id;
2094 BitField<29, 2, SplitMode> split_mode;
2095 };
2096 };
1047 2097
1048 INSERT_PADDING_WORDS_NOINIT(0x2); 2098 struct VertexIdCopy {
2099 union {
2100 BitField<0, 1, u32> enable;
2101 BitField<4, 8, u32> attribute_slot;
2102 };
2103 };
1049 2104
1050 u32 vb_element_base; 2105 struct ShaderBasedCull {
1051 u32 vb_base_instance; 2106 union {
2107 BitField<1, 1, u32> batch_cull_enable;
2108 BitField<0, 1, u32> before_fetch_enable;
2109 };
2110 };
1052 2111
1053 INSERT_PADDING_WORDS_NOINIT(0x35); 2112 struct ClassVersion {
2113 union {
2114 BitField<0, 16, u32> current;
2115 BitField<16, 16, u32> oldest_supported;
2116 };
2117 };
1054 2118
1055 u32 clip_distance_enabled; 2119 struct PrimitiveRestart {
2120 u32 enabled;
2121 u32 index;
2122 };
1056 2123
1057 u32 samplecnt_enable; 2124 struct OutputVertexId {
2125 union {
2126 BitField<12, 1, u32> uses_array_start;
2127 };
2128 };
1058 2129
1059 float point_size; 2130 enum class PointCenterMode : u32 {
2131 GL = 0,
2132 D3D = 1,
2133 };
1060 2134
1061 INSERT_PADDING_WORDS_NOINIT(0x1); 2135 enum class LineSmoothParams : u32 {
2136 Falloff_1_00 = 0,
2137 Falloff_1_33 = 1,
2138 Falloff_1_66 = 2,
2139 };
1062 2140
1063 u32 point_sprite_enable; 2141 struct LineSmoothEdgeTable {
2142 union {
2143 BitField<0, 8, u32> v0;
2144 BitField<8, 8, u32> v1;
2145 BitField<16, 8, u32> v2;
2146 BitField<24, 8, u32> v3;
2147 };
2148 };
1064 2149
1065 INSERT_PADDING_WORDS_NOINIT(0x3); 2150 struct LineStippleParams {
2151 union {
2152 BitField<0, 8, u32> factor;
2153 BitField<8, 16, u32> pattern;
2154 };
2155 };
1066 2156
1067 CounterReset counter_reset; 2157 enum class ProvokingVertex : u32 {
2158 First = 0,
2159 Last = 1,
2160 };
1068 2161
1069 u32 multisample_enable; 2162 struct ShaderControl {
2163 enum class Partial : u32 {
2164 Zero = 0,
2165 Infinity = 1,
2166 };
2167 enum class FP32NanBehavior : u32 {
2168 Legacy = 0,
2169 FP64Compatible = 1,
2170 };
2171 enum class FP32F2INanBehavior : u32 {
2172 PassZero = 0,
2173 PassIndefinite = 1,
2174 };
1070 2175
1071 u32 zeta_enable; 2176 union {
2177 BitField<0, 1, Partial> default_partial;
2178 BitField<1, 1, FP32NanBehavior> fp32_nan_behavior;
2179 BitField<2, 1, FP32F2INanBehavior> fp32_f2i_nan_behavior;
2180 };
2181 };
1072 2182
1073 union { 2183 struct SphVersion {
1074 BitField<0, 1, u32> alpha_to_coverage; 2184 union {
1075 BitField<4, 1, u32> alpha_to_one; 2185 BitField<0, 16, u32> current;
1076 } multisample_control; 2186 BitField<16, 16, u32> oldest_supported;
2187 };
2188 };
1077 2189
1078 INSERT_PADDING_WORDS_NOINIT(0x4); 2190 struct AlphaToCoverageOverride {
2191 union {
2192 BitField<0, 1, u32> qualify_by_anti_alias_enable;
2193 BitField<1, 1, u32> qualify_by_ps_sample_mask_enable;
2194 };
2195 };
1079 2196
1080 struct { 2197 struct AamVersion {
1081 u32 address_high; 2198 union {
1082 u32 address_low; 2199 BitField<0, 16, u32> current;
1083 ConditionMode mode; 2200 BitField<16, 16, u32> oldest_supported;
2201 };
2202 };
1084 2203
1085 GPUVAddr Address() const { 2204 struct IndexBuffer {
1086 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2205 u32 start_addr_high;
1087 address_low); 2206 u32 start_addr_low;
1088 } 2207 u32 limit_addr_high;
1089 } condition; 2208 u32 limit_addr_low;
2209 IndexFormat format;
2210 u32 first;
2211 u32 count;
2212
2213 unsigned FormatSizeInBytes() const {
2214 switch (format) {
2215 case IndexFormat::UnsignedByte:
2216 return 1;
2217 case IndexFormat::UnsignedShort:
2218 return 2;
2219 case IndexFormat::UnsignedInt:
2220 return 4;
2221 }
2222 ASSERT(false);
2223 return 1;
2224 }
1090 2225
1091 struct { 2226 GPUVAddr StartAddress() const {
1092 u32 address_high; 2227 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_addr_high) << 32) |
1093 u32 address_low; 2228 start_addr_low);
1094 u32 limit; 2229 }
1095 2230
1096 GPUVAddr Address() const { 2231 GPUVAddr EndAddress() const {
1097 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2232 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_addr_high) << 32) |
1098 address_low); 2233 limit_addr_low);
1099 } 2234 }
1100 } tsc;
1101 2235
1102 INSERT_PADDING_WORDS_NOINIT(0x1); 2236 /// Adjust the index buffer offset so it points to the first desired index.
2237 GPUVAddr IndexStart() const {
2238 return StartAddress() +
2239 static_cast<size_t>(first) * static_cast<size_t>(FormatSizeInBytes());
2240 }
2241 };
1103 2242
1104 float polygon_offset_factor; 2243 struct IndexBufferSmall {
2244 union {
2245 BitField<0, 16, u32> first;
2246 BitField<16, 12, u32> count;
2247 BitField<28, 4, PrimitiveTopology> topology;
2248 };
2249 };
1105 2250
1106 u32 line_smooth_enable; 2251 struct VertexStreamInstances {
2252 std::array<u32, NumVertexArrays> is_instanced;
1107 2253
1108 struct { 2254 /// Returns whether the vertex array specified by index is supposed to be
1109 u32 address_high; 2255 /// accessed per instance or not.
1110 u32 address_low; 2256 bool IsInstancingEnabled(std::size_t index) const {
1111 u32 limit; 2257 return is_instanced[index];
2258 }
2259 };
1112 2260
1113 GPUVAddr Address() const { 2261 struct AttributePointSize {
1114 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2262 union {
1115 address_low); 2263 BitField<0, 1, u32> enabled;
1116 } 2264 BitField<4, 8, u32> slot;
1117 } tic; 2265 };
2266 };
1118 2267
1119 INSERT_PADDING_WORDS_NOINIT(0x5); 2268 struct ViewportClipControl {
2269 enum class GeometryGuardband : u32 {
2270 Scale256 = 0,
2271 Scale1 = 1,
2272 };
2273 enum class GeometryClip : u32 {
2274 WZero = 0,
2275 Passthrough = 1,
2276 FrustumXY = 2,
2277 FrustumXYZ = 3,
2278 WZeroNoZCull = 4,
2279 FrustumZ = 5,
2280 WZeroTriFillOrClip = 6,
2281 };
2282 enum class GeometryGuardbandZ : u32 {
2283 SameAsXY = 0,
2284 Scale256 = 1,
2285 Scale1 = 2,
2286 };
1120 2287
1121 u32 stencil_two_side_enable; 2288 union {
1122 StencilOp stencil_back_op_fail; 2289 BitField<0, 1, u32> depth_0_to_1;
1123 StencilOp stencil_back_op_zfail; 2290 BitField<3, 1, u32> pixel_min_z;
1124 StencilOp stencil_back_op_zpass; 2291 BitField<4, 1, u32> pixel_max_z;
1125 ComparisonOp stencil_back_func_func; 2292 BitField<7, 1, GeometryGuardband> geometry_guardband;
2293 BitField<11, 3, GeometryClip> geometry_clip;
2294 BitField<1, 2, GeometryGuardbandZ> geometry_guardband_z;
2295 };
2296 };
1126 2297
1127 INSERT_PADDING_WORDS_NOINIT(0x4); 2298 enum class PrimitiveTopologyControl : u32 {
2299 UseInBeginMethods = 0,
2300 UseSeparateState = 1,
2301 };
1128 2302
1129 u32 framebuffer_srgb; 2303 struct WindowClip {
2304 enum class Type : u32 {
2305 Inclusive = 0,
2306 Exclusive = 1,
2307 ClipAll = 2,
2308 };
1130 2309
1131 float polygon_offset_units; 2310 u32 enable;
2311 Type type;
2312 };
1132 2313
1133 INSERT_PADDING_WORDS_NOINIT(0x4); 2314 enum class InvalidateZCull : u32 {
2315 Invalidate = 0,
2316 };
1134 2317
1135 Tegra::Texture::MsaaMode multisample_mode; 2318 struct ZCull {
2319 union {
2320 BitField<0, 1, u32> z_enable;
2321 BitField<1, 1, u32> stencil_enable;
2322 };
2323 union {
2324 BitField<0, 1, u32> z_min_enbounded;
2325 BitField<1, 1, u32> z_max_unbounded;
2326 };
2327 };
1136 2328
1137 INSERT_PADDING_WORDS_NOINIT(0xC); 2329 struct LogicOp {
2330 enum class Op : u32 {
2331 Clear = 0x1500,
2332 And = 0x1501,
2333 AndReverse = 0x1502,
2334 Copy = 0x1503,
2335 AndInverted = 0x1504,
2336 NoOp = 0x1505,
2337 Xor = 0x1506,
2338 Or = 0x1507,
2339 Nor = 0x1508,
2340 Equiv = 0x1509,
2341 Invert = 0x150A,
2342 OrReverse = 0x150B,
2343 CopyInverted = 0x150C,
2344 OrInverted = 0x150D,
2345 Nand = 0x150E,
2346 Set = 0x150F,
2347 };
1138 2348
1139 union { 2349 u32 enable;
1140 BitField<2, 1, u32> coord_origin; 2350 Op op;
1141 BitField<3, 10, u32> enable; 2351 };
1142 } point_coord_replace;
1143
1144 struct {
1145 u32 code_address_high;
1146 u32 code_address_low;
1147
1148 GPUVAddr CodeAddress() const {
1149 return static_cast<GPUVAddr>(
1150 (static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low);
1151 }
1152 } code_address;
1153 INSERT_PADDING_WORDS_NOINIT(1);
1154
1155 struct {
1156 u32 vertex_end_gl;
1157 union {
1158 u32 vertex_begin_gl;
1159 BitField<0, 16, PrimitiveTopology> topology;
1160 BitField<26, 1, u32> instance_next;
1161 BitField<27, 1, u32> instance_cont;
1162 };
1163 } draw;
1164
1165 INSERT_PADDING_WORDS_NOINIT(0xA);
1166
1167 struct {
1168 u32 enabled;
1169 u32 index;
1170 } primitive_restart;
1171
1172 INSERT_PADDING_WORDS_NOINIT(0xE);
1173
1174 u32 provoking_vertex_last;
1175
1176 INSERT_PADDING_WORDS_NOINIT(0x50);
1177
1178 struct {
1179 u32 start_addr_high;
1180 u32 start_addr_low;
1181 u32 end_addr_high;
1182 u32 end_addr_low;
1183 IndexFormat format;
1184 u32 first;
1185 u32 count;
1186
1187 unsigned FormatSizeInBytes() const {
1188 switch (format) {
1189 case IndexFormat::UnsignedByte:
1190 return 1;
1191 case IndexFormat::UnsignedShort:
1192 return 2;
1193 case IndexFormat::UnsignedInt:
1194 return 4;
1195 }
1196 ASSERT(false);
1197 return 1;
1198 }
1199
1200 GPUVAddr StartAddress() const {
1201 return static_cast<GPUVAddr>(
1202 (static_cast<GPUVAddr>(start_addr_high) << 32) | start_addr_low);
1203 }
1204
1205 GPUVAddr EndAddress() const {
1206 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) |
1207 end_addr_low);
1208 }
1209
1210 /// Adjust the index buffer offset so it points to the first desired index.
1211 GPUVAddr IndexStart() const {
1212 return StartAddress() + static_cast<size_t>(first) *
1213 static_cast<size_t>(FormatSizeInBytes());
1214 }
1215 } index_array;
1216 2352
1217 union { 2353 struct ClearSurface {
1218 BitField<0, 16, u32> first; 2354 union {
1219 BitField<16, 16, u32> count; 2355 u32 raw;
1220 } small_index; 2356 BitField<0, 1, u32> Z;
2357 BitField<1, 1, u32> S;
2358 BitField<2, 1, u32> R;
2359 BitField<3, 1, u32> G;
2360 BitField<4, 1, u32> B;
2361 BitField<5, 1, u32> A;
2362 BitField<6, 4, u32> RT;
2363 BitField<10, 16, u32> layer;
2364 };
2365 };
1221 2366
1222 union { 2367 struct ReportSemaphore {
1223 BitField<0, 16, u32> first; 2368 struct Compare {
1224 BitField<16, 16, u32> count; 2369 u32 initial_sequence;
1225 } small_index_2; 2370 u32 initial_mode;
2371 u32 unknown1;
2372 u32 unknown2;
2373 u32 current_sequence;
2374 u32 current_mode;
2375 };
1226 2376
1227 INSERT_PADDING_WORDS_NOINIT(0x5); 2377 enum class Operation : u32 {
2378 Release = 0,
2379 Acquire = 1,
2380 ReportOnly = 2,
2381 Trap = 3,
2382 };
1228 2383
1229 INSERT_PADDING_WORDS_NOINIT(0x1F); 2384 enum class Release : u32 {
2385 AfterAllPreceedingReads = 0,
2386 AfterAllPreceedingWrites = 1,
2387 };
1230 2388
1231 float polygon_offset_clamp; 2389 enum class Acquire : u32 {
2390 BeforeAnyFollowingWrites = 0,
2391 BeforeAnyFollowingReads = 1,
2392 };
1232 2393
1233 struct { 2394 enum class Location : u32 {
1234 u32 is_instanced[NumVertexArrays]; 2395 None = 0,
2396 VertexFetch = 1,
2397 VertexShader = 2,
2398 VPC = 4,
2399 StreamingOutput = 5,
2400 GeometryShader = 6,
2401 ZCull = 7,
2402 TessellationInit = 8,
2403 TessellationShader = 9,
2404 PixelShader = 10,
2405 DepthTest = 12,
2406 All = 15,
2407 };
1235 2408
1236 /// Returns whether the vertex array specified by index is supposed to be 2409 enum class Comparison : u32 {
1237 /// accessed per instance or not. 2410 NotEqual = 0,
1238 bool IsInstancingEnabled(std::size_t index) const { 2411 GreaterOrEqual = 1,
1239 return is_instanced[index]; 2412 };
1240 }
1241 } instanced_arrays;
1242 2413
1243 INSERT_PADDING_WORDS_NOINIT(0x4); 2414 enum class Report : u32 {
2415 Payload = 0, // "None" in docs, but confirmed via hardware to return the payload
2416 VerticesGenerated = 1,
2417 ZPassPixelCount = 2,
2418 PrimitivesGenerated = 3,
2419 AlphaBetaClocks = 4,
2420 VertexShaderInvocations = 5,
2421 StreamingPrimitivesNeededMinusSucceeded = 6,
2422 GeometryShaderInvocations = 7,
2423 GeometryShaderPrimitivesGenerated = 9,
2424 ZCullStats0 = 10,
2425 StreamingPrimitivesSucceeded = 11,
2426 ZCullStats1 = 12,
2427 StreamingPrimitivesNeeded = 13,
2428 ZCullStats2 = 14,
2429 ClipperInvocations = 15,
2430 ZCullStats3 = 16,
2431 ClipperPrimitivesGenerated = 17,
2432 VtgPrimitivesOut = 18,
2433 PixelShaderInvocations = 19,
2434 ZPassPixelCount64 = 21,
2435 IEEECleanColorTarget = 24,
2436 IEEECleanZetaTarget = 25,
2437 StreamingByteCount = 26,
2438 TessellationInitInvocations = 27,
2439 BoundingRectangle = 28,
2440 TessellationShaderInvocations = 29,
2441 TotalStreamingPrimitivesNeededMinusSucceeded = 30,
2442 TessellationShaderPrimitivesGenerated = 31,
2443 };
1244 2444
1245 union { 2445 u32 address_high;
1246 BitField<0, 1, u32> enable; 2446 u32 address_low;
1247 BitField<4, 8, u32> unk4; 2447 u32 payload;
1248 } vp_point_size; 2448 union {
2449 u32 raw;
2450 BitField<0, 2, Operation> operation;
2451 BitField<4, 1, Release> release;
2452 BitField<8, 1, Acquire> acquire;
2453 BitField<12, 4, Location> location;
2454 BitField<16, 1, Comparison> comparison;
2455 BitField<20, 1, u32> awaken_enable;
2456 BitField<23, 5, Report> report;
2457 BitField<28, 1, u32> short_query;
2458 BitField<5, 3, u32> sub_report;
2459 BitField<21, 1, u32> dword_number;
2460 BitField<2, 1, u32> disable_flush;
2461 BitField<3, 1, u32> reduction_enable;
2462 BitField<9, 3, ReductionOp> reduction_op;
2463 BitField<17, 2, u32> format_signed;
2464 } query;
1249 2465
1250 INSERT_PADDING_WORDS_NOINIT(1); 2466 GPUVAddr Address() const {
2467 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
2468 address_low);
2469 }
2470 };
1251 2471
1252 u32 cull_test_enabled; 2472 struct VertexStream {
1253 FrontFace front_face; 2473 union {
1254 CullFace cull_face; 2474 BitField<0, 12, u32> stride;
2475 BitField<12, 1, u32> enable;
2476 };
2477 u32 address_high;
2478 u32 address_low;
2479 u32 frequency;
1255 2480
1256 u32 pixel_center_integer; 2481 GPUVAddr Address() const {
2482 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
2483 address_low);
2484 }
1257 2485
1258 INSERT_PADDING_WORDS_NOINIT(0x1); 2486 bool IsEnabled() const {
2487 return enable != 0 && Address() != 0;
2488 }
2489 };
2490 static_assert(sizeof(VertexStream) == 0x10);
1259 2491
1260 u32 viewport_transform_enabled; 2492 struct VertexStreamLimit {
2493 u32 address_high;
2494 u32 address_low;
1261 2495
1262 INSERT_PADDING_WORDS_NOINIT(0x3); 2496 GPUVAddr Address() const {
2497 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
2498 address_low);
2499 }
2500 };
2501 static_assert(sizeof(VertexStreamLimit) == 0x8);
1263 2502
1264 union { 2503 enum class ShaderType : u32 {
1265 BitField<0, 1, u32> depth_range_0_1; 2504 VertexA = 0,
1266 BitField<3, 1, u32> depth_clamp_near; 2505 VertexB = 1,
1267 BitField<4, 1, u32> depth_clamp_far; 2506 TessellationInit = 2,
1268 BitField<11, 1, u32> depth_clamp_disabled; 2507 Tessellation = 3,
1269 } view_volume_clip_control; 2508 Geometry = 4,
2509 Pixel = 5,
2510 };
1270 2511
1271 INSERT_PADDING_WORDS_NOINIT(0xC); 2512 struct Pipeline {
2513 union {
2514 BitField<0, 1, u32> enable;
2515 BitField<4, 4, ShaderType> program;
2516 };
2517 u32 offset;
2518 u32 reservedA;
2519 u32 register_count;
2520 u32 binding_group;
2521 std::array<u32, 4> reserved;
2522 INSERT_PADDING_BYTES_NOINIT(0x1C);
2523 };
2524 static_assert(sizeof(Pipeline) == 0x40);
1272 2525
1273 PrimitiveTopologyOverride topology_override; 2526 bool IsShaderConfigEnabled(std::size_t index) const {
2527 // The VertexB is always enabled.
2528 if (index == static_cast<std::size_t>(ShaderType::VertexB)) {
2529 return true;
2530 }
2531 return pipelines[index].enable != 0;
2532 }
1274 2533
1275 INSERT_PADDING_WORDS_NOINIT(0x12); 2534 bool IsShaderConfigEnabled(ShaderType type) const {
2535 return IsShaderConfigEnabled(static_cast<std::size_t>(type));
2536 }
1276 2537
1277 u32 depth_bounds_enable; 2538 struct ConstantBuffer {
2539 u32 size;
2540 u32 address_high;
2541 u32 address_low;
2542 u32 offset;
2543 std::array<u32, NumCBData> buffer;
1278 2544
1279 INSERT_PADDING_WORDS_NOINIT(1); 2545 GPUVAddr Address() const {
2546 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
2547 address_low);
2548 }
2549 };
1280 2550
1281 struct { 2551 struct BindGroup {
1282 u32 enable; 2552 std::array<u32, 4> reserved;
1283 LogicOperation operation; 2553 union {
1284 } logic_op; 2554 u32 raw_config;
2555 BitField<0, 1, u32> valid;
2556 BitField<4, 5, u32> shader_slot;
2557 };
2558 INSERT_PADDING_BYTES_NOINIT(0xC);
2559 };
2560 static_assert(sizeof(BindGroup) == 0x20);
1285 2561
1286 INSERT_PADDING_WORDS_NOINIT(0x1); 2562 struct StreamOutLayout {
2563 union {
2564 BitField<0, 8, u32> attribute0;
2565 BitField<8, 8, u32> attribute1;
2566 BitField<16, 8, u32> attribute2;
2567 BitField<24, 8, u32> attribute3;
2568 };
2569 };
1287 2570
2571 struct ShaderPerformance {
2572 struct ControlA {
1288 union { 2573 union {
1289 u32 raw; 2574 BitField<0, 2, u32> event0;
1290 BitField<0, 1, u32> Z; 2575 BitField<2, 3, u32> bit0;
1291 BitField<1, 1, u32> S; 2576 BitField<5, 2, u32> event1;
1292 BitField<2, 1, u32> R; 2577 BitField<7, 3, u32> bit1;
1293 BitField<3, 1, u32> G; 2578 BitField<10, 2, u32> event2;
1294 BitField<4, 1, u32> B; 2579 BitField<12, 3, u32> bit2;
1295 BitField<5, 1, u32> A; 2580 BitField<15, 2, u32> event3;
1296 BitField<6, 4, u32> RT; 2581 BitField<17, 3, u32> bit3;
1297 BitField<10, 11, u32> layer; 2582 BitField<20, 2, u32> event4;
1298 } clear_buffers; 2583 BitField<22, 3, u32> bit4;
1299 INSERT_PADDING_WORDS_NOINIT(0xB); 2584 BitField<25, 2, u32> event5;
1300 std::array<ColorMask, NumRenderTargets> color_mask; 2585 BitField<27, 3, u32> bit5;
1301 INSERT_PADDING_WORDS_NOINIT(0x38); 2586 BitField<30, 2, u32> spare;
1302 2587 };
1303 struct { 2588 };
1304 u32 query_address_high;
1305 u32 query_address_low;
1306 u32 query_sequence;
1307 union {
1308 u32 raw;
1309 BitField<0, 2, QueryOperation> operation;
1310 BitField<4, 1, u32> fence;
1311 BitField<12, 4, QueryUnit> unit;
1312 BitField<16, 1, QuerySyncCondition> sync_cond;
1313 BitField<23, 5, QuerySelect> select;
1314 BitField<28, 1, u32> short_query;
1315 } query_get;
1316
1317 GPUVAddr QueryAddress() const {
1318 return static_cast<GPUVAddr>(
1319 (static_cast<GPUVAddr>(query_address_high) << 32) | query_address_low);
1320 }
1321 } query;
1322
1323 INSERT_PADDING_WORDS_NOINIT(0x3C);
1324
1325 struct {
1326 union {
1327 BitField<0, 12, u32> stride;
1328 BitField<12, 1, u32> enable;
1329 };
1330 u32 start_high;
1331 u32 start_low;
1332 u32 divisor;
1333
1334 GPUVAddr StartAddress() const {
1335 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_high) << 32) |
1336 start_low);
1337 }
1338
1339 bool IsEnabled() const {
1340 return enable != 0 && StartAddress() != 0;
1341 }
1342
1343 } vertex_array[NumVertexArrays];
1344
1345 Blend independent_blend[NumRenderTargets];
1346
1347 struct {
1348 u32 limit_high;
1349 u32 limit_low;
1350
1351 GPUVAddr LimitAddress() const {
1352 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) |
1353 limit_low);
1354 }
1355 } vertex_array_limit[NumVertexArrays];
1356
1357 struct {
1358 union {
1359 BitField<0, 1, u32> enable;
1360 BitField<4, 4, ShaderProgram> program;
1361 };
1362 u32 offset;
1363 INSERT_PADDING_WORDS_NOINIT(14);
1364 } shader_config[MaxShaderProgram];
1365
1366 INSERT_PADDING_WORDS_NOINIT(0x60);
1367
1368 u32 firmware[0x20];
1369
1370 struct {
1371 u32 cb_size;
1372 u32 cb_address_high;
1373 u32 cb_address_low;
1374 u32 cb_pos;
1375 std::array<u32, NumCBData> cb_data;
1376
1377 GPUVAddr BufferAddress() const {
1378 return static_cast<GPUVAddr>(
1379 (static_cast<GPUVAddr>(cb_address_high) << 32) | cb_address_low);
1380 }
1381 } const_buffer;
1382
1383 INSERT_PADDING_WORDS_NOINIT(0x10);
1384
1385 struct {
1386 union {
1387 u32 raw_config;
1388 BitField<0, 1, u32> valid;
1389 BitField<4, 5, u32> index;
1390 };
1391 INSERT_PADDING_WORDS_NOINIT(7);
1392 } cb_bind[MaxShaderStage];
1393
1394 INSERT_PADDING_WORDS_NOINIT(0x56);
1395
1396 u32 tex_cb_index;
1397
1398 INSERT_PADDING_WORDS_NOINIT(0x7D);
1399
1400 std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
1401
1402 INSERT_PADDING_WORDS_NOINIT(0x298);
1403
1404 struct {
1405 /// Compressed address of a buffer that holds information about bound SSBOs.
1406 /// This address is usually bound to c0 in the shaders.
1407 u32 buffer_address;
1408
1409 GPUVAddr BufferAddress() const {
1410 return static_cast<GPUVAddr>(buffer_address) << 8;
1411 }
1412 } ssbo_info;
1413 2589
1414 INSERT_PADDING_WORDS_NOINIT(0x11); 2590 struct ControlB {
2591 union {
2592 BitField<0, 1, u32> edge;
2593 BitField<1, 2, u32> mode;
2594 BitField<3, 1, u32> windowed;
2595 BitField<4, 16, u32> func;
2596 };
2597 };
1415 2598
1416 struct { 2599 std::array<u32, 8> values_upper;
1417 u32 address[MaxShaderStage]; 2600 std::array<u32, 8> values;
1418 u32 size[MaxShaderStage]; 2601 std::array<u32, 8> events;
1419 } tex_info_buffers; 2602 std::array<ControlA, 8> control_a;
2603 std::array<ControlB, 8> control_b;
2604 u32 trap_control_mask;
2605 u32 start_shader_mask;
2606 u32 stop_shader_mask;
2607 };
1420 2608
1421 INSERT_PADDING_WORDS_NOINIT(0xCC); 2609 // clang-format off
2610 union {
2611 struct {
2612 ID object_id; ///< 0x0000
2613 INSERT_PADDING_BYTES_NOINIT(0xFC);
2614 u32 nop; ///< 0x0100
2615 Notify notify; ///< 0x0104
2616 u32 wait_for_idle; ///< 0x0110
2617 LoadMME load_mme; ///< 0x0114
2618 ShadowRamControl shadow_ram_control; ///< 0x0124
2619 PeerSemaphore peer; ///< 0x0128
2620 GlobalRender global_render; ///< 0x0130
2621 u32 go_idle; ///< 0x013C
2622 u32 trigger; ///< 0x0140
2623 u32 trigger_wfi; ///< 0x0144
2624 INSERT_PADDING_BYTES_NOINIT(0x8);
2625 u32 instrumentation_method_header; ///< 0x0150
2626 u32 instrumentation_method_data; ///< 0x0154
2627 INSERT_PADDING_BYTES_NOINIT(0x28);
2628 Upload::Registers upload; ///< 0x0180
2629 LaunchDMA launch_dma; ///< 0x01B0
2630 u32 inline_data; ///< 0x01B4
2631 INSERT_PADDING_BYTES_NOINIT(0x24);
2632 I2M i2m; ///< 0x01DC
2633 u32 run_ds_now; ///< 0x0200
2634 OpportunisticEarlyZ opportunistic_early_z; ///< 0x0204
2635 INSERT_PADDING_BYTES_NOINIT(0x4);
2636 u32 aliased_line_width_enabled; ///< 0x020C
2637 u32 mandated_early_z; ///< 0x0210
2638 GeometryShaderDmFifo gs_dm_fifo; ///< 0x0214
2639 L2CacheControl l2_cache_control; ///< 0x0218
2640 InvalidateShaderCache invalidate_shader_cache; ///< 0x021C
2641 INSERT_PADDING_BYTES_NOINIT(0xA8);
2642 SyncInfo sync_info; ///< 0x02C8
2643 INSERT_PADDING_BYTES_NOINIT(0x4);
2644 u32 prim_circular_buffer_throttle; ///< 0x02D0
2645 u32 flush_invalidate_rop_mini_cache; ///< 0x02D4
2646 SurfaceClipBlockId surface_clip_block_id; ///< 0x02D8
2647 u32 alpha_circular_buffer_size; ///< 0x02DC
2648 DecompressSurface decompress_surface; ///< 0x02E0
2649 ZCullRopBypass zcull_rop_bypass; ///< 0x02E4
2650 ZCullSubregion zcull_subregion; ///< 0x02E8
2651 RasterBoundingBox raster_bounding_box; ///< 0x02EC
2652 u32 peer_semaphore_release; ///< 0x02F0
2653 u32 iterated_blend_optimization; ///< 0x02F4
2654 ZCullSubregionAllocation zcull_subregion_allocation; ///< 0x02F8
2655 ZCullSubregionAlgorithm zcull_subregion_algorithm; ///< 0x02FC
2656 PixelShaderOutputSampleMaskUsage ps_output_sample_mask_usage; ///< 0x0300
2657 u32 draw_zero_index; ///< 0x0304
2658 L1Configuration l1_configuration; ///< 0x0308
2659 u32 render_enable_control_load_const_buffer; ///< 0x030C
2660 SPAVersion spa_version; ///< 0x0310
2661 u32 ieee_clean_update; ///< 0x0314
2662 SnapGrid snap_grid; ///< 0x0318
2663 Tessellation tessellation; ///< 0x0320
2664 SubTilingPerf sub_tiling_perf; ///< 0x0360
2665 ZCullSubregionReport zcull_subregion_report; ///< 0x036C
2666 BalancedPrimitiveWorkload balanced_primitive_workload; ///< 0x0374
2667 u32 max_patches_per_batch; ///< 0x0378
2668 u32 rasterize_enable; ///< 0x037C
2669 TransformFeedback transform_feedback; ///< 0x0380
2670 u32 raster_input; ///< 0x0740
2671 u32 transform_feedback_enabled; ///< 0x0744
2672 u32 primitive_restart_topology_change_enable; ///< 0x0748
2673 u32 alpha_fraction; ///< 0x074C
2674 INSERT_PADDING_BYTES_NOINIT(0x4);
2675 HybridAntiAliasControl hybrid_aa_control; ///< 0x0754
2676 INSERT_PADDING_BYTES_NOINIT(0x24);
2677 ShaderLocalMemory shader_local_memory; ///< 0x077C
2678 u32 color_zero_bandwidth_clear; ///< 0x07A4
2679 u32 z_zero_bandwidth_clear; ///< 0x07A8
2680 u32 isbe_save_restore_program_offset; ///< 0x07AC
2681 INSERT_PADDING_BYTES_NOINIT(0x10);
2682 ZCullRegion zcull_region; ///< 0x07C0
2683 ZetaReadOnly zeta_read_only; ///< 0x07F8
2684 INSERT_PADDING_BYTES_NOINIT(0x4);
2685 std::array<RenderTargetConfig, NumRenderTargets> rt; ///< 0x0800
2686 std::array<ViewportTransform, NumViewports> viewport_transform; ///< 0x0A00
2687 std::array<Viewport, NumViewports> viewports; ///< 0x0C00
2688 std::array<Window, 8> windows; ///< 0x0D00
2689 std::array<ClipIdExtent, 4> clip_id_extent; ///< 0x0D40
2690 u32 max_geometry_instances_per_task; ///< 0x0D60
2691 VisibleCallLimit visible_call_limit; ///< 0x0D64
2692 StatisticsCounter statistics_count; ///< 0x0D68
2693 ClearRect clear_rect; ///< 0x0D6C
2694 VertexBuffer vertex_buffer; ///< 0x0D74
2695 DepthMode depth_mode; ///< 0x0D7C
2696 std::array<f32, 4> clear_color; ///< 0x0D80
2697 f32 clear_depth; ///< 0x0D90
2698 u32 shader_cache_icache_prefetch; ///< 0x0D94
2699 u32 force_transition_to_beta; ///< 0x0D98
2700 u32 reduce_colour_thresholds; ///< 0x0D9C
2701 s32 clear_stencil; ///< 0x0DA0
2702 InvalidateShaderCacheNoWFI invalidate_shader_cache_no_wfi; ///< 0x0DA4
2703 ZCullSerialization zcull_serialization; ///< 0x0DA8
2704 PolygonMode polygon_mode_front; ///< 0x0DAC
2705 PolygonMode polygon_mode_back; ///< 0x0DB0
2706 u32 polygon_smooth; ///< 0x0DB4
2707 u32 zeta_mark_clean_ieee; ///< 0x0DB8
2708 ZCullDirFormat zcull_dir_format; ///< 0x0DBC
2709 u32 polygon_offset_point_enable; ///< 0x0DC0
2710 u32 polygon_offset_line_enable; ///< 0x0DC4
2711 u32 polygon_offset_fill_enable; ///< 0x0DC8
2712 u32 patch_vertices; ///< 0x0DCC
2713 IteratedBlend iterated_blend; ///< 0x0DD0
2714 ZCullCriterion zcull_criteria; ///< 0x0DD8
2715 INSERT_PADDING_BYTES_NOINIT(0x4);
2716 u32 fragment_barrier; ///< 0x0DE0
2717 u32 sm_timeout; ///< 0x0DE4
2718 u32 primitive_restart_array; ///< 0x0DE8
2719 INSERT_PADDING_BYTES_NOINIT(0x4);
2720 LoadIteratedBlend load_iterated_blend; ///< 0x0DF0
2721 u32 window_offset_x; ///< 0x0DF8
2722 u32 window_offset_y; ///< 0x0DFC
2723 std::array<ScissorTest, NumViewports> scissor_test; ///< 0x0E00
2724 INSERT_PADDING_BYTES_NOINIT(0x10);
2725 u32 select_texture_headers; ///< 0x0F10
2726 VPCPerf vpc_perf; ///< 0x0F14
2727 u32 pm_local_trigger; ///< 0x0F18
2728 u32 post_z_pixel_imask; ///< 0x0F1C
2729 INSERT_PADDING_BYTES_NOINIT(0x20);
2730 ConstantColorRendering const_color_rendering; ///< 0x0F40
2731 s32 stencil_back_ref; ///< 0x0F54
2732 u32 stencil_back_mask; ///< 0x0F58
2733 u32 stencil_back_func_mask; ///< 0x0F5C
2734 INSERT_PADDING_BYTES_NOINIT(0x24);
2735 VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84
2736 u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C
2737 u32 color_mask_common; ///< 0x0F90
2738 INSERT_PADDING_BYTES_NOINIT(0x4);
2739 VTGWarpWatermarks vtg_warp_watermarks; ///< 0x0F98
2740 f32 depth_bounds[2]; ///< 0x0F9C
2741 SampleMask::Target sample_mask_target; ///< 0x0FA4
2742 u32 color_target_mrt_enable; ///< 0x0FAC
2743 NonMultisampledZ non_multisampled_z; ///< 0x0FB0
2744 TIRMode tir_mode; ///< 0x0FB4
2745 AntiAliasRaster anti_alias_raster; ///< 0x0FB8
2746 SampleMask::Pos sample_mask_pos; ///< 0x0FBC
2747 SurfaceClipIDMemory surface_clip_id_memory; ///< 0x0FCC
2748 TIRModulation tir_modulation; ///< 0x0FD4
2749 u32 blend_control_allow_float_pixel_kills; ///< 0x0FDC
2750 Zeta zeta; ///< 0x0FE0
2751 SurfaceClip surface_clip; ///< 0x0FF4
2752 u32 tiled_cache_treat_heavy_as_light; ///< 0x0FFC
2753 L2CacheVAFRequests l2_cache_vaf; ///< 0x1000
2754 ViewportMulticast viewport_multicast; ///< 0x1004
2755 u32 tessellation_cut_height; ///< 0x1008
2756 u32 max_gs_instances_per_task; ///< 0x100C
2757 u32 max_gs_output_vertices_per_task; ///< 0x1010
2758 u32 reserved_sw_method0; ///< 0x1014
2759 u32 gs_output_cb_storage_multiplier; ///< 0x1018
2760 u32 beta_cb_storage_constant; ///< 0x101C
2761 u32 ti_output_cb_storage_multiplier; ///< 0x1020
2762 u32 alpha_cb_storage_constraint; ///< 0x1024
2763 u32 reserved_sw_method1; ///< 0x1028
2764 u32 reserved_sw_method2; ///< 0x102C
2765 std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030
2766 std::array<u32, 15> spare_nop; ///< 0x1044
2767 INSERT_PADDING_BYTES_NOINIT(0x30);
2768 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0
2769 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC
2770 std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0
2771 ReduceColorThreshold reduce_color_thresholds_unorm10; ///< 0x10E0
2772 ReduceColorThreshold reduce_color_thresholds_unorm16; ///< 0x10E4
2773 ReduceColorThreshold reduce_color_thresholds_fp11; ///< 0x10E8
2774 ReduceColorThreshold reduce_color_thresholds_fp16; ///< 0x10EC
2775 ReduceColorThreshold reduce_color_thresholds_srgb8; ///< 0x10F0
2776 u32 unbind_all_constant_buffers; ///< 0x10F4
2777 ClearControl clear_control; ///< 0x10F8
2778 L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_reads; ///< 0x10FC
2779 u32 reserved_sw_method14; ///< 0x1100
2780 u32 reserved_sw_method15; ///< 0x1104
2781 INSERT_PADDING_BYTES_NOINIT(0x4);
2782 u32 no_operation_data_high; ///< 0x110C
2783 u32 depth_bias_control; ///< 0x1110
2784 u32 pm_trigger_end; ///< 0x1114
2785 u32 vertex_id_base; ///< 0x1118
2786 u32 stencil_compression_enabled; ///< 0x111C
2787 VertexOutputAttributeSkipMasks vertex_output_attribute_skip_masks; ///< 0x1120
2788 TIRControl tir_control; ///< 0x1130
2789 u32 mutable_method_treat_mutable_as_heavy; ///< 0x1134
2790 u32 post_ps_use_pre_ps_coverage; ///< 0x1138
2791 FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C
2792 u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140
2793 u32 flush_pending_writes_sm_gloal_store; ///< 0x1144
2794 INSERT_PADDING_BYTES_NOINIT(0x18);
2795 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160
2796 std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0
2797 u32 offset_render_target_index_by_viewport_index; ///< 0x11F0
2798 u32 force_heavy_method_sync; ///< 0x11F4
2799 MultisampleCoverageToColor multisample_coverage_to_color; ///< 0x11F8
2800 DecompressZetaSurface decompress_zeta_surface; ///< 0x11FC
2801 INSERT_PADDING_BYTES_NOINIT(0x8);
2802 ZetaSparse zeta_sparse; ///< 0x1208
2803 u32 invalidate_sampler_cache; ///< 0x120C
2804 u32 invalidate_texture_header_cache; ///< 0x1210
2805 VertexArray vertex_array_instance_first; ///< 0x1214
2806 VertexArray vertex_array_instance_subsequent; ///< 0x1218
2807 RtControl rt_control; ///< 0x121C
2808 CompressionThresholdSamples compression_threshold_samples; ///< 0x1220
2809 PixelShaderInterlockControl ps_interlock_control; ///< 0x1224
2810 ZetaSize zeta_size; ///< 0x1228
2811 SamplerBinding sampler_binding; ///< 0x1234
2812 INSERT_PADDING_BYTES_NOINIT(0x4);
2813 u32 draw_auto_byte_count; ///< 0x123C
2814 std::array<u32, 8> post_vtg_shader_attrib_skip_mask; ///< 0x1240
2815 PsTicketDispenserValue ps_ticket_dispenser_value; ///< 0x1260
2816 INSERT_PADDING_BYTES_NOINIT(0x1C);
2817 u32 circular_buffer_size; ///< 0x1280
2818 RegisterWatermarks vtg_register_watermarks; ///< 0x1284
2819 InvalidateTextureDataCacheNoWfi invalidate_texture_cache_no_wfi; ///< 0x1288
2820 INSERT_PADDING_BYTES_NOINIT(0x4);
2821 L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_reads; ///< 0x1290
2822 INSERT_PADDING_BYTES_NOINIT(0x10);
2823 u32 primitive_restart_topology_change_index; ///< 0x12A4
2824 INSERT_PADDING_BYTES_NOINIT(0x20);
2825 ZCullRegionEnable zcull_region_enable; ///< 0x12C8
2826 u32 depth_test_enable; ///< 0x12CC
2827 FillMode fill_mode; ///< 0x12D0
2828 ShadeMode shade_mode; ///< 0x12D4
2829 L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_writes; ///< 0x12D8
2830 L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_writes; ///< 0x12DC
2831 AlphaToCoverageDither alpha_to_coverage_dither; ///< 0x12E0
2832 u32 blend_per_target_enabled; ///< 0x12E4
2833 u32 depth_write_enabled; ///< 0x12E8
2834 u32 alpha_test_enabled; ///< 0x12EC
2835 INSERT_PADDING_BYTES_NOINIT(0x10);
2836 InlineIndex4x8 inline_index_4x8; ///< 0x1300
2837 D3DCullMode d3d_cull_mode; ///< 0x1308
2838 ComparisonOp depth_test_func; ///< 0x130C
2839 f32 alpha_test_ref; ///< 0x1310
2840 ComparisonOp alpha_test_func; ///< 0x1314
2841 u32 draw_auto_stride; ///< 0x1318
2842 BlendColor blend_color; ///< 0x131C
2843 INSERT_PADDING_BYTES_NOINIT(0x4);
2844 InvalidateCacheLines invalidate_sampler_cache_lines; ///< 0x1330
2845 InvalidateCacheLines invalidate_texture_header_cache_lines; ///< 0x1334
2846 InvalidateCacheLines invalidate_texture_data_cache_lines; ///< 0x1338
2847 Blend blend; ///< 0x133C
2848 u32 stencil_enable; ///< 0x1380
2849 StencilOp stencil_front_op; ///< 0x1384
2850 s32 stencil_front_ref; ///< 0x1394
2851 s32 stencil_front_func_mask; ///< 0x1398
2852 s32 stencil_front_mask; ///< 0x139C
2853 INSERT_PADDING_BYTES_NOINIT(0x4);
2854 u32 draw_auto_start_byte_count; ///< 0x13A4
2855 PsSaturate frag_color_clamp; ///< 0x13A8
2856 WindowOrigin window_origin; ///< 0x13AC
2857 f32 line_width_smooth; ///< 0x13B0
2858 f32 line_width_aliased; ///< 0x13B4
2859 INSERT_PADDING_BYTES_NOINIT(0x60);
2860 u32 line_override_multisample; ///< 0x1418
2861 INSERT_PADDING_BYTES_NOINIT(0x4);
2862 u32 alpha_hysteresis_rounds; ///< 0x1420
2863 InvalidateCacheLines invalidate_sampler_cache_no_wfi; ///< 0x1424
2864 InvalidateCacheLines invalidate_texture_header_cache_no_wfi; ///< 0x1428
2865 INSERT_PADDING_BYTES_NOINIT(0x8);
2866 u32 global_base_vertex_index; ///< 0x1434
2867 u32 global_base_instance_index; ///< 0x1438
2868 INSERT_PADDING_BYTES_NOINIT(0x14);
2869 RegisterWatermarks ps_warp_watermarks; ///< 0x1450
2870 RegisterWatermarks ps_regster_watermarks; ///< 0x1454
2871 INSERT_PADDING_BYTES_NOINIT(0xC);
2872 u32 store_zcull; ///< 0x1464
2873 INSERT_PADDING_BYTES_NOINIT(0x18);
2874 std::array<IteratedBlendConstants, NumRenderTargets>
2875 iterated_blend_constants; ///< 0x1480
2876 u32 load_zcull; ///< 0x1500
2877 u32 surface_clip_id_height; ///< 0x1504
2878 Window surface_clip_id_clear_rect; ///< 0x1508
2879 UserClip::Enable user_clip_enable; ///< 0x1510
2880 u32 zpass_pixel_count_enable; ///< 0x1514
2881 f32 point_size; ///< 0x1518
2882 u32 zcull_stats_enable; ///< 0x151C
2883 u32 point_sprite_enable; ///< 0x1520
2884 INSERT_PADDING_BYTES_NOINIT(0x4);
2885 u32 shader_exceptions_enable; ///< 0x1528
2886 INSERT_PADDING_BYTES_NOINIT(0x4);
2887 ClearReport clear_report_value; ///< 0x1530
2888 u32 anti_alias_enable; ///< 0x1534
2889 u32 zeta_enable; ///< 0x1538
2890 AntiAliasAlphaControl anti_alias_alpha_control; ///< 0x153C
2891 INSERT_PADDING_BYTES_NOINIT(0x10);
2892 RenderEnable render_enable; ///< 0x1550
2893 TexSampler tex_sampler; ///< 0x155C
2894 INSERT_PADDING_BYTES_NOINIT(0x4);
2895 f32 slope_scale_depth_bias; ///< 0x156C
2896 u32 line_anti_alias_enable; ///< 0x1570
2897 TexHeader tex_header; ///< 0x1574
2898 INSERT_PADDING_BYTES_NOINIT(0x10);
2899 u32 active_zcull_region_id; ///< 0x1590
2900 u32 stencil_two_side_enable; ///< 0x1594
2901 StencilOp stencil_back_op; ///< 0x1598
2902 INSERT_PADDING_BYTES_NOINIT(0x10);
2903 u32 framebuffer_srgb; ///< 0x15B8
2904 f32 depth_bias; ///< 0x15BC
2905 INSERT_PADDING_BYTES_NOINIT(0x8);
2906 ZCullRegionFormat zcull_region_format; ///< 0x15C8
2907 RtLayer rt_layer; ///< 0x15CC
2908 Tegra::Texture::MsaaMode anti_alias_samples_mode; ///< 0x15D0
2909 INSERT_PADDING_BYTES_NOINIT(0x10);
2910 u32 edge_flag; ///< 0x15E4
2911 u32 draw_inline_index; ///< 0x15E8
2912 InlineIndex2x16 inline_index_2x16; ///< 0x15EC
2913 VertexGlobalBaseOffset vertex_global_base_offset; ///< 0x15F4
2914 ZCullRegionPixelOffset zcull_region_pixel_offset; ///< 0x15FC
2915 PointSprite point_sprite; ///< 0x1604
2916 ProgramRegion program_region; ///< 0x1608
2917 DefaultAttributes default_attributes; ///< 0x1610
2918 Draw draw; ///< 0x1614
2919 VertexIdCopy vertex_id_copy; ///< 0x161C
2920 u32 add_to_primitive_id; ///< 0x1620
2921 u32 load_to_primitive_id; ///< 0x1624
2922 INSERT_PADDING_BYTES_NOINIT(0x4);
2923 ShaderBasedCull shader_based_cull; ///< 0x162C
2924 INSERT_PADDING_BYTES_NOINIT(0x8);
2925 ClassVersion class_version; ///< 0x1638
2926 INSERT_PADDING_BYTES_NOINIT(0x8);
2927 PrimitiveRestart primitive_restart; ///< 0x1644
2928 OutputVertexId output_vertex_id; ///< 0x164C
2929 INSERT_PADDING_BYTES_NOINIT(0x8);
2930 u32 anti_alias_point_enable; ///< 0x1658
2931 PointCenterMode point_center_mode; ///< 0x165C
2932 INSERT_PADDING_BYTES_NOINIT(0x8);
2933 LineSmoothParams line_smooth_params; ///< 0x1668
2934 u32 line_stipple_enable; ///< 0x166C
2935 std::array<LineSmoothEdgeTable, 4> line_smooth_edge_table; ///< 0x1670
2936 LineStippleParams line_stipple_params; ///< 0x1680
2937 ProvokingVertex provoking_vertex; ///< 0x1684
2938 u32 two_sided_light_enabled; ///< 0x1688
2939 u32 polygon_stipple_enabled; ///< 0x168C
2940 ShaderControl shader_control; ///< 0x1690
2941 INSERT_PADDING_BYTES_NOINIT(0xC);
2942 ClassVersion class_version_check; ///< 0x16A0
2943 SphVersion sph_version; ///< 0x16A4
2944 SphVersion sph_version_check; ///< 0x16A8
2945 INSERT_PADDING_BYTES_NOINIT(0x8);
2946 AlphaToCoverageOverride alpha_to_coverage_override; ///< 0x16B4
2947 INSERT_PADDING_BYTES_NOINIT(0x48);
2948 std::array<u32, 32> polygon_stipple_pattern; ///< 0x1700
2949 INSERT_PADDING_BYTES_NOINIT(0x10);
2950 AamVersion aam_version; ///< 0x1790
2951 AamVersion aam_version_check; ///< 0x1794
2952 INSERT_PADDING_BYTES_NOINIT(0x4);
2953 u32 zeta_layer_offset; ///< 0x179C
2954 INSERT_PADDING_BYTES_NOINIT(0x28);
2955 IndexBuffer index_buffer; ///< 0x17C8
2956 IndexBufferSmall index_buffer32_first; ///< 0x17E4
2957 IndexBufferSmall index_buffer16_first; ///< 0x17E8
2958 IndexBufferSmall index_buffer8_first; ///< 0x17EC
2959 IndexBufferSmall index_buffer32_subsequent; ///< 0x17F0
2960 IndexBufferSmall index_buffer16_subsequent; ///< 0x17F4
2961 IndexBufferSmall index_buffer8_subsequent; ///< 0x17F8
2962 INSERT_PADDING_BYTES_NOINIT(0x80);
2963 f32 depth_bias_clamp; ///< 0x187C
2964 VertexStreamInstances vertex_stream_instances; ///< 0x1880
2965 INSERT_PADDING_BYTES_NOINIT(0x10);
2966 AttributePointSize point_size_attribute; ///< 0x1910
2967 INSERT_PADDING_BYTES_NOINIT(0x4);
2968 u32 gl_cull_test_enabled; ///< 0x1918
2969 FrontFace gl_front_face; ///< 0x191C
2970 CullFace gl_cull_face; ///< 0x1920
2971 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924
2972 INSERT_PADDING_BYTES_NOINIT(0x4);
2973 u32 viewport_scale_offset_enbled; ///< 0x192C
2974 INSERT_PADDING_BYTES_NOINIT(0xC);
2975 ViewportClipControl viewport_clip_control; ///< 0x193C
2976 UserClip::Op user_clip_op; ///< 0x1940
2977 RenderEnable::Override render_enable_override; ///< 0x1944
2978 PrimitiveTopologyControl primitive_topology_control; ///< 0x1948
2979 WindowClip window_clip_enable; ///< 0x194C
2980 INSERT_PADDING_BYTES_NOINIT(0x4);
2981 InvalidateZCull invalidate_zcull; ///< 0x1958
2982 INSERT_PADDING_BYTES_NOINIT(0xC);
2983 ZCull zcull; ///< 0x1968
2984 PrimitiveTopologyOverride topology_override; ///< 0x1970
2985 INSERT_PADDING_BYTES_NOINIT(0x4);
2986 u32 zcull_sync; ///< 0x1978
2987 u32 clip_id_test_enable; ///< 0x197C
2988 u32 surface_clip_id_width; ///< 0x1980
2989 u32 clip_id; ///< 0x1984
2990 INSERT_PADDING_BYTES_NOINIT(0x34);
2991 u32 depth_bounds_enable; ///< 0x19BC
2992 u32 blend_float_zero_times_anything_is_zero; ///< 0x19C0
2993 LogicOp logic_op; ///< 0x19C4
2994 u32 z_compression_enable; ///< 0x19CC
2995 ClearSurface clear_surface; ///< 0x19D0
2996 u32 clear_clip_id_surface; ///< 0x19D4
2997 INSERT_PADDING_BYTES_NOINIT(0x8);
2998 std::array<u32, NumRenderTargets> color_compression_enable; ///< 0x19E0
2999 std::array<ColorMask, NumRenderTargets> color_mask; ///< 0x1A00
3000 INSERT_PADDING_BYTES_NOINIT(0xC);
3001 u32 pipe_nop; ///< 0x1A2C
3002 std::array<u32, 4> spare; ///< 0x1A30
3003 INSERT_PADDING_BYTES_NOINIT(0xC0);
3004 ReportSemaphore report_semaphore; ///< 0x1B00
3005 INSERT_PADDING_BYTES_NOINIT(0xF0);
3006 std::array<VertexStream, NumVertexArrays> vertex_streams; ///< 0x1C00
3007 BlendPerTarget blend_per_target[NumRenderTargets]; ///< 0x1E00
3008 std::array<VertexStreamLimit, NumVertexArrays> vertex_stream_limits; ///< 0x1F00
3009 std::array<Pipeline, MaxShaderProgram> pipelines; ///< 0x2000
3010 INSERT_PADDING_BYTES_NOINIT(0x180);
3011 u32 falcon[32]; ///< 0x2300
3012 ConstantBuffer const_buffer; ///< 0x2380
3013 INSERT_PADDING_BYTES_NOINIT(0x30);
3014 BindGroup bind_groups[MaxShaderStage]; ///< 0x2400
3015 INSERT_PADDING_BYTES_NOINIT(0x160);
3016 u32 color_clamp_enable; ///< 0x2600
3017 INSERT_PADDING_BYTES_NOINIT(0x4);
3018 u32 bindless_texture_const_buffer_slot; ///< 0x2608
3019 u32 trap_handler; ///< 0x260C
3020 INSERT_PADDING_BYTES_NOINIT(0x1F0);
3021 std::array<std::array<StreamOutLayout, 32>, NumTransformFeedbackBuffers>
3022 stream_out_layout; ///< 0x2800
3023 INSERT_PADDING_BYTES_NOINIT(0x93C);
3024 ShaderPerformance shader_performance; ///< 0x333C
3025 INSERT_PADDING_BYTES_NOINIT(0x18);
3026 std::array<u32, 0x100> shadow_scratch; ///< 0x3400
1422 }; 3027 };
1423 std::array<u32, NUM_REGS> reg_array; 3028 std::array<u32, NUM_REGS> reg_array;
1424 }; 3029 };
1425 }; 3030 };
3031 // clang-format on
1426 3032
1427 Regs regs{}; 3033 Regs regs{};
1428 3034
@@ -1438,8 +3044,6 @@ public:
1438 }; 3044 };
1439 3045
1440 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; 3046 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
1441
1442 u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering.
1443 }; 3047 };
1444 3048
1445 State state{}; 3049 State state{};
@@ -1454,11 +3058,6 @@ public:
1454 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 3058 void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
1455 u32 methods_pending) override; 3059 u32 methods_pending) override;
1456 3060
1457 /// Write the value to the register identified by method.
1458 void CallMethodFromMME(u32 method, u32 method_argument);
1459
1460 void FlushMMEInlineDraw();
1461
1462 bool ShouldExecute() const { 3061 bool ShouldExecute() const {
1463 return execute_on; 3062 return execute_on;
1464 } 3063 }
@@ -1471,21 +3070,6 @@ public:
1471 return *rasterizer; 3070 return *rasterizer;
1472 } 3071 }
1473 3072
1474 enum class MMEDrawMode : u32 {
1475 Undefined,
1476 Array,
1477 Indexed,
1478 };
1479
1480 struct MMEDrawState {
1481 MMEDrawMode current_mode{MMEDrawMode::Undefined};
1482 u32 current_count{};
1483 u32 instance_count{};
1484 bool instance_mode{};
1485 bool gl_begin_consume{};
1486 u32 gl_end_count{};
1487 } mme_draw;
1488
1489 struct DirtyState { 3073 struct DirtyState {
1490 using Flags = std::bitset<std::numeric_limits<u8>::max()>; 3074 using Flags = std::bitset<std::numeric_limits<u8>::max()>;
1491 using Table = std::array<u8, Regs::NUM_REGS>; 3075 using Table = std::array<u8, Regs::NUM_REGS>;
@@ -1495,6 +3079,8 @@ public:
1495 Tables tables{}; 3079 Tables tables{};
1496 } dirty; 3080 } dirty;
1497 3081
3082 std::vector<u8> inline_index_draw_indexes;
3083
1498private: 3084private:
1499 void InitializeRegisterDefaults(); 3085 void InitializeRegisterDefaults();
1500 3086
@@ -1554,14 +3140,12 @@ private:
1554 /// Handles a write to the CB_BIND register. 3140 /// Handles a write to the CB_BIND register.
1555 void ProcessCBBind(size_t stage_index); 3141 void ProcessCBBind(size_t stage_index);
1556 3142
1557 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
1558 void DrawArrays();
1559
1560 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) 3143 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
1561 void ProcessTopologyOverride(); 3144 void ProcessTopologyOverride();
1562 3145
1563 // Handles a instance drawcall from MME 3146 void ProcessDraw(u32 instance_count = 1);
1564 void StepInstance(MMEDrawMode expected_mode, u32 count); 3147
3148 void ProcessDeferredDraw();
1565 3149
1566 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3150 /// Returns a query's value or an empty object if the value will be deferred through a cache.
1567 std::optional<u64> GetQueryResult(); 3151 std::optional<u64> GetQueryResult();
@@ -1574,8 +3158,6 @@ private:
1574 /// Start offsets of each macro in macro_memory 3158 /// Start offsets of each macro in macro_memory
1575 std::array<u32, 0x80> macro_positions{}; 3159 std::array<u32, 0x80> macro_positions{};
1576 3160
1577 std::array<bool, Regs::NUM_REGS> mme_inline{};
1578
1579 /// Macro method that is currently being executed / being fed parameters. 3161 /// Macro method that is currently being executed / being fed parameters.
1580 u32 executing_macro = 0; 3162 u32 executing_macro = 0;
1581 /// Parameters that have been submitted to the macro call so far. 3163 /// Parameters that have been submitted to the macro call so far.
@@ -1588,150 +3170,355 @@ private:
1588 3170
1589 bool execute_on{true}; 3171 bool execute_on{true};
1590 bool use_topology_override{false}; 3172 bool use_topology_override{false};
3173
3174 std::array<bool, Regs::NUM_REGS> draw_command{};
3175 std::vector<u32> deferred_draw_method;
1591}; 3176};
1592 3177
1593#define ASSERT_REG_POSITION(field_name, position) \ 3178#define ASSERT_REG_POSITION(field_name, position) \
1594 static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \ 3179 static_assert(offsetof(Maxwell3D::Regs, field_name) == position, \
1595 "Field " #field_name " has invalid position") 3180 "Field " #field_name " has invalid position")
1596 3181
1597ASSERT_REG_POSITION(wait_for_idle, 0x44); 3182ASSERT_REG_POSITION(object_id, 0x0000);
1598ASSERT_REG_POSITION(macros, 0x45); 3183ASSERT_REG_POSITION(nop, 0x0100);
1599ASSERT_REG_POSITION(shadow_ram_control, 0x49); 3184ASSERT_REG_POSITION(notify, 0x0104);
1600ASSERT_REG_POSITION(upload, 0x60); 3185ASSERT_REG_POSITION(wait_for_idle, 0x0110);
1601ASSERT_REG_POSITION(exec_upload, 0x6C); 3186ASSERT_REG_POSITION(load_mme, 0x0114);
1602ASSERT_REG_POSITION(data_upload, 0x6D); 3187ASSERT_REG_POSITION(shadow_ram_control, 0x0124);
1603ASSERT_REG_POSITION(force_early_fragment_tests, 0x84); 3188ASSERT_REG_POSITION(peer, 0x0128);
1604ASSERT_REG_POSITION(sync_info, 0xB2); 3189ASSERT_REG_POSITION(global_render, 0x0130);
1605ASSERT_REG_POSITION(tess_mode, 0xC8); 3190ASSERT_REG_POSITION(go_idle, 0x013C);
1606ASSERT_REG_POSITION(tess_level_outer, 0xC9); 3191ASSERT_REG_POSITION(trigger, 0x0140);
1607ASSERT_REG_POSITION(tess_level_inner, 0xCD); 3192ASSERT_REG_POSITION(trigger_wfi, 0x0144);
1608ASSERT_REG_POSITION(rasterize_enable, 0xDF); 3193ASSERT_REG_POSITION(instrumentation_method_header, 0x0150);
1609ASSERT_REG_POSITION(tfb_bindings, 0xE0); 3194ASSERT_REG_POSITION(instrumentation_method_data, 0x0154);
1610ASSERT_REG_POSITION(tfb_layouts, 0x1C0); 3195ASSERT_REG_POSITION(upload, 0x0180);
1611ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 3196ASSERT_REG_POSITION(launch_dma, 0x01B0);
1612ASSERT_REG_POSITION(rt, 0x200); 3197ASSERT_REG_POSITION(inline_data, 0x01B4);
1613ASSERT_REG_POSITION(viewport_transform, 0x280); 3198ASSERT_REG_POSITION(i2m, 0x01DC);
1614ASSERT_REG_POSITION(viewports, 0x300); 3199ASSERT_REG_POSITION(run_ds_now, 0x0200);
1615ASSERT_REG_POSITION(vertex_buffer, 0x35D); 3200ASSERT_REG_POSITION(opportunistic_early_z, 0x0204);
1616ASSERT_REG_POSITION(depth_mode, 0x35F); 3201ASSERT_REG_POSITION(aliased_line_width_enabled, 0x020C);
1617ASSERT_REG_POSITION(clear_color[0], 0x360); 3202ASSERT_REG_POSITION(mandated_early_z, 0x0210);
1618ASSERT_REG_POSITION(clear_depth, 0x364); 3203ASSERT_REG_POSITION(gs_dm_fifo, 0x0214);
1619ASSERT_REG_POSITION(clear_stencil, 0x368); 3204ASSERT_REG_POSITION(l2_cache_control, 0x0218);
1620ASSERT_REG_POSITION(polygon_mode_front, 0x36B); 3205ASSERT_REG_POSITION(invalidate_shader_cache, 0x021C);
1621ASSERT_REG_POSITION(polygon_mode_back, 0x36C); 3206ASSERT_REG_POSITION(sync_info, 0x02C8);
1622ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370); 3207ASSERT_REG_POSITION(prim_circular_buffer_throttle, 0x02D0);
1623ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371); 3208ASSERT_REG_POSITION(flush_invalidate_rop_mini_cache, 0x02D4);
1624ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372); 3209ASSERT_REG_POSITION(surface_clip_block_id, 0x02D8);
1625ASSERT_REG_POSITION(patch_vertices, 0x373); 3210ASSERT_REG_POSITION(alpha_circular_buffer_size, 0x02DC);
1626ASSERT_REG_POSITION(fragment_barrier, 0x378); 3211ASSERT_REG_POSITION(decompress_surface, 0x02E0);
1627ASSERT_REG_POSITION(scissor_test, 0x380); 3212ASSERT_REG_POSITION(zcull_rop_bypass, 0x02E4);
1628ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 3213ASSERT_REG_POSITION(zcull_subregion, 0x02E8);
1629ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 3214ASSERT_REG_POSITION(raster_bounding_box, 0x02EC);
1630ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 3215ASSERT_REG_POSITION(peer_semaphore_release, 0x02F0);
1631ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x3DD); 3216ASSERT_REG_POSITION(iterated_blend_optimization, 0x02F4);
1632ASSERT_REG_POSITION(tiled_cache_barrier, 0x3DF); 3217ASSERT_REG_POSITION(zcull_subregion_allocation, 0x02F8);
1633ASSERT_REG_POSITION(color_mask_common, 0x3E4); 3218ASSERT_REG_POSITION(zcull_subregion_algorithm, 0x02FC);
1634ASSERT_REG_POSITION(depth_bounds, 0x3E7); 3219ASSERT_REG_POSITION(ps_output_sample_mask_usage, 0x0300);
1635ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 3220ASSERT_REG_POSITION(draw_zero_index, 0x0304);
1636ASSERT_REG_POSITION(multisample_raster_enable, 0x3ED); 3221ASSERT_REG_POSITION(l1_configuration, 0x0308);
1637ASSERT_REG_POSITION(multisample_raster_samples, 0x3EE); 3222ASSERT_REG_POSITION(render_enable_control_load_const_buffer, 0x030C);
1638ASSERT_REG_POSITION(multisample_sample_mask, 0x3EF); 3223ASSERT_REG_POSITION(spa_version, 0x0310);
1639ASSERT_REG_POSITION(zeta, 0x3F8); 3224ASSERT_REG_POSITION(ieee_clean_update, 0x0314);
1640ASSERT_REG_POSITION(render_area, 0x3FD); 3225ASSERT_REG_POSITION(snap_grid, 0x0318);
1641ASSERT_REG_POSITION(clear_flags, 0x43E); 3226ASSERT_REG_POSITION(tessellation, 0x0320);
1642ASSERT_REG_POSITION(fill_rectangle, 0x44F); 3227ASSERT_REG_POSITION(sub_tiling_perf, 0x0360);
1643ASSERT_REG_POSITION(conservative_raster_enable, 0x452); 3228ASSERT_REG_POSITION(zcull_subregion_report, 0x036C);
1644ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 3229ASSERT_REG_POSITION(balanced_primitive_workload, 0x0374);
1645ASSERT_REG_POSITION(multisample_sample_locations, 0x478); 3230ASSERT_REG_POSITION(max_patches_per_batch, 0x0378);
1646ASSERT_REG_POSITION(multisample_coverage_to_color, 0x47E); 3231ASSERT_REG_POSITION(rasterize_enable, 0x037C);
1647ASSERT_REG_POSITION(rt_control, 0x487); 3232ASSERT_REG_POSITION(transform_feedback, 0x0380);
1648ASSERT_REG_POSITION(zeta_width, 0x48a); 3233ASSERT_REG_POSITION(transform_feedback.controls, 0x0700);
1649ASSERT_REG_POSITION(zeta_height, 0x48b); 3234ASSERT_REG_POSITION(raster_input, 0x0740);
1650ASSERT_REG_POSITION(zeta_depth, 0x48c); 3235ASSERT_REG_POSITION(transform_feedback_enabled, 0x0744);
1651ASSERT_REG_POSITION(sampler_index, 0x48D); 3236ASSERT_REG_POSITION(primitive_restart_topology_change_enable, 0x0748);
1652ASSERT_REG_POSITION(gp_passthrough_mask, 0x490); 3237ASSERT_REG_POSITION(alpha_fraction, 0x074C);
1653ASSERT_REG_POSITION(depth_test_enable, 0x4B3); 3238ASSERT_REG_POSITION(hybrid_aa_control, 0x0754);
1654ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); 3239ASSERT_REG_POSITION(shader_local_memory, 0x077C);
1655ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); 3240ASSERT_REG_POSITION(color_zero_bandwidth_clear, 0x07A4);
1656ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); 3241ASSERT_REG_POSITION(z_zero_bandwidth_clear, 0x07A8);
1657ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 3242ASSERT_REG_POSITION(isbe_save_restore_program_offset, 0x07AC);
1658ASSERT_REG_POSITION(depth_test_func, 0x4C3); 3243ASSERT_REG_POSITION(zcull_region, 0x07C0);
1659ASSERT_REG_POSITION(alpha_test_ref, 0x4C4); 3244ASSERT_REG_POSITION(zeta_read_only, 0x07F8);
1660ASSERT_REG_POSITION(alpha_test_func, 0x4C5); 3245ASSERT_REG_POSITION(rt, 0x0800);
1661ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6); 3246ASSERT_REG_POSITION(viewport_transform, 0x0A00);
1662ASSERT_REG_POSITION(blend_color, 0x4C7); 3247ASSERT_REG_POSITION(viewports, 0x0C00);
1663ASSERT_REG_POSITION(blend, 0x4CF); 3248ASSERT_REG_POSITION(windows, 0x0D00);
1664ASSERT_REG_POSITION(stencil_enable, 0x4E0); 3249ASSERT_REG_POSITION(clip_id_extent, 0x0D40);
1665ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); 3250ASSERT_REG_POSITION(max_geometry_instances_per_task, 0x0D60);
1666ASSERT_REG_POSITION(stencil_front_op_zfail, 0x4E2); 3251ASSERT_REG_POSITION(visible_call_limit, 0x0D64);
1667ASSERT_REG_POSITION(stencil_front_op_zpass, 0x4E3); 3252ASSERT_REG_POSITION(statistics_count, 0x0D68);
1668ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4); 3253ASSERT_REG_POSITION(clear_rect, 0x0D6C);
1669ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 3254ASSERT_REG_POSITION(vertex_buffer, 0x0D74);
1670ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 3255ASSERT_REG_POSITION(depth_mode, 0x0D7C);
1671ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 3256ASSERT_REG_POSITION(clear_color, 0x0D80);
1672ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); 3257ASSERT_REG_POSITION(clear_depth, 0x0D90);
1673ASSERT_REG_POSITION(screen_y_control, 0x4EB); 3258ASSERT_REG_POSITION(shader_cache_icache_prefetch, 0x0D94);
1674ASSERT_REG_POSITION(line_width_smooth, 0x4EC); 3259ASSERT_REG_POSITION(force_transition_to_beta, 0x0D98);
1675ASSERT_REG_POSITION(line_width_aliased, 0x4ED); 3260ASSERT_REG_POSITION(reduce_colour_thresholds, 0x0D9C);
1676ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x509); 3261ASSERT_REG_POSITION(clear_stencil, 0x0DA0);
1677ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x50A); 3262ASSERT_REG_POSITION(invalidate_shader_cache_no_wfi, 0x0DA4);
1678ASSERT_REG_POSITION(vb_element_base, 0x50D); 3263ASSERT_REG_POSITION(zcull_serialization, 0x0DA8);
1679ASSERT_REG_POSITION(vb_base_instance, 0x50E); 3264ASSERT_REG_POSITION(polygon_mode_front, 0x0DAC);
1680ASSERT_REG_POSITION(clip_distance_enabled, 0x544); 3265ASSERT_REG_POSITION(polygon_mode_back, 0x0DB0);
1681ASSERT_REG_POSITION(samplecnt_enable, 0x545); 3266ASSERT_REG_POSITION(polygon_smooth, 0x0DB4);
1682ASSERT_REG_POSITION(point_size, 0x546); 3267ASSERT_REG_POSITION(zeta_mark_clean_ieee, 0x0DB8);
1683ASSERT_REG_POSITION(point_sprite_enable, 0x548); 3268ASSERT_REG_POSITION(zcull_dir_format, 0x0DBC);
1684ASSERT_REG_POSITION(counter_reset, 0x54C); 3269ASSERT_REG_POSITION(polygon_offset_point_enable, 0x0DC0);
1685ASSERT_REG_POSITION(multisample_enable, 0x54D); 3270ASSERT_REG_POSITION(polygon_offset_line_enable, 0x0DC4);
1686ASSERT_REG_POSITION(zeta_enable, 0x54E); 3271ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x0DC8);
1687ASSERT_REG_POSITION(multisample_control, 0x54F); 3272ASSERT_REG_POSITION(patch_vertices, 0x0DCC);
1688ASSERT_REG_POSITION(condition, 0x554); 3273ASSERT_REG_POSITION(iterated_blend, 0x0DD0);
1689ASSERT_REG_POSITION(tsc, 0x557); 3274ASSERT_REG_POSITION(zcull_criteria, 0x0DD8);
1690ASSERT_REG_POSITION(polygon_offset_factor, 0x55B); 3275ASSERT_REG_POSITION(fragment_barrier, 0x0DE0);
1691ASSERT_REG_POSITION(line_smooth_enable, 0x55C); 3276ASSERT_REG_POSITION(sm_timeout, 0x0DE4);
1692ASSERT_REG_POSITION(tic, 0x55D); 3277ASSERT_REG_POSITION(primitive_restart_array, 0x0DE8);
1693ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 3278ASSERT_REG_POSITION(load_iterated_blend, 0x0DF0);
1694ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); 3279ASSERT_REG_POSITION(window_offset_x, 0x0DF8);
1695ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); 3280ASSERT_REG_POSITION(window_offset_y, 0x0DFC);
1696ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); 3281ASSERT_REG_POSITION(scissor_test, 0x0E00);
1697ASSERT_REG_POSITION(stencil_back_func_func, 0x569); 3282ASSERT_REG_POSITION(select_texture_headers, 0x0F10);
1698ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); 3283ASSERT_REG_POSITION(vpc_perf, 0x0F14);
1699ASSERT_REG_POSITION(polygon_offset_units, 0x56F); 3284ASSERT_REG_POSITION(pm_local_trigger, 0x0F18);
1700ASSERT_REG_POSITION(multisample_mode, 0x574); 3285ASSERT_REG_POSITION(post_z_pixel_imask, 0x0F1C);
1701ASSERT_REG_POSITION(point_coord_replace, 0x581); 3286ASSERT_REG_POSITION(const_color_rendering, 0x0F40);
1702ASSERT_REG_POSITION(code_address, 0x582); 3287ASSERT_REG_POSITION(stencil_back_ref, 0x0F54);
1703ASSERT_REG_POSITION(draw, 0x585); 3288ASSERT_REG_POSITION(stencil_back_mask, 0x0F58);
1704ASSERT_REG_POSITION(primitive_restart, 0x591); 3289ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C);
1705ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); 3290ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84);
1706ASSERT_REG_POSITION(index_array, 0x5F2); 3291ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C);
1707ASSERT_REG_POSITION(small_index, 0x5F9); 3292ASSERT_REG_POSITION(color_mask_common, 0x0F90);
1708ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); 3293ASSERT_REG_POSITION(vtg_warp_watermarks, 0x0F98);
1709ASSERT_REG_POSITION(instanced_arrays, 0x620); 3294ASSERT_REG_POSITION(depth_bounds, 0x0F9C);
1710ASSERT_REG_POSITION(vp_point_size, 0x644); 3295ASSERT_REG_POSITION(sample_mask_target, 0x0FA4);
1711ASSERT_REG_POSITION(cull_test_enabled, 0x646); 3296ASSERT_REG_POSITION(color_target_mrt_enable, 0x0FAC);
1712ASSERT_REG_POSITION(front_face, 0x647); 3297ASSERT_REG_POSITION(non_multisampled_z, 0x0FB0);
1713ASSERT_REG_POSITION(cull_face, 0x648); 3298ASSERT_REG_POSITION(tir_mode, 0x0FB4);
1714ASSERT_REG_POSITION(pixel_center_integer, 0x649); 3299ASSERT_REG_POSITION(anti_alias_raster, 0x0FB8);
1715ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); 3300ASSERT_REG_POSITION(sample_mask_pos, 0x0FBC);
1716ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); 3301ASSERT_REG_POSITION(surface_clip_id_memory, 0x0FCC);
1717ASSERT_REG_POSITION(topology_override, 0x65C); 3302ASSERT_REG_POSITION(tir_modulation, 0x0FD4);
1718ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); 3303ASSERT_REG_POSITION(blend_control_allow_float_pixel_kills, 0x0FDC);
1719ASSERT_REG_POSITION(logic_op, 0x671); 3304ASSERT_REG_POSITION(zeta, 0x0FE0);
1720ASSERT_REG_POSITION(clear_buffers, 0x674); 3305ASSERT_REG_POSITION(surface_clip, 0x0FF4);
1721ASSERT_REG_POSITION(color_mask, 0x680); 3306ASSERT_REG_POSITION(tiled_cache_treat_heavy_as_light, 0x0FFC);
1722ASSERT_REG_POSITION(query, 0x6C0); 3307ASSERT_REG_POSITION(l2_cache_vaf, 0x1000);
1723ASSERT_REG_POSITION(vertex_array[0], 0x700); 3308ASSERT_REG_POSITION(viewport_multicast, 0x1004);
1724ASSERT_REG_POSITION(independent_blend, 0x780); 3309ASSERT_REG_POSITION(tessellation_cut_height, 0x1008);
1725ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); 3310ASSERT_REG_POSITION(max_gs_instances_per_task, 0x100C);
1726ASSERT_REG_POSITION(shader_config[0], 0x800); 3311ASSERT_REG_POSITION(max_gs_output_vertices_per_task, 0x1010);
1727ASSERT_REG_POSITION(firmware, 0x8C0); 3312ASSERT_REG_POSITION(reserved_sw_method0, 0x1014);
1728ASSERT_REG_POSITION(const_buffer, 0x8E0); 3313ASSERT_REG_POSITION(gs_output_cb_storage_multiplier, 0x1018);
1729ASSERT_REG_POSITION(cb_bind[0], 0x904); 3314ASSERT_REG_POSITION(beta_cb_storage_constant, 0x101C);
1730ASSERT_REG_POSITION(tex_cb_index, 0x982); 3315ASSERT_REG_POSITION(ti_output_cb_storage_multiplier, 0x1020);
1731ASSERT_REG_POSITION(tfb_varying_locs, 0xA00); 3316ASSERT_REG_POSITION(alpha_cb_storage_constraint, 0x1024);
1732ASSERT_REG_POSITION(ssbo_info, 0xD18); 3317ASSERT_REG_POSITION(reserved_sw_method1, 0x1028);
1733ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A); 3318ASSERT_REG_POSITION(reserved_sw_method2, 0x102C);
1734ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F); 3319ASSERT_REG_POSITION(tir_modulation_coeff, 0x1030);
3320ASSERT_REG_POSITION(spare_nop, 0x1044);
3321ASSERT_REG_POSITION(reserved_sw_method3_to_7, 0x10B0);
3322ASSERT_REG_POSITION(reduce_color_thresholds_unorm8, 0x10CC);
3323ASSERT_REG_POSITION(reserved_sw_method10_to_13, 0x10D0);
3324ASSERT_REG_POSITION(reduce_color_thresholds_unorm10, 0x10E0);
3325ASSERT_REG_POSITION(reduce_color_thresholds_unorm16, 0x10E4);
3326ASSERT_REG_POSITION(reduce_color_thresholds_fp11, 0x10E8);
3327ASSERT_REG_POSITION(reduce_color_thresholds_fp16, 0x10EC);
3328ASSERT_REG_POSITION(reduce_color_thresholds_srgb8, 0x10F0);
3329ASSERT_REG_POSITION(unbind_all_constant_buffers, 0x10F4);
3330ASSERT_REG_POSITION(clear_control, 0x10F8);
3331ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_reads, 0x10FC);
3332ASSERT_REG_POSITION(reserved_sw_method14, 0x1100);
3333ASSERT_REG_POSITION(reserved_sw_method15, 0x1104);
3334ASSERT_REG_POSITION(no_operation_data_high, 0x110C);
3335ASSERT_REG_POSITION(depth_bias_control, 0x1110);
3336ASSERT_REG_POSITION(pm_trigger_end, 0x1114);
3337ASSERT_REG_POSITION(vertex_id_base, 0x1118);
3338ASSERT_REG_POSITION(stencil_compression_enabled, 0x111C);
3339ASSERT_REG_POSITION(vertex_output_attribute_skip_masks, 0x1120);
3340ASSERT_REG_POSITION(tir_control, 0x1130);
3341ASSERT_REG_POSITION(mutable_method_treat_mutable_as_heavy, 0x1134);
3342ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138);
3343ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C);
3344ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140);
3345ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144);
3346ASSERT_REG_POSITION(vertex_attrib_format, 0x1160);
3347ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0);
3348ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0);
3349ASSERT_REG_POSITION(force_heavy_method_sync, 0x11F4);
3350ASSERT_REG_POSITION(multisample_coverage_to_color, 0x11F8);
3351ASSERT_REG_POSITION(decompress_zeta_surface, 0x11FC);
3352ASSERT_REG_POSITION(zeta_sparse, 0x1208);
3353ASSERT_REG_POSITION(invalidate_sampler_cache, 0x120C);
3354ASSERT_REG_POSITION(invalidate_texture_header_cache, 0x1210);
3355ASSERT_REG_POSITION(vertex_array_instance_first, 0x1214);
3356ASSERT_REG_POSITION(vertex_array_instance_subsequent, 0x1218);
3357ASSERT_REG_POSITION(rt_control, 0x121C);
3358ASSERT_REG_POSITION(compression_threshold_samples, 0x1220);
3359ASSERT_REG_POSITION(ps_interlock_control, 0x1224);
3360ASSERT_REG_POSITION(zeta_size, 0x1228);
3361ASSERT_REG_POSITION(sampler_binding, 0x1234);
3362ASSERT_REG_POSITION(draw_auto_byte_count, 0x123C);
3363ASSERT_REG_POSITION(post_vtg_shader_attrib_skip_mask, 0x1240);
3364ASSERT_REG_POSITION(ps_ticket_dispenser_value, 0x1260);
3365ASSERT_REG_POSITION(circular_buffer_size, 0x1280);
3366ASSERT_REG_POSITION(vtg_register_watermarks, 0x1284);
3367ASSERT_REG_POSITION(invalidate_texture_cache_no_wfi, 0x1288);
3368ASSERT_REG_POSITION(l2_cache_rop_interlocked_reads, 0x1290);
3369ASSERT_REG_POSITION(primitive_restart_topology_change_index, 0x12A4);
3370ASSERT_REG_POSITION(zcull_region_enable, 0x12C8);
3371ASSERT_REG_POSITION(depth_test_enable, 0x12CC);
3372ASSERT_REG_POSITION(fill_mode, 0x12D0);
3373ASSERT_REG_POSITION(shade_mode, 0x12D4);
3374ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_writes, 0x12D8);
3375ASSERT_REG_POSITION(l2_cache_rop_interlocked_writes, 0x12DC);
3376ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0);
3377ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4);
3378ASSERT_REG_POSITION(depth_write_enabled, 0x12E8);
3379ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC);
3380ASSERT_REG_POSITION(inline_index_4x8, 0x1300);
3381ASSERT_REG_POSITION(d3d_cull_mode, 0x1308);
3382ASSERT_REG_POSITION(depth_test_func, 0x130C);
3383ASSERT_REG_POSITION(alpha_test_ref, 0x1310);
3384ASSERT_REG_POSITION(alpha_test_func, 0x1314);
3385ASSERT_REG_POSITION(draw_auto_stride, 0x1318);
3386ASSERT_REG_POSITION(blend_color, 0x131C);
3387ASSERT_REG_POSITION(invalidate_sampler_cache_lines, 0x1330);
3388ASSERT_REG_POSITION(invalidate_texture_header_cache_lines, 0x1334);
3389ASSERT_REG_POSITION(invalidate_texture_data_cache_lines, 0x1338);
3390ASSERT_REG_POSITION(blend, 0x133C);
3391ASSERT_REG_POSITION(stencil_enable, 0x1380);
3392ASSERT_REG_POSITION(stencil_front_op, 0x1384);
3393ASSERT_REG_POSITION(stencil_front_ref, 0x1394);
3394ASSERT_REG_POSITION(stencil_front_func_mask, 0x1398);
3395ASSERT_REG_POSITION(stencil_front_mask, 0x139C);
3396ASSERT_REG_POSITION(draw_auto_start_byte_count, 0x13A4);
3397ASSERT_REG_POSITION(frag_color_clamp, 0x13A8);
3398ASSERT_REG_POSITION(window_origin, 0x13AC);
3399ASSERT_REG_POSITION(line_width_smooth, 0x13B0);
3400ASSERT_REG_POSITION(line_width_aliased, 0x13B4);
3401ASSERT_REG_POSITION(line_override_multisample, 0x1418);
3402ASSERT_REG_POSITION(alpha_hysteresis_rounds, 0x1420);
3403ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x1424);
3404ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x1428);
3405ASSERT_REG_POSITION(global_base_vertex_index, 0x1434);
3406ASSERT_REG_POSITION(global_base_instance_index, 0x1438);
3407ASSERT_REG_POSITION(ps_warp_watermarks, 0x1450);
3408ASSERT_REG_POSITION(ps_regster_watermarks, 0x1454);
3409ASSERT_REG_POSITION(store_zcull, 0x1464);
3410ASSERT_REG_POSITION(iterated_blend_constants, 0x1480);
3411ASSERT_REG_POSITION(load_zcull, 0x1500);
3412ASSERT_REG_POSITION(surface_clip_id_height, 0x1504);
3413ASSERT_REG_POSITION(surface_clip_id_clear_rect, 0x1508);
3414ASSERT_REG_POSITION(user_clip_enable, 0x1510);
3415ASSERT_REG_POSITION(zpass_pixel_count_enable, 0x1514);
3416ASSERT_REG_POSITION(point_size, 0x1518);
3417ASSERT_REG_POSITION(zcull_stats_enable, 0x151C);
3418ASSERT_REG_POSITION(point_sprite_enable, 0x1520);
3419ASSERT_REG_POSITION(shader_exceptions_enable, 0x1528);
3420ASSERT_REG_POSITION(clear_report_value, 0x1530);
3421ASSERT_REG_POSITION(anti_alias_enable, 0x1534);
3422ASSERT_REG_POSITION(zeta_enable, 0x1538);
3423ASSERT_REG_POSITION(anti_alias_alpha_control, 0x153C);
3424ASSERT_REG_POSITION(render_enable, 0x1550);
3425ASSERT_REG_POSITION(tex_sampler, 0x155C);
3426ASSERT_REG_POSITION(slope_scale_depth_bias, 0x156C);
3427ASSERT_REG_POSITION(line_anti_alias_enable, 0x1570);
3428ASSERT_REG_POSITION(tex_header, 0x1574);
3429ASSERT_REG_POSITION(active_zcull_region_id, 0x1590);
3430ASSERT_REG_POSITION(stencil_two_side_enable, 0x1594);
3431ASSERT_REG_POSITION(stencil_back_op, 0x1598);
3432ASSERT_REG_POSITION(framebuffer_srgb, 0x15B8);
3433ASSERT_REG_POSITION(depth_bias, 0x15BC);
3434ASSERT_REG_POSITION(zcull_region_format, 0x15C8);
3435ASSERT_REG_POSITION(rt_layer, 0x15CC);
3436ASSERT_REG_POSITION(anti_alias_samples_mode, 0x15D0);
3437ASSERT_REG_POSITION(edge_flag, 0x15E4);
3438ASSERT_REG_POSITION(draw_inline_index, 0x15E8);
3439ASSERT_REG_POSITION(inline_index_2x16, 0x15EC);
3440ASSERT_REG_POSITION(vertex_global_base_offset, 0x15F4);
3441ASSERT_REG_POSITION(zcull_region_pixel_offset, 0x15FC);
3442ASSERT_REG_POSITION(point_sprite, 0x1604);
3443ASSERT_REG_POSITION(program_region, 0x1608);
3444ASSERT_REG_POSITION(default_attributes, 0x1610);
3445ASSERT_REG_POSITION(draw, 0x1614);
3446ASSERT_REG_POSITION(vertex_id_copy, 0x161C);
3447ASSERT_REG_POSITION(add_to_primitive_id, 0x1620);
3448ASSERT_REG_POSITION(load_to_primitive_id, 0x1624);
3449ASSERT_REG_POSITION(shader_based_cull, 0x162C);
3450ASSERT_REG_POSITION(class_version, 0x1638);
3451ASSERT_REG_POSITION(primitive_restart, 0x1644);
3452ASSERT_REG_POSITION(output_vertex_id, 0x164C);
3453ASSERT_REG_POSITION(anti_alias_point_enable, 0x1658);
3454ASSERT_REG_POSITION(point_center_mode, 0x165C);
3455ASSERT_REG_POSITION(line_smooth_params, 0x1668);
3456ASSERT_REG_POSITION(line_stipple_enable, 0x166C);
3457ASSERT_REG_POSITION(line_smooth_edge_table, 0x1670);
3458ASSERT_REG_POSITION(line_stipple_params, 0x1680);
3459ASSERT_REG_POSITION(provoking_vertex, 0x1684);
3460ASSERT_REG_POSITION(two_sided_light_enabled, 0x1688);
3461ASSERT_REG_POSITION(polygon_stipple_enabled, 0x168C);
3462ASSERT_REG_POSITION(shader_control, 0x1690);
3463ASSERT_REG_POSITION(class_version_check, 0x16A0);
3464ASSERT_REG_POSITION(sph_version, 0x16A4);
3465ASSERT_REG_POSITION(sph_version_check, 0x16A8);
3466ASSERT_REG_POSITION(alpha_to_coverage_override, 0x16B4);
3467ASSERT_REG_POSITION(polygon_stipple_pattern, 0x1700);
3468ASSERT_REG_POSITION(aam_version, 0x1790);
3469ASSERT_REG_POSITION(aam_version_check, 0x1794);
3470ASSERT_REG_POSITION(zeta_layer_offset, 0x179C);
3471ASSERT_REG_POSITION(index_buffer, 0x17C8);
3472ASSERT_REG_POSITION(index_buffer32_first, 0x17E4);
3473ASSERT_REG_POSITION(index_buffer16_first, 0x17E8);
3474ASSERT_REG_POSITION(index_buffer8_first, 0x17EC);
3475ASSERT_REG_POSITION(index_buffer32_subsequent, 0x17F0);
3476ASSERT_REG_POSITION(index_buffer16_subsequent, 0x17F4);
3477ASSERT_REG_POSITION(index_buffer8_subsequent, 0x17F8);
3478ASSERT_REG_POSITION(depth_bias_clamp, 0x187C);
3479ASSERT_REG_POSITION(vertex_stream_instances, 0x1880);
3480ASSERT_REG_POSITION(point_size_attribute, 0x1910);
3481ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);
3482ASSERT_REG_POSITION(gl_front_face, 0x191C);
3483ASSERT_REG_POSITION(gl_cull_face, 0x1920);
3484ASSERT_REG_POSITION(viewport_pixel_center, 0x1924);
3485ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C);
3486ASSERT_REG_POSITION(viewport_clip_control, 0x193C);
3487ASSERT_REG_POSITION(user_clip_op, 0x1940);
3488ASSERT_REG_POSITION(render_enable_override, 0x1944);
3489ASSERT_REG_POSITION(primitive_topology_control, 0x1948);
3490ASSERT_REG_POSITION(window_clip_enable, 0x194C);
3491ASSERT_REG_POSITION(invalidate_zcull, 0x1958);
3492ASSERT_REG_POSITION(zcull, 0x1968);
3493ASSERT_REG_POSITION(topology_override, 0x1970);
3494ASSERT_REG_POSITION(zcull_sync, 0x1978);
3495ASSERT_REG_POSITION(clip_id_test_enable, 0x197C);
3496ASSERT_REG_POSITION(surface_clip_id_width, 0x1980);
3497ASSERT_REG_POSITION(clip_id, 0x1984);
3498ASSERT_REG_POSITION(depth_bounds_enable, 0x19BC);
3499ASSERT_REG_POSITION(blend_float_zero_times_anything_is_zero, 0x19C0);
3500ASSERT_REG_POSITION(logic_op, 0x19C4);
3501ASSERT_REG_POSITION(z_compression_enable, 0x19CC);
3502ASSERT_REG_POSITION(clear_surface, 0x19D0);
3503ASSERT_REG_POSITION(clear_clip_id_surface, 0x19D4);
3504ASSERT_REG_POSITION(color_compression_enable, 0x19E0);
3505ASSERT_REG_POSITION(color_mask, 0x1A00);
3506ASSERT_REG_POSITION(pipe_nop, 0x1A2C);
3507ASSERT_REG_POSITION(spare, 0x1A30);
3508ASSERT_REG_POSITION(report_semaphore, 0x1B00);
3509ASSERT_REG_POSITION(vertex_streams, 0x1C00);
3510ASSERT_REG_POSITION(blend_per_target, 0x1E00);
3511ASSERT_REG_POSITION(vertex_stream_limits, 0x1F00);
3512ASSERT_REG_POSITION(pipelines, 0x2000);
3513ASSERT_REG_POSITION(falcon, 0x2300);
3514ASSERT_REG_POSITION(const_buffer, 0x2380);
3515ASSERT_REG_POSITION(bind_groups, 0x2400);
3516ASSERT_REG_POSITION(color_clamp_enable, 0x2600);
3517ASSERT_REG_POSITION(bindless_texture_const_buffer_slot, 0x2608);
3518ASSERT_REG_POSITION(trap_handler, 0x260C);
3519ASSERT_REG_POSITION(stream_out_layout, 0x2800);
3520ASSERT_REG_POSITION(shader_performance, 0x333C);
3521ASSERT_REG_POSITION(shadow_scratch, 0x3400);
1735 3522
1736#undef ASSERT_REG_POSITION 3523#undef ASSERT_REG_POSITION
1737 3524
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 0efe58282..4eb7a100d 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/algorithm.h"
4#include "common/assert.h" 5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "common/microprofile.h" 7#include "common/microprofile.h"
@@ -54,90 +55,124 @@ void MaxwellDMA::Launch() {
54 const LaunchDMA& launch = regs.launch_dma; 55 const LaunchDMA& launch = regs.launch_dma;
55 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); 56 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
56 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); 57 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
57 ASSERT(regs.dst_params.origin.x == 0);
58 ASSERT(regs.dst_params.origin.y == 0);
59 58
60 const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; 59 if (launch.multi_line_enable) {
61 const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; 60 const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
61 const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
62 62
63 if (!is_src_pitch && !is_dst_pitch) { 63 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 64 // If both the source and the destination are in block layout, assert.
65 UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented"); 65 UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
66 return; 66 return;
67 } 67 }
68 68
69 if (is_src_pitch && is_dst_pitch) { 69 if (is_src_pitch && is_dst_pitch) {
70 CopyPitchToPitch(); 70 for (u32 line = 0; line < regs.line_count; ++line) {
71 const GPUVAddr source_line =
72 regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
73 const GPUVAddr dest_line =
74 regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
75 memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
76 }
77 } else {
78 if (!is_src_pitch && is_dst_pitch) {
79 CopyBlockLinearToPitch();
80 } else {
81 CopyPitchToBlockLinear();
82 }
83 }
71 } else { 84 } else {
72 ASSERT(launch.multi_line_enable == 1); 85 // TODO: allow multisized components.
73 86 auto& accelerate = rasterizer->AccessAccelerateDMA();
74 if (!is_src_pitch && is_dst_pitch) { 87 const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
75 CopyBlockLinearToPitch(); 88 if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
89 ASSERT(regs.remap_const.component_size_minus_one == 3);
90 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
91 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
92 memory_manager.WriteBlockUnsafe(regs.offset_out,
93 reinterpret_cast<u8*>(tmp_buffer.data()),
94 regs.line_length_in * sizeof(u32));
76 } else { 95 } else {
77 CopyPitchToBlockLinear(); 96 auto convert_linear_2_blocklinear_addr = [](u64 address) {
97 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
98 ((address & 0x180) >> 1) | ((address & 0x20) << 3);
99 };
100 auto src_kind = memory_manager.GetPageKind(regs.offset_in);
101 auto dst_kind = memory_manager.GetPageKind(regs.offset_out);
102 const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));
103 const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));
104 if (!is_src_pitch && is_dst_pitch) {
105 std::vector<u8> tmp_buffer(regs.line_length_in);
106 std::vector<u8> dst_buffer(regs.line_length_in);
107 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
108 regs.line_length_in);
109 for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
110 dst_buffer[offset] =
111 tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) -
112 regs.offset_in];
113 }
114 memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
115 } else if (is_src_pitch && !is_dst_pitch) {
116 std::vector<u8> tmp_buffer(regs.line_length_in);
117 std::vector<u8> dst_buffer(regs.line_length_in);
118 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
119 regs.line_length_in);
120 for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
121 dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) -
122 regs.offset_out] = tmp_buffer[offset];
123 }
124 memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
125 } else {
126 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
127 std::vector<u8> tmp_buffer(regs.line_length_in);
128 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
129 regs.line_length_in);
130 memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(),
131 regs.line_length_in);
132 }
133 }
78 } 134 }
79 } 135 }
80 ReleaseSemaphore();
81}
82 136
83void MaxwellDMA::CopyPitchToPitch() { 137 ReleaseSemaphore();
84 // When `multi_line_enable` bit is enabled we copy a 2D image of dimensions
85 // (line_length_in, line_count).
86 // Otherwise the copy is performed as if we were copying a 1D buffer of length line_length_in.
87 const bool remap_enabled = regs.launch_dma.remap_enable != 0;
88 if (regs.launch_dma.multi_line_enable) {
89 UNIMPLEMENTED_IF(remap_enabled);
90
91 // Perform a line-by-line copy.
92 // We're going to take a subrect of size (line_length_in, line_count) from the source
93 // rectangle. There is no need to manually flush/invalidate the regions because CopyBlock
94 // does that for us.
95 for (u32 line = 0; line < regs.line_count; ++line) {
96 const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
97 const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
98 memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
99 }
100 return;
101 }
102 // TODO: allow multisized components.
103 auto& accelerate = rasterizer->AccessAccelerateDMA();
104 const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
105 const bool is_buffer_clear = remap_enabled && is_const_a_dst;
106 if (is_buffer_clear) {
107 ASSERT(regs.remap_const.component_size_minus_one == 3);
108 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
109 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
110 memory_manager.WriteBlockUnsafe(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()),
111 regs.line_length_in * sizeof(u32));
112 return;
113 }
114 UNIMPLEMENTED_IF(remap_enabled);
115 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
116 std::vector<u8> tmp_buffer(regs.line_length_in);
117 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in);
118 memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in);
119 }
120} 138}
121 139
122void MaxwellDMA::CopyBlockLinearToPitch() { 140void MaxwellDMA::CopyBlockLinearToPitch() {
123 UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0); 141 UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
124 UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0);
125 UNIMPLEMENTED_IF(regs.src_params.layer != 0); 142 UNIMPLEMENTED_IF(regs.src_params.layer != 0);
126 143
144 const bool is_remapping = regs.launch_dma.remap_enable != 0;
145
127 // Optimized path for micro copies. 146 // Optimized path for micro copies.
128 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 147 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
129 if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X && 148 if (!is_remapping && dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X &&
130 regs.src_params.height > GOB_SIZE_Y) { 149 regs.src_params.height > GOB_SIZE_Y) {
131 FastCopyBlockLinearToPitch(); 150 FastCopyBlockLinearToPitch();
132 return; 151 return;
133 } 152 }
134 153
135 // Deswizzle the input and copy it over. 154 // Deswizzle the input and copy it over.
136 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
137 const u32 bytes_per_pixel =
138 regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
139 const Parameters& src_params = regs.src_params; 155 const Parameters& src_params = regs.src_params;
140 const u32 width = src_params.width; 156
157 const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
158 const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
159
160 const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
161
162 u32 width = src_params.width;
163 u32 x_elements = regs.line_length_in;
164 u32 x_offset = src_params.origin.x;
165 u32 bpp_shift = 0U;
166 if (!is_remapping) {
167 bpp_shift = Common::FoldRight(
168 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
169 width, x_elements, x_offset, static_cast<u32>(regs.offset_in));
170 width >>= bpp_shift;
171 x_elements >>= bpp_shift;
172 x_offset >>= bpp_shift;
173 }
174
175 const u32 bytes_per_pixel = base_bpp << bpp_shift;
141 const u32 height = src_params.height; 176 const u32 height = src_params.height;
142 const u32 depth = src_params.depth; 177 const u32 depth = src_params.depth;
143 const u32 block_height = src_params.block_size.height; 178 const u32 block_height = src_params.block_size.height;
@@ -155,30 +190,45 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
155 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 190 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
156 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 191 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
157 192
158 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, 193 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
159 block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(), 194 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
160 read_buffer.data()); 195 regs.pitch_out);
161 196
162 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 197 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
163} 198}
164 199
165void MaxwellDMA::CopyPitchToBlockLinear() { 200void MaxwellDMA::CopyPitchToBlockLinear() {
166 UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); 201 UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one");
167 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); 202 UNIMPLEMENTED_IF(regs.dst_params.layer != 0);
203
204 const bool is_remapping = regs.launch_dma.remap_enable != 0;
205 const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
206 const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
168 207
169 const auto& dst_params = regs.dst_params; 208 const auto& dst_params = regs.dst_params;
170 const u32 bytes_per_pixel = 209
171 regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1; 210 const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
172 const u32 width = dst_params.width; 211
212 u32 width = dst_params.width;
213 u32 x_elements = regs.line_length_in;
214 u32 x_offset = dst_params.origin.x;
215 u32 bpp_shift = 0U;
216 if (!is_remapping) {
217 bpp_shift = Common::FoldRight(
218 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
219 width, x_elements, x_offset, static_cast<u32>(regs.offset_out));
220 width >>= bpp_shift;
221 x_elements >>= bpp_shift;
222 x_offset >>= bpp_shift;
223 }
224
225 const u32 bytes_per_pixel = base_bpp << bpp_shift;
173 const u32 height = dst_params.height; 226 const u32 height = dst_params.height;
174 const u32 depth = dst_params.depth; 227 const u32 depth = dst_params.depth;
175 const u32 block_height = dst_params.block_size.height; 228 const u32 block_height = dst_params.block_size.height;
176 const u32 block_depth = dst_params.block_size.depth; 229 const u32 block_depth = dst_params.block_size.depth;
177 const size_t dst_size = 230 const size_t dst_size =
178 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 231 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
179 const size_t dst_layer_size =
180 CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
181
182 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; 232 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
183 233
184 if (read_buffer.size() < src_size) { 234 if (read_buffer.size() < src_size) {
@@ -188,32 +238,23 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
188 write_buffer.resize(dst_size); 238 write_buffer.resize(dst_size);
189 } 239 }
190 240
241 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
191 if (Settings::IsGPULevelExtreme()) { 242 if (Settings::IsGPULevelExtreme()) {
192 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
193 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 243 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
194 } else { 244 } else {
195 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
196 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); 245 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
197 } 246 }
198 247
199 // If the input is linear and the output is tiled, swizzle the input and copy it over. 248 // If the input is linear and the output is tiled, swizzle the input and copy it over.
200 if (regs.dst_params.block_size.depth > 0) { 249 SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
201 ASSERT(dst_params.layer == 0); 250 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
202 SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height, 251 regs.pitch_in);
203 bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
204 dst_params.origin.y, write_buffer.data(), read_buffer.data());
205 } else {
206 SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
207 write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
208 block_height, dst_params.origin.x, dst_params.origin.y);
209 }
210 252
211 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 253 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
212} 254}
213 255
214void MaxwellDMA::FastCopyBlockLinearToPitch() { 256void MaxwellDMA::FastCopyBlockLinearToPitch() {
215 const u32 bytes_per_pixel = 257 const u32 bytes_per_pixel = 1U;
216 regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
217 const size_t src_size = GOB_SIZE; 258 const size_t src_size = GOB_SIZE;
218 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 259 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
219 u32 pos_x = regs.src_params.origin.x; 260 u32 pos_x = regs.src_params.origin.x;
@@ -239,9 +280,10 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
239 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); 280 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
240 } 281 }
241 282
242 UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, 283 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, regs.src_params.width,
243 bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y, 284 regs.src_params.height, 1, pos_x, pos_y, regs.line_length_in, regs.line_count,
244 write_buffer.data(), read_buffer.data()); 285 regs.src_params.block_size.height, regs.src_params.block_size.depth,
286 regs.pitch_out);
245 287
246 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 288 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
247} 289}
@@ -249,16 +291,24 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
249void MaxwellDMA::ReleaseSemaphore() { 291void MaxwellDMA::ReleaseSemaphore() {
250 const auto type = regs.launch_dma.semaphore_type; 292 const auto type = regs.launch_dma.semaphore_type;
251 const GPUVAddr address = regs.semaphore.address; 293 const GPUVAddr address = regs.semaphore.address;
294 const u32 payload = regs.semaphore.payload;
252 switch (type) { 295 switch (type) {
253 case LaunchDMA::SemaphoreType::NONE: 296 case LaunchDMA::SemaphoreType::NONE:
254 break; 297 break;
255 case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: 298 case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: {
256 memory_manager.Write<u32>(address, regs.semaphore.payload); 299 std::function<void()> operation(
300 [this, address, payload] { memory_manager.Write<u32>(address, payload); });
301 rasterizer->SignalFence(std::move(operation));
257 break; 302 break;
258 case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: 303 }
259 memory_manager.Write<u64>(address, static_cast<u64>(regs.semaphore.payload)); 304 case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: {
260 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks()); 305 std::function<void()> operation([this, address, payload] {
306 memory_manager.Write<u64>(address + sizeof(u64), system.GPU().GetTicks());
307 memory_manager.Write<u64>(address, payload);
308 });
309 rasterizer->SignalFence(std::move(operation));
261 break; 310 break;
311 }
262 default: 312 default:
263 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); 313 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
264 } 314 }
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 074bac92c..953e34adc 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -189,10 +189,16 @@ public:
189 BitField<4, 3, Swizzle> dst_y; 189 BitField<4, 3, Swizzle> dst_y;
190 BitField<8, 3, Swizzle> dst_z; 190 BitField<8, 3, Swizzle> dst_z;
191 BitField<12, 3, Swizzle> dst_w; 191 BitField<12, 3, Swizzle> dst_w;
192 BitField<0, 12, u32> dst_components_raw;
192 BitField<16, 2, u32> component_size_minus_one; 193 BitField<16, 2, u32> component_size_minus_one;
193 BitField<20, 2, u32> num_src_components_minus_one; 194 BitField<20, 2, u32> num_src_components_minus_one;
194 BitField<24, 2, u32> num_dst_components_minus_one; 195 BitField<24, 2, u32> num_dst_components_minus_one;
195 }; 196 };
197
198 Swizzle GetComponent(size_t i) const {
199 const u32 raw = dst_components_raw;
200 return static_cast<Swizzle>((raw >> (i * 3)) & 0x7);
201 }
196 }; 202 };
197 static_assert(sizeof(RemapConst) == 12); 203 static_assert(sizeof(RemapConst) == 12);
198 204
@@ -213,8 +219,6 @@ private:
213 /// registers. 219 /// registers.
214 void Launch(); 220 void Launch();
215 221
216 void CopyPitchToPitch();
217
218 void CopyBlockLinearToPitch(); 222 void CopyBlockLinearToPitch();
219 223
220 void CopyPitchToBlockLinear(); 224 void CopyPitchToBlockLinear();
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
new file mode 100644
index 000000000..3977bb0fb
--- /dev/null
+++ b/src/video_core/engines/puller.cpp
@@ -0,0 +1,305 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/core.h"
8#include "video_core/control/channel_state.h"
9#include "video_core/dma_pusher.h"
10#include "video_core/engines/fermi_2d.h"
11#include "video_core/engines/kepler_compute.h"
12#include "video_core/engines/kepler_memory.h"
13#include "video_core/engines/maxwell_3d.h"
14#include "video_core/engines/maxwell_dma.h"
15#include "video_core/engines/puller.h"
16#include "video_core/gpu.h"
17#include "video_core/memory_manager.h"
18#include "video_core/rasterizer_interface.h"
19
20namespace Tegra::Engines {
21
22Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_,
23 Control::ChannelState& channel_state_)
24 : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{
25 channel_state_} {}
26
27Puller::~Puller() = default;
28
29void Puller::ProcessBindMethod(const MethodCall& method_call) {
30 // Bind the current subchannel to the desired engine id.
31 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
32 method_call.argument);
33 const auto engine_id = static_cast<EngineID>(method_call.argument);
34 bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id);
35 switch (engine_id) {
36 case EngineID::FERMI_TWOD_A:
37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel);
38 break;
39 case EngineID::MAXWELL_B:
40 dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel);
41 break;
42 case EngineID::KEPLER_COMPUTE_B:
43 dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel);
44 break;
45 case EngineID::MAXWELL_DMA_COPY_A:
46 dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel);
47 break;
48 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
49 dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel);
50 break;
51 default:
52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
53 }
54}
55
56void Puller::ProcessFenceActionMethod() {
57 switch (regs.fence_action.op) {
58 case Puller::FenceOperation::Acquire:
59 // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
60 // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
61 rasterizer->ReleaseFences();
62 break;
63 case Puller::FenceOperation::Increment:
64 rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
65 break;
66 default:
67 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
68 }
69}
70
71void Puller::ProcessSemaphoreTriggerMethod() {
72 const auto semaphoreOperationMask = 0xF;
73 const auto op =
74 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
75 if (op == GpuSemaphoreOperation::WriteLong) {
76 const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
77 const u32 payload = regs.semaphore_sequence;
78 [this, sequence_address, payload] {
79 memory_manager.Write<u64>(sequence_address + sizeof(u64), gpu.GetTicks());
80 memory_manager.Write<u64>(sequence_address, payload);
81 }();
82 } else {
83 do {
84 const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())};
85 regs.acquire_source = true;
86 regs.acquire_value = regs.semaphore_sequence;
87 if (op == GpuSemaphoreOperation::AcquireEqual) {
88 regs.acquire_active = true;
89 regs.acquire_mode = false;
90 if (word != regs.acquire_value) {
91 rasterizer->ReleaseFences();
92 continue;
93 }
94 } else if (op == GpuSemaphoreOperation::AcquireGequal) {
95 regs.acquire_active = true;
96 regs.acquire_mode = true;
97 if (word < regs.acquire_value) {
98 rasterizer->ReleaseFences();
99 continue;
100 }
101 } else if (op == GpuSemaphoreOperation::AcquireMask) {
102 if (word && regs.semaphore_sequence == 0) {
103 rasterizer->ReleaseFences();
104 continue;
105 }
106 } else {
107 LOG_ERROR(HW_GPU, "Invalid semaphore operation");
108 }
109 } while (false);
110 }
111}
112
113void Puller::ProcessSemaphoreRelease() {
114 const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
115 const u32 payload = regs.semaphore_release;
116 std::function<void()> operation([this, sequence_address, payload] {
117 memory_manager.Write<u32>(sequence_address, payload);
118 });
119 rasterizer->SyncOperation(std::move(operation));
120}
121
122void Puller::ProcessSemaphoreAcquire() {
123 u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
124 const auto value = regs.semaphore_acquire;
125 while (word != value) {
126 regs.acquire_active = true;
127 regs.acquire_value = value;
128 std::this_thread::sleep_for(std::chrono::milliseconds(1));
129 rasterizer->ReleaseFences();
130 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
131 // TODO(kemathe73) figure out how to do the acquire_timeout
132 regs.acquire_mode = false;
133 regs.acquire_source = false;
134 }
135}
136
137/// Calls a GPU puller method.
138void Puller::CallPullerMethod(const MethodCall& method_call) {
139 regs.reg_array[method_call.method] = method_call.argument;
140 const auto method = static_cast<BufferMethods>(method_call.method);
141
142 switch (method) {
143 case BufferMethods::BindObject: {
144 ProcessBindMethod(method_call);
145 break;
146 }
147 case BufferMethods::Nop:
148 case BufferMethods::SemaphoreAddressHigh:
149 case BufferMethods::SemaphoreAddressLow:
150 case BufferMethods::SemaphoreSequencePayload:
151 case BufferMethods::SyncpointPayload:
152 break;
153 case BufferMethods::WrcacheFlush:
154 case BufferMethods::RefCnt:
155 rasterizer->SignalReference();
156 break;
157 case BufferMethods::SyncpointOperation:
158 ProcessFenceActionMethod();
159 break;
160 case BufferMethods::WaitForIdle:
161 rasterizer->WaitForIdle();
162 break;
163 case BufferMethods::SemaphoreOperation: {
164 ProcessSemaphoreTriggerMethod();
165 break;
166 }
167 case BufferMethods::NonStallInterrupt: {
168 LOG_ERROR(HW_GPU, "Special puller engine method NonStallInterrupt not implemented");
169 break;
170 }
171 case BufferMethods::MemOpA: {
172 LOG_ERROR(HW_GPU, "Memory Operation A");
173 break;
174 }
175 case BufferMethods::MemOpB: {
176 // Implement this better.
177 rasterizer->InvalidateGPUCache();
178 break;
179 }
180 case BufferMethods::MemOpC:
181 case BufferMethods::MemOpD: {
182 LOG_ERROR(HW_GPU, "Memory Operation C,D");
183 break;
184 }
185 case BufferMethods::SemaphoreAcquire: {
186 ProcessSemaphoreAcquire();
187 break;
188 }
189 case BufferMethods::SemaphoreRelease: {
190 ProcessSemaphoreRelease();
191 break;
192 }
193 case BufferMethods::Yield: {
194 // TODO(Kmather73): Research and implement this method.
195 LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
196 break;
197 }
198 default:
199 LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method);
200 break;
201 }
202}
203
204/// Calls a GPU engine method.
205void Puller::CallEngineMethod(const MethodCall& method_call) {
206 const EngineID engine = bound_engines[method_call.subchannel];
207
208 switch (engine) {
209 case EngineID::FERMI_TWOD_A:
210 channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument,
211 method_call.IsLastCall());
212 break;
213 case EngineID::MAXWELL_B:
214 channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument,
215 method_call.IsLastCall());
216 break;
217 case EngineID::KEPLER_COMPUTE_B:
218 channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument,
219 method_call.IsLastCall());
220 break;
221 case EngineID::MAXWELL_DMA_COPY_A:
222 channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument,
223 method_call.IsLastCall());
224 break;
225 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
226 channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument,
227 method_call.IsLastCall());
228 break;
229 default:
230 UNIMPLEMENTED_MSG("Unimplemented engine");
231 }
232}
233
234/// Calls a GPU engine multivalue method.
235void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
236 u32 methods_pending) {
237 const EngineID engine = bound_engines[subchannel];
238
239 switch (engine) {
240 case EngineID::FERMI_TWOD_A:
241 channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
242 break;
243 case EngineID::MAXWELL_B:
244 channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
245 break;
246 case EngineID::KEPLER_COMPUTE_B:
247 channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
248 break;
249 case EngineID::MAXWELL_DMA_COPY_A:
250 channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
251 break;
252 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
253 channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
254 break;
255 default:
256 UNIMPLEMENTED_MSG("Unimplemented engine");
257 }
258}
259
260/// Calls a GPU method.
261void Puller::CallMethod(const MethodCall& method_call) {
262 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
263 method_call.subchannel);
264
265 ASSERT(method_call.subchannel < bound_engines.size());
266
267 if (ExecuteMethodOnEngine(method_call.method)) {
268 CallEngineMethod(method_call);
269 } else {
270 CallPullerMethod(method_call);
271 }
272}
273
274/// Calls a GPU multivalue method.
275void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
276 u32 methods_pending) {
277 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
278
279 ASSERT(subchannel < bound_engines.size());
280
281 if (ExecuteMethodOnEngine(method)) {
282 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
283 } else {
284 for (std::size_t i = 0; i < amount; i++) {
285 CallPullerMethod(MethodCall{
286 method,
287 base_start[i],
288 subchannel,
289 methods_pending - static_cast<u32>(i),
290 });
291 }
292 }
293}
294
295void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
296 rasterizer = rasterizer_;
297}
298
299/// Determines where the method should be executed.
300[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) {
301 const auto buffer_method = static_cast<BufferMethods>(method);
302 return buffer_method >= BufferMethods::NonPullerMethods;
303}
304
305} // namespace Tegra::Engines
diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h
new file mode 100644
index 000000000..d4175ee94
--- /dev/null
+++ b/src/video_core/engines/puller.h
@@ -0,0 +1,177 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8#include <vector>
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "video_core/engines/engine_interface.h"
13
14namespace Core {
15class System;
16}
17
18namespace Tegra {
19class MemoryManager;
20class DmaPusher;
21
22enum class EngineID {
23 FERMI_TWOD_A = 0x902D, // 2D Engine
24 MAXWELL_B = 0xB197, // 3D Engine
25 KEPLER_COMPUTE_B = 0xB1C0,
26 KEPLER_INLINE_TO_MEMORY_B = 0xA140,
27 MAXWELL_DMA_COPY_A = 0xB0B5,
28};
29
30namespace Control {
31struct ChannelState;
32}
33} // namespace Tegra
34
35namespace VideoCore {
36class RasterizerInterface;
37}
38
39namespace Tegra::Engines {
40
41class Puller final {
42public:
43 struct MethodCall {
44 u32 method{};
45 u32 argument{};
46 u32 subchannel{};
47 u32 method_count{};
48
49 explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0)
50 : method(method_), argument(argument_), subchannel(subchannel_),
51 method_count(method_count_) {}
52
53 [[nodiscard]] bool IsLastCall() const {
54 return method_count <= 1;
55 }
56 };
57
58 enum class FenceOperation : u32 {
59 Acquire = 0,
60 Increment = 1,
61 };
62
63 union FenceAction {
64 u32 raw;
65 BitField<0, 1, FenceOperation> op;
66 BitField<8, 24, u32> syncpoint_id;
67 };
68
69 explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher,
70 Control::ChannelState& channel_state);
71 ~Puller();
72
73 void CallMethod(const MethodCall& method_call);
74
75 void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
76 u32 methods_pending);
77
78 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
79
80 void CallPullerMethod(const MethodCall& method_call);
81
82 void CallEngineMethod(const MethodCall& method_call);
83
84 void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
85 u32 methods_pending);
86
87private:
88 Tegra::GPU& gpu;
89
90 MemoryManager& memory_manager;
91 DmaPusher& dma_pusher;
92 Control::ChannelState& channel_state;
93 VideoCore::RasterizerInterface* rasterizer = nullptr;
94
95 static constexpr std::size_t NUM_REGS = 0x800;
96 struct Regs {
97 static constexpr size_t NUM_REGS = 0x40;
98
99 union {
100 struct {
101 INSERT_PADDING_WORDS_NOINIT(0x4);
102 struct {
103 u32 address_high;
104 u32 address_low;
105
106 [[nodiscard]] GPUVAddr SemaphoreAddress() const {
107 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
108 address_low);
109 }
110 } semaphore_address;
111
112 u32 semaphore_sequence;
113 u32 semaphore_trigger;
114 INSERT_PADDING_WORDS_NOINIT(0xC);
115
116 // The pusher and the puller share the reference counter, the pusher only has read
117 // access
118 u32 reference_count;
119 INSERT_PADDING_WORDS_NOINIT(0x5);
120
121 u32 semaphore_acquire;
122 u32 semaphore_release;
123 u32 fence_value;
124 FenceAction fence_action;
125 INSERT_PADDING_WORDS_NOINIT(0xE2);
126
127 // Puller state
128 u32 acquire_mode;
129 u32 acquire_source;
130 u32 acquire_active;
131 u32 acquire_timeout;
132 u32 acquire_value;
133 };
134 std::array<u32, NUM_REGS> reg_array;
135 };
136 } regs{};
137
138 void ProcessBindMethod(const MethodCall& method_call);
139 void ProcessFenceActionMethod();
140 void ProcessSemaphoreAcquire();
141 void ProcessSemaphoreRelease();
142 void ProcessSemaphoreTriggerMethod();
143 [[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
144
145 /// Mapping of command subchannels to their bound engine ids
146 std::array<EngineID, 8> bound_engines{};
147
148 enum class GpuSemaphoreOperation {
149 AcquireEqual = 0x1,
150 WriteLong = 0x2,
151 AcquireGequal = 0x4,
152 AcquireMask = 0x8,
153 };
154
155#define ASSERT_REG_POSITION(field_name, position) \
156 static_assert(offsetof(Regs, field_name) == position * 4, \
157 "Field " #field_name " has invalid position")
158
159 ASSERT_REG_POSITION(semaphore_address, 0x4);
160 ASSERT_REG_POSITION(semaphore_sequence, 0x6);
161 ASSERT_REG_POSITION(semaphore_trigger, 0x7);
162 ASSERT_REG_POSITION(reference_count, 0x14);
163 ASSERT_REG_POSITION(semaphore_acquire, 0x1A);
164 ASSERT_REG_POSITION(semaphore_release, 0x1B);
165 ASSERT_REG_POSITION(fence_value, 0x1C);
166 ASSERT_REG_POSITION(fence_action, 0x1D);
167
168 ASSERT_REG_POSITION(acquire_mode, 0x100);
169 ASSERT_REG_POSITION(acquire_source, 0x101);
170 ASSERT_REG_POSITION(acquire_active, 0x102);
171 ASSERT_REG_POSITION(acquire_timeout, 0x103);
172 ASSERT_REG_POSITION(acquire_value, 0x104);
173
174#undef ASSERT_REG_POSITION
175};
176
177} // namespace Tegra::Engines
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 1e9832ddd..c390ac91b 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -4,40 +4,24 @@
4#pragma once 4#pragma once
5 5
6#include <algorithm> 6#include <algorithm>
7#include <cstring>
8#include <deque>
9#include <functional>
10#include <memory>
7#include <queue> 11#include <queue>
8 12
9#include "common/common_types.h" 13#include "common/common_types.h"
10#include "video_core/delayed_destruction_ring.h" 14#include "video_core/delayed_destruction_ring.h"
11#include "video_core/gpu.h" 15#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 16#include "video_core/host1x/host1x.h"
17#include "video_core/host1x/syncpoint_manager.h"
13#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
14 19
15namespace VideoCommon { 20namespace VideoCommon {
16 21
17class FenceBase { 22class FenceBase {
18public: 23public:
19 explicit FenceBase(u32 payload_, bool is_stubbed_) 24 explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
20 : address{}, payload{payload_}, is_semaphore{false}, is_stubbed{is_stubbed_} {}
21
22 explicit FenceBase(GPUVAddr address_, u32 payload_, bool is_stubbed_)
23 : address{address_}, payload{payload_}, is_semaphore{true}, is_stubbed{is_stubbed_} {}
24
25 GPUVAddr GetAddress() const {
26 return address;
27 }
28
29 u32 GetPayload() const {
30 return payload;
31 }
32
33 bool IsSemaphore() const {
34 return is_semaphore;
35 }
36
37private:
38 GPUVAddr address;
39 u32 payload;
40 bool is_semaphore;
41 25
42protected: 26protected:
43 bool is_stubbed; 27 bool is_stubbed;
@@ -57,30 +41,28 @@ public:
57 buffer_cache.AccumulateFlushes(); 41 buffer_cache.AccumulateFlushes();
58 } 42 }
59 43
60 void SignalSemaphore(GPUVAddr addr, u32 value) { 44 void SyncOperation(std::function<void()>&& func) {
45 uncommitted_operations.emplace_back(std::move(func));
46 }
47
48 void SignalFence(std::function<void()>&& func) {
61 TryReleasePendingFences(); 49 TryReleasePendingFences();
62 const bool should_flush = ShouldFlush(); 50 const bool should_flush = ShouldFlush();
63 CommitAsyncFlushes(); 51 CommitAsyncFlushes();
64 TFence new_fence = CreateFence(addr, value, !should_flush); 52 uncommitted_operations.emplace_back(std::move(func));
53 CommitOperations();
54 TFence new_fence = CreateFence(!should_flush);
65 fences.push(new_fence); 55 fences.push(new_fence);
66 QueueFence(new_fence); 56 QueueFence(new_fence);
67 if (should_flush) { 57 if (should_flush) {
68 rasterizer.FlushCommands(); 58 rasterizer.FlushCommands();
69 } 59 }
70 rasterizer.SyncGuestHost();
71 } 60 }
72 61
73 void SignalSyncPoint(u32 value) { 62 void SignalSyncPoint(u32 value) {
74 TryReleasePendingFences(); 63 syncpoint_manager.IncrementGuest(value);
75 const bool should_flush = ShouldFlush(); 64 std::function<void()> func([this, value] { syncpoint_manager.IncrementHost(value); });
76 CommitAsyncFlushes(); 65 SignalFence(std::move(func));
77 TFence new_fence = CreateFence(value, !should_flush);
78 fences.push(new_fence);
79 QueueFence(new_fence);
80 if (should_flush) {
81 rasterizer.FlushCommands();
82 }
83 rasterizer.SyncGuestHost();
84 } 66 }
85 67
86 void WaitPendingFences() { 68 void WaitPendingFences() {
@@ -90,11 +72,10 @@ public:
90 WaitFence(current_fence); 72 WaitFence(current_fence);
91 } 73 }
92 PopAsyncFlushes(); 74 PopAsyncFlushes();
93 if (current_fence->IsSemaphore()) { 75 auto operations = std::move(pending_operations.front());
94 gpu_memory.template Write<u32>(current_fence->GetAddress(), 76 pending_operations.pop_front();
95 current_fence->GetPayload()); 77 for (auto& operation : operations) {
96 } else { 78 operation();
97 gpu.IncrementSyncPoint(current_fence->GetPayload());
98 } 79 }
99 PopFence(); 80 PopFence();
100 } 81 }
@@ -104,16 +85,14 @@ protected:
104 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 85 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
105 TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, 86 TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
106 TQueryCache& query_cache_) 87 TQueryCache& query_cache_)
107 : rasterizer{rasterizer_}, gpu{gpu_}, gpu_memory{gpu.MemoryManager()}, 88 : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
108 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} 89 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
109 90
110 virtual ~FenceManager() = default; 91 virtual ~FenceManager() = default;
111 92
112 /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is 93 /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
113 /// true 94 /// true
114 virtual TFence CreateFence(u32 value, bool is_stubbed) = 0; 95 virtual TFence CreateFence(bool is_stubbed) = 0;
115 /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true
116 virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0;
117 /// Queues a fence into the backend if the fence isn't stubbed. 96 /// Queues a fence into the backend if the fence isn't stubbed.
118 virtual void QueueFence(TFence& fence) = 0; 97 virtual void QueueFence(TFence& fence) = 0;
119 /// Notifies that the backend fence has been signaled/reached in host GPU. 98 /// Notifies that the backend fence has been signaled/reached in host GPU.
@@ -123,7 +102,7 @@ protected:
123 102
124 VideoCore::RasterizerInterface& rasterizer; 103 VideoCore::RasterizerInterface& rasterizer;
125 Tegra::GPU& gpu; 104 Tegra::GPU& gpu;
126 Tegra::MemoryManager& gpu_memory; 105 Tegra::Host1x::SyncpointManager& syncpoint_manager;
127 TTextureCache& texture_cache; 106 TTextureCache& texture_cache;
128 TTBufferCache& buffer_cache; 107 TTBufferCache& buffer_cache;
129 TQueryCache& query_cache; 108 TQueryCache& query_cache;
@@ -136,11 +115,10 @@ private:
136 return; 115 return;
137 } 116 }
138 PopAsyncFlushes(); 117 PopAsyncFlushes();
139 if (current_fence->IsSemaphore()) { 118 auto operations = std::move(pending_operations.front());
140 gpu_memory.template Write<u32>(current_fence->GetAddress(), 119 pending_operations.pop_front();
141 current_fence->GetPayload()); 120 for (auto& operation : operations) {
142 } else { 121 operation();
143 gpu.IncrementSyncPoint(current_fence->GetPayload());
144 } 122 }
145 PopFence(); 123 PopFence();
146 } 124 }
@@ -159,16 +137,20 @@ private:
159 } 137 }
160 138
161 void PopAsyncFlushes() { 139 void PopAsyncFlushes() {
162 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 140 {
163 texture_cache.PopAsyncFlushes(); 141 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
164 buffer_cache.PopAsyncFlushes(); 142 texture_cache.PopAsyncFlushes();
143 buffer_cache.PopAsyncFlushes();
144 }
165 query_cache.PopAsyncFlushes(); 145 query_cache.PopAsyncFlushes();
166 } 146 }
167 147
168 void CommitAsyncFlushes() { 148 void CommitAsyncFlushes() {
169 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 149 {
170 texture_cache.CommitAsyncFlushes(); 150 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
171 buffer_cache.CommitAsyncFlushes(); 151 texture_cache.CommitAsyncFlushes();
152 buffer_cache.CommitAsyncFlushes();
153 }
172 query_cache.CommitAsyncFlushes(); 154 query_cache.CommitAsyncFlushes();
173 } 155 }
174 156
@@ -177,7 +159,13 @@ private:
177 fences.pop(); 159 fences.pop();
178 } 160 }
179 161
162 void CommitOperations() {
163 pending_operations.emplace_back(std::move(uncommitted_operations));
164 }
165
180 std::queue<TFence> fences; 166 std::queue<TFence> fences;
167 std::deque<std::function<void()>> uncommitted_operations;
168 std::deque<std::deque<std::function<void()>>> pending_operations;
181 169
182 DelayedDestructionRing<TFence, 6> delayed_destruction_ring; 170 DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
183}; 171};
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 33431f2a0..28b38273e 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -14,10 +14,11 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
17#include "core/hardware_interrupt_manager.h"
18#include "core/hle/service/nvdrv/nvdata.h" 17#include "core/hle/service/nvdrv/nvdata.h"
19#include "core/perf_stats.h" 18#include "core/perf_stats.h"
20#include "video_core/cdma_pusher.h" 19#include "video_core/cdma_pusher.h"
20#include "video_core/control/channel_state.h"
21#include "video_core/control/scheduler.h"
21#include "video_core/dma_pusher.h" 22#include "video_core/dma_pusher.h"
22#include "video_core/engines/fermi_2d.h" 23#include "video_core/engines/fermi_2d.h"
23#include "video_core/engines/kepler_compute.h" 24#include "video_core/engines/kepler_compute.h"
@@ -26,75 +27,64 @@
26#include "video_core/engines/maxwell_dma.h" 27#include "video_core/engines/maxwell_dma.h"
27#include "video_core/gpu.h" 28#include "video_core/gpu.h"
28#include "video_core/gpu_thread.h" 29#include "video_core/gpu_thread.h"
30#include "video_core/host1x/host1x.h"
31#include "video_core/host1x/syncpoint_manager.h"
29#include "video_core/memory_manager.h" 32#include "video_core/memory_manager.h"
30#include "video_core/renderer_base.h" 33#include "video_core/renderer_base.h"
31#include "video_core/shader_notify.h" 34#include "video_core/shader_notify.h"
32 35
33namespace Tegra { 36namespace Tegra {
34 37
35MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
36
37struct GPU::Impl { 38struct GPU::Impl {
38 explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) 39 explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_)
39 : gpu{gpu_}, system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>( 40 : gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_},
40 system)},
41 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, gpu)}, use_nvdec{use_nvdec_},
42 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
43 fermi_2d{std::make_unique<Engines::Fermi2D>()},
44 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
45 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
46 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
47 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, 41 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
48 gpu_thread{system_, is_async_} {} 42 gpu_thread{system_, is_async_}, scheduler{std::make_unique<Control::Scheduler>(gpu)} {}
49 43
50 ~Impl() = default; 44 ~Impl() = default;
51 45
52 /// Binds a renderer to the GPU. 46 std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
53 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { 47 auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
54 renderer = std::move(renderer_); 48 channels.emplace(channel_id, channel_state);
55 rasterizer = renderer->ReadRasterizer(); 49 scheduler->DeclareChannel(channel_state);
56 50 return channel_state;
57 memory_manager->BindRasterizer(rasterizer);
58 maxwell_3d->BindRasterizer(rasterizer);
59 fermi_2d->BindRasterizer(rasterizer);
60 kepler_compute->BindRasterizer(rasterizer);
61 kepler_memory->BindRasterizer(rasterizer);
62 maxwell_dma->BindRasterizer(rasterizer);
63 } 51 }
64 52
65 /// Calls a GPU method. 53 void BindChannel(s32 channel_id) {
66 void CallMethod(const GPU::MethodCall& method_call) { 54 if (bound_channel == channel_id) {
67 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, 55 return;
68 method_call.subchannel); 56 }
57 auto it = channels.find(channel_id);
58 ASSERT(it != channels.end());
59 bound_channel = channel_id;
60 current_channel = it->second.get();
69 61
70 ASSERT(method_call.subchannel < bound_engines.size()); 62 rasterizer->BindChannel(*current_channel);
63 }
71 64
72 if (ExecuteMethodOnEngine(method_call.method)) { 65 std::shared_ptr<Control::ChannelState> AllocateChannel() {
73 CallEngineMethod(method_call); 66 return CreateChannel(new_channel_id++);
74 } else {
75 CallPullerMethod(method_call);
76 }
77 } 67 }
78 68
79 /// Calls a GPU multivalue method. 69 void InitChannel(Control::ChannelState& to_init) {
80 void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, 70 to_init.Init(system, gpu);
81 u32 methods_pending) { 71 to_init.BindRasterizer(rasterizer);
82 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); 72 rasterizer->InitializeChannel(to_init);
73 }
83 74
84 ASSERT(subchannel < bound_engines.size()); 75 void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
76 memory_manager.BindRasterizer(rasterizer);
77 }
85 78
86 if (ExecuteMethodOnEngine(method)) { 79 void ReleaseChannel(Control::ChannelState& to_release) {
87 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); 80 UNIMPLEMENTED();
88 } else { 81 }
89 for (std::size_t i = 0; i < amount; i++) { 82
90 CallPullerMethod(GPU::MethodCall{ 83 /// Binds a renderer to the GPU.
91 method, 84 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
92 base_start[i], 85 renderer = std::move(renderer_);
93 subchannel, 86 rasterizer = renderer->ReadRasterizer();
94 methods_pending - static_cast<u32>(i), 87 host1x.MemoryManager().BindRasterizer(rasterizer);
95 });
96 }
97 }
98 } 88 }
99 89
100 /// Flush all current written commands into the host GPU for execution. 90 /// Flush all current written commands into the host GPU for execution.
@@ -103,85 +93,82 @@ struct GPU::Impl {
103 } 93 }
104 94
105 /// Synchronizes CPU writes with Host GPU memory. 95 /// Synchronizes CPU writes with Host GPU memory.
106 void SyncGuestHost() { 96 void InvalidateGPUCache() {
107 rasterizer->SyncGuestHost(); 97 rasterizer->InvalidateGPUCache();
108 } 98 }
109 99
110 /// Signal the ending of command list. 100 /// Signal the ending of command list.
111 void OnCommandListEnd() { 101 void OnCommandListEnd() {
112 if (is_async) { 102 gpu_thread.OnCommandListEnd();
113 // This command only applies to asynchronous GPU mode
114 gpu_thread.OnCommandListEnd();
115 }
116 } 103 }
117 104
118 /// Request a host GPU memory flush from the CPU. 105 /// Request a host GPU memory flush from the CPU.
119 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size) { 106 template <typename Func>
120 std::unique_lock lck{flush_request_mutex}; 107 [[nodiscard]] u64 RequestSyncOperation(Func&& action) {
121 const u64 fence = ++last_flush_fence; 108 std::unique_lock lck{sync_request_mutex};
122 flush_requests.emplace_back(fence, addr, size); 109 const u64 fence = ++last_sync_fence;
110 sync_requests.emplace_back(action);
123 return fence; 111 return fence;
124 } 112 }
125 113
126 /// Obtains current flush request fence id. 114 /// Obtains current flush request fence id.
127 [[nodiscard]] u64 CurrentFlushRequestFence() const { 115 [[nodiscard]] u64 CurrentSyncRequestFence() const {
128 return current_flush_fence.load(std::memory_order_relaxed); 116 return current_sync_fence.load(std::memory_order_relaxed);
117 }
118
119 void WaitForSyncOperation(const u64 fence) {
120 std::unique_lock lck{sync_request_mutex};
121 sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; });
129 } 122 }
130 123
131 /// Tick pending requests within the GPU. 124 /// Tick pending requests within the GPU.
132 void TickWork() { 125 void TickWork() {
133 std::unique_lock lck{flush_request_mutex}; 126 std::unique_lock lck{sync_request_mutex};
134 while (!flush_requests.empty()) { 127 while (!sync_requests.empty()) {
135 auto& request = flush_requests.front(); 128 auto request = std::move(sync_requests.front());
136 const u64 fence = request.fence; 129 sync_requests.pop_front();
137 const VAddr addr = request.addr; 130 sync_request_mutex.unlock();
138 const std::size_t size = request.size; 131 request();
139 flush_requests.pop_front(); 132 current_sync_fence.fetch_add(1, std::memory_order_release);
140 flush_request_mutex.unlock(); 133 sync_request_mutex.lock();
141 rasterizer->FlushRegion(addr, size); 134 sync_request_cv.notify_all();
142 current_flush_fence.store(fence);
143 flush_request_mutex.lock();
144 } 135 }
145 } 136 }
146 137
147 /// Returns a reference to the Maxwell3D GPU engine. 138 /// Returns a reference to the Maxwell3D GPU engine.
148 [[nodiscard]] Engines::Maxwell3D& Maxwell3D() { 139 [[nodiscard]] Engines::Maxwell3D& Maxwell3D() {
149 return *maxwell_3d; 140 ASSERT(current_channel);
141 return *current_channel->maxwell_3d;
150 } 142 }
151 143
152 /// Returns a const reference to the Maxwell3D GPU engine. 144 /// Returns a const reference to the Maxwell3D GPU engine.
153 [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const { 145 [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const {
154 return *maxwell_3d; 146 ASSERT(current_channel);
147 return *current_channel->maxwell_3d;
155 } 148 }
156 149
157 /// Returns a reference to the KeplerCompute GPU engine. 150 /// Returns a reference to the KeplerCompute GPU engine.
158 [[nodiscard]] Engines::KeplerCompute& KeplerCompute() { 151 [[nodiscard]] Engines::KeplerCompute& KeplerCompute() {
159 return *kepler_compute; 152 ASSERT(current_channel);
153 return *current_channel->kepler_compute;
160 } 154 }
161 155
162 /// Returns a reference to the KeplerCompute GPU engine. 156 /// Returns a reference to the KeplerCompute GPU engine.
163 [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const { 157 [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const {
164 return *kepler_compute; 158 ASSERT(current_channel);
165 } 159 return *current_channel->kepler_compute;
166
167 /// Returns a reference to the GPU memory manager.
168 [[nodiscard]] Tegra::MemoryManager& MemoryManager() {
169 return *memory_manager;
170 }
171
172 /// Returns a const reference to the GPU memory manager.
173 [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const {
174 return *memory_manager;
175 } 160 }
176 161
177 /// Returns a reference to the GPU DMA pusher. 162 /// Returns a reference to the GPU DMA pusher.
178 [[nodiscard]] Tegra::DmaPusher& DmaPusher() { 163 [[nodiscard]] Tegra::DmaPusher& DmaPusher() {
179 return *dma_pusher; 164 ASSERT(current_channel);
165 return *current_channel->dma_pusher;
180 } 166 }
181 167
182 /// Returns a const reference to the GPU DMA pusher. 168 /// Returns a const reference to the GPU DMA pusher.
183 [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const { 169 [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const {
184 return *dma_pusher; 170 ASSERT(current_channel);
171 return *current_channel->dma_pusher;
185 } 172 }
186 173
187 /// Returns a reference to the underlying renderer. 174 /// Returns a reference to the underlying renderer.
@@ -204,77 +191,6 @@ struct GPU::Impl {
204 return *shader_notify; 191 return *shader_notify;
205 } 192 }
206 193
207 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
208 void WaitFence(u32 syncpoint_id, u32 value) {
209 // Synced GPU, is always in sync
210 if (!is_async) {
211 return;
212 }
213 if (syncpoint_id == UINT32_MAX) {
214 // TODO: Research what this does.
215 LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented");
216 return;
217 }
218 MICROPROFILE_SCOPE(GPU_wait);
219 std::unique_lock lock{sync_mutex};
220 sync_cv.wait(lock, [=, this] {
221 if (shutting_down.load(std::memory_order_relaxed)) {
222 // We're shutting down, ensure no threads continue to wait for the next syncpoint
223 return true;
224 }
225 return syncpoints.at(syncpoint_id).load() >= value;
226 });
227 }
228
229 void IncrementSyncPoint(u32 syncpoint_id) {
230 auto& syncpoint = syncpoints.at(syncpoint_id);
231 syncpoint++;
232 std::scoped_lock lock{sync_mutex};
233 sync_cv.notify_all();
234 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
235 if (!interrupt.empty()) {
236 u32 value = syncpoint.load();
237 auto it = interrupt.begin();
238 while (it != interrupt.end()) {
239 if (value >= *it) {
240 TriggerCpuInterrupt(syncpoint_id, *it);
241 it = interrupt.erase(it);
242 continue;
243 }
244 it++;
245 }
246 }
247 }
248
249 [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const {
250 return syncpoints.at(syncpoint_id).load();
251 }
252
253 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
254 std::scoped_lock lock{sync_mutex};
255 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
256 bool contains = std::any_of(interrupt.begin(), interrupt.end(),
257 [value](u32 in_value) { return in_value == value; });
258 if (contains) {
259 return;
260 }
261 interrupt.emplace_back(value);
262 }
263
264 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
265 std::scoped_lock lock{sync_mutex};
266 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
267 const auto iter =
268 std::find_if(interrupt.begin(), interrupt.end(),
269 [value](u32 interrupt_value) { return value == interrupt_value; });
270
271 if (iter == interrupt.end()) {
272 return false;
273 }
274 interrupt.erase(iter);
275 return true;
276 }
277
278 [[nodiscard]] u64 GetTicks() const { 194 [[nodiscard]] u64 GetTicks() const {
279 // This values were reversed engineered by fincs from NVN 195 // This values were reversed engineered by fincs from NVN
280 // The gpu clock is reported in units of 385/625 nanoseconds 196 // The gpu clock is reported in units of 385/625 nanoseconds
@@ -306,7 +222,7 @@ struct GPU::Impl {
306 /// This can be used to launch any necessary threads and register any necessary 222 /// This can be used to launch any necessary threads and register any necessary
307 /// core timing events. 223 /// core timing events.
308 void Start() { 224 void Start() {
309 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); 225 gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
310 cpu_context = renderer->GetRenderWindow().CreateSharedContext(); 226 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
311 cpu_context->MakeCurrent(); 227 cpu_context->MakeCurrent();
312 } 228 }
@@ -328,8 +244,8 @@ struct GPU::Impl {
328 } 244 }
329 245
330 /// Push GPU command entries to be processed 246 /// Push GPU command entries to be processed
331 void PushGPUEntries(Tegra::CommandList&& entries) { 247 void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
332 gpu_thread.SubmitList(std::move(entries)); 248 gpu_thread.SubmitList(channel, std::move(entries));
333 } 249 }
334 250
335 /// Push GPU command buffer entries to be processed 251 /// Push GPU command buffer entries to be processed
@@ -339,7 +255,7 @@ struct GPU::Impl {
339 } 255 }
340 256
341 if (!cdma_pushers.contains(id)) { 257 if (!cdma_pushers.contains(id)) {
342 cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(gpu)); 258 cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(host1x));
343 } 259 }
344 260
345 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working 261 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
@@ -376,308 +292,55 @@ struct GPU::Impl {
376 gpu_thread.FlushAndInvalidateRegion(addr, size); 292 gpu_thread.FlushAndInvalidateRegion(addr, size);
377 } 293 }
378 294
379 void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const { 295 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer,
380 auto& interrupt_manager = system.InterruptManager(); 296 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) {
381 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); 297 size_t current_request_counter{};
382 } 298 {
383 299 std::unique_lock<std::mutex> lk(request_swap_mutex);
384 void ProcessBindMethod(const GPU::MethodCall& method_call) { 300 if (free_swap_counters.empty()) {
385 // Bind the current subchannel to the desired engine id. 301 current_request_counter = request_swap_counters.size();
386 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, 302 request_swap_counters.emplace_back(num_fences);
387 method_call.argument);
388 const auto engine_id = static_cast<EngineID>(method_call.argument);
389 bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id);
390 switch (engine_id) {
391 case EngineID::FERMI_TWOD_A:
392 dma_pusher->BindSubchannel(fermi_2d.get(), method_call.subchannel);
393 break;
394 case EngineID::MAXWELL_B:
395 dma_pusher->BindSubchannel(maxwell_3d.get(), method_call.subchannel);
396 break;
397 case EngineID::KEPLER_COMPUTE_B:
398 dma_pusher->BindSubchannel(kepler_compute.get(), method_call.subchannel);
399 break;
400 case EngineID::MAXWELL_DMA_COPY_A:
401 dma_pusher->BindSubchannel(maxwell_dma.get(), method_call.subchannel);
402 break;
403 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
404 dma_pusher->BindSubchannel(kepler_memory.get(), method_call.subchannel);
405 break;
406 default:
407 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
408 }
409 }
410
411 void ProcessFenceActionMethod() {
412 switch (regs.fence_action.op) {
413 case GPU::FenceOperation::Acquire:
414 WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
415 break;
416 case GPU::FenceOperation::Increment:
417 IncrementSyncPoint(regs.fence_action.syncpoint_id);
418 break;
419 default:
420 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
421 }
422 }
423
424 void ProcessWaitForInterruptMethod() {
425 // TODO(bunnei) ImplementMe
426 LOG_WARNING(HW_GPU, "(STUBBED) called");
427 }
428
429 void ProcessSemaphoreTriggerMethod() {
430 const auto semaphoreOperationMask = 0xF;
431 const auto op =
432 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
433 if (op == GpuSemaphoreOperation::WriteLong) {
434 struct Block {
435 u32 sequence;
436 u32 zeros = 0;
437 u64 timestamp;
438 };
439
440 Block block{};
441 block.sequence = regs.semaphore_sequence;
442 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
443 // CoreTiming
444 block.timestamp = GetTicks();
445 memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block,
446 sizeof(block));
447 } else {
448 const u32 word{memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress())};
449 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
450 (op == GpuSemaphoreOperation::AcquireGequal &&
451 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
452 (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) {
453 // Nothing to do in this case
454 } else { 303 } else {
455 regs.acquire_source = true; 304 current_request_counter = free_swap_counters.front();
456 regs.acquire_value = regs.semaphore_sequence; 305 request_swap_counters[current_request_counter] = num_fences;
457 if (op == GpuSemaphoreOperation::AcquireEqual) { 306 free_swap_counters.pop_front();
458 regs.acquire_active = true;
459 regs.acquire_mode = false;
460 } else if (op == GpuSemaphoreOperation::AcquireGequal) {
461 regs.acquire_active = true;
462 regs.acquire_mode = true;
463 } else if (op == GpuSemaphoreOperation::AcquireMask) {
464 // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with
465 // semaphore_sequence, gives a non-0 result
466 LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented");
467 } else {
468 LOG_ERROR(HW_GPU, "Invalid semaphore operation");
469 }
470 } 307 }
471 } 308 }
472 } 309 const auto wait_fence =
473 310 RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] {
474 void ProcessSemaphoreRelease() { 311 auto& syncpoint_manager = host1x.GetSyncpointManager();
475 memory_manager->Write<u32>(regs.semaphore_address.SemaphoreAddress(), 312 if (num_fences == 0) {
476 regs.semaphore_release); 313 renderer->SwapBuffers(framebuffer);
477 } 314 }
478 315 const auto executer = [this, current_request_counter,
479 void ProcessSemaphoreAcquire() { 316 framebuffer_copy = *framebuffer]() {
480 const u32 word = memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress()); 317 {
481 const auto value = regs.semaphore_acquire; 318 std::unique_lock<std::mutex> lk(request_swap_mutex);
482 if (word != value) { 319 if (--request_swap_counters[current_request_counter] != 0) {
483 regs.acquire_active = true; 320 return;
484 regs.acquire_value = value; 321 }
485 // TODO(kemathe73) figure out how to do the acquire_timeout 322 free_swap_counters.push_back(current_request_counter);
486 regs.acquire_mode = false;
487 regs.acquire_source = false;
488 }
489 }
490
491 /// Calls a GPU puller method.
492 void CallPullerMethod(const GPU::MethodCall& method_call) {
493 regs.reg_array[method_call.method] = method_call.argument;
494 const auto method = static_cast<BufferMethods>(method_call.method);
495
496 switch (method) {
497 case BufferMethods::BindObject: {
498 ProcessBindMethod(method_call);
499 break;
500 }
501 case BufferMethods::Nop:
502 case BufferMethods::SemaphoreAddressHigh:
503 case BufferMethods::SemaphoreAddressLow:
504 case BufferMethods::SemaphoreSequence:
505 break;
506 case BufferMethods::UnkCacheFlush:
507 rasterizer->SyncGuestHost();
508 break;
509 case BufferMethods::WrcacheFlush:
510 rasterizer->SignalReference();
511 break;
512 case BufferMethods::FenceValue:
513 break;
514 case BufferMethods::RefCnt:
515 rasterizer->SignalReference();
516 break;
517 case BufferMethods::FenceAction:
518 ProcessFenceActionMethod();
519 break;
520 case BufferMethods::WaitForInterrupt:
521 rasterizer->WaitForIdle();
522 break;
523 case BufferMethods::SemaphoreTrigger: {
524 ProcessSemaphoreTriggerMethod();
525 break;
526 }
527 case BufferMethods::NotifyIntr: {
528 // TODO(Kmather73): Research and implement this method.
529 LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented");
530 break;
531 }
532 case BufferMethods::Unk28: {
533 // TODO(Kmather73): Research and implement this method.
534 LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented");
535 break;
536 }
537 case BufferMethods::SemaphoreAcquire: {
538 ProcessSemaphoreAcquire();
539 break;
540 }
541 case BufferMethods::SemaphoreRelease: {
542 ProcessSemaphoreRelease();
543 break;
544 }
545 case BufferMethods::Yield: {
546 // TODO(Kmather73): Research and implement this method.
547 LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
548 break;
549 }
550 default:
551 LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method);
552 break;
553 }
554 }
555
556 /// Calls a GPU engine method.
557 void CallEngineMethod(const GPU::MethodCall& method_call) {
558 const EngineID engine = bound_engines[method_call.subchannel];
559
560 switch (engine) {
561 case EngineID::FERMI_TWOD_A:
562 fermi_2d->CallMethod(method_call.method, method_call.argument,
563 method_call.IsLastCall());
564 break;
565 case EngineID::MAXWELL_B:
566 maxwell_3d->CallMethod(method_call.method, method_call.argument,
567 method_call.IsLastCall());
568 break;
569 case EngineID::KEPLER_COMPUTE_B:
570 kepler_compute->CallMethod(method_call.method, method_call.argument,
571 method_call.IsLastCall());
572 break;
573 case EngineID::MAXWELL_DMA_COPY_A:
574 maxwell_dma->CallMethod(method_call.method, method_call.argument,
575 method_call.IsLastCall());
576 break;
577 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
578 kepler_memory->CallMethod(method_call.method, method_call.argument,
579 method_call.IsLastCall());
580 break;
581 default:
582 UNIMPLEMENTED_MSG("Unimplemented engine");
583 }
584 }
585
586 /// Calls a GPU engine multivalue method.
587 void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
588 u32 methods_pending) {
589 const EngineID engine = bound_engines[subchannel];
590
591 switch (engine) {
592 case EngineID::FERMI_TWOD_A:
593 fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
594 break;
595 case EngineID::MAXWELL_B:
596 maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
597 break;
598 case EngineID::KEPLER_COMPUTE_B:
599 kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
600 break;
601 case EngineID::MAXWELL_DMA_COPY_A:
602 maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
603 break;
604 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
605 kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
606 break;
607 default:
608 UNIMPLEMENTED_MSG("Unimplemented engine");
609 }
610 }
611
612 /// Determines where the method should be executed.
613 [[nodiscard]] bool ExecuteMethodOnEngine(u32 method) {
614 const auto buffer_method = static_cast<BufferMethods>(method);
615 return buffer_method >= BufferMethods::NonPullerMethods;
616 }
617
618 struct Regs {
619 static constexpr size_t NUM_REGS = 0x40;
620
621 union {
622 struct {
623 INSERT_PADDING_WORDS_NOINIT(0x4);
624 struct {
625 u32 address_high;
626 u32 address_low;
627
628 [[nodiscard]] GPUVAddr SemaphoreAddress() const {
629 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
630 address_low);
631 } 323 }
632 } semaphore_address; 324 renderer->SwapBuffers(&framebuffer_copy);
633 325 };
634 u32 semaphore_sequence; 326 for (size_t i = 0; i < num_fences; i++) {
635 u32 semaphore_trigger; 327 syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
636 INSERT_PADDING_WORDS_NOINIT(0xC); 328 }
637 329 });
638 // The pusher and the puller share the reference counter, the pusher only has read 330 gpu_thread.TickGPU();
639 // access 331 WaitForSyncOperation(wait_fence);
640 u32 reference_count; 332 }
641 INSERT_PADDING_WORDS_NOINIT(0x5);
642
643 u32 semaphore_acquire;
644 u32 semaphore_release;
645 u32 fence_value;
646 GPU::FenceAction fence_action;
647 INSERT_PADDING_WORDS_NOINIT(0xE2);
648
649 // Puller state
650 u32 acquire_mode;
651 u32 acquire_source;
652 u32 acquire_active;
653 u32 acquire_timeout;
654 u32 acquire_value;
655 };
656 std::array<u32, NUM_REGS> reg_array;
657 };
658 } regs{};
659 333
660 GPU& gpu; 334 GPU& gpu;
661 Core::System& system; 335 Core::System& system;
662 std::unique_ptr<Tegra::MemoryManager> memory_manager; 336 Host1x::Host1x& host1x;
663 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 337
664 std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers; 338 std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers;
665 std::unique_ptr<VideoCore::RendererBase> renderer; 339 std::unique_ptr<VideoCore::RendererBase> renderer;
666 VideoCore::RasterizerInterface* rasterizer = nullptr; 340 VideoCore::RasterizerInterface* rasterizer = nullptr;
667 const bool use_nvdec; 341 const bool use_nvdec;
668 342
669 /// Mapping of command subchannels to their bound engine ids 343 s32 new_channel_id{1};
670 std::array<EngineID, 8> bound_engines{};
671 /// 3D engine
672 std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
673 /// 2D engine
674 std::unique_ptr<Engines::Fermi2D> fermi_2d;
675 /// Compute engine
676 std::unique_ptr<Engines::KeplerCompute> kepler_compute;
677 /// DMA engine
678 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
679 /// Inline memory engine
680 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
681 /// Shader build notifier 344 /// Shader build notifier
682 std::unique_ptr<VideoCore::ShaderNotify> shader_notify; 345 std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
683 /// When true, we are about to shut down emulation session, so terminate outstanding tasks 346 /// When true, we are about to shut down emulation session, so terminate outstanding tasks
@@ -692,51 +355,25 @@ struct GPU::Impl {
692 355
693 std::condition_variable sync_cv; 356 std::condition_variable sync_cv;
694 357
695 struct FlushRequest { 358 std::list<std::function<void()>> sync_requests;
696 explicit FlushRequest(u64 fence_, VAddr addr_, std::size_t size_) 359 std::atomic<u64> current_sync_fence{};
697 : fence{fence_}, addr{addr_}, size{size_} {} 360 u64 last_sync_fence{};
698 u64 fence; 361 std::mutex sync_request_mutex;
699 VAddr addr; 362 std::condition_variable sync_request_cv;
700 std::size_t size;
701 };
702
703 std::list<FlushRequest> flush_requests;
704 std::atomic<u64> current_flush_fence{};
705 u64 last_flush_fence{};
706 std::mutex flush_request_mutex;
707 363
708 const bool is_async; 364 const bool is_async;
709 365
710 VideoCommon::GPUThread::ThreadManager gpu_thread; 366 VideoCommon::GPUThread::ThreadManager gpu_thread;
711 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; 367 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
712 368
713#define ASSERT_REG_POSITION(field_name, position) \ 369 std::unique_ptr<Tegra::Control::Scheduler> scheduler;
714 static_assert(offsetof(Regs, field_name) == position * 4, \ 370 std::unordered_map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels;
715 "Field " #field_name " has invalid position") 371 Tegra::Control::ChannelState* current_channel;
716 372 s32 bound_channel{-1};
717 ASSERT_REG_POSITION(semaphore_address, 0x4); 373
718 ASSERT_REG_POSITION(semaphore_sequence, 0x6); 374 std::deque<size_t> free_swap_counters;
719 ASSERT_REG_POSITION(semaphore_trigger, 0x7); 375 std::deque<size_t> request_swap_counters;
720 ASSERT_REG_POSITION(reference_count, 0x14); 376 std::mutex request_swap_mutex;
721 ASSERT_REG_POSITION(semaphore_acquire, 0x1A);
722 ASSERT_REG_POSITION(semaphore_release, 0x1B);
723 ASSERT_REG_POSITION(fence_value, 0x1C);
724 ASSERT_REG_POSITION(fence_action, 0x1D);
725
726 ASSERT_REG_POSITION(acquire_mode, 0x100);
727 ASSERT_REG_POSITION(acquire_source, 0x101);
728 ASSERT_REG_POSITION(acquire_active, 0x102);
729 ASSERT_REG_POSITION(acquire_timeout, 0x103);
730 ASSERT_REG_POSITION(acquire_value, 0x104);
731
732#undef ASSERT_REG_POSITION
733
734 enum class GpuSemaphoreOperation {
735 AcquireEqual = 0x1,
736 WriteLong = 0x2,
737 AcquireGequal = 0x4,
738 AcquireMask = 0x8,
739 };
740}; 377};
741 378
742GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) 379GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
@@ -744,25 +381,36 @@ GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
744 381
745GPU::~GPU() = default; 382GPU::~GPU() = default;
746 383
747void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer) { 384std::shared_ptr<Control::ChannelState> GPU::AllocateChannel() {
748 impl->BindRenderer(std::move(renderer)); 385 return impl->AllocateChannel();
386}
387
388void GPU::InitChannel(Control::ChannelState& to_init) {
389 impl->InitChannel(to_init);
390}
391
392void GPU::BindChannel(s32 channel_id) {
393 impl->BindChannel(channel_id);
749} 394}
750 395
751void GPU::CallMethod(const MethodCall& method_call) { 396void GPU::ReleaseChannel(Control::ChannelState& to_release) {
752 impl->CallMethod(method_call); 397 impl->ReleaseChannel(to_release);
753} 398}
754 399
755void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, 400void GPU::InitAddressSpace(Tegra::MemoryManager& memory_manager) {
756 u32 methods_pending) { 401 impl->InitAddressSpace(memory_manager);
757 impl->CallMultiMethod(method, subchannel, base_start, amount, methods_pending); 402}
403
404void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer) {
405 impl->BindRenderer(std::move(renderer));
758} 406}
759 407
760void GPU::FlushCommands() { 408void GPU::FlushCommands() {
761 impl->FlushCommands(); 409 impl->FlushCommands();
762} 410}
763 411
764void GPU::SyncGuestHost() { 412void GPU::InvalidateGPUCache() {
765 impl->SyncGuestHost(); 413 impl->InvalidateGPUCache();
766} 414}
767 415
768void GPU::OnCommandListEnd() { 416void GPU::OnCommandListEnd() {
@@ -770,17 +418,32 @@ void GPU::OnCommandListEnd() {
770} 418}
771 419
772u64 GPU::RequestFlush(VAddr addr, std::size_t size) { 420u64 GPU::RequestFlush(VAddr addr, std::size_t size) {
773 return impl->RequestFlush(addr, size); 421 return impl->RequestSyncOperation(
422 [this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); });
774} 423}
775 424
776u64 GPU::CurrentFlushRequestFence() const { 425u64 GPU::CurrentSyncRequestFence() const {
777 return impl->CurrentFlushRequestFence(); 426 return impl->CurrentSyncRequestFence();
427}
428
429void GPU::WaitForSyncOperation(u64 fence) {
430 return impl->WaitForSyncOperation(fence);
778} 431}
779 432
780void GPU::TickWork() { 433void GPU::TickWork() {
781 impl->TickWork(); 434 impl->TickWork();
782} 435}
783 436
437/// Gets a mutable reference to the Host1x interface
438Host1x::Host1x& GPU::Host1x() {
439 return impl->host1x;
440}
441
442/// Gets an immutable reference to the Host1x interface.
443const Host1x::Host1x& GPU::Host1x() const {
444 return impl->host1x;
445}
446
784Engines::Maxwell3D& GPU::Maxwell3D() { 447Engines::Maxwell3D& GPU::Maxwell3D() {
785 return impl->Maxwell3D(); 448 return impl->Maxwell3D();
786} 449}
@@ -797,14 +460,6 @@ const Engines::KeplerCompute& GPU::KeplerCompute() const {
797 return impl->KeplerCompute(); 460 return impl->KeplerCompute();
798} 461}
799 462
800Tegra::MemoryManager& GPU::MemoryManager() {
801 return impl->MemoryManager();
802}
803
804const Tegra::MemoryManager& GPU::MemoryManager() const {
805 return impl->MemoryManager();
806}
807
808Tegra::DmaPusher& GPU::DmaPusher() { 463Tegra::DmaPusher& GPU::DmaPusher() {
809 return impl->DmaPusher(); 464 return impl->DmaPusher();
810} 465}
@@ -829,24 +484,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
829 return impl->ShaderNotify(); 484 return impl->ShaderNotify();
830} 485}
831 486
832void GPU::WaitFence(u32 syncpoint_id, u32 value) { 487void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer,
833 impl->WaitFence(syncpoint_id, value); 488 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) {
834} 489 impl->RequestSwapBuffers(framebuffer, fences, num_fences);
835
836void GPU::IncrementSyncPoint(u32 syncpoint_id) {
837 impl->IncrementSyncPoint(syncpoint_id);
838}
839
840u32 GPU::GetSyncpointValue(u32 syncpoint_id) const {
841 return impl->GetSyncpointValue(syncpoint_id);
842}
843
844void GPU::RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
845 impl->RegisterSyncptInterrupt(syncpoint_id, value);
846}
847
848bool GPU::CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
849 return impl->CancelSyncptInterrupt(syncpoint_id, value);
850} 490}
851 491
852u64 GPU::GetTicks() const { 492u64 GPU::GetTicks() const {
@@ -881,8 +521,8 @@ void GPU::ReleaseContext() {
881 impl->ReleaseContext(); 521 impl->ReleaseContext();
882} 522}
883 523
884void GPU::PushGPUEntries(Tegra::CommandList&& entries) { 524void GPU::PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
885 impl->PushGPUEntries(std::move(entries)); 525 impl->PushGPUEntries(channel, std::move(entries));
886} 526}
887 527
888void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) { 528void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b939ba315..d0709dc69 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -24,11 +24,15 @@ namespace Tegra {
24class DmaPusher; 24class DmaPusher;
25struct CommandList; 25struct CommandList;
26 26
27// TODO: Implement the commented ones
27enum class RenderTargetFormat : u32 { 28enum class RenderTargetFormat : u32 {
28 NONE = 0x0, 29 NONE = 0x0,
29 R32B32G32A32_FLOAT = 0xC0, 30 R32B32G32A32_FLOAT = 0xC0,
30 R32G32B32A32_SINT = 0xC1, 31 R32G32B32A32_SINT = 0xC1,
31 R32G32B32A32_UINT = 0xC2, 32 R32G32B32A32_UINT = 0xC2,
33 // R32G32B32X32_FLOAT = 0xC3,
34 // R32G32B32X32_SINT = 0xC4,
35 // R32G32B32X32_UINT = 0xC5,
32 R16G16B16A16_UNORM = 0xC6, 36 R16G16B16A16_UNORM = 0xC6,
33 R16G16B16A16_SNORM = 0xC7, 37 R16G16B16A16_SNORM = 0xC7,
34 R16G16B16A16_SINT = 0xC8, 38 R16G16B16A16_SINT = 0xC8,
@@ -38,8 +42,8 @@ enum class RenderTargetFormat : u32 {
38 R32G32_SINT = 0xCC, 42 R32G32_SINT = 0xCC,
39 R32G32_UINT = 0xCD, 43 R32G32_UINT = 0xCD,
40 R16G16B16X16_FLOAT = 0xCE, 44 R16G16B16X16_FLOAT = 0xCE,
41 B8G8R8A8_UNORM = 0xCF, 45 A8R8G8B8_UNORM = 0xCF,
42 B8G8R8A8_SRGB = 0xD0, 46 A8R8G8B8_SRGB = 0xD0,
43 A2B10G10R10_UNORM = 0xD1, 47 A2B10G10R10_UNORM = 0xD1,
44 A2B10G10R10_UINT = 0xD2, 48 A2B10G10R10_UINT = 0xD2,
45 A8B8G8R8_UNORM = 0xD5, 49 A8B8G8R8_UNORM = 0xD5,
@@ -52,10 +56,13 @@ enum class RenderTargetFormat : u32 {
52 R16G16_SINT = 0xDC, 56 R16G16_SINT = 0xDC,
53 R16G16_UINT = 0xDD, 57 R16G16_UINT = 0xDD,
54 R16G16_FLOAT = 0xDE, 58 R16G16_FLOAT = 0xDE,
59 // A2R10G10B10_UNORM = 0xDF,
55 B10G11R11_FLOAT = 0xE0, 60 B10G11R11_FLOAT = 0xE0,
56 R32_SINT = 0xE3, 61 R32_SINT = 0xE3,
57 R32_UINT = 0xE4, 62 R32_UINT = 0xE4,
58 R32_FLOAT = 0xE5, 63 R32_FLOAT = 0xE5,
64 // X8R8G8B8_UNORM = 0xE6,
65 // X8R8G8B8_SRGB = 0xE7,
59 R5G6B5_UNORM = 0xE8, 66 R5G6B5_UNORM = 0xE8,
60 A1R5G5B5_UNORM = 0xE9, 67 A1R5G5B5_UNORM = 0xE9,
61 R8G8_UNORM = 0xEA, 68 R8G8_UNORM = 0xEA,
@@ -71,17 +78,42 @@ enum class RenderTargetFormat : u32 {
71 R8_SNORM = 0xF4, 78 R8_SNORM = 0xF4,
72 R8_SINT = 0xF5, 79 R8_SINT = 0xF5,
73 R8_UINT = 0xF6, 80 R8_UINT = 0xF6,
81
82 /*
83 A8_UNORM = 0xF7,
84 X1R5G5B5_UNORM = 0xF8,
85 X8B8G8R8_UNORM = 0xF9,
86 X8B8G8R8_SRGB = 0xFA,
87 Z1R5G5B5_UNORM = 0xFB,
88 O1R5G5B5_UNORM = 0xFC,
89 Z8R8G8B8_UNORM = 0xFD,
90 O8R8G8B8_UNORM = 0xFE,
91 R32_UNORM = 0xFF,
92 A16_UNORM = 0x40,
93 A16_FLOAT = 0x41,
94 A32_FLOAT = 0x42,
95 A8R8_UNORM = 0x43,
96 R16A16_UNORM = 0x44,
97 R16A16_FLOAT = 0x45,
98 R32A32_FLOAT = 0x46,
99 B8G8R8A8_UNORM = 0x47,
100 */
74}; 101};
75 102
76enum class DepthFormat : u32 { 103enum class DepthFormat : u32 {
77 D32_FLOAT = 0xA, 104 Z32_FLOAT = 0xA,
78 D16_UNORM = 0x13, 105 Z16_UNORM = 0x13,
79 S8_UINT_Z24_UNORM = 0x14, 106 Z24_UNORM_S8_UINT = 0x14,
80 D24X8_UNORM = 0x15, 107 X8Z24_UNORM = 0x15,
81 D24S8_UNORM = 0x16, 108 S8Z24_UNORM = 0x16,
82 S8_UINT = 0x17, 109 S8_UINT = 0x17,
83 D24C8_UNORM = 0x18, 110 V8Z24_UNORM = 0x18,
84 D32_FLOAT_S8X24_UINT = 0x19, 111 Z32_FLOAT_X24S8_UINT = 0x19,
112 /*
113 X8Z24_UNORM_X16V8S8_UINT = 0x1D,
114 Z32_FLOAT_X16V8X8_UINT = 0x1E,
115 Z32_FLOAT_X16V8S8_UINT = 0x1F,
116 */
85}; 117};
86 118
87namespace Engines { 119namespace Engines {
@@ -89,73 +121,58 @@ class Maxwell3D;
89class KeplerCompute; 121class KeplerCompute;
90} // namespace Engines 122} // namespace Engines
91 123
92enum class EngineID { 124namespace Control {
93 FERMI_TWOD_A = 0x902D, // 2D Engine 125struct ChannelState;
94 MAXWELL_B = 0xB197, // 3D Engine 126}
95 KEPLER_COMPUTE_B = 0xB1C0, 127
96 KEPLER_INLINE_TO_MEMORY_B = 0xA140, 128namespace Host1x {
97 MAXWELL_DMA_COPY_A = 0xB0B5, 129class Host1x;
98}; 130} // namespace Host1x
99 131
100class MemoryManager; 132class MemoryManager;
101 133
102class GPU final { 134class GPU final {
103public: 135public:
104 struct MethodCall {
105 u32 method{};
106 u32 argument{};
107 u32 subchannel{};
108 u32 method_count{};
109
110 explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0)
111 : method(method_), argument(argument_), subchannel(subchannel_),
112 method_count(method_count_) {}
113
114 [[nodiscard]] bool IsLastCall() const {
115 return method_count <= 1;
116 }
117 };
118
119 enum class FenceOperation : u32 {
120 Acquire = 0,
121 Increment = 1,
122 };
123
124 union FenceAction {
125 u32 raw;
126 BitField<0, 1, FenceOperation> op;
127 BitField<8, 24, u32> syncpoint_id;
128 };
129
130 explicit GPU(Core::System& system, bool is_async, bool use_nvdec); 136 explicit GPU(Core::System& system, bool is_async, bool use_nvdec);
131 ~GPU(); 137 ~GPU();
132 138
133 /// Binds a renderer to the GPU. 139 /// Binds a renderer to the GPU.
134 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer); 140 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
135 141
136 /// Calls a GPU method.
137 void CallMethod(const MethodCall& method_call);
138
139 /// Calls a GPU multivalue method.
140 void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
141 u32 methods_pending);
142
143 /// Flush all current written commands into the host GPU for execution. 142 /// Flush all current written commands into the host GPU for execution.
144 void FlushCommands(); 143 void FlushCommands();
145 /// Synchronizes CPU writes with Host GPU memory. 144 /// Synchronizes CPU writes with Host GPU memory.
146 void SyncGuestHost(); 145 void InvalidateGPUCache();
147 /// Signal the ending of command list. 146 /// Signal the ending of command list.
148 void OnCommandListEnd(); 147 void OnCommandListEnd();
149 148
149 std::shared_ptr<Control::ChannelState> AllocateChannel();
150
151 void InitChannel(Control::ChannelState& to_init);
152
153 void BindChannel(s32 channel_id);
154
155 void ReleaseChannel(Control::ChannelState& to_release);
156
157 void InitAddressSpace(Tegra::MemoryManager& memory_manager);
158
150 /// Request a host GPU memory flush from the CPU. 159 /// Request a host GPU memory flush from the CPU.
151 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); 160 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
152 161
153 /// Obtains current flush request fence id. 162 /// Obtains current flush request fence id.
154 [[nodiscard]] u64 CurrentFlushRequestFence() const; 163 [[nodiscard]] u64 CurrentSyncRequestFence() const;
164
165 void WaitForSyncOperation(u64 fence);
155 166
156 /// Tick pending requests within the GPU. 167 /// Tick pending requests within the GPU.
157 void TickWork(); 168 void TickWork();
158 169
170 /// Gets a mutable reference to the Host1x interface
171 [[nodiscard]] Host1x::Host1x& Host1x();
172
173 /// Gets an immutable reference to the Host1x interface.
174 [[nodiscard]] const Host1x::Host1x& Host1x() const;
175
159 /// Returns a reference to the Maxwell3D GPU engine. 176 /// Returns a reference to the Maxwell3D GPU engine.
160 [[nodiscard]] Engines::Maxwell3D& Maxwell3D(); 177 [[nodiscard]] Engines::Maxwell3D& Maxwell3D();
161 178
@@ -168,12 +185,6 @@ public:
168 /// Returns a reference to the KeplerCompute GPU engine. 185 /// Returns a reference to the KeplerCompute GPU engine.
169 [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const; 186 [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const;
170 187
171 /// Returns a reference to the GPU memory manager.
172 [[nodiscard]] Tegra::MemoryManager& MemoryManager();
173
174 /// Returns a const reference to the GPU memory manager.
175 [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const;
176
177 /// Returns a reference to the GPU DMA pusher. 188 /// Returns a reference to the GPU DMA pusher.
178 [[nodiscard]] Tegra::DmaPusher& DmaPusher(); 189 [[nodiscard]] Tegra::DmaPusher& DmaPusher();
179 190
@@ -192,17 +203,6 @@ public:
192 /// Returns a const reference to the shader notifier. 203 /// Returns a const reference to the shader notifier.
193 [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; 204 [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;
194 205
195 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
196 void WaitFence(u32 syncpoint_id, u32 value);
197
198 void IncrementSyncPoint(u32 syncpoint_id);
199
200 [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const;
201
202 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
203
204 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
205
206 [[nodiscard]] u64 GetTicks() const; 206 [[nodiscard]] u64 GetTicks() const;
207 207
208 [[nodiscard]] bool IsAsync() const; 208 [[nodiscard]] bool IsAsync() const;
@@ -211,6 +211,9 @@ public:
211 211
212 void RendererFrameEndNotify(); 212 void RendererFrameEndNotify();
213 213
214 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer,
215 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences);
216
214 /// Performs any additional setup necessary in order to begin GPU emulation. 217 /// Performs any additional setup necessary in order to begin GPU emulation.
215 /// This can be used to launch any necessary threads and register any necessary 218 /// This can be used to launch any necessary threads and register any necessary
216 /// core timing events. 219 /// core timing events.
@@ -226,7 +229,7 @@ public:
226 void ReleaseContext(); 229 void ReleaseContext();
227 230
228 /// Push GPU command entries to be processed 231 /// Push GPU command entries to be processed
229 void PushGPUEntries(Tegra::CommandList&& entries); 232 void PushGPUEntries(s32 channel, Tegra::CommandList&& entries);
230 233
231 /// Push GPU command buffer entries to be processed 234 /// Push GPU command buffer entries to be processed
232 void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries); 235 void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);
@@ -248,7 +251,7 @@ public:
248 251
249private: 252private:
250 struct Impl; 253 struct Impl;
251 std::unique_ptr<Impl> impl; 254 mutable std::unique_ptr<Impl> impl;
252}; 255};
253 256
254} // namespace Tegra 257} // namespace Tegra
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index d43f7175a..1bd477011 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -8,6 +8,7 @@
8#include "common/thread.h" 8#include "common/thread.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
11#include "video_core/control/scheduler.h"
11#include "video_core/dma_pusher.h" 12#include "video_core/dma_pusher.h"
12#include "video_core/gpu.h" 13#include "video_core/gpu.h"
13#include "video_core/gpu_thread.h" 14#include "video_core/gpu_thread.h"
@@ -18,8 +19,8 @@ namespace VideoCommon::GPUThread {
18/// Runs the GPU thread 19/// Runs the GPU thread
19static void RunThread(std::stop_token stop_token, Core::System& system, 20static void RunThread(std::stop_token stop_token, Core::System& system,
20 VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, 21 VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
21 Tegra::DmaPusher& dma_pusher, SynchState& state) { 22 Tegra::Control::Scheduler& scheduler, SynchState& state) {
22 std::string name = "yuzu:GPU"; 23 std::string name = "GPU";
23 MicroProfileOnThreadCreate(name.c_str()); 24 MicroProfileOnThreadCreate(name.c_str());
24 SCOPE_EXIT({ MicroProfileOnThreadExit(); }); 25 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
25 26
@@ -36,8 +37,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
36 break; 37 break;
37 } 38 }
38 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { 39 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
39 dma_pusher.Push(std::move(submit_list->entries)); 40 scheduler.Push(submit_list->channel, std::move(submit_list->entries));
40 dma_pusher.DispatchCalls();
41 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { 41 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
42 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 42 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
43 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) { 43 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
@@ -68,14 +68,14 @@ ThreadManager::~ThreadManager() = default;
68 68
69void ThreadManager::StartThread(VideoCore::RendererBase& renderer, 69void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
70 Core::Frontend::GraphicsContext& context, 70 Core::Frontend::GraphicsContext& context,
71 Tegra::DmaPusher& dma_pusher) { 71 Tegra::Control::Scheduler& scheduler) {
72 rasterizer = renderer.ReadRasterizer(); 72 rasterizer = renderer.ReadRasterizer();
73 thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), 73 thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
74 std::ref(dma_pusher), std::ref(state)); 74 std::ref(scheduler), std::ref(state));
75} 75}
76 76
77void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 77void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
78 PushCommand(SubmitListCommand(std::move(entries))); 78 PushCommand(SubmitListCommand(channel, std::move(entries)));
79} 79}
80 80
81void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 81void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
@@ -93,8 +93,12 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) {
93 } 93 }
94 auto& gpu = system.GPU(); 94 auto& gpu = system.GPU();
95 u64 fence = gpu.RequestFlush(addr, size); 95 u64 fence = gpu.RequestFlush(addr, size);
96 PushCommand(GPUTickCommand(), true); 96 TickGPU();
97 ASSERT(fence <= gpu.CurrentFlushRequestFence()); 97 gpu.WaitForSyncOperation(fence);
98}
99
100void ThreadManager::TickGPU() {
101 PushCommand(GPUTickCommand());
98} 102}
99 103
100void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 104void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 2f8210cb9..64628d3e3 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -15,7 +15,9 @@
15 15
16namespace Tegra { 16namespace Tegra {
17struct FramebufferConfig; 17struct FramebufferConfig;
18class DmaPusher; 18namespace Control {
19class Scheduler;
20}
19} // namespace Tegra 21} // namespace Tegra
20 22
21namespace Core { 23namespace Core {
@@ -34,8 +36,10 @@ namespace VideoCommon::GPUThread {
34 36
35/// Command to signal to the GPU thread that a command list is ready for processing 37/// Command to signal to the GPU thread that a command list is ready for processing
36struct SubmitListCommand final { 38struct SubmitListCommand final {
37 explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {} 39 explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
40 : channel{channel_}, entries{std::move(entries_)} {}
38 41
42 s32 channel;
39 Tegra::CommandList entries; 43 Tegra::CommandList entries;
40}; 44};
41 45
@@ -112,10 +116,10 @@ public:
112 116
113 /// Creates and starts the GPU thread. 117 /// Creates and starts the GPU thread.
114 void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, 118 void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
115 Tegra::DmaPusher& dma_pusher); 119 Tegra::Control::Scheduler& scheduler);
116 120
117 /// Push GPU command entries to be processed 121 /// Push GPU command entries to be processed
118 void SubmitList(Tegra::CommandList&& entries); 122 void SubmitList(s32 channel, Tegra::CommandList&& entries);
119 123
120 /// Swap buffers (render frame) 124 /// Swap buffers (render frame)
121 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); 125 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
@@ -131,6 +135,8 @@ public:
131 135
132 void OnCommandListEnd(); 136 void OnCommandListEnd();
133 137
138 void TickGPU();
139
134private: 140private:
135 /// Pushes a command to be executed by the GPU thread 141 /// Pushes a command to be executed by the GPU thread
136 u64 PushCommand(CommandData&& command_data, bool block = false); 142 u64 PushCommand(CommandData&& command_data, bool block = false);
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index a5eb97b7f..42e7d6e4f 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -6,11 +6,11 @@
6#include <vector> 6#include <vector>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/settings.h" 8#include "common/settings.h"
9#include "video_core/command_classes/codecs/codec.h" 9#include "video_core/host1x/codecs/codec.h"
10#include "video_core/command_classes/codecs/h264.h" 10#include "video_core/host1x/codecs/h264.h"
11#include "video_core/command_classes/codecs/vp8.h" 11#include "video_core/host1x/codecs/vp8.h"
12#include "video_core/command_classes/codecs/vp9.h" 12#include "video_core/host1x/codecs/vp9.h"
13#include "video_core/gpu.h" 13#include "video_core/host1x/host1x.h"
14#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
15 15
16extern "C" { 16extern "C" {
@@ -73,10 +73,10 @@ void AVFrameDeleter(AVFrame* ptr) {
73 av_frame_free(&ptr); 73 av_frame_free(&ptr);
74} 74}
75 75
76Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) 76Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
77 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), 77 : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
78 vp8_decoder(std::make_unique<Decoder::VP8>(gpu)), 78 vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
79 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} 79 vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
80 80
81Codec::~Codec() { 81Codec::~Codec() {
82 if (!initialized) { 82 if (!initialized) {
@@ -168,11 +168,11 @@ void Codec::InitializeGpuDecoder() {
168void Codec::Initialize() { 168void Codec::Initialize() {
169 const AVCodecID codec = [&] { 169 const AVCodecID codec = [&] {
170 switch (current_codec) { 170 switch (current_codec) {
171 case NvdecCommon::VideoCodec::H264: 171 case Host1x::NvdecCommon::VideoCodec::H264:
172 return AV_CODEC_ID_H264; 172 return AV_CODEC_ID_H264;
173 case NvdecCommon::VideoCodec::VP8: 173 case Host1x::NvdecCommon::VideoCodec::VP8:
174 return AV_CODEC_ID_VP8; 174 return AV_CODEC_ID_VP8;
175 case NvdecCommon::VideoCodec::VP9: 175 case Host1x::NvdecCommon::VideoCodec::VP9:
176 return AV_CODEC_ID_VP9; 176 return AV_CODEC_ID_VP9;
177 default: 177 default:
178 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); 178 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
@@ -197,7 +197,7 @@ void Codec::Initialize() {
197 initialized = true; 197 initialized = true;
198} 198}
199 199
200void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { 200void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
201 if (current_codec != codec) { 201 if (current_codec != codec) {
202 current_codec = codec; 202 current_codec = codec;
203 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName()); 203 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
@@ -215,11 +215,11 @@ void Codec::Decode() {
215 bool vp9_hidden_frame = false; 215 bool vp9_hidden_frame = false;
216 const auto& frame_data = [&]() { 216 const auto& frame_data = [&]() {
217 switch (current_codec) { 217 switch (current_codec) {
218 case Tegra::NvdecCommon::VideoCodec::H264: 218 case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
219 return h264_decoder->ComposeFrame(state, is_first_frame); 219 return h264_decoder->ComposeFrame(state, is_first_frame);
220 case Tegra::NvdecCommon::VideoCodec::VP8: 220 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
221 return vp8_decoder->ComposeFrame(state); 221 return vp8_decoder->ComposeFrame(state);
222 case Tegra::NvdecCommon::VideoCodec::VP9: 222 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
223 vp9_decoder->ComposeFrame(state); 223 vp9_decoder->ComposeFrame(state);
224 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 224 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
225 return vp9_decoder->GetFrameBytes(); 225 return vp9_decoder->GetFrameBytes();
@@ -287,21 +287,21 @@ AVFramePtr Codec::GetCurrentFrame() {
287 return frame; 287 return frame;
288} 288}
289 289
290NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { 290Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
291 return current_codec; 291 return current_codec;
292} 292}
293 293
294std::string_view Codec::GetCurrentCodecName() const { 294std::string_view Codec::GetCurrentCodecName() const {
295 switch (current_codec) { 295 switch (current_codec) {
296 case NvdecCommon::VideoCodec::None: 296 case Host1x::NvdecCommon::VideoCodec::None:
297 return "None"; 297 return "None";
298 case NvdecCommon::VideoCodec::H264: 298 case Host1x::NvdecCommon::VideoCodec::H264:
299 return "H264"; 299 return "H264";
300 case NvdecCommon::VideoCodec::VP8: 300 case Host1x::NvdecCommon::VideoCodec::VP8:
301 return "VP8"; 301 return "VP8";
302 case NvdecCommon::VideoCodec::H265: 302 case Host1x::NvdecCommon::VideoCodec::H265:
303 return "H265"; 303 return "H265";
304 case NvdecCommon::VideoCodec::VP9: 304 case Host1x::NvdecCommon::VideoCodec::VP9:
305 return "VP9"; 305 return "VP9";
306 default: 306 default:
307 return "Unknown"; 307 return "Unknown";
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/host1x/codecs/codec.h
index 0c2405465..0d45fb7fe 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/host1x/codecs/codec.h
@@ -6,8 +6,8 @@
6#include <memory> 6#include <memory>
7#include <string_view> 7#include <string_view>
8#include <queue> 8#include <queue>
9 9#include "common/common_types.h"
10#include "video_core/command_classes/nvdec_common.h" 10#include "video_core/host1x/nvdec_common.h"
11 11
12extern "C" { 12extern "C" {
13#if defined(__GNUC__) || defined(__clang__) 13#if defined(__GNUC__) || defined(__clang__)
@@ -21,7 +21,6 @@ extern "C" {
21} 21}
22 22
23namespace Tegra { 23namespace Tegra {
24class GPU;
25 24
26void AVFrameDeleter(AVFrame* ptr); 25void AVFrameDeleter(AVFrame* ptr);
27using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>; 26using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
@@ -32,16 +31,20 @@ class VP8;
32class VP9; 31class VP9;
33} // namespace Decoder 32} // namespace Decoder
34 33
34namespace Host1x {
35class Host1x;
36} // namespace Host1x
37
35class Codec { 38class Codec {
36public: 39public:
37 explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs); 40 explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs);
38 ~Codec(); 41 ~Codec();
39 42
40 /// Initialize the codec, returning success or failure 43 /// Initialize the codec, returning success or failure
41 void Initialize(); 44 void Initialize();
42 45
43 /// Sets NVDEC video stream codec 46 /// Sets NVDEC video stream codec
44 void SetTargetCodec(NvdecCommon::VideoCodec codec); 47 void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec);
45 48
46 /// Call decoders to construct headers, decode AVFrame with ffmpeg 49 /// Call decoders to construct headers, decode AVFrame with ffmpeg
47 void Decode(); 50 void Decode();
@@ -50,7 +53,7 @@ public:
50 [[nodiscard]] AVFramePtr GetCurrentFrame(); 53 [[nodiscard]] AVFramePtr GetCurrentFrame();
51 54
52 /// Returns the value of current_codec 55 /// Returns the value of current_codec
53 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; 56 [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
54 57
55 /// Return name of the current codec 58 /// Return name of the current codec
56 [[nodiscard]] std::string_view GetCurrentCodecName() const; 59 [[nodiscard]] std::string_view GetCurrentCodecName() const;
@@ -63,14 +66,14 @@ private:
63 bool CreateGpuAvDevice(); 66 bool CreateGpuAvDevice();
64 67
65 bool initialized{}; 68 bool initialized{};
66 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; 69 Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
67 70
68 const AVCodec* av_codec{nullptr}; 71 const AVCodec* av_codec{nullptr};
69 AVCodecContext* av_codec_ctx{nullptr}; 72 AVCodecContext* av_codec_ctx{nullptr};
70 AVBufferRef* av_gpu_decoder{nullptr}; 73 AVBufferRef* av_gpu_decoder{nullptr};
71 74
72 GPU& gpu; 75 Host1x::Host1x& host1x;
73 const NvdecCommon::NvdecRegisters& state; 76 const Host1x::NvdecCommon::NvdecRegisters& state;
74 std::unique_ptr<Decoder::H264> h264_decoder; 77 std::unique_ptr<Decoder::H264> h264_decoder;
75 std::unique_ptr<Decoder::VP8> vp8_decoder; 78 std::unique_ptr<Decoder::VP8> vp8_decoder;
76 std::unique_ptr<Decoder::VP9> vp9_decoder; 79 std::unique_ptr<Decoder::VP9> vp9_decoder;
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index e2acd54d4..e87bd65fa 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -5,8 +5,8 @@
5#include <bit> 5#include <bit>
6 6
7#include "common/settings.h" 7#include "common/settings.h"
8#include "video_core/command_classes/codecs/h264.h" 8#include "video_core/host1x/codecs/h264.h"
9#include "video_core/gpu.h" 9#include "video_core/host1x/host1x.h"
10#include "video_core/memory_manager.h" 10#include "video_core/memory_manager.h"
11 11
12namespace Tegra::Decoder { 12namespace Tegra::Decoder {
@@ -24,19 +24,20 @@ constexpr std::array<u8, 16> zig_zag_scan{
24}; 24};
25} // Anonymous namespace 25} // Anonymous namespace
26 26
27H264::H264(GPU& gpu_) : gpu(gpu_) {} 27H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
28 28
29H264::~H264() = default; 29H264::~H264() = default;
30 30
31const std::vector<u8>& H264::ComposeFrame(const NvdecCommon::NvdecRegisters& state, 31const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
32 bool is_first_frame) { 32 bool is_first_frame) {
33 H264DecoderContext context; 33 H264DecoderContext context;
34 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); 34 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
35 sizeof(H264DecoderContext));
35 36
36 const s64 frame_number = context.h264_parameter_set.frame_number.Value(); 37 const s64 frame_number = context.h264_parameter_set.frame_number.Value();
37 if (!is_first_frame && frame_number != 0) { 38 if (!is_first_frame && frame_number != 0) {
38 frame.resize(context.stream_len); 39 frame.resize(context.stream_len);
39 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); 40 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
40 return frame; 41 return frame;
41 } 42 }
42 43
@@ -155,8 +156,8 @@ const std::vector<u8>& H264::ComposeFrame(const NvdecCommon::NvdecRegisters& sta
155 frame.resize(encoded_header.size() + context.stream_len); 156 frame.resize(encoded_header.size() + context.stream_len);
156 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); 157 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
157 158
158 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, 159 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
159 frame.data() + encoded_header.size(), context.stream_len); 160 frame.data() + encoded_header.size(), context.stream_len);
160 161
161 return frame; 162 return frame;
162} 163}
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/host1x/codecs/h264.h
index 261574364..5cc86454e 100644
--- a/src/video_core/command_classes/codecs/h264.h
+++ b/src/video_core/host1x/codecs/h264.h
@@ -8,10 +8,14 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/command_classes/nvdec_common.h" 11#include "video_core/host1x/nvdec_common.h"
12 12
13namespace Tegra { 13namespace Tegra {
14class GPU; 14
15namespace Host1x {
16class Host1x;
17} // namespace Host1x
18
15namespace Decoder { 19namespace Decoder {
16 20
17class H264BitWriter { 21class H264BitWriter {
@@ -55,16 +59,16 @@ private:
55 59
56class H264 { 60class H264 {
57public: 61public:
58 explicit H264(GPU& gpu); 62 explicit H264(Host1x::Host1x& host1x);
59 ~H264(); 63 ~H264();
60 64
61 /// Compose the H264 frame for FFmpeg decoding 65 /// Compose the H264 frame for FFmpeg decoding
62 [[nodiscard]] const std::vector<u8>& ComposeFrame(const NvdecCommon::NvdecRegisters& state, 66 [[nodiscard]] const std::vector<u8>& ComposeFrame(
63 bool is_first_frame = false); 67 const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame = false);
64 68
65private: 69private:
66 std::vector<u8> frame; 70 std::vector<u8> frame;
67 GPU& gpu; 71 Host1x::Host1x& host1x;
68 72
69 struct H264ParameterSet { 73 struct H264ParameterSet {
70 s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00 74 s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
diff --git a/src/video_core/command_classes/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp
index c83b9bbc2..28fb12cb8 100644
--- a/src/video_core/command_classes/codecs/vp8.cpp
+++ b/src/video_core/host1x/codecs/vp8.cpp
@@ -3,18 +3,18 @@
3 3
4#include <vector> 4#include <vector>
5 5
6#include "video_core/command_classes/codecs/vp8.h" 6#include "video_core/host1x/codecs/vp8.h"
7#include "video_core/gpu.h" 7#include "video_core/host1x/host1x.h"
8#include "video_core/memory_manager.h" 8#include "video_core/memory_manager.h"
9 9
10namespace Tegra::Decoder { 10namespace Tegra::Decoder {
11VP8::VP8(GPU& gpu_) : gpu(gpu_) {} 11VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {}
12 12
13VP8::~VP8() = default; 13VP8::~VP8() = default;
14 14
15const std::vector<u8>& VP8::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { 15const std::vector<u8>& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
16 VP8PictureInfo info; 16 VP8PictureInfo info;
17 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); 17 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo));
18 18
19 const bool is_key_frame = info.key_frame == 1u; 19 const bool is_key_frame = info.key_frame == 1u;
20 const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size); 20 const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size);
@@ -45,7 +45,7 @@ const std::vector<u8>& VP8::ComposeFrame(const NvdecCommon::NvdecRegisters& stat
45 frame[9] = static_cast<u8>(((info.frame_height >> 8) & 0x3f)); 45 frame[9] = static_cast<u8>(((info.frame_height >> 8) & 0x3f));
46 } 46 }
47 const u64 bitstream_offset = state.frame_bitstream_offset; 47 const u64 bitstream_offset = state.frame_bitstream_offset;
48 gpu.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); 48 host1x.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size);
49 49
50 return frame; 50 return frame;
51} 51}
diff --git a/src/video_core/command_classes/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h
index 3357667b0..5bf07ecab 100644
--- a/src/video_core/command_classes/codecs/vp8.h
+++ b/src/video_core/host1x/codecs/vp8.h
@@ -8,23 +8,28 @@
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/command_classes/nvdec_common.h" 11#include "video_core/host1x/nvdec_common.h"
12 12
13namespace Tegra { 13namespace Tegra {
14class GPU; 14
15namespace Host1x {
16class Host1x;
17} // namespace Host1x
18
15namespace Decoder { 19namespace Decoder {
16 20
17class VP8 { 21class VP8 {
18public: 22public:
19 explicit VP8(GPU& gpu); 23 explicit VP8(Host1x::Host1x& host1x);
20 ~VP8(); 24 ~VP8();
21 25
22 /// Compose the VP8 frame for FFmpeg decoding 26 /// Compose the VP8 frame for FFmpeg decoding
23 [[nodiscard]] const std::vector<u8>& ComposeFrame(const NvdecCommon::NvdecRegisters& state); 27 [[nodiscard]] const std::vector<u8>& ComposeFrame(
28 const Host1x::NvdecCommon::NvdecRegisters& state);
24 29
25private: 30private:
26 std::vector<u8> frame; 31 std::vector<u8> frame;
27 GPU& gpu; 32 Host1x::Host1x& host1x;
28 33
29 struct VP8PictureInfo { 34 struct VP8PictureInfo {
30 INSERT_PADDING_WORDS_NOINIT(14); 35 INSERT_PADDING_WORDS_NOINIT(14);
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp
index c01431441..cf40c9012 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/host1x/codecs/vp9.cpp
@@ -4,8 +4,8 @@
4#include <algorithm> // for std::copy 4#include <algorithm> // for std::copy
5#include <numeric> 5#include <numeric>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "video_core/command_classes/codecs/vp9.h" 7#include "video_core/host1x/codecs/vp9.h"
8#include "video_core/gpu.h" 8#include "video_core/host1x/host1x.h"
9#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
10 10
11namespace Tegra::Decoder { 11namespace Tegra::Decoder {
@@ -236,7 +236,7 @@ constexpr std::array<u8, 254> map_lut{
236} 236}
237} // Anonymous namespace 237} // Anonymous namespace
238 238
239VP9::VP9(GPU& gpu_) : gpu{gpu_} {} 239VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {}
240 240
241VP9::~VP9() = default; 241VP9::~VP9() = default;
242 242
@@ -355,9 +355,9 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
355 } 355 }
356} 356}
357 357
358Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { 358Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) {
359 PictureInfo picture_info; 359 PictureInfo picture_info;
360 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); 360 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
361 Vp9PictureInfo vp9_info = picture_info.Convert(); 361 Vp9PictureInfo vp9_info = picture_info.Convert();
362 362
363 InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); 363 InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
@@ -372,18 +372,19 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
372 372
373void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { 373void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
374 EntropyProbs entropy; 374 EntropyProbs entropy;
375 gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); 375 host1x.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
376 entropy.Convert(dst); 376 entropy.Convert(dst);
377} 377}
378 378
379Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) { 379Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
380 Vp9FrameContainer current_frame{}; 380 Vp9FrameContainer current_frame{};
381 { 381 {
382 gpu.SyncGuestHost(); 382 // gpu.SyncGuestHost(); epic, why?
383 current_frame.info = GetVp9PictureInfo(state); 383 current_frame.info = GetVp9PictureInfo(state);
384 current_frame.bit_stream.resize(current_frame.info.bitstream_size); 384 current_frame.bit_stream.resize(current_frame.info.bitstream_size);
385 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), 385 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
386 current_frame.info.bitstream_size); 386 current_frame.bit_stream.data(),
387 current_frame.info.bitstream_size);
387 } 388 }
388 if (!next_frame.bit_stream.empty()) { 389 if (!next_frame.bit_stream.empty()) {
389 Vp9FrameContainer temp{ 390 Vp9FrameContainer temp{
@@ -769,7 +770,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
769 return uncomp_writer; 770 return uncomp_writer;
770} 771}
771 772
772void VP9::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { 773void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
773 std::vector<u8> bitstream; 774 std::vector<u8> bitstream;
774 { 775 {
775 Vp9FrameContainer curr_frame = GetCurrentFrame(state); 776 Vp9FrameContainer curr_frame = GetCurrentFrame(state);
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h
index ecc40e8b1..d4083e8d3 100644
--- a/src/video_core/command_classes/codecs/vp9.h
+++ b/src/video_core/host1x/codecs/vp9.h
@@ -8,11 +8,15 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/stream.h" 10#include "common/stream.h"
11#include "video_core/command_classes/codecs/vp9_types.h" 11#include "video_core/host1x/codecs/vp9_types.h"
12#include "video_core/command_classes/nvdec_common.h" 12#include "video_core/host1x/nvdec_common.h"
13 13
14namespace Tegra { 14namespace Tegra {
15class GPU; 15
16namespace Host1x {
17class Host1x;
18} // namespace Host1x
19
16namespace Decoder { 20namespace Decoder {
17 21
18/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the 22/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
@@ -106,7 +110,7 @@ private:
106 110
107class VP9 { 111class VP9 {
108public: 112public:
109 explicit VP9(GPU& gpu_); 113 explicit VP9(Host1x::Host1x& host1x);
110 ~VP9(); 114 ~VP9();
111 115
112 VP9(const VP9&) = delete; 116 VP9(const VP9&) = delete;
@@ -117,7 +121,7 @@ public:
117 121
118 /// Composes the VP9 frame from the GPU state information. 122 /// Composes the VP9 frame from the GPU state information.
119 /// Based on the official VP9 spec documentation 123 /// Based on the official VP9 spec documentation
120 void ComposeFrame(const NvdecCommon::NvdecRegisters& state); 124 void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state);
121 125
122 /// Returns true if the most recent frame was a hidden frame. 126 /// Returns true if the most recent frame was a hidden frame.
123 [[nodiscard]] bool WasFrameHidden() const { 127 [[nodiscard]] bool WasFrameHidden() const {
@@ -162,19 +166,21 @@ private:
162 void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); 166 void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
163 167
164 /// Returns VP9 information from NVDEC provided offset and size 168 /// Returns VP9 information from NVDEC provided offset and size
165 [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state); 169 [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(
170 const Host1x::NvdecCommon::NvdecRegisters& state);
166 171
167 /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct 172 /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
168 void InsertEntropy(u64 offset, Vp9EntropyProbs& dst); 173 void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
169 174
170 /// Returns frame to be decoded after buffering 175 /// Returns frame to be decoded after buffering
171 [[nodiscard]] Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state); 176 [[nodiscard]] Vp9FrameContainer GetCurrentFrame(
177 const Host1x::NvdecCommon::NvdecRegisters& state);
172 178
173 /// Use NVDEC providied information to compose the headers for the current frame 179 /// Use NVDEC providied information to compose the headers for the current frame
174 [[nodiscard]] std::vector<u8> ComposeCompressedHeader(); 180 [[nodiscard]] std::vector<u8> ComposeCompressedHeader();
175 [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); 181 [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
176 182
177 GPU& gpu; 183 Host1x::Host1x& host1x;
178 std::vector<u8> frame; 184 std::vector<u8> frame;
179 185
180 std::array<s8, 4> loop_filter_ref_deltas{}; 186 std::array<s8, 4> loop_filter_ref_deltas{};
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h
index bb3d8df6e..adad8ed7e 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/host1x/codecs/vp9_types.h
@@ -9,7 +9,6 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Tegra { 11namespace Tegra {
12class GPU;
13 12
14namespace Decoder { 13namespace Decoder {
15struct Vp9FrameDimensions { 14struct Vp9FrameDimensions {
diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp
new file mode 100644
index 000000000..dceefdb7f
--- /dev/null
+++ b/src/video_core/host1x/control.cpp
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/assert.h"
5#include "video_core/host1x/control.h"
6#include "video_core/host1x/host1x.h"
7
8namespace Tegra::Host1x {
9
10Control::Control(Host1x& host1x_) : host1x(host1x_) {}
11
12Control::~Control() = default;
13
14void Control::ProcessMethod(Method method, u32 argument) {
15 switch (method) {
16 case Method::LoadSyncptPayload32:
17 syncpoint_value = argument;
18 break;
19 case Method::WaitSyncpt:
20 case Method::WaitSyncpt32:
21 Execute(argument);
22 break;
23 default:
24 UNIMPLEMENTED_MSG("Control method 0x{:X}", static_cast<u32>(method));
25 break;
26 }
27}
28
29void Control::Execute(u32 data) {
30 host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
31}
32
33} // namespace Tegra::Host1x
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/host1x/control.h
index bb48a4381..e117888a3 100644
--- a/src/video_core/command_classes/host1x.h
+++ b/src/video_core/host1x/control.h
@@ -1,15 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
6#include "common/common_types.h" 7#include "common/common_types.h"
7 8
8namespace Tegra { 9namespace Tegra {
9class GPU; 10
11namespace Host1x {
12
13class Host1x;
10class Nvdec; 14class Nvdec;
11 15
12class Host1x { 16class Control {
13public: 17public:
14 enum class Method : u32 { 18 enum class Method : u32 {
15 WaitSyncpt = 0x8, 19 WaitSyncpt = 0x8,
@@ -17,8 +21,8 @@ public:
17 WaitSyncpt32 = 0x50, 21 WaitSyncpt32 = 0x50,
18 }; 22 };
19 23
20 explicit Host1x(GPU& gpu); 24 explicit Control(Host1x& host1x);
21 ~Host1x(); 25 ~Control();
22 26
23 /// Writes the method into the state, Invoke Execute() if encountered 27 /// Writes the method into the state, Invoke Execute() if encountered
24 void ProcessMethod(Method method, u32 argument); 28 void ProcessMethod(Method method, u32 argument);
@@ -28,7 +32,9 @@ private:
28 void Execute(u32 data); 32 void Execute(u32 data);
29 33
30 u32 syncpoint_value{}; 34 u32 syncpoint_value{};
31 GPU& gpu; 35 Host1x& host1x;
32}; 36};
33 37
38} // namespace Host1x
39
34} // namespace Tegra 40} // namespace Tegra
diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp
new file mode 100644
index 000000000..7c317a85d
--- /dev/null
+++ b/src/video_core/host1x/host1x.cpp
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core.h"
5#include "video_core/host1x/host1x.h"
6
7namespace Tegra {
8
9namespace Host1x {
10
11Host1x::Host1x(Core::System& system_)
12 : system{system_}, syncpoint_manager{}, memory_manager{system, 32, 12},
13 allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
14
15} // namespace Host1x
16
17} // namespace Tegra
diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h
new file mode 100644
index 000000000..57082ae54
--- /dev/null
+++ b/src/video_core/host1x/host1x.h
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8#include "common/address_space.h"
9#include "video_core/host1x/syncpoint_manager.h"
10#include "video_core/memory_manager.h"
11
12namespace Core {
13class System;
14} // namespace Core
15
16namespace Tegra {
17
18namespace Host1x {
19
20class Host1x {
21public:
22 explicit Host1x(Core::System& system);
23
24 SyncpointManager& GetSyncpointManager() {
25 return syncpoint_manager;
26 }
27
28 const SyncpointManager& GetSyncpointManager() const {
29 return syncpoint_manager;
30 }
31
32 Tegra::MemoryManager& MemoryManager() {
33 return memory_manager;
34 }
35
36 const Tegra::MemoryManager& MemoryManager() const {
37 return memory_manager;
38 }
39
40 Common::FlatAllocator<u32, 0, 32>& Allocator() {
41 return *allocator;
42 }
43
44 const Common::FlatAllocator<u32, 0, 32>& Allocator() const {
45 return *allocator;
46 }
47
48private:
49 Core::System& system;
50 SyncpointManager syncpoint_manager;
51 Tegra::MemoryManager memory_manager;
52 std::unique_ptr<Common::FlatAllocator<u32, 0, 32>> allocator;
53};
54
55} // namespace Host1x
56
57} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/host1x/nvdec.cpp
index 4fbbe3da6..a4bd5b79f 100644
--- a/src/video_core/command_classes/nvdec.cpp
+++ b/src/video_core/host1x/nvdec.cpp
@@ -2,15 +2,16 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "video_core/command_classes/nvdec.h" 5#include "video_core/host1x/host1x.h"
6#include "video_core/gpu.h" 6#include "video_core/host1x/nvdec.h"
7 7
8namespace Tegra { 8namespace Tegra::Host1x {
9 9
10#define NVDEC_REG_INDEX(field_name) \ 10#define NVDEC_REG_INDEX(field_name) \
11 (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) 11 (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
12 12
13Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {} 13Nvdec::Nvdec(Host1x& host1x_)
14 : host1x(host1x_), state{}, codec(std::make_unique<Codec>(host1x, state)) {}
14 15
15Nvdec::~Nvdec() = default; 16Nvdec::~Nvdec() = default;
16 17
@@ -44,4 +45,4 @@ void Nvdec::Execute() {
44 } 45 }
45} 46}
46 47
47} // namespace Tegra 48} // namespace Tegra::Host1x
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/host1x/nvdec.h
index 488531fc6..3949d5181 100644
--- a/src/video_core/command_classes/nvdec.h
+++ b/src/video_core/host1x/nvdec.h
@@ -6,14 +6,17 @@
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "video_core/command_classes/codecs/codec.h" 9#include "video_core/host1x/codecs/codec.h"
10 10
11namespace Tegra { 11namespace Tegra {
12class GPU; 12
13namespace Host1x {
14
15class Host1x;
13 16
14class Nvdec { 17class Nvdec {
15public: 18public:
16 explicit Nvdec(GPU& gpu); 19 explicit Nvdec(Host1x& host1x);
17 ~Nvdec(); 20 ~Nvdec();
18 21
19 /// Writes the method into the state, Invoke Execute() if encountered 22 /// Writes the method into the state, Invoke Execute() if encountered
@@ -26,8 +29,11 @@ private:
26 /// Invoke codec to decode a frame 29 /// Invoke codec to decode a frame
27 void Execute(); 30 void Execute();
28 31
29 GPU& gpu; 32 Host1x& host1x;
30 NvdecCommon::NvdecRegisters state; 33 NvdecCommon::NvdecRegisters state;
31 std::unique_ptr<Codec> codec; 34 std::unique_ptr<Codec> codec;
32}; 35};
36
37} // namespace Host1x
38
33} // namespace Tegra 39} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/host1x/nvdec_common.h
index 521e5b52b..49d67ebbe 100644
--- a/src/video_core/command_classes/nvdec_common.h
+++ b/src/video_core/host1x/nvdec_common.h
@@ -7,7 +7,7 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Tegra::NvdecCommon { 10namespace Tegra::Host1x::NvdecCommon {
11 11
12enum class VideoCodec : u64 { 12enum class VideoCodec : u64 {
13 None = 0x0, 13 None = 0x0,
@@ -94,4 +94,4 @@ ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);
94 94
95#undef ASSERT_REG_POSITION 95#undef ASSERT_REG_POSITION
96 96
97} // namespace Tegra::NvdecCommon 97} // namespace Tegra::Host1x::NvdecCommon
diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/host1x/sync_manager.cpp
index 67e58046f..5ef9ea217 100644
--- a/src/video_core/command_classes/sync_manager.cpp
+++ b/src/video_core/host1x/sync_manager.cpp
@@ -3,10 +3,13 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include "sync_manager.h" 5#include "sync_manager.h"
6#include "video_core/gpu.h" 6#include "video_core/host1x/host1x.h"
7#include "video_core/host1x/syncpoint_manager.h"
7 8
8namespace Tegra { 9namespace Tegra {
9SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {} 10namespace Host1x {
11
12SyncptIncrManager::SyncptIncrManager(Host1x& host1x_) : host1x(host1x_) {}
10SyncptIncrManager::~SyncptIncrManager() = default; 13SyncptIncrManager::~SyncptIncrManager() = default;
11 14
12void SyncptIncrManager::Increment(u32 id) { 15void SyncptIncrManager::Increment(u32 id) {
@@ -36,8 +39,12 @@ void SyncptIncrManager::IncrementAllDone() {
36 if (!increments[done_count].complete) { 39 if (!increments[done_count].complete) {
37 break; 40 break;
38 } 41 }
39 gpu.IncrementSyncPoint(increments[done_count].syncpt_id); 42 auto& syncpoint_manager = host1x.GetSyncpointManager();
43 syncpoint_manager.IncrementGuest(increments[done_count].syncpt_id);
44 syncpoint_manager.IncrementHost(increments[done_count].syncpt_id);
40 } 45 }
41 increments.erase(increments.begin(), increments.begin() + done_count); 46 increments.erase(increments.begin(), increments.begin() + done_count);
42} 47}
48
49} // namespace Host1x
43} // namespace Tegra 50} // namespace Tegra
diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/host1x/sync_manager.h
index 6dfaae080..7bb77fa27 100644
--- a/src/video_core/command_classes/sync_manager.h
+++ b/src/video_core/host1x/sync_manager.h
@@ -8,7 +8,11 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Tegra { 10namespace Tegra {
11class GPU; 11
12namespace Host1x {
13
14class Host1x;
15
12struct SyncptIncr { 16struct SyncptIncr {
13 u32 id; 17 u32 id;
14 u32 class_id; 18 u32 class_id;
@@ -21,7 +25,7 @@ struct SyncptIncr {
21 25
22class SyncptIncrManager { 26class SyncptIncrManager {
23public: 27public:
24 explicit SyncptIncrManager(GPU& gpu); 28 explicit SyncptIncrManager(Host1x& host1x);
25 ~SyncptIncrManager(); 29 ~SyncptIncrManager();
26 30
27 /// Add syncpoint id and increment all 31 /// Add syncpoint id and increment all
@@ -41,7 +45,9 @@ private:
41 std::mutex increment_lock; 45 std::mutex increment_lock;
42 u32 current_id{}; 46 u32 current_id{};
43 47
44 GPU& gpu; 48 Host1x& host1x;
45}; 49};
46 50
51} // namespace Host1x
52
47} // namespace Tegra 53} // namespace Tegra
diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp
new file mode 100644
index 000000000..a44fc83d3
--- /dev/null
+++ b/src/video_core/host1x/syncpoint_manager.cpp
@@ -0,0 +1,106 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/microprofile.h"
5#include "video_core/host1x/syncpoint_manager.h"
6
7namespace Tegra {
8
9namespace Host1x {
10
11MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
12
13SyncpointManager::ActionHandle SyncpointManager::RegisterAction(
14 std::atomic<u32>& syncpoint, std::list<RegisteredAction>& action_storage, u32 expected_value,
15 std::function<void()>&& action) {
16 if (syncpoint.load(std::memory_order_acquire) >= expected_value) {
17 action();
18 return {};
19 }
20
21 std::unique_lock lk(guard);
22 if (syncpoint.load(std::memory_order_relaxed) >= expected_value) {
23 action();
24 return {};
25 }
26 auto it = action_storage.begin();
27 while (it != action_storage.end()) {
28 if (it->expected_value >= expected_value) {
29 break;
30 }
31 ++it;
32 }
33 return action_storage.emplace(it, expected_value, std::move(action));
34}
35
36void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage,
37 ActionHandle& handle) {
38 std::unique_lock lk(guard);
39
40 // We want to ensure the iterator still exists prior to erasing it
41 // Otherwise, if an invalid iterator was passed in then it could lead to UB
42 // It is important to avoid UB in that case since the deregister isn't called from a locked
43 // context
44 for (auto it = action_storage.begin(); it != action_storage.end(); it++) {
45 if (it == handle) {
46 action_storage.erase(it);
47 return;
48 }
49 }
50}
51
52void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) {
53 DeregisterAction(guest_action_storage[syncpoint_id], handle);
54}
55
56void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle) {
57 DeregisterAction(host_action_storage[syncpoint_id], handle);
58}
59
60void SyncpointManager::IncrementGuest(u32 syncpoint_id) {
61 Increment(syncpoints_guest[syncpoint_id], wait_guest_cv, guest_action_storage[syncpoint_id]);
62}
63
64void SyncpointManager::IncrementHost(u32 syncpoint_id) {
65 Increment(syncpoints_host[syncpoint_id], wait_host_cv, host_action_storage[syncpoint_id]);
66}
67
68void SyncpointManager::WaitGuest(u32 syncpoint_id, u32 expected_value) {
69 Wait(syncpoints_guest[syncpoint_id], wait_guest_cv, expected_value);
70}
71
72void SyncpointManager::WaitHost(u32 syncpoint_id, u32 expected_value) {
73 MICROPROFILE_SCOPE(GPU_wait);
74 Wait(syncpoints_host[syncpoint_id], wait_host_cv, expected_value);
75}
76
77void SyncpointManager::Increment(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv,
78 std::list<RegisteredAction>& action_storage) {
79 auto new_value{syncpoint.fetch_add(1, std::memory_order_acq_rel) + 1};
80
81 std::unique_lock lk(guard);
82 auto it = action_storage.begin();
83 while (it != action_storage.end()) {
84 if (it->expected_value > new_value) {
85 break;
86 }
87 it->action();
88 it = action_storage.erase(it);
89 }
90 wait_cv.notify_all();
91}
92
93void SyncpointManager::Wait(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv,
94 u32 expected_value) {
95 const auto pred = [&]() { return syncpoint.load(std::memory_order_acquire) >= expected_value; };
96 if (pred()) {
97 return;
98 }
99
100 std::unique_lock lk(guard);
101 wait_cv.wait(lk, pred);
102}
103
104} // namespace Host1x
105
106} // namespace Tegra
diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h
new file mode 100644
index 000000000..50a264e23
--- /dev/null
+++ b/src/video_core/host1x/syncpoint_manager.h
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <atomic>
8#include <condition_variable>
9#include <functional>
10#include <list>
11#include <mutex>
12
13#include "common/common_types.h"
14
15namespace Tegra {
16
17namespace Host1x {
18
19class SyncpointManager {
20public:
21 u32 GetGuestSyncpointValue(u32 id) const {
22 return syncpoints_guest[id].load(std::memory_order_acquire);
23 }
24
25 u32 GetHostSyncpointValue(u32 id) const {
26 return syncpoints_host[id].load(std::memory_order_acquire);
27 }
28
29 struct RegisteredAction {
30 explicit RegisteredAction(u32 expected_value_, std::function<void()>&& action_)
31 : expected_value{expected_value_}, action{std::move(action_)} {}
32 u32 expected_value;
33 std::function<void()> action;
34 };
35 using ActionHandle = std::list<RegisteredAction>::iterator;
36
37 template <typename Func>
38 ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) {
39 std::function<void()> func(action);
40 return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id],
41 expected_value, std::move(func));
42 }
43
44 template <typename Func>
45 ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) {
46 std::function<void()> func(action);
47 return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id],
48 expected_value, std::move(func));
49 }
50
51 void DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle);
52
53 void DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle);
54
55 void IncrementGuest(u32 syncpoint_id);
56
57 void IncrementHost(u32 syncpoint_id);
58
59 void WaitGuest(u32 syncpoint_id, u32 expected_value);
60
61 void WaitHost(u32 syncpoint_id, u32 expected_value);
62
63 bool IsReadyGuest(u32 syncpoint_id, u32 expected_value) const {
64 return syncpoints_guest[syncpoint_id].load(std::memory_order_acquire) >= expected_value;
65 }
66
67 bool IsReadyHost(u32 syncpoint_id, u32 expected_value) const {
68 return syncpoints_host[syncpoint_id].load(std::memory_order_acquire) >= expected_value;
69 }
70
71private:
72 void Increment(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv,
73 std::list<RegisteredAction>& action_storage);
74
75 ActionHandle RegisterAction(std::atomic<u32>& syncpoint,
76 std::list<RegisteredAction>& action_storage, u32 expected_value,
77 std::function<void()>&& action);
78
79 void DeregisterAction(std::list<RegisteredAction>& action_storage, ActionHandle& handle);
80
81 void Wait(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv, u32 expected_value);
82
83 static constexpr size_t NUM_MAX_SYNCPOINTS = 192;
84
85 std::array<std::atomic<u32>, NUM_MAX_SYNCPOINTS> syncpoints_guest{};
86 std::array<std::atomic<u32>, NUM_MAX_SYNCPOINTS> syncpoints_host{};
87
88 std::array<std::list<RegisteredAction>, NUM_MAX_SYNCPOINTS> guest_action_storage;
89 std::array<std::list<RegisteredAction>, NUM_MAX_SYNCPOINTS> host_action_storage;
90
91 std::mutex guard;
92 std::condition_variable wait_guest_cv;
93 std::condition_variable wait_host_cv;
94};
95
96} // namespace Host1x
97
98} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/host1x/vic.cpp
index 7c17df353..ac0b7d20e 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -18,14 +18,17 @@ extern "C" {
18#include "common/bit_field.h" 18#include "common/bit_field.h"
19#include "common/logging/log.h" 19#include "common/logging/log.h"
20 20
21#include "video_core/command_classes/nvdec.h"
22#include "video_core/command_classes/vic.h"
23#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
24#include "video_core/gpu.h" 22#include "video_core/host1x/host1x.h"
23#include "video_core/host1x/nvdec.h"
24#include "video_core/host1x/vic.h"
25#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
26#include "video_core/textures/decoders.h" 26#include "video_core/textures/decoders.h"
27 27
28namespace Tegra { 28namespace Tegra {
29
30namespace Host1x {
31
29namespace { 32namespace {
30enum class VideoPixelFormat : u64_le { 33enum class VideoPixelFormat : u64_le {
31 RGBA8 = 0x1f, 34 RGBA8 = 0x1f,
@@ -46,8 +49,8 @@ union VicConfig {
46 BitField<46, 14, u64_le> surface_height_minus1; 49 BitField<46, 14, u64_le> surface_height_minus1;
47}; 50};
48 51
49Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) 52Vic::Vic(Host1x& host1x_, std::shared_ptr<Nvdec> nvdec_processor_)
50 : gpu(gpu_), 53 : host1x(host1x_),
51 nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {} 54 nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {}
52 55
53Vic::~Vic() = default; 56Vic::~Vic() = default;
@@ -78,7 +81,7 @@ void Vic::Execute() {
78 LOG_ERROR(Service_NVDRV, "VIC Luma address not set."); 81 LOG_ERROR(Service_NVDRV, "VIC Luma address not set.");
79 return; 82 return;
80 } 83 }
81 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)}; 84 const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
82 const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); 85 const AVFramePtr frame_ptr = nvdec_processor->GetFrame();
83 const auto* frame = frame_ptr.get(); 86 const auto* frame = frame_ptr.get();
84 if (!frame) { 87 if (!frame) {
@@ -153,15 +156,16 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
153 const u32 block_height = static_cast<u32>(config.block_linear_height_log2); 156 const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
154 const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); 157 const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
155 luma_buffer.resize(size); 158 luma_buffer.resize(size);
156 Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), 159 std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height);
157 converted_frame_buf_addr, block_height, 0, 0); 160 Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height,
161 block_height, 0, width * 4);
158 162
159 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); 163 host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
160 } else { 164 } else {
161 // send pitch linear frame 165 // send pitch linear frame
162 const size_t linear_size = width * height * 4; 166 const size_t linear_size = width * height * 4;
163 gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, 167 host1x.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
164 linear_size); 168 linear_size);
165 } 169 }
166} 170}
167 171
@@ -189,8 +193,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
189 luma_buffer[dst + x] = luma_src[src + x]; 193 luma_buffer[dst + x] = luma_src[src + x];
190 } 194 }
191 } 195 }
192 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), 196 host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
193 luma_buffer.size()); 197 luma_buffer.size());
194 198
195 // Chroma 199 // Chroma
196 const std::size_t half_height = frame_height / 2; 200 const std::size_t half_height = frame_height / 2;
@@ -231,8 +235,10 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
231 ASSERT(false); 235 ASSERT(false);
232 break; 236 break;
233 } 237 }
234 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), 238 host1x.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
235 chroma_buffer.size()); 239 chroma_buffer.size());
236} 240}
237 241
242} // namespace Host1x
243
238} // namespace Tegra 244} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/host1x/vic.h
index 010daa6b6..2b78786e8 100644
--- a/src/video_core/command_classes/vic.h
+++ b/src/video_core/host1x/vic.h
@@ -10,7 +10,10 @@
10struct SwsContext; 10struct SwsContext;
11 11
12namespace Tegra { 12namespace Tegra {
13class GPU; 13
14namespace Host1x {
15
16class Host1x;
14class Nvdec; 17class Nvdec;
15union VicConfig; 18union VicConfig;
16 19
@@ -25,7 +28,7 @@ public:
25 SetOutputSurfaceChromaUnusedOffset = 0x1ca 28 SetOutputSurfaceChromaUnusedOffset = 0x1ca
26 }; 29 };
27 30
28 explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor); 31 explicit Vic(Host1x& host1x, std::shared_ptr<Nvdec> nvdec_processor);
29 32
30 ~Vic(); 33 ~Vic();
31 34
@@ -39,8 +42,8 @@ private:
39 42
40 void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); 43 void WriteYUVFrame(const AVFrame* frame, const VicConfig& config);
41 44
42 GPU& gpu; 45 Host1x& host1x;
43 std::shared_ptr<Tegra::Nvdec> nvdec_processor; 46 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
44 47
45 /// Avoid reallocation of the following buffers every frame, as their 48 /// Avoid reallocation of the following buffers every frame, as their
46 /// size does not change during a stream 49 /// size does not change during a stream
@@ -58,4 +61,6 @@ private:
58 s32 scaler_height{}; 61 s32 scaler_height{};
59}; 62};
60 63
64} // namespace Host1x
65
61} // namespace Tegra 66} // namespace Tegra
diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp
index 3441a5fe5..d608678a3 100644
--- a/src/video_core/host_shaders/astc_decoder.comp
+++ b/src/video_core/host_shaders/astc_decoder.comp
@@ -1065,7 +1065,7 @@ TexelWeightParams DecodeBlockInfo() {
1065void FillError(ivec3 coord) { 1065void FillError(ivec3 coord) {
1066 for (uint j = 0; j < block_dims.y; j++) { 1066 for (uint j = 0; j < block_dims.y; j++) {
1067 for (uint i = 0; i < block_dims.x; i++) { 1067 for (uint i = 0; i < block_dims.x; i++) {
1068 imageStore(dest_image, coord + ivec3(i, j, 0), vec4(1.0, 1.0, 0.0, 1.0)); 1068 imageStore(dest_image, coord + ivec3(i, j, 0), vec4(0.0, 0.0, 0.0, 0.0));
1069 } 1069 }
1070 } 1070 }
1071} 1071}
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index 43f8b5904..f61d5998e 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -8,6 +8,7 @@
8 8
9#include <boost/container_hash/hash.hpp> 9#include <boost/container_hash/hash.hpp>
10 10
11#include <fstream>
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/fs/fs.h" 13#include "common/fs/fs.h"
13#include "common/fs/path_util.h" 14#include "common/fs/path_util.h"
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 58382755b..f896591bf 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -3,6 +3,8 @@
3 3
4#include <array> 4#include <array>
5#include <vector> 5#include <vector>
6#include "common/scope_exit.h"
7#include "video_core/dirty_flags.h"
6#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
7#include "video_core/macro/macro.h" 9#include "video_core/macro/macro.h"
8#include "video_core/macro/macro_hle.h" 10#include "video_core/macro/macro_hle.h"
@@ -19,71 +21,116 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
19 21
20 maxwell3d.regs.draw.topology.Assign( 22 maxwell3d.regs.draw.topology.Assign(
21 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff)); 23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
22 maxwell3d.regs.vb_base_instance = parameters[5]; 24 maxwell3d.regs.global_base_instance_index = parameters[5];
23 maxwell3d.mme_draw.instance_count = instance_count; 25 maxwell3d.regs.global_base_vertex_index = parameters[3];
24 maxwell3d.regs.vb_element_base = parameters[3]; 26 maxwell3d.regs.index_buffer.count = parameters[1];
25 maxwell3d.regs.index_array.count = parameters[1]; 27 maxwell3d.regs.index_buffer.first = parameters[4];
26 maxwell3d.regs.index_array.first = parameters[4];
27 28
28 if (maxwell3d.ShouldExecute()) { 29 if (maxwell3d.ShouldExecute()) {
29 maxwell3d.Rasterizer().Draw(true, true); 30 maxwell3d.Rasterizer().Draw(true, instance_count);
30 } 31 }
31 maxwell3d.regs.index_array.count = 0; 32 maxwell3d.regs.index_buffer.count = 0;
32 maxwell3d.mme_draw.instance_count = 0;
33 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
34} 33}
35 34
36void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 35void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
37 const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 36 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
38 37
39 maxwell3d.regs.vertex_buffer.first = parameters[3]; 38 maxwell3d.regs.vertex_buffer.first = parameters[3];
40 maxwell3d.regs.vertex_buffer.count = parameters[1]; 39 maxwell3d.regs.vertex_buffer.count = parameters[1];
41 maxwell3d.regs.vb_base_instance = parameters[4]; 40 maxwell3d.regs.global_base_instance_index = parameters[4];
42 maxwell3d.regs.draw.topology.Assign( 41 maxwell3d.regs.draw.topology.Assign(
43 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 42 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
44 maxwell3d.mme_draw.instance_count = count;
45 43
46 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
47 maxwell3d.Rasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, instance_count);
48 } 46 }
49 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
50 maxwell3d.mme_draw.instance_count = 0;
51 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
52} 48}
53 49
54void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 50void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
55 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 51 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
56 const u32 element_base = parameters[4]; 52 const u32 element_base = parameters[4];
57 const u32 base_instance = parameters[5]; 53 const u32 base_instance = parameters[5];
58 maxwell3d.regs.index_array.first = parameters[3]; 54 maxwell3d.regs.index_buffer.first = parameters[3];
59 maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? 55 maxwell3d.regs.vertex_id_base = element_base;
60 maxwell3d.regs.index_array.count = parameters[1]; 56 maxwell3d.regs.index_buffer.count = parameters[1];
61 maxwell3d.regs.vb_element_base = element_base; 57 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
62 maxwell3d.regs.vb_base_instance = base_instance; 58 maxwell3d.regs.global_base_vertex_index = element_base;
63 maxwell3d.mme_draw.instance_count = instance_count; 59 maxwell3d.regs.global_base_instance_index = base_instance;
64 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 60 maxwell3d.CallMethod(0x8e3, 0x640, true);
65 maxwell3d.CallMethodFromMME(0x8e4, element_base); 61 maxwell3d.CallMethod(0x8e4, element_base, true);
66 maxwell3d.CallMethodFromMME(0x8e5, base_instance); 62 maxwell3d.CallMethod(0x8e5, base_instance, true);
67 maxwell3d.regs.draw.topology.Assign( 63 maxwell3d.regs.draw.topology.Assign(
68 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 64 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
69 if (maxwell3d.ShouldExecute()) { 65 if (maxwell3d.ShouldExecute()) {
70 maxwell3d.Rasterizer().Draw(true, true); 66 maxwell3d.Rasterizer().Draw(true, instance_count);
67 }
68 maxwell3d.regs.vertex_id_base = 0x0;
69 maxwell3d.regs.index_buffer.count = 0;
70 maxwell3d.regs.global_base_vertex_index = 0x0;
71 maxwell3d.regs.global_base_instance_index = 0x0;
72 maxwell3d.CallMethod(0x8e3, 0x640, true);
73 maxwell3d.CallMethod(0x8e4, 0x0, true);
74 maxwell3d.CallMethod(0x8e5, 0x0, true);
75}
76
77// Multidraw Indirect
78void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
79 SCOPE_EXIT({
80 // Clean everything.
81 maxwell3d.regs.vertex_id_base = 0x0;
82 maxwell3d.regs.index_buffer.count = 0;
83 maxwell3d.regs.global_base_vertex_index = 0x0;
84 maxwell3d.regs.global_base_instance_index = 0x0;
85 maxwell3d.CallMethod(0x8e3, 0x640, true);
86 maxwell3d.CallMethod(0x8e4, 0x0, true);
87 maxwell3d.CallMethod(0x8e5, 0x0, true);
88 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
89 });
90 const u32 start_indirect = parameters[0];
91 const u32 end_indirect = parameters[1];
92 if (start_indirect >= end_indirect) {
93 // Nothing to do.
94 return;
95 }
96 const auto topology =
97 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
98 maxwell3d.regs.draw.topology.Assign(topology);
99 const u32 padding = parameters[3];
100 const std::size_t max_draws = parameters[4];
101
102 const u32 indirect_words = 5 + padding;
103 const std::size_t first_draw = start_indirect;
104 const std::size_t effective_draws = end_indirect - start_indirect;
105 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
106
107 for (std::size_t index = first_draw; index < last_draw; index++) {
108 const std::size_t base = index * indirect_words + 5;
109 const u32 num_vertices = parameters[base];
110 const u32 instance_count = parameters[base + 1];
111 const u32 first_index = parameters[base + 2];
112 const u32 base_vertex = parameters[base + 3];
113 const u32 base_instance = parameters[base + 4];
114 maxwell3d.regs.index_buffer.first = first_index;
115 maxwell3d.regs.vertex_id_base = base_vertex;
116 maxwell3d.regs.index_buffer.count = num_vertices;
117 maxwell3d.regs.global_base_vertex_index = base_vertex;
118 maxwell3d.regs.global_base_instance_index = base_instance;
119 maxwell3d.CallMethod(0x8e3, 0x640, true);
120 maxwell3d.CallMethod(0x8e4, base_vertex, true);
121 maxwell3d.CallMethod(0x8e5, base_instance, true);
122 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
123 if (maxwell3d.ShouldExecute()) {
124 maxwell3d.Rasterizer().Draw(true, instance_count);
125 }
71 } 126 }
72 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
73 maxwell3d.regs.index_array.count = 0;
74 maxwell3d.regs.vb_element_base = 0x0;
75 maxwell3d.regs.vb_base_instance = 0x0;
76 maxwell3d.mme_draw.instance_count = 0;
77 maxwell3d.CallMethodFromMME(0x8e3, 0x640);
78 maxwell3d.CallMethodFromMME(0x8e4, 0x0);
79 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
80 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
81} 127}
82 128
83constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ 129constexpr std::array<std::pair<u64, HLEFunction>, 4> hle_funcs{{
84 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, 130 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
85 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, 131 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
86 {0x0217920100488FF7, &HLE_0217920100488FF7}, 132 {0x0217920100488FF7, &HLE_0217920100488FF7},
133 {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164},
87}}; 134}};
88 135
89class HLEMacroImpl final : public CachedMacro { 136class HLEMacroImpl final : public CachedMacro {
@@ -99,6 +146,7 @@ private:
99 Engines::Maxwell3D& maxwell3d; 146 Engines::Maxwell3D& maxwell3d;
100 HLEFunction func; 147 HLEFunction func;
101}; 148};
149
102} // Anonymous namespace 150} // Anonymous namespace
103 151
104HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} 152HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {}
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index f670b1bca..c0d32c112 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -335,7 +335,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) {
335} 335}
336 336
337void MacroInterpreterImpl::Send(u32 value) { 337void MacroInterpreterImpl::Send(u32 value) {
338 maxwell3d.CallMethodFromMME(method_address.address, value); 338 maxwell3d.CallMethod(method_address.address, value, true);
339 // Increment the method address by the method increment. 339 // Increment the method address by the method increment.
340 method_address.address.Assign(method_address.address.Value() + 340 method_address.address.Assign(method_address.address.Value() +
341 method_address.increment.Value()); 341 method_address.increment.Value());
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index aca25d902..25c1ce798 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -279,28 +279,13 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
279 auto dst = Compile_GetRegister(opcode.src_a, RESULT); 279 auto dst = Compile_GetRegister(opcode.src_a, RESULT);
280 auto src = Compile_GetRegister(opcode.src_b, eax); 280 auto src = Compile_GetRegister(opcode.src_b, eax);
281 281
282 if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) {
283 shr(src, opcode.bf_src_bit);
284 } else if (opcode.bf_src_bit == 31) {
285 xor_(src, src);
286 }
287 // Don't bother masking the whole register since we're using a 32 bit register
288 if (opcode.bf_size != 31 && opcode.bf_size != 0) {
289 and_(src, opcode.GetBitfieldMask());
290 } else if (opcode.bf_size == 0) {
291 xor_(src, src);
292 }
293 if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) {
294 shl(src, opcode.bf_dst_bit);
295 } else if (opcode.bf_dst_bit == 31) {
296 xor_(src, src);
297 }
298
299 const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); 282 const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
300 if (mask != 0xffffffff) { 283 and_(dst, mask);
301 and_(dst, mask); 284 shr(src, opcode.bf_src_bit);
302 } 285 and_(src, opcode.GetBitfieldMask());
286 shl(src, opcode.bf_dst_bit);
303 or_(dst, src); 287 or_(dst, src);
288
304 Compile_ProcessResult(opcode.result_operation, opcode.dst); 289 Compile_ProcessResult(opcode.result_operation, opcode.dst);
305} 290}
306 291
@@ -309,17 +294,9 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
309 const auto src = Compile_GetRegister(opcode.src_b, RESULT); 294 const auto src = Compile_GetRegister(opcode.src_b, RESULT);
310 295
311 shr(src, dst.cvt8()); 296 shr(src, dst.cvt8());
312 if (opcode.bf_size != 0 && opcode.bf_size != 31) { 297 and_(src, opcode.GetBitfieldMask());
313 and_(src, opcode.GetBitfieldMask()); 298 shl(src, opcode.bf_dst_bit);
314 } else if (opcode.bf_size == 0) {
315 xor_(src, src);
316 }
317 299
318 if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) {
319 shl(src, opcode.bf_dst_bit);
320 } else if (opcode.bf_dst_bit == 31) {
321 xor_(src, src);
322 }
323 Compile_ProcessResult(opcode.result_operation, opcode.dst); 300 Compile_ProcessResult(opcode.result_operation, opcode.dst);
324} 301}
325 302
@@ -327,13 +304,8 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
327 const auto dst = Compile_GetRegister(opcode.src_a, ecx); 304 const auto dst = Compile_GetRegister(opcode.src_a, ecx);
328 const auto src = Compile_GetRegister(opcode.src_b, RESULT); 305 const auto src = Compile_GetRegister(opcode.src_b, RESULT);
329 306
330 if (opcode.bf_src_bit != 0) { 307 shr(src, opcode.bf_src_bit);
331 shr(src, opcode.bf_src_bit); 308 and_(src, opcode.GetBitfieldMask());
332 }
333
334 if (opcode.bf_size != 31) {
335 and_(src, opcode.GetBitfieldMask());
336 }
337 shl(src, dst.cvt8()); 309 shl(src, dst.cvt8());
338 310
339 Compile_ProcessResult(opcode.result_operation, opcode.dst); 311 Compile_ProcessResult(opcode.result_operation, opcode.dst);
@@ -374,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
374} 346}
375 347
376void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { 348void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
377 maxwell3d->CallMethodFromMME(method_address.address, value); 349 maxwell3d->CallMethod(method_address.address, value, true);
378} 350}
379 351
380void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { 352void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
@@ -429,17 +401,11 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
429 Xbyak::Label handle_post_exit{}; 401 Xbyak::Label handle_post_exit{};
430 Xbyak::Label skip{}; 402 Xbyak::Label skip{};
431 jmp(skip, T_NEAR); 403 jmp(skip, T_NEAR);
432 if (opcode.is_exit) { 404
433 L(handle_post_exit); 405 L(handle_post_exit);
434 // Execute 1 instruction 406 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
435 mov(BRANCH_HOLDER, end_of_code); 407 jmp(labels[jump_address], T_NEAR);
436 // Jump to next instruction to skip delay slot check 408
437 jmp(labels[jump_address], T_NEAR);
438 } else {
439 L(handle_post_exit);
440 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
441 jmp(labels[jump_address], T_NEAR);
442 }
443 L(skip); 409 L(skip);
444 mov(BRANCH_HOLDER, handle_post_exit); 410 mov(BRANCH_HOLDER, handle_post_exit);
445 jmp(delay_skip[pc], T_NEAR); 411 jmp(delay_skip[pc], T_NEAR);
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index bf9eb735d..384350dbd 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -7,6 +7,7 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/device_memory.h"
10#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
12#include "core/memory.h" 13#include "core/memory.h"
@@ -16,172 +17,239 @@
16 17
17namespace Tegra { 18namespace Tegra {
18 19
19MemoryManager::MemoryManager(Core::System& system_) 20std::atomic<size_t> MemoryManager::unique_identifier_generator{};
20 : system{system_}, page_table(page_table_size) {} 21
22MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_,
23 u64 page_bits_)
24 : system{system_}, memory{system.Memory()}, device_memory{system.DeviceMemory()},
25 address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
26 entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
27 page_bits != big_page_bits ? page_bits : 0},
28 unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} {
29 address_space_size = 1ULL << address_space_bits;
30 page_size = 1ULL << page_bits;
31 page_mask = page_size - 1ULL;
32 big_page_size = 1ULL << big_page_bits;
33 big_page_mask = big_page_size - 1ULL;
34 const u64 page_table_bits = address_space_bits - page_bits;
35 const u64 big_page_table_bits = address_space_bits - big_page_bits;
36 const u64 page_table_size = 1ULL << page_table_bits;
37 const u64 big_page_table_size = 1ULL << big_page_table_bits;
38 page_table_mask = page_table_size - 1;
39 big_page_table_mask = big_page_table_size - 1;
40
41 big_entries.resize(big_page_table_size / 32, 0);
42 big_page_table_cpu.resize(big_page_table_size);
43 big_page_continous.resize(big_page_table_size / continous_bits, 0);
44 std::array<PTEKind, 32> kind_valus;
45 kind_valus.fill(PTEKind::INVALID);
46 big_kinds.resize(big_page_table_size / 32, kind_valus);
47 entries.resize(page_table_size / 32, 0);
48 kinds.resize(big_page_table_size / 32, kind_valus);
49}
21 50
22MemoryManager::~MemoryManager() = default; 51MemoryManager::~MemoryManager() = default;
23 52
24void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { 53template <bool is_big_page>
25 rasterizer = rasterizer_; 54MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const {
26} 55 if constexpr (is_big_page) {
27 56 position = position >> big_page_bits;
28GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { 57 const u64 entry_mask = big_entries[position / 32];
29 u64 remaining_size{size}; 58 const size_t sub_index = position % 32;
30 for (u64 offset{}; offset < size; offset += page_size) { 59 return static_cast<EntryType>((entry_mask >> (2 * sub_index)) & 0x03ULL);
31 if (remaining_size < page_size) { 60 } else {
32 SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size); 61 position = position >> page_bits;
33 } else { 62 const u64 entry_mask = entries[position / 32];
34 SetPageEntry(gpu_addr + offset, page_entry + offset); 63 const size_t sub_index = position % 32;
35 } 64 return static_cast<EntryType>((entry_mask >> (2 * sub_index)) & 0x03ULL);
36 remaining_size -= page_size;
37 } 65 }
38 return gpu_addr;
39} 66}
40 67
41GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { 68template <bool is_big_page>
42 const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); 69void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
43 if (it != map_ranges.end() && it->first == gpu_addr) { 70 if constexpr (is_big_page) {
44 it->second = size; 71 position = position >> big_page_bits;
72 const u64 entry_mask = big_entries[position / 32];
73 const size_t sub_index = position % 32;
74 big_entries[position / 32] =
75 (~(3ULL << sub_index * 2) & entry_mask) | (static_cast<u64>(entry) << sub_index * 2);
45 } else { 76 } else {
46 map_ranges.insert(it, MapRange{gpu_addr, size}); 77 position = position >> page_bits;
78 const u64 entry_mask = entries[position / 32];
79 const size_t sub_index = position % 32;
80 entries[position / 32] =
81 (~(3ULL << sub_index * 2) & entry_mask) | (static_cast<u64>(entry) << sub_index * 2);
47 } 82 }
48 return UpdateRange(gpu_addr, cpu_addr, size);
49} 83}
50 84
51GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { 85PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
52 return Map(cpu_addr, *FindFreeRange(size, align), size); 86 auto entry = GetEntry<true>(gpu_addr);
87 if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] {
88 return GetKind<true>(gpu_addr);
89 } else {
90 return GetKind<false>(gpu_addr);
91 }
53} 92}
54 93
55GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { 94template <bool is_big_page>
56 const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true); 95PTEKind MemoryManager::GetKind(size_t position) const {
57 ASSERT(gpu_addr); 96 if constexpr (is_big_page) {
58 return Map(cpu_addr, *gpu_addr, size); 97 position = position >> big_page_bits;
98 const size_t sub_index = position % 32;
99 return big_kinds[position / 32][sub_index];
100 } else {
101 position = position >> page_bits;
102 const size_t sub_index = position % 32;
103 return kinds[position / 32][sub_index];
104 }
59} 105}
60 106
61void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { 107template <bool is_big_page>
62 if (size == 0) { 108void MemoryManager::SetKind(size_t position, PTEKind kind) {
63 return; 109 if constexpr (is_big_page) {
64 } 110 position = position >> big_page_bits;
65 const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); 111 const size_t sub_index = position % 32;
66 if (it != map_ranges.end()) { 112 big_kinds[position / 32][sub_index] = kind;
67 ASSERT(it->first == gpu_addr);
68 map_ranges.erase(it);
69 } else { 113 } else {
70 ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr); 114 position = position >> page_bits;
115 const size_t sub_index = position % 32;
116 kinds[position / 32][sub_index] = kind;
71 } 117 }
72 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); 118}
73
74 for (const auto& [map_addr, map_size] : submapped_ranges) {
75 // Flush and invalidate through the GPU interface, to be asynchronous if possible.
76 const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
77 ASSERT(cpu_addr);
78 119
79 rasterizer->UnmapMemory(*cpu_addr, map_size); 120inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
80 } 121 const u64 entry_mask = big_page_continous[big_page_index / continous_bits];
122 const size_t sub_index = big_page_index % continous_bits;
123 return ((entry_mask >> sub_index) & 0x1ULL) != 0;
124}
81 125
82 UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); 126inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value) {
127 const u64 continous_mask = big_page_continous[big_page_index / continous_bits];
128 const size_t sub_index = big_page_index % continous_bits;
129 big_page_continous[big_page_index / continous_bits] =
130 (~(1ULL << sub_index) & continous_mask) | (value ? 1ULL << sub_index : 0);
83} 131}
84 132
85std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { 133template <MemoryManager::EntryType entry_type>
134GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size,
135 PTEKind kind) {
136 [[maybe_unused]] u64 remaining_size{size};
137 if constexpr (entry_type == EntryType::Mapped) {
138 page_table.ReserveRange(gpu_addr, size);
139 }
86 for (u64 offset{}; offset < size; offset += page_size) { 140 for (u64 offset{}; offset < size; offset += page_size) {
87 if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { 141 const GPUVAddr current_gpu_addr = gpu_addr + offset;
88 return std::nullopt; 142 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
143 SetEntry<false>(current_gpu_addr, entry_type);
144 SetKind<false>(current_gpu_addr, kind);
145 if (current_entry_type != entry_type) {
146 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size);
147 }
148 if constexpr (entry_type == EntryType::Mapped) {
149 const VAddr current_cpu_addr = cpu_addr + offset;
150 const auto index = PageEntryIndex<false>(current_gpu_addr);
151 const u32 sub_value = static_cast<u32>(current_cpu_addr >> cpu_page_bits);
152 page_table[index] = sub_value;
89 } 153 }
154 remaining_size -= page_size;
90 } 155 }
91 156 return gpu_addr;
92 return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
93}
94
95GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
96 return *AllocateFixed(*FindFreeRange(size, align), size);
97} 157}
98 158
99void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) { 159template <MemoryManager::EntryType entry_type>
100 if (!page_entry.IsValid()) { 160GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr,
101 return; 161 size_t size, PTEKind kind) {
162 [[maybe_unused]] u64 remaining_size{size};
163 for (u64 offset{}; offset < size; offset += big_page_size) {
164 const GPUVAddr current_gpu_addr = gpu_addr + offset;
165 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
166 SetEntry<true>(current_gpu_addr, entry_type);
167 SetKind<true>(current_gpu_addr, kind);
168 if (current_entry_type != entry_type) {
169 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size);
170 }
171 if constexpr (entry_type == EntryType::Mapped) {
172 const VAddr current_cpu_addr = cpu_addr + offset;
173 const auto index = PageEntryIndex<true>(current_gpu_addr);
174 const u32 sub_value = static_cast<u32>(current_cpu_addr >> cpu_page_bits);
175 big_page_table_cpu[index] = sub_value;
176 const bool is_continous = ([&] {
177 uintptr_t base_ptr{
178 reinterpret_cast<uintptr_t>(memory.GetPointerSilent(current_cpu_addr))};
179 if (base_ptr == 0) {
180 return false;
181 }
182 for (VAddr start_cpu = current_cpu_addr + page_size;
183 start_cpu < current_cpu_addr + big_page_size; start_cpu += page_size) {
184 base_ptr += page_size;
185 auto next_ptr = reinterpret_cast<uintptr_t>(memory.GetPointerSilent(start_cpu));
186 if (next_ptr == 0 || base_ptr != next_ptr) {
187 return false;
188 }
189 }
190 return true;
191 })();
192 SetBigPageContinous(index, is_continous);
193 }
194 remaining_size -= big_page_size;
102 } 195 }
103 196 return gpu_addr;
104 ASSERT(system.CurrentProcess()
105 ->PageTable()
106 .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
107 .IsSuccess());
108} 197}
109 198
110void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) { 199void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
111 if (!page_entry.IsValid()) { 200 rasterizer = rasterizer_;
112 return;
113 }
114
115 ASSERT(system.CurrentProcess()
116 ->PageTable()
117 .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
118 .IsSuccess());
119} 201}
120 202
121PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { 203GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, PTEKind kind,
122 return page_table[PageEntryIndex(gpu_addr)]; 204 bool is_big_pages) {
205 if (is_big_pages) [[likely]] {
206 return BigPageTableOp<EntryType::Mapped>(gpu_addr, cpu_addr, size, kind);
207 }
208 return PageTableOp<EntryType::Mapped>(gpu_addr, cpu_addr, size, kind);
123} 209}
124 210
125void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { 211GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) {
126 // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to 212 if (is_big_pages) [[likely]] {
127 // improper tracking, but should be fixed in the future. 213 return BigPageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
128
129 //// Unlock the old page
130 // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
131
132 //// Lock the new page
133 // TryLockPage(page_entry, size);
134 auto& current_page = page_table[PageEntryIndex(gpu_addr)];
135
136 if ((!current_page.IsValid() && page_entry.IsValid()) ||
137 current_page.ToAddress() != page_entry.ToAddress()) {
138 rasterizer->ModifyGPUMemory(gpu_addr, size);
139 } 214 }
140 215 return PageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
141 current_page = page_entry;
142} 216}
143 217
144std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align, 218void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
145 bool start_32bit_address) const { 219 if (size == 0) {
146 if (!align) { 220 return;
147 align = page_size;
148 } else {
149 align = Common::AlignUp(align, page_size);
150 } 221 }
222 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
151 223
152 u64 available_size{}; 224 for (const auto& [map_addr, map_size] : submapped_ranges) {
153 GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start}; 225 // Flush and invalidate through the GPU interface, to be asynchronous if possible.
154 while (gpu_addr + available_size < address_space_size) { 226 const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
155 if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) { 227 ASSERT(cpu_addr);
156 available_size += page_size;
157
158 if (available_size >= size) {
159 return gpu_addr;
160 }
161 } else {
162 gpu_addr += available_size + page_size;
163 available_size = 0;
164 228
165 const auto remainder{gpu_addr % align}; 229 rasterizer->UnmapMemory(*cpu_addr, map_size);
166 if (remainder) {
167 gpu_addr = (gpu_addr - remainder) + align;
168 }
169 }
170 } 230 }
171 231
172 return std::nullopt; 232 BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
233 PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
173} 234}
174 235
175std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { 236std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
176 if (gpu_addr == 0) { 237 if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] {
177 return std::nullopt; 238 return std::nullopt;
178 } 239 }
179 const auto page_entry{GetPageEntry(gpu_addr)}; 240 if (GetEntry<true>(gpu_addr) != EntryType::Mapped) [[unlikely]] {
180 if (!page_entry.IsValid()) { 241 if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
181 return std::nullopt; 242 return std::nullopt;
243 }
244
245 const VAddr cpu_addr_base = static_cast<VAddr>(page_table[PageEntryIndex<false>(gpu_addr)])
246 << cpu_page_bits;
247 return cpu_addr_base + (gpu_addr & page_mask);
182 } 248 }
183 249
184 return page_entry.ToAddress() + (gpu_addr & page_mask); 250 const VAddr cpu_addr_base =
251 static_cast<VAddr>(big_page_table_cpu[PageEntryIndex<true>(gpu_addr)]) << cpu_page_bits;
252 return cpu_addr_base + (gpu_addr & big_page_mask);
185} 253}
186 254
187std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { 255std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const {
@@ -189,7 +257,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t s
189 const size_t page_last{(addr + size + page_size - 1) >> page_bits}; 257 const size_t page_last{(addr + size + page_size - 1) >> page_bits};
190 while (page_index < page_last) { 258 while (page_index < page_last) {
191 const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; 259 const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
192 if (page_addr && *page_addr != 0) { 260 if (page_addr) {
193 return page_addr; 261 return page_addr;
194 } 262 }
195 ++page_index; 263 ++page_index;
@@ -232,126 +300,298 @@ template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
232template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data); 300template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
233 301
234u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { 302u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
235 if (!GetPageEntry(gpu_addr).IsValid()) {
236 return {};
237 }
238
239 const auto address{GpuToCpuAddress(gpu_addr)}; 303 const auto address{GpuToCpuAddress(gpu_addr)};
240 if (!address) { 304 if (!address) {
241 return {}; 305 return {};
242 } 306 }
243 307
244 return system.Memory().GetPointer(*address); 308 return memory.GetPointer(*address);
245} 309}
246 310
247const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { 311const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
248 if (!GetPageEntry(gpu_addr).IsValid()) {
249 return {};
250 }
251
252 const auto address{GpuToCpuAddress(gpu_addr)}; 312 const auto address{GpuToCpuAddress(gpu_addr)};
253 if (!address) { 313 if (!address) {
254 return {}; 314 return {};
255 } 315 }
256 316
257 return system.Memory().GetPointer(*address); 317 return memory.GetPointer(*address);
258} 318}
259 319
260size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept { 320#ifdef _MSC_VER // no need for gcc / clang but msvc's compiler is more conservative with inlining.
261 auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first); 321#pragma inline_recursion(on)
262 --it; 322#endif
263 return it->second - (gpu_addr - it->first); 323
264} 324template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
265 325inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
266void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 326 FuncMapped&& func_mapped, FuncReserved&& func_reserved,
267 bool is_safe) const { 327 FuncUnmapped&& func_unmapped) const {
328 static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>;
329 static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>;
330 static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>;
331 u64 used_page_size;
332 u64 used_page_mask;
333 u64 used_page_bits;
334 if constexpr (is_big_pages) {
335 used_page_size = big_page_size;
336 used_page_mask = big_page_mask;
337 used_page_bits = big_page_bits;
338 } else {
339 used_page_size = page_size;
340 used_page_mask = page_mask;
341 used_page_bits = page_bits;
342 }
268 std::size_t remaining_size{size}; 343 std::size_t remaining_size{size};
269 std::size_t page_index{gpu_src_addr >> page_bits}; 344 std::size_t page_index{gpu_src_addr >> used_page_bits};
270 std::size_t page_offset{gpu_src_addr & page_mask}; 345 std::size_t page_offset{gpu_src_addr & used_page_mask};
346 GPUVAddr current_address = gpu_src_addr;
271 347
272 while (remaining_size > 0) { 348 while (remaining_size > 0) {
273 const std::size_t copy_amount{ 349 const std::size_t copy_amount{
274 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; 350 std::min(static_cast<std::size_t>(used_page_size) - page_offset, remaining_size)};
275 const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; 351 auto entry = GetEntry<is_big_pages>(current_address);
276 if (page_addr && *page_addr != 0) { 352 if (entry == EntryType::Mapped) [[likely]] {
277 const auto src_addr{*page_addr + page_offset}; 353 if constexpr (BOOL_BREAK_MAPPED) {
278 if (is_safe) { 354 if (func_mapped(page_index, page_offset, copy_amount)) {
279 // Flush must happen on the rasterizer interface, such that memory is always 355 return;
280 // synchronous when it is read (even when in asynchronous GPU mode). 356 }
281 // Fixes Dead Cells title menu. 357 } else {
282 rasterizer->FlushRegion(src_addr, copy_amount); 358 func_mapped(page_index, page_offset, copy_amount);
283 } 359 }
284 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
285 } else {
286 std::memset(dest_buffer, 0, copy_amount);
287 }
288 360
361 } else if (entry == EntryType::Reserved) {
362 if constexpr (BOOL_BREAK_RESERVED) {
363 if (func_reserved(page_index, page_offset, copy_amount)) {
364 return;
365 }
366 } else {
367 func_reserved(page_index, page_offset, copy_amount);
368 }
369
370 } else [[unlikely]] {
371 if constexpr (BOOL_BREAK_UNMAPPED) {
372 if (func_unmapped(page_index, page_offset, copy_amount)) {
373 return;
374 }
375 } else {
376 func_unmapped(page_index, page_offset, copy_amount);
377 }
378 }
289 page_index++; 379 page_index++;
290 page_offset = 0; 380 page_offset = 0;
291 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
292 remaining_size -= copy_amount; 381 remaining_size -= copy_amount;
382 current_address += copy_amount;
293 } 383 }
294} 384}
295 385
386template <bool is_safe>
387void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
388 std::size_t size) const {
389 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
390 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
391 std::memset(dest_buffer, 0, copy_amount);
392 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
393 };
394 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
395 const VAddr cpu_addr_base =
396 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
397 if constexpr (is_safe) {
398 rasterizer->FlushRegion(cpu_addr_base, copy_amount);
399 }
400 u8* physical = memory.GetPointer(cpu_addr_base);
401 std::memcpy(dest_buffer, physical, copy_amount);
402 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
403 };
404 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
405 const VAddr cpu_addr_base =
406 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
407 if constexpr (is_safe) {
408 rasterizer->FlushRegion(cpu_addr_base, copy_amount);
409 }
410 if (!IsBigPageContinous(page_index)) [[unlikely]] {
411 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
412 } else {
413 u8* physical = memory.GetPointer(cpu_addr_base);
414 std::memcpy(dest_buffer, physical, copy_amount);
415 }
416 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
417 };
418 auto read_short_pages = [&](std::size_t page_index, std::size_t offset,
419 std::size_t copy_amount) {
420 GPUVAddr base = (page_index << big_page_bits) + offset;
421 MemoryOperation<false>(base, copy_amount, mapped_normal, set_to_zero, set_to_zero);
422 };
423 MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
424}
425
296void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { 426void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
297 ReadBlockImpl(gpu_src_addr, dest_buffer, size, true); 427 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size);
298} 428}
299 429
300void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 430void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
301 const std::size_t size) const { 431 const std::size_t size) const {
302 ReadBlockImpl(gpu_src_addr, dest_buffer, size, false); 432 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size);
303} 433}
304 434
305void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, 435template <bool is_safe>
306 bool is_safe) { 436void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer,
307 std::size_t remaining_size{size}; 437 std::size_t size) {
308 std::size_t page_index{gpu_dest_addr >> page_bits}; 438 auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
309 std::size_t page_offset{gpu_dest_addr & page_mask}; 439 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
310 440 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
311 while (remaining_size > 0) { 441 };
312 const std::size_t copy_amount{ 442 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
313 std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; 443 const VAddr cpu_addr_base =
314 const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; 444 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
315 if (page_addr && *page_addr != 0) { 445 if constexpr (is_safe) {
316 const auto dest_addr{*page_addr + page_offset}; 446 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
317
318 if (is_safe) {
319 // Invalidate must happen on the rasterizer interface, such that memory is always
320 // synchronous when it is written (even when in asynchronous GPU mode).
321 rasterizer->InvalidateRegion(dest_addr, copy_amount);
322 }
323 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
324 } 447 }
325 448 u8* physical = memory.GetPointer(cpu_addr_base);
326 page_index++; 449 std::memcpy(physical, src_buffer, copy_amount);
327 page_offset = 0;
328 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; 450 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
329 remaining_size -= copy_amount; 451 };
330 } 452 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
453 const VAddr cpu_addr_base =
454 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
455 if constexpr (is_safe) {
456 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
457 }
458 if (!IsBigPageContinous(page_index)) [[unlikely]] {
459 memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
460 } else {
461 u8* physical = memory.GetPointer(cpu_addr_base);
462 std::memcpy(physical, src_buffer, copy_amount);
463 }
464 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
465 };
466 auto write_short_pages = [&](std::size_t page_index, std::size_t offset,
467 std::size_t copy_amount) {
468 GPUVAddr base = (page_index << big_page_bits) + offset;
469 MemoryOperation<false>(base, copy_amount, mapped_normal, just_advance, just_advance);
470 };
471 MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
331} 472}
332 473
333void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { 474void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
334 WriteBlockImpl(gpu_dest_addr, src_buffer, size, true); 475 WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size);
335} 476}
336 477
337void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, 478void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
338 std::size_t size) { 479 std::size_t size) {
339 WriteBlockImpl(gpu_dest_addr, src_buffer, size, false); 480 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size);
340} 481}
341 482
342void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { 483void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
343 size_t remaining_size{size}; 484 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
344 size_t page_index{gpu_addr >> page_bits}; 485 [[maybe_unused]] std::size_t offset,
345 size_t page_offset{gpu_addr & page_mask}; 486 [[maybe_unused]] std::size_t copy_amount) {};
346 while (remaining_size > 0) { 487
347 const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; 488 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
348 if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { 489 const VAddr cpu_addr_base =
349 rasterizer->FlushRegion(*page_addr + page_offset, num_bytes); 490 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
491 rasterizer->FlushRegion(cpu_addr_base, copy_amount);
492 };
493 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
494 const VAddr cpu_addr_base =
495 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
496 rasterizer->FlushRegion(cpu_addr_base, copy_amount);
497 };
498 auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
499 std::size_t copy_amount) {
500 GPUVAddr base = (page_index << big_page_bits) + offset;
501 MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
502 };
503 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
504}
505
506bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const {
507 bool result = false;
508 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
509 [[maybe_unused]] std::size_t offset,
510 [[maybe_unused]] std::size_t copy_amount) { return false; };
511
512 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
513 const VAddr cpu_addr_base =
514 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
515 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount);
516 return result;
517 };
518 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
519 const VAddr cpu_addr_base =
520 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
521 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount);
522 return result;
523 };
524 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
525 std::size_t copy_amount) {
526 GPUVAddr base = (page_index << big_page_bits) + offset;
527 MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
528 return result;
529 };
530 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, check_short_pages);
531 return result;
532}
533
534size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
535 std::optional<VAddr> old_page_addr{};
536 size_t range_so_far = 0;
537 bool result{false};
538 auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
539 std::size_t copy_amount) {
540 result = true;
541 return true;
542 };
543 auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
544 const VAddr cpu_addr_base =
545 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
546 if (old_page_addr && *old_page_addr != cpu_addr_base) {
547 result = true;
548 return true;
350 } 549 }
351 ++page_index; 550 range_so_far += copy_amount;
352 page_offset = 0; 551 old_page_addr = {cpu_addr_base + copy_amount};
353 remaining_size -= num_bytes; 552 return false;
354 } 553 };
554 auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
555 const VAddr cpu_addr_base =
556 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
557 if (old_page_addr && *old_page_addr != cpu_addr_base) {
558 return true;
559 }
560 range_so_far += copy_amount;
561 old_page_addr = {cpu_addr_base + copy_amount};
562 return false;
563 };
564 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
565 std::size_t copy_amount) {
566 GPUVAddr base = (page_index << big_page_bits) + offset;
567 MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
568 return result;
569 };
570 MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
571 return range_so_far;
572}
573
574void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
575 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
576 [[maybe_unused]] std::size_t offset,
577 [[maybe_unused]] std::size_t copy_amount) {};
578
579 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
580 const VAddr cpu_addr_base =
581 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
582 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
583 };
584 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
585 const VAddr cpu_addr_base =
586 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
587 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount);
588 };
589 auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
590 std::size_t copy_amount) {
591 GPUVAddr base = (page_index << big_page_bits) + offset;
592 MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
593 };
594 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
355} 595}
356 596
357void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { 597void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
@@ -365,87 +605,134 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std
365} 605}
366 606
367bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { 607bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
368 const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; 608 if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
369 if (!cpu_addr) { 609 size_t page_index = gpu_addr >> big_page_bits;
610 if (IsBigPageContinous(page_index)) [[likely]] {
611 const std::size_t page{(page_index & big_page_mask) + size};
612 return page <= big_page_size;
613 }
614 const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
615 return page <= Core::Memory::YUZU_PAGESIZE;
616 }
617 if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
370 return false; 618 return false;
371 } 619 }
372 const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; 620 const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
373 return page <= Core::Memory::YUZU_PAGESIZE; 621 return page <= Core::Memory::YUZU_PAGESIZE;
374} 622}
375 623
376bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { 624bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
377 size_t page_index{gpu_addr >> page_bits};
378 const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
379 std::optional<VAddr> old_page_addr{}; 625 std::optional<VAddr> old_page_addr{};
380 while (page_index != page_last) { 626 bool result{true};
381 const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; 627 auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
382 if (!page_addr || *page_addr == 0) { 628 std::size_t copy_amount) {
383 return false; 629 result = false;
630 return true;
631 };
632 auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
633 const VAddr cpu_addr_base =
634 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
635 if (old_page_addr && *old_page_addr != cpu_addr_base) {
636 result = false;
637 return true;
384 } 638 }
385 if (old_page_addr) { 639 old_page_addr = {cpu_addr_base + copy_amount};
386 if (*old_page_addr + page_size != *page_addr) { 640 return false;
387 return false; 641 };
388 } 642 auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
643 const VAddr cpu_addr_base =
644 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
645 if (old_page_addr && *old_page_addr != cpu_addr_base) {
646 result = false;
647 return true;
389 } 648 }
390 old_page_addr = page_addr; 649 old_page_addr = {cpu_addr_base + copy_amount};
391 ++page_index; 650 return false;
392 } 651 };
393 return true; 652 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
653 std::size_t copy_amount) {
654 GPUVAddr base = (page_index << big_page_bits) + offset;
655 MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
656 return !result;
657 };
658 MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
659 return result;
394} 660}
395 661
396bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const { 662bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
397 size_t page_index{gpu_addr >> page_bits}; 663 bool result{true};
398 const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; 664 auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
399 while (page_index < page_last) { 665 [[maybe_unused]] std::size_t copy_amount) {
400 if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) { 666 result = false;
401 return false; 667 return true;
402 } 668 };
403 ++page_index; 669 auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
404 } 670 [[maybe_unused]] std::size_t copy_amount) { return false; };
405 return true; 671 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
672 std::size_t copy_amount) {
673 GPUVAddr base = (page_index << big_page_bits) + offset;
674 MemoryOperation<false>(base, copy_amount, pass, pass, fail);
675 return !result;
676 };
677 MemoryOperation<true>(gpu_addr, size, pass, fail, check_short_pages);
678 return result;
406} 679}
407 680
408std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( 681std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
409 GPUVAddr gpu_addr, std::size_t size) const { 682 GPUVAddr gpu_addr, std::size_t size) const {
410 std::vector<std::pair<GPUVAddr, std::size_t>> result{}; 683 std::vector<std::pair<GPUVAddr, std::size_t>> result{};
411 size_t page_index{gpu_addr >> page_bits};
412 size_t remaining_size{size};
413 size_t page_offset{gpu_addr & page_mask};
414 std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{}; 684 std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
415 std::optional<VAddr> old_page_addr{}; 685 std::optional<VAddr> old_page_addr{};
416 const auto extend_size = [&last_segment, &page_index, &page_offset](std::size_t bytes) { 686 const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
417 if (!last_segment) { 687 [[maybe_unused]] std::size_t offset,
418 const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset; 688 [[maybe_unused]] std::size_t copy_amount) {
419 last_segment = {new_base_addr, bytes};
420 } else {
421 last_segment->second += bytes;
422 }
423 };
424 const auto split = [&last_segment, &result] {
425 if (last_segment) { 689 if (last_segment) {
426 result.push_back(*last_segment); 690 result.push_back(*last_segment);
427 last_segment = std::nullopt; 691 last_segment = std::nullopt;
428 } 692 }
429 }; 693 };
430 while (remaining_size > 0) { 694 const auto extend_size_big = [this, &split, &old_page_addr,
431 const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; 695 &last_segment](std::size_t page_index, std::size_t offset,
432 const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; 696 std::size_t copy_amount) {
433 if (!page_addr || *page_addr == 0) { 697 const VAddr cpu_addr_base =
434 split(); 698 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
435 } else if (old_page_addr) { 699 if (old_page_addr) {
436 if (*old_page_addr + page_size != *page_addr) { 700 if (*old_page_addr != cpu_addr_base) {
437 split(); 701 split(0, 0, 0);
702 }
703 }
704 old_page_addr = {cpu_addr_base + copy_amount};
705 if (!last_segment) {
706 const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset;
707 last_segment = {new_base_addr, copy_amount};
708 } else {
709 last_segment->second += copy_amount;
710 }
711 };
712 const auto extend_size_short = [this, &split, &old_page_addr,
713 &last_segment](std::size_t page_index, std::size_t offset,
714 std::size_t copy_amount) {
715 const VAddr cpu_addr_base =
716 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
717 if (old_page_addr) {
718 if (*old_page_addr != cpu_addr_base) {
719 split(0, 0, 0);
438 } 720 }
439 extend_size(num_bytes); 721 }
722 old_page_addr = {cpu_addr_base + copy_amount};
723 if (!last_segment) {
724 const GPUVAddr new_base_addr = (page_index << page_bits) + offset;
725 last_segment = {new_base_addr, copy_amount};
440 } else { 726 } else {
441 extend_size(num_bytes); 727 last_segment->second += copy_amount;
442 } 728 }
443 ++page_index; 729 };
444 page_offset = 0; 730 auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
445 remaining_size -= num_bytes; 731 GPUVAddr base = (page_index << big_page_bits) + offset;
446 old_page_addr = page_addr; 732 MemoryOperation<false>(base, copy_amount, extend_size_short, split, split);
447 } 733 };
448 split(); 734 MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
735 split(0, 0, 0);
449 return result; 736 return result;
450} 737}
451 738
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 74f9ce175..ab4bc9ec6 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -3,73 +3,40 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <atomic>
6#include <map> 7#include <map>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
9 10
10#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/multi_level_page_table.h"
13#include "common/virtual_buffer.h"
14#include "video_core/pte_kind.h"
11 15
12namespace VideoCore { 16namespace VideoCore {
13class RasterizerInterface; 17class RasterizerInterface;
14} 18}
15 19
16namespace Core { 20namespace Core {
21class DeviceMemory;
22namespace Memory {
23class Memory;
24} // namespace Memory
17class System; 25class System;
18} 26} // namespace Core
19 27
20namespace Tegra { 28namespace Tegra {
21 29
22class PageEntry final {
23public:
24 enum class State : u32 {
25 Unmapped = static_cast<u32>(-1),
26 Allocated = static_cast<u32>(-2),
27 };
28
29 constexpr PageEntry() = default;
30 constexpr PageEntry(State state_) : state{state_} {}
31 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
32
33 [[nodiscard]] constexpr bool IsUnmapped() const {
34 return state == State::Unmapped;
35 }
36
37 [[nodiscard]] constexpr bool IsAllocated() const {
38 return state == State::Allocated;
39 }
40
41 [[nodiscard]] constexpr bool IsValid() const {
42 return !IsUnmapped() && !IsAllocated();
43 }
44
45 [[nodiscard]] constexpr VAddr ToAddress() const {
46 if (!IsValid()) {
47 return {};
48 }
49
50 return static_cast<VAddr>(state) << ShiftBits;
51 }
52
53 [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
54 // If this is a reserved value, offsets do not apply
55 if (!IsValid()) {
56 return *this;
57 }
58 return PageEntry{(static_cast<VAddr>(state) << ShiftBits) + offset};
59 }
60
61private:
62 static constexpr std::size_t ShiftBits{12};
63
64 State state{State::Unmapped};
65};
66static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
67
68class MemoryManager final { 30class MemoryManager final {
69public: 31public:
70 explicit MemoryManager(Core::System& system_); 32 explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40,
33 u64 big_page_bits_ = 16, u64 page_bits_ = 12);
71 ~MemoryManager(); 34 ~MemoryManager();
72 35
36 size_t GetID() const {
37 return unique_identifier;
38 }
39
73 /// Binds a renderer to the memory manager. 40 /// Binds a renderer to the memory manager.
74 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 41 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
75 42
@@ -86,9 +53,6 @@ public:
86 [[nodiscard]] u8* GetPointer(GPUVAddr addr); 53 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
87 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; 54 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
88 55
89 /// Returns the number of bytes until the end of the memory map containing the given GPU address
90 [[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept;
91
92 /** 56 /**
93 * ReadBlock and WriteBlock are full read and write operations over virtual 57 * ReadBlock and WriteBlock are full read and write operations over virtual
94 * GPU Memory. It's important to use these when GPU memory may not be continuous 58 * GPU Memory. It's important to use these when GPU memory may not be continuous
@@ -135,54 +99,109 @@ public:
135 std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, 99 std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr,
136 std::size_t size) const; 100 std::size_t size) const;
137 101
138 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 102 GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size,
139 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 103 PTEKind kind = PTEKind::INVALID, bool is_big_pages = true);
140 [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size); 104 GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true);
141 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
142 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
143 void Unmap(GPUVAddr gpu_addr, std::size_t size); 105 void Unmap(GPUVAddr gpu_addr, std::size_t size);
144 106
145 void FlushRegion(GPUVAddr gpu_addr, size_t size) const; 107 void FlushRegion(GPUVAddr gpu_addr, size_t size) const;
146 108
109 void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const;
110
111 bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const;
112
113 size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const;
114
115 bool IsWithinGPUAddressRange(GPUVAddr gpu_addr) const {
116 return gpu_addr < address_space_size;
117 }
118
119 PTEKind GetPageKind(GPUVAddr gpu_addr) const;
120
147private: 121private:
148 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 122 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
149 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 123 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
150 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 124 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
151 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align, 125
152 bool start_32bit_address = false) const; 126 template <bool is_safe>
153 127 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
154 void TryLockPage(PageEntry page_entry, std::size_t size); 128
155 void TryUnlockPage(PageEntry page_entry, std::size_t size); 129 template <bool is_safe>
156 130 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
157 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 131
158 bool is_safe) const; 132 template <bool is_big_page>
159 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, 133 [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
160 bool is_safe); 134 if constexpr (is_big_page) {
161 135 return (gpu_addr >> big_page_bits) & big_page_table_mask;
162 [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { 136 } else {
163 return (gpu_addr >> page_bits) & page_table_mask; 137 return (gpu_addr >> page_bits) & page_table_mask;
138 }
164 } 139 }
165 140
166 static constexpr u64 address_space_size = 1ULL << 40; 141 inline bool IsBigPageContinous(size_t big_page_index) const;
167 static constexpr u64 address_space_start = 1ULL << 32; 142 inline void SetBigPageContinous(size_t big_page_index, bool value);
168 static constexpr u64 address_space_start_low = 1ULL << 16;
169 static constexpr u64 page_bits{16};
170 static constexpr u64 page_size{1 << page_bits};
171 static constexpr u64 page_mask{page_size - 1};
172 static constexpr u64 page_table_bits{24};
173 static constexpr u64 page_table_size{1 << page_table_bits};
174 static constexpr u64 page_table_mask{page_table_size - 1};
175 143
176 Core::System& system; 144 Core::System& system;
145 Core::Memory::Memory& memory;
146 Core::DeviceMemory& device_memory;
147
148 const u64 address_space_bits;
149 const u64 page_bits;
150 u64 address_space_size;
151 u64 page_size;
152 u64 page_mask;
153 u64 page_table_mask;
154 static constexpr u64 cpu_page_bits{12};
155
156 const u64 big_page_bits;
157 u64 big_page_size;
158 u64 big_page_mask;
159 u64 big_page_table_mask;
177 160
178 VideoCore::RasterizerInterface* rasterizer = nullptr; 161 VideoCore::RasterizerInterface* rasterizer = nullptr;
179 162
180 std::vector<PageEntry> page_table; 163 enum class EntryType : u64 {
164 Free = 0,
165 Reserved = 1,
166 Mapped = 2,
167 };
168
169 std::vector<u64> entries;
170 std::vector<u64> big_entries;
171
172 template <EntryType entry_type>
173 GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size,
174 PTEKind kind);
175
176 template <EntryType entry_type>
177 GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size,
178 PTEKind kind);
179
180 template <bool is_big_page>
181 inline EntryType GetEntry(size_t position) const;
182
183 template <bool is_big_page>
184 inline void SetEntry(size_t position, EntryType entry);
185
186 std::vector<std::array<PTEKind, 32>> kinds;
187 std::vector<std::array<PTEKind, 32>> big_kinds;
188
189 template <bool is_big_page>
190 inline PTEKind GetKind(size_t position) const;
191
192 template <bool is_big_page>
193 inline void SetKind(size_t position, PTEKind kind);
194
195 Common::MultiLevelPageTable<u32> page_table;
196 Common::VirtualBuffer<u32> big_page_table_cpu;
197
198 std::vector<u64> big_page_continous;
199
200 constexpr static size_t continous_bits = 64;
181 201
182 using MapRange = std::pair<GPUVAddr, size_t>; 202 const size_t unique_identifier;
183 std::vector<MapRange> map_ranges;
184 203
185 std::vector<std::pair<VAddr, std::size_t>> cache_invalidate_queue; 204 static std::atomic<size_t> unique_identifier_generator;
186}; 205};
187 206
188} // namespace Tegra 207} // namespace Tegra
diff --git a/src/video_core/pte_kind.h b/src/video_core/pte_kind.h
new file mode 100644
index 000000000..591d7214b
--- /dev/null
+++ b/src/video_core/pte_kind.h
@@ -0,0 +1,264 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Tegra {
9
10// https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/volta/gv100/dev_mmu.ref.txt
11enum class PTEKind : u8 {
12 INVALID = 0xff,
13 PITCH = 0x00,
14 Z16 = 0x01,
15 Z16_2C = 0x02,
16 Z16_MS2_2C = 0x03,
17 Z16_MS4_2C = 0x04,
18 Z16_MS8_2C = 0x05,
19 Z16_MS16_2C = 0x06,
20 Z16_2Z = 0x07,
21 Z16_MS2_2Z = 0x08,
22 Z16_MS4_2Z = 0x09,
23 Z16_MS8_2Z = 0x0a,
24 Z16_MS16_2Z = 0x0b,
25 Z16_2CZ = 0x36,
26 Z16_MS2_2CZ = 0x37,
27 Z16_MS4_2CZ = 0x38,
28 Z16_MS8_2CZ = 0x39,
29 Z16_MS16_2CZ = 0x5f,
30 Z16_4CZ = 0x0c,
31 Z16_MS2_4CZ = 0x0d,
32 Z16_MS4_4CZ = 0x0e,
33 Z16_MS8_4CZ = 0x0f,
34 Z16_MS16_4CZ = 0x10,
35 S8Z24 = 0x11,
36 S8Z24_1Z = 0x12,
37 S8Z24_MS2_1Z = 0x13,
38 S8Z24_MS4_1Z = 0x14,
39 S8Z24_MS8_1Z = 0x15,
40 S8Z24_MS16_1Z = 0x16,
41 S8Z24_2CZ = 0x17,
42 S8Z24_MS2_2CZ = 0x18,
43 S8Z24_MS4_2CZ = 0x19,
44 S8Z24_MS8_2CZ = 0x1a,
45 S8Z24_MS16_2CZ = 0x1b,
46 S8Z24_2CS = 0x1c,
47 S8Z24_MS2_2CS = 0x1d,
48 S8Z24_MS4_2CS = 0x1e,
49 S8Z24_MS8_2CS = 0x1f,
50 S8Z24_MS16_2CS = 0x20,
51 S8Z24_4CSZV = 0x21,
52 S8Z24_MS2_4CSZV = 0x22,
53 S8Z24_MS4_4CSZV = 0x23,
54 S8Z24_MS8_4CSZV = 0x24,
55 S8Z24_MS16_4CSZV = 0x25,
56 V8Z24_MS4_VC12 = 0x26,
57 V8Z24_MS4_VC4 = 0x27,
58 V8Z24_MS8_VC8 = 0x28,
59 V8Z24_MS8_VC24 = 0x29,
60 V8Z24_MS4_VC12_1ZV = 0x2e,
61 V8Z24_MS4_VC4_1ZV = 0x2f,
62 V8Z24_MS8_VC8_1ZV = 0x30,
63 V8Z24_MS8_VC24_1ZV = 0x31,
64 V8Z24_MS4_VC12_2CS = 0x32,
65 V8Z24_MS4_VC4_2CS = 0x33,
66 V8Z24_MS8_VC8_2CS = 0x34,
67 V8Z24_MS8_VC24_2CS = 0x35,
68 V8Z24_MS4_VC12_2CZV = 0x3a,
69 V8Z24_MS4_VC4_2CZV = 0x3b,
70 V8Z24_MS8_VC8_2CZV = 0x3c,
71 V8Z24_MS8_VC24_2CZV = 0x3d,
72 V8Z24_MS4_VC12_2ZV = 0x3e,
73 V8Z24_MS4_VC4_2ZV = 0x3f,
74 V8Z24_MS8_VC8_2ZV = 0x40,
75 V8Z24_MS8_VC24_2ZV = 0x41,
76 V8Z24_MS4_VC12_4CSZV = 0x42,
77 V8Z24_MS4_VC4_4CSZV = 0x43,
78 V8Z24_MS8_VC8_4CSZV = 0x44,
79 V8Z24_MS8_VC24_4CSZV = 0x45,
80 Z24S8 = 0x46,
81 Z24S8_1Z = 0x47,
82 Z24S8_MS2_1Z = 0x48,
83 Z24S8_MS4_1Z = 0x49,
84 Z24S8_MS8_1Z = 0x4a,
85 Z24S8_MS16_1Z = 0x4b,
86 Z24S8_2CS = 0x4c,
87 Z24S8_MS2_2CS = 0x4d,
88 Z24S8_MS4_2CS = 0x4e,
89 Z24S8_MS8_2CS = 0x4f,
90 Z24S8_MS16_2CS = 0x50,
91 Z24S8_2CZ = 0x51,
92 Z24S8_MS2_2CZ = 0x52,
93 Z24S8_MS4_2CZ = 0x53,
94 Z24S8_MS8_2CZ = 0x54,
95 Z24S8_MS16_2CZ = 0x55,
96 Z24S8_4CSZV = 0x56,
97 Z24S8_MS2_4CSZV = 0x57,
98 Z24S8_MS4_4CSZV = 0x58,
99 Z24S8_MS8_4CSZV = 0x59,
100 Z24S8_MS16_4CSZV = 0x5a,
101 Z24V8_MS4_VC12 = 0x5b,
102 Z24V8_MS4_VC4 = 0x5c,
103 Z24V8_MS8_VC8 = 0x5d,
104 Z24V8_MS8_VC24 = 0x5e,
105 YUV_B8C1_2Y = 0x60,
106 YUV_B8C2_2Y = 0x61,
107 YUV_B10C1_2Y = 0x62,
108 YUV_B10C2_2Y = 0x6b,
109 YUV_B12C1_2Y = 0x6c,
110 YUV_B12C2_2Y = 0x6d,
111 Z24V8_MS4_VC12_1ZV = 0x63,
112 Z24V8_MS4_VC4_1ZV = 0x64,
113 Z24V8_MS8_VC8_1ZV = 0x65,
114 Z24V8_MS8_VC24_1ZV = 0x66,
115 Z24V8_MS4_VC12_2CS = 0x67,
116 Z24V8_MS4_VC4_2CS = 0x68,
117 Z24V8_MS8_VC8_2CS = 0x69,
118 Z24V8_MS8_VC24_2CS = 0x6a,
119 Z24V8_MS4_VC12_2CZV = 0x6f,
120 Z24V8_MS4_VC4_2CZV = 0x70,
121 Z24V8_MS8_VC8_2CZV = 0x71,
122 Z24V8_MS8_VC24_2CZV = 0x72,
123 Z24V8_MS4_VC12_2ZV = 0x73,
124 Z24V8_MS4_VC4_2ZV = 0x74,
125 Z24V8_MS8_VC8_2ZV = 0x75,
126 Z24V8_MS8_VC24_2ZV = 0x76,
127 Z24V8_MS4_VC12_4CSZV = 0x77,
128 Z24V8_MS4_VC4_4CSZV = 0x78,
129 Z24V8_MS8_VC8_4CSZV = 0x79,
130 Z24V8_MS8_VC24_4CSZV = 0x7a,
131 ZF32 = 0x7b,
132 ZF32_1Z = 0x7c,
133 ZF32_MS2_1Z = 0x7d,
134 ZF32_MS4_1Z = 0x7e,
135 ZF32_MS8_1Z = 0x7f,
136 ZF32_MS16_1Z = 0x80,
137 ZF32_2CS = 0x81,
138 ZF32_MS2_2CS = 0x82,
139 ZF32_MS4_2CS = 0x83,
140 ZF32_MS8_2CS = 0x84,
141 ZF32_MS16_2CS = 0x85,
142 ZF32_2CZ = 0x86,
143 ZF32_MS2_2CZ = 0x87,
144 ZF32_MS4_2CZ = 0x88,
145 ZF32_MS8_2CZ = 0x89,
146 ZF32_MS16_2CZ = 0x8a,
147 X8Z24_X16V8S8_MS4_VC12 = 0x8b,
148 X8Z24_X16V8S8_MS4_VC4 = 0x8c,
149 X8Z24_X16V8S8_MS8_VC8 = 0x8d,
150 X8Z24_X16V8S8_MS8_VC24 = 0x8e,
151 X8Z24_X16V8S8_MS4_VC12_1CS = 0x8f,
152 X8Z24_X16V8S8_MS4_VC4_1CS = 0x90,
153 X8Z24_X16V8S8_MS8_VC8_1CS = 0x91,
154 X8Z24_X16V8S8_MS8_VC24_1CS = 0x92,
155 X8Z24_X16V8S8_MS4_VC12_1ZV = 0x97,
156 X8Z24_X16V8S8_MS4_VC4_1ZV = 0x98,
157 X8Z24_X16V8S8_MS8_VC8_1ZV = 0x99,
158 X8Z24_X16V8S8_MS8_VC24_1ZV = 0x9a,
159 X8Z24_X16V8S8_MS4_VC12_1CZV = 0x9b,
160 X8Z24_X16V8S8_MS4_VC4_1CZV = 0x9c,
161 X8Z24_X16V8S8_MS8_VC8_1CZV = 0x9d,
162 X8Z24_X16V8S8_MS8_VC24_1CZV = 0x9e,
163 X8Z24_X16V8S8_MS4_VC12_2CS = 0x9f,
164 X8Z24_X16V8S8_MS4_VC4_2CS = 0xa0,
165 X8Z24_X16V8S8_MS8_VC8_2CS = 0xa1,
166 X8Z24_X16V8S8_MS8_VC24_2CS = 0xa2,
167 X8Z24_X16V8S8_MS4_VC12_2CSZV = 0xa3,
168 X8Z24_X16V8S8_MS4_VC4_2CSZV = 0xa4,
169 X8Z24_X16V8S8_MS8_VC8_2CSZV = 0xa5,
170 X8Z24_X16V8S8_MS8_VC24_2CSZV = 0xa6,
171 ZF32_X16V8S8_MS4_VC12 = 0xa7,
172 ZF32_X16V8S8_MS4_VC4 = 0xa8,
173 ZF32_X16V8S8_MS8_VC8 = 0xa9,
174 ZF32_X16V8S8_MS8_VC24 = 0xaa,
175 ZF32_X16V8S8_MS4_VC12_1CS = 0xab,
176 ZF32_X16V8S8_MS4_VC4_1CS = 0xac,
177 ZF32_X16V8S8_MS8_VC8_1CS = 0xad,
178 ZF32_X16V8S8_MS8_VC24_1CS = 0xae,
179 ZF32_X16V8S8_MS4_VC12_1ZV = 0xb3,
180 ZF32_X16V8S8_MS4_VC4_1ZV = 0xb4,
181 ZF32_X16V8S8_MS8_VC8_1ZV = 0xb5,
182 ZF32_X16V8S8_MS8_VC24_1ZV = 0xb6,
183 ZF32_X16V8S8_MS4_VC12_1CZV = 0xb7,
184 ZF32_X16V8S8_MS4_VC4_1CZV = 0xb8,
185 ZF32_X16V8S8_MS8_VC8_1CZV = 0xb9,
186 ZF32_X16V8S8_MS8_VC24_1CZV = 0xba,
187 ZF32_X16V8S8_MS4_VC12_2CS = 0xbb,
188 ZF32_X16V8S8_MS4_VC4_2CS = 0xbc,
189 ZF32_X16V8S8_MS8_VC8_2CS = 0xbd,
190 ZF32_X16V8S8_MS8_VC24_2CS = 0xbe,
191 ZF32_X16V8S8_MS4_VC12_2CSZV = 0xbf,
192 ZF32_X16V8S8_MS4_VC4_2CSZV = 0xc0,
193 ZF32_X16V8S8_MS8_VC8_2CSZV = 0xc1,
194 ZF32_X16V8S8_MS8_VC24_2CSZV = 0xc2,
195 ZF32_X24S8 = 0xc3,
196 ZF32_X24S8_1CS = 0xc4,
197 ZF32_X24S8_MS2_1CS = 0xc5,
198 ZF32_X24S8_MS4_1CS = 0xc6,
199 ZF32_X24S8_MS8_1CS = 0xc7,
200 ZF32_X24S8_MS16_1CS = 0xc8,
201 ZF32_X24S8_2CSZV = 0xce,
202 ZF32_X24S8_MS2_2CSZV = 0xcf,
203 ZF32_X24S8_MS4_2CSZV = 0xd0,
204 ZF32_X24S8_MS8_2CSZV = 0xd1,
205 ZF32_X24S8_MS16_2CSZV = 0xd2,
206 ZF32_X24S8_2CS = 0xd3,
207 ZF32_X24S8_MS2_2CS = 0xd4,
208 ZF32_X24S8_MS4_2CS = 0xd5,
209 ZF32_X24S8_MS8_2CS = 0xd6,
210 ZF32_X24S8_MS16_2CS = 0xd7,
211 S8 = 0x2a,
212 S8_2S = 0x2b,
213 GENERIC_16BX2 = 0xfe,
214 C32_2C = 0xd8,
215 C32_2CBR = 0xd9,
216 C32_2CBA = 0xda,
217 C32_2CRA = 0xdb,
218 C32_2BRA = 0xdc,
219 C32_MS2_2C = 0xdd,
220 C32_MS2_2CBR = 0xde,
221 C32_MS2_4CBRA = 0xcc,
222 C32_MS4_2C = 0xdf,
223 C32_MS4_2CBR = 0xe0,
224 C32_MS4_2CBA = 0xe1,
225 C32_MS4_2CRA = 0xe2,
226 C32_MS4_2BRA = 0xe3,
227 C32_MS4_4CBRA = 0x2c,
228 C32_MS8_MS16_2C = 0xe4,
229 C32_MS8_MS16_2CRA = 0xe5,
230 C64_2C = 0xe6,
231 C64_2CBR = 0xe7,
232 C64_2CBA = 0xe8,
233 C64_2CRA = 0xe9,
234 C64_2BRA = 0xea,
235 C64_MS2_2C = 0xeb,
236 C64_MS2_2CBR = 0xec,
237 C64_MS2_4CBRA = 0xcd,
238 C64_MS4_2C = 0xed,
239 C64_MS4_2CBR = 0xee,
240 C64_MS4_2CBA = 0xef,
241 C64_MS4_2CRA = 0xf0,
242 C64_MS4_2BRA = 0xf1,
243 C64_MS4_4CBRA = 0x2d,
244 C64_MS8_MS16_2C = 0xf2,
245 C64_MS8_MS16_2CRA = 0xf3,
246 C128_2C = 0xf4,
247 C128_2CR = 0xf5,
248 C128_MS2_2C = 0xf6,
249 C128_MS2_2CR = 0xf7,
250 C128_MS4_2C = 0xf8,
251 C128_MS4_2CR = 0xf9,
252 C128_MS8_MS16_2C = 0xfa,
253 C128_MS8_MS16_2CR = 0xfb,
254 X8C24 = 0xfc,
255 PITCH_NO_SWIZZLE = 0xfd,
256 SMSKED_MESSAGE = 0xca,
257 SMHOST_MESSAGE = 0xcb,
258};
259
260constexpr bool IsPitchKind(PTEKind kind) {
261 return kind == PTEKind::PITCH || kind == PTEKind::PITCH_NO_SWIZZLE;
262}
263
264} // namespace Tegra
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 889b606b3..00ce53e3e 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -17,6 +17,7 @@
17 17
18#include "common/assert.h" 18#include "common/assert.h"
19#include "common/settings.h" 19#include "common/settings.h"
20#include "video_core/control/channel_state_cache.h"
20#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
21#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
22#include "video_core/rasterizer_interface.h" 23#include "video_core/rasterizer_interface.h"
@@ -90,13 +91,10 @@ private:
90}; 91};
91 92
92template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> 93template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
93class QueryCacheBase { 94class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
94public: 95public:
95 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, 96 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_)
96 Tegra::Engines::Maxwell3D& maxwell3d_, 97 : rasterizer{rasterizer_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
97 Tegra::MemoryManager& gpu_memory_)
98 : rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
99 gpu_memory{gpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
100 VideoCore::QueryType::SamplesPassed}}} {} 98 VideoCore::QueryType::SamplesPassed}}} {}
101 99
102 void InvalidateRegion(VAddr addr, std::size_t size) { 100 void InvalidateRegion(VAddr addr, std::size_t size) {
@@ -117,13 +115,13 @@ public:
117 */ 115 */
118 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) { 116 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) {
119 std::unique_lock lock{mutex}; 117 std::unique_lock lock{mutex};
120 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 118 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
121 ASSERT(cpu_addr); 119 ASSERT(cpu_addr);
122 120
123 CachedQuery* query = TryGet(*cpu_addr); 121 CachedQuery* query = TryGet(*cpu_addr);
124 if (!query) { 122 if (!query) {
125 ASSERT_OR_EXECUTE(cpu_addr, return;); 123 ASSERT_OR_EXECUTE(cpu_addr, return;);
126 u8* const host_ptr = gpu_memory.GetPointer(gpu_addr); 124 u8* const host_ptr = gpu_memory->GetPointer(gpu_addr);
127 125
128 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); 126 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
129 } 127 }
@@ -137,8 +135,10 @@ public:
137 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 135 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
138 void UpdateCounters() { 136 void UpdateCounters() {
139 std::unique_lock lock{mutex}; 137 std::unique_lock lock{mutex};
140 const auto& regs = maxwell3d.regs; 138 if (maxwell3d) {
141 Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); 139 const auto& regs = maxwell3d->regs;
140 Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable);
141 }
142 } 142 }
143 143
144 /// Resets a counter to zero. It doesn't disable the query after resetting. 144 /// Resets a counter to zero. It doesn't disable the query after resetting.
@@ -264,8 +264,6 @@ private:
264 static constexpr unsigned YUZU_PAGEBITS = 12; 264 static constexpr unsigned YUZU_PAGEBITS = 12;
265 265
266 VideoCore::RasterizerInterface& rasterizer; 266 VideoCore::RasterizerInterface& rasterizer;
267 Tegra::Engines::Maxwell3D& maxwell3d;
268 Tegra::MemoryManager& gpu_memory;
269 267
270 std::recursive_mutex mutex; 268 std::recursive_mutex mutex;
271 269
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index a04a76481..1cbfef090 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -16,6 +16,9 @@ class MemoryManager;
16namespace Engines { 16namespace Engines {
17class AccelerateDMAInterface; 17class AccelerateDMAInterface;
18} 18}
19namespace Control {
20struct ChannelState;
21}
19} // namespace Tegra 22} // namespace Tegra
20 23
21namespace VideoCore { 24namespace VideoCore {
@@ -37,7 +40,7 @@ public:
37 virtual ~RasterizerInterface() = default; 40 virtual ~RasterizerInterface() = default;
38 41
39 /// Dispatches a draw invocation 42 /// Dispatches a draw invocation
40 virtual void Draw(bool is_indexed, bool is_instanced) = 0; 43 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
41 44
42 /// Clear the current framebuffer 45 /// Clear the current framebuffer
43 virtual void Clear() = 0; 46 virtual void Clear() = 0;
@@ -59,7 +62,10 @@ public:
59 virtual void DisableGraphicsUniformBuffer(size_t stage, u32 index) = 0; 62 virtual void DisableGraphicsUniformBuffer(size_t stage, u32 index) = 0;
60 63
61 /// Signal a GPU based semaphore as a fence 64 /// Signal a GPU based semaphore as a fence
62 virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0; 65 virtual void SignalFence(std::function<void()>&& func) = 0;
66
67 /// Send an operation to be done after a certain amount of flushes.
68 virtual void SyncOperation(std::function<void()>&& func) = 0;
63 69
64 /// Signal a GPU based syncpoint as a fence 70 /// Signal a GPU based syncpoint as a fence
65 virtual void SignalSyncPoint(u32 value) = 0; 71 virtual void SignalSyncPoint(u32 value) = 0;
@@ -86,13 +92,13 @@ public:
86 virtual void OnCPUWrite(VAddr addr, u64 size) = 0; 92 virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
87 93
88 /// Sync memory between guest and host. 94 /// Sync memory between guest and host.
89 virtual void SyncGuestHost() = 0; 95 virtual void InvalidateGPUCache() = 0;
90 96
91 /// Unmap memory range 97 /// Unmap memory range
92 virtual void UnmapMemory(VAddr addr, u64 size) = 0; 98 virtual void UnmapMemory(VAddr addr, u64 size) = 0;
93 99
94 /// Remap GPU memory range. This means underneath backing memory changed 100 /// Remap GPU memory range. This means underneath backing memory changed
95 virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0; 101 virtual void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) = 0;
96 102
97 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 103 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
98 /// and invalidated 104 /// and invalidated
@@ -123,7 +129,7 @@ public:
123 [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; 129 [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0;
124 130
125 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 131 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
126 std::span<u8> memory) = 0; 132 std::span<const u8> memory) = 0;
127 133
128 /// Attempt to use a faster method to display the framebuffer to screen 134 /// Attempt to use a faster method to display the framebuffer to screen
129 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, 135 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
@@ -137,5 +143,11 @@ public:
137 /// Initialize disk cached resources for the game being emulated 143 /// Initialize disk cached resources for the game being emulated
138 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 144 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
139 const DiskResourceLoadCallback& callback) {} 145 const DiskResourceLoadCallback& callback) {}
146
147 virtual void InitializeChannel(Tegra::Control::ChannelState& channel) {}
148
149 virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
150
151 virtual void ReleaseChannel(s32 channel_id) {}
140}; 152};
141} // namespace VideoCore 153} // namespace VideoCore
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 45791aa75..e8761a747 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <thread>
5
4#include "common/logging/log.h" 6#include "common/logging/log.h"
5#include "core/frontend/emu_window.h" 7#include "core/frontend/emu_window.h"
6#include "video_core/renderer_base.h" 8#include "video_core/renderer_base.h"
@@ -35,8 +37,12 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb
35 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); 37 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
36 return; 38 return;
37 } 39 }
40 auto async_callback{[callback = std::move(callback)](bool invert_y) {
41 std::thread t{callback, invert_y};
42 t.detach();
43 }};
38 renderer_settings.screenshot_bits = data; 44 renderer_settings.screenshot_bits = data;
39 renderer_settings.screenshot_complete_callback = std::move(callback); 45 renderer_settings.screenshot_complete_callback = async_callback;
40 renderer_settings.screenshot_framebuffer_layout = layout; 46 renderer_settings.screenshot_framebuffer_layout = layout;
41 renderer_settings.screenshot_requested = true; 47 renderer_settings.screenshot_requested = true;
42} 48}
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 32450ee1d..08f4d69ab 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -168,7 +168,7 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
168 if (has_unified_vertex_buffers) { 168 if (has_unified_vertex_buffers) {
169 buffer.MakeResident(GL_READ_ONLY); 169 buffer.MakeResident(GL_READ_ONLY);
170 glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset, 170 glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset,
171 static_cast<GLsizeiptr>(size)); 171 static_cast<GLsizeiptr>(Common::AlignUp(size, 4)));
172 } else { 172 } else {
173 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); 173 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
174 index_buffer_offset = offset; 174 index_buffer_offset = offset;
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 1f0f156ed..26d066004 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -28,12 +28,11 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep
28} 28}
29 29
30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, 30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_,
31 BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, 31 BufferCache& buffer_cache_, ProgramManager& program_manager_,
32 Tegra::Engines::KeplerCompute& kepler_compute_, 32 const Shader::Info& info_, std::string code,
33 ProgramManager& program_manager_, const Shader::Info& info_, 33 std::vector<u32> code_v)
34 std::string code, std::vector<u32> code_v) 34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
35 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, gpu_memory{gpu_memory_}, 35 program_manager{program_manager_}, info{info_} {
36 kepler_compute{kepler_compute_}, program_manager{program_manager_}, info{info_} {
37 switch (device.GetShaderBackend()) { 36 switch (device.GetShaderBackend()) {
38 case Settings::ShaderBackend::GLSL: 37 case Settings::ShaderBackend::GLSL:
39 source_program = CreateProgram(code, GL_COMPUTE_SHADER); 38 source_program = CreateProgram(code, GL_COMPUTE_SHADER);
@@ -86,7 +85,7 @@ void ComputePipeline::Configure() {
86 GLsizei texture_binding{}; 85 GLsizei texture_binding{};
87 GLsizei image_binding{}; 86 GLsizei image_binding{};
88 87
89 const auto& qmd{kepler_compute.launch_description}; 88 const auto& qmd{kepler_compute->launch_description};
90 const auto& cbufs{qmd.const_buffer_config}; 89 const auto& cbufs{qmd.const_buffer_config};
91 const bool via_header_index{qmd.linked_tsc != 0}; 90 const bool via_header_index{qmd.linked_tsc != 0};
92 const auto read_handle{[&](const auto& desc, u32 index) { 91 const auto read_handle{[&](const auto& desc, u32 index) {
@@ -101,12 +100,13 @@ void ComputePipeline::Configure() {
101 const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; 100 const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset};
102 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + 101 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() +
103 secondary_offset}; 102 secondary_offset};
104 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 103 const u32 lhs_raw{gpu_memory->Read<u32>(addr) << desc.shift_left};
105 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 104 const u32 rhs_raw{gpu_memory->Read<u32>(separate_addr)
105 << desc.secondary_shift_left};
106 return TexturePair(lhs_raw | rhs_raw, via_header_index); 106 return TexturePair(lhs_raw | rhs_raw, via_header_index);
107 } 107 }
108 } 108 }
109 return TexturePair(gpu_memory.Read<u32>(addr), via_header_index); 109 return TexturePair(gpu_memory->Read<u32>(addr), via_header_index);
110 }}; 110 }};
111 const auto add_image{[&](const auto& desc, bool blacklist) { 111 const auto add_image{[&](const auto& desc, bool blacklist) {
112 for (u32 index = 0; index < desc.count; ++index) { 112 for (u32 index = 0; index < desc.count; ++index) {
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 723f27f11..6534dec32 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -49,10 +49,8 @@ static_assert(std::is_trivially_constructible_v<ComputePipelineKey>);
49class ComputePipeline { 49class ComputePipeline {
50public: 50public:
51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, 51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_,
52 BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, 52 BufferCache& buffer_cache_, ProgramManager& program_manager_,
53 Tegra::Engines::KeplerCompute& kepler_compute_, 53 const Shader::Info& info_, std::string code, std::vector<u32> code_v);
54 ProgramManager& program_manager_, const Shader::Info& info_,
55 std::string code, std::vector<u32> code_v);
56 54
57 void Configure(); 55 void Configure();
58 56
@@ -60,11 +58,17 @@ public:
60 return writes_global_memory; 58 return writes_global_memory;
61 } 59 }
62 60
61 void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_,
62 Tegra::MemoryManager* gpu_memory_) {
63 kepler_compute = kepler_compute_;
64 gpu_memory = gpu_memory_;
65 }
66
63private: 67private:
64 TextureCache& texture_cache; 68 TextureCache& texture_cache;
65 BufferCache& buffer_cache; 69 BufferCache& buffer_cache;
66 Tegra::MemoryManager& gpu_memory; 70 Tegra::MemoryManager* gpu_memory;
67 Tegra::Engines::KeplerCompute& kepler_compute; 71 Tegra::Engines::KeplerCompute* kepler_compute;
68 ProgramManager& program_manager; 72 ProgramManager& program_manager;
69 73
70 Shader::Info info; 74 Shader::Info info;
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 6e82c2e28..91463f854 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -10,10 +10,7 @@
10 10
11namespace OpenGL { 11namespace OpenGL {
12 12
13GLInnerFence::GLInnerFence(u32 payload_, bool is_stubbed_) : FenceBase{payload_, is_stubbed_} {} 13GLInnerFence::GLInnerFence(bool is_stubbed_) : FenceBase{is_stubbed_} {}
14
15GLInnerFence::GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_)
16 : FenceBase{address_, payload_, is_stubbed_} {}
17 14
18GLInnerFence::~GLInnerFence() = default; 15GLInnerFence::~GLInnerFence() = default;
19 16
@@ -48,12 +45,8 @@ FenceManagerOpenGL::FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterize
48 BufferCache& buffer_cache_, QueryCache& query_cache_) 45 BufferCache& buffer_cache_, QueryCache& query_cache_)
49 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_} {} 46 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_} {}
50 47
51Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { 48Fence FenceManagerOpenGL::CreateFence(bool is_stubbed) {
52 return std::make_shared<GLInnerFence>(value, is_stubbed); 49 return std::make_shared<GLInnerFence>(is_stubbed);
53}
54
55Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
56 return std::make_shared<GLInnerFence>(addr, value, is_stubbed);
57} 50}
58 51
59void FenceManagerOpenGL::QueueFence(Fence& fence) { 52void FenceManagerOpenGL::QueueFence(Fence& fence) {
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index 14ff00db2..f1446e732 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -16,8 +16,7 @@ namespace OpenGL {
16 16
17class GLInnerFence : public VideoCommon::FenceBase { 17class GLInnerFence : public VideoCommon::FenceBase {
18public: 18public:
19 explicit GLInnerFence(u32 payload_, bool is_stubbed_); 19 explicit GLInnerFence(bool is_stubbed_);
20 explicit GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_);
21 ~GLInnerFence(); 20 ~GLInnerFence();
22 21
23 void Queue(); 22 void Queue();
@@ -40,8 +39,7 @@ public:
40 QueryCache& query_cache); 39 QueryCache& query_cache);
41 40
42protected: 41protected:
43 Fence CreateFence(u32 value, bool is_stubbed) override; 42 Fence CreateFence(bool is_stubbed) override;
44 Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
45 void QueueFence(Fence& fence) override; 43 void QueueFence(Fence& fence) override;
46 bool IsFenceSignaled(Fence& fence) const override; 44 bool IsFenceSignaled(Fence& fence) const override;
47 void WaitFence(Fence& fence) override; 45 void WaitFence(Fence& fence) override;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c297f7121..daceb05f4 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -73,8 +73,8 @@ GLenum AssemblyStage(size_t stage_index) {
73/// @param location Hardware location 73/// @param location Hardware location
74/// @return Pair of ARB_transform_feedback3 token stream first and third arguments 74/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
75/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt 75/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
76std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) { 76std::pair<GLint, GLint> TransformFeedbackEnum(u32 location) {
77 const u8 index = location / 4; 77 const auto index = location / 4;
78 if (index >= 8 && index <= 39) { 78 if (index >= 8 && index <= 39) {
79 return {GL_GENERIC_ATTRIB_NV, index - 8}; 79 return {GL_GENERIC_ATTRIB_NV, index - 8};
80 } 80 }
@@ -169,15 +169,15 @@ ConfigureFuncPtr ConfigureFunc(const std::array<Shader::Info, 5>& infos, u32 ena
169} 169}
170} // Anonymous namespace 170} // Anonymous namespace
171 171
172GraphicsPipeline::GraphicsPipeline( 172GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_cache_,
173 const Device& device, TextureCache& texture_cache_, BufferCache& buffer_cache_, 173 BufferCache& buffer_cache_, ProgramManager& program_manager_,
174 Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, 174 StateTracker& state_tracker_, ShaderWorker* thread_worker,
175 ProgramManager& program_manager_, StateTracker& state_tracker_, ShaderWorker* thread_worker, 175 VideoCore::ShaderNotify* shader_notify,
176 VideoCore::ShaderNotify* shader_notify, std::array<std::string, 5> sources, 176 std::array<std::string, 5> sources,
177 std::array<std::vector<u32>, 5> sources_spirv, const std::array<const Shader::Info*, 5>& infos, 177 std::array<std::vector<u32>, 5> sources_spirv,
178 const GraphicsPipelineKey& key_) 178 const std::array<const Shader::Info*, 5>& infos,
179 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 179 const GraphicsPipelineKey& key_)
180 gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_}, 180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
181 state_tracker{state_tracker_}, key{key_} { 181 state_tracker{state_tracker_}, key{key_} {
182 if (shader_notify) { 182 if (shader_notify) {
183 shader_notify->MarkShaderBuilding(); 183 shader_notify->MarkShaderBuilding();
@@ -285,8 +285,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
285 buffer_cache.runtime.SetBaseStorageBindings(base_storage_bindings); 285 buffer_cache.runtime.SetBaseStorageBindings(base_storage_bindings);
286 buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers); 286 buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers);
287 287
288 const auto& regs{maxwell3d.regs}; 288 const auto& regs{maxwell3d->regs};
289 const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; 289 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
290 const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 290 const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
291 const Shader::Info& info{stage_infos[stage]}; 291 const Shader::Info& info{stage_infos[stage]};
292 buffer_cache.UnbindGraphicsStorageBuffers(stage); 292 buffer_cache.UnbindGraphicsStorageBuffers(stage);
@@ -299,7 +299,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
299 ++ssbo_index; 299 ++ssbo_index;
300 } 300 }
301 } 301 }
302 const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; 302 const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers};
303 const auto read_handle{[&](const auto& desc, u32 index) { 303 const auto read_handle{[&](const auto& desc, u32 index) {
304 ASSERT(cbufs[desc.cbuf_index].enabled); 304 ASSERT(cbufs[desc.cbuf_index].enabled);
305 const u32 index_offset{index << desc.size_shift}; 305 const u32 index_offset{index << desc.size_shift};
@@ -312,13 +312,14 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
312 const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; 312 const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
313 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + 313 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
314 second_offset}; 314 second_offset};
315 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 315 const u32 lhs_raw{gpu_memory->Read<u32>(addr) << desc.shift_left};
316 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 316 const u32 rhs_raw{gpu_memory->Read<u32>(separate_addr)
317 << desc.secondary_shift_left};
317 const u32 raw{lhs_raw | rhs_raw}; 318 const u32 raw{lhs_raw | rhs_raw};
318 return TexturePair(raw, via_header_index); 319 return TexturePair(raw, via_header_index);
319 } 320 }
320 } 321 }
321 return TexturePair(gpu_memory.Read<u32>(addr), via_header_index); 322 return TexturePair(gpu_memory->Read<u32>(addr), via_header_index);
322 }}; 323 }};
323 const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { 324 const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE {
324 for (u32 index = 0; index < desc.count; ++index) { 325 for (u32 index = 0; index < desc.count; ++index) {
@@ -567,10 +568,25 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
567 ++current_stream; 568 ++current_stream;
568 569
569 const auto& locations = key.xfb_state.varyings[feedback]; 570 const auto& locations = key.xfb_state.varyings[feedback];
570 std::optional<u8> current_index; 571 std::optional<u32> current_index;
571 for (u32 offset = 0; offset < layout.varying_count; ++offset) { 572 for (u32 offset = 0; offset < layout.varying_count; ++offset) {
572 const u8 location = locations[offset]; 573 const auto get_attribute = [&locations](u32 index) -> u32 {
573 const u8 index = location / 4; 574 switch (index % 4) {
575 case 0:
576 return locations[index / 4].attribute0.Value();
577 case 1:
578 return locations[index / 4].attribute1.Value();
579 case 2:
580 return locations[index / 4].attribute2.Value();
581 case 3:
582 return locations[index / 4].attribute3.Value();
583 }
584 UNREACHABLE();
585 return 0;
586 };
587
588 const auto attribute{get_attribute(offset)};
589 const auto index = attribute / 4U;
574 590
575 if (current_index == index) { 591 if (current_index == index) {
576 // Increase number of components of the previous attachment 592 // Increase number of components of the previous attachment
@@ -579,7 +595,7 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
579 } 595 }
580 current_index = index; 596 current_index = index;
581 597
582 std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location); 598 std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(attribute);
583 cursor[1] = 1; 599 cursor[1] = 1;
584 cursor += XFB_ENTRY_STRIDE; 600 cursor += XFB_ENTRY_STRIDE;
585 } 601 }
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 4ec15b966..ea53ddb46 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -37,8 +37,8 @@ struct GraphicsPipelineKey {
37 BitField<0, 1, u32> xfb_enabled; 37 BitField<0, 1, u32> xfb_enabled;
38 BitField<1, 1, u32> early_z; 38 BitField<1, 1, u32> early_z;
39 BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology; 39 BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology;
40 BitField<6, 2, Maxwell::TessellationPrimitive> tessellation_primitive; 40 BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive;
41 BitField<8, 2, Maxwell::TessellationSpacing> tessellation_spacing; 41 BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing;
42 BitField<10, 1, u32> tessellation_clockwise; 42 BitField<10, 1, u32> tessellation_clockwise;
43 }; 43 };
44 std::array<u32, 3> padding; 44 std::array<u32, 3> padding;
@@ -71,10 +71,9 @@ static_assert(std::is_trivially_constructible_v<GraphicsPipelineKey>);
71class GraphicsPipeline { 71class GraphicsPipeline {
72public: 72public:
73 explicit GraphicsPipeline(const Device& device, TextureCache& texture_cache_, 73 explicit GraphicsPipeline(const Device& device, TextureCache& texture_cache_,
74 BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, 74 BufferCache& buffer_cache_, ProgramManager& program_manager_,
75 Tegra::Engines::Maxwell3D& maxwell3d_, 75 StateTracker& state_tracker_, ShaderWorker* thread_worker,
76 ProgramManager& program_manager_, StateTracker& state_tracker_, 76 VideoCore::ShaderNotify* shader_notify,
77 ShaderWorker* thread_worker, VideoCore::ShaderNotify* shader_notify,
78 std::array<std::string, 5> sources, 77 std::array<std::string, 5> sources,
79 std::array<std::vector<u32>, 5> sources_spirv, 78 std::array<std::vector<u32>, 5> sources_spirv,
80 const std::array<const Shader::Info*, 5>& infos, 79 const std::array<const Shader::Info*, 5>& infos,
@@ -107,6 +106,11 @@ public:
107 }; 106 };
108 } 107 }
109 108
109 void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) {
110 maxwell3d = maxwell3d_;
111 gpu_memory = gpu_memory_;
112 }
113
110private: 114private:
111 template <typename Spec> 115 template <typename Spec>
112 void ConfigureImpl(bool is_indexed); 116 void ConfigureImpl(bool is_indexed);
@@ -119,8 +123,8 @@ private:
119 123
120 TextureCache& texture_cache; 124 TextureCache& texture_cache;
121 BufferCache& buffer_cache; 125 BufferCache& buffer_cache;
122 Tegra::MemoryManager& gpu_memory; 126 Tegra::MemoryManager* gpu_memory;
123 Tegra::Engines::Maxwell3D& maxwell3d; 127 Tegra::Engines::Maxwell3D* maxwell3d;
124 ProgramManager& program_manager; 128 ProgramManager& program_manager;
125 StateTracker& state_tracker; 129 StateTracker& state_tracker;
126 const GraphicsPipelineKey key; 130 const GraphicsPipelineKey key;
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index ed40f5791..5070db441 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -26,9 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
26 26
27} // Anonymous namespace 27} // Anonymous namespace
28 28
29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, 29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_)
30 Tegra::MemoryManager& gpu_memory_) 30 : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {}
31 : QueryCacheBase(rasterizer_, maxwell3d_, gpu_memory_), gl_rasterizer{rasterizer_} {}
32 31
33QueryCache::~QueryCache() = default; 32QueryCache::~QueryCache() = default;
34 33
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index 8a49f1ef0..14ce59990 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -28,8 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
28class QueryCache final 28class QueryCache final
29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
30public: 30public:
31 explicit QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, 31 explicit QueryCache(RasterizerOpenGL& rasterizer_);
32 Tegra::MemoryManager& gpu_memory_);
33 ~QueryCache(); 32 ~QueryCache();
34 33
35 OGLQuery AllocateQuery(VideoCore::QueryType type); 34 OGLQuery AllocateQuery(VideoCore::QueryType type);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f018333af..79d7908d4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -16,7 +16,7 @@
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h" 17#include "common/scope_exit.h"
18#include "common/settings.h" 18#include "common/settings.h"
19 19#include "video_core/control/channel_state.h"
20#include "video_core/engines/kepler_compute.h" 20#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
@@ -56,22 +56,20 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
56 Core::Memory::Memory& cpu_memory_, const Device& device_, 56 Core::Memory::Memory& cpu_memory_, const Device& device_,
57 ScreenInfo& screen_info_, ProgramManager& program_manager_, 57 ScreenInfo& screen_info_, ProgramManager& program_manager_,
58 StateTracker& state_tracker_) 58 StateTracker& state_tracker_)
59 : RasterizerAccelerated(cpu_memory_), gpu(gpu_), maxwell3d(gpu.Maxwell3D()), 59 : RasterizerAccelerated(cpu_memory_), gpu(gpu_), device(device_), screen_info(screen_info_),
60 kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_), 60 program_manager(program_manager_), state_tracker(state_tracker_),
61 screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_),
62 texture_cache_runtime(device, program_manager, state_tracker), 61 texture_cache_runtime(device, program_manager, state_tracker),
63 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), 62 texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device),
64 buffer_cache_runtime(device), 63 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
65 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), 64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
66 shader_cache(*this, emu_window_, maxwell3d, kepler_compute, gpu_memory, device, texture_cache, 65 state_tracker, gpu.ShaderNotify()),
67 buffer_cache, program_manager, state_tracker, gpu.ShaderNotify()), 66 query_cache(*this), accelerate_dma(buffer_cache),
68 query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache),
69 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} 67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {}
70 68
71RasterizerOpenGL::~RasterizerOpenGL() = default; 69RasterizerOpenGL::~RasterizerOpenGL() = default;
72 70
73void RasterizerOpenGL::SyncVertexFormats() { 71void RasterizerOpenGL::SyncVertexFormats() {
74 auto& flags = maxwell3d.dirty.flags; 72 auto& flags = maxwell3d->dirty.flags;
75 if (!flags[Dirty::VertexFormats]) { 73 if (!flags[Dirty::VertexFormats]) {
76 return; 74 return;
77 } 75 }
@@ -89,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() {
89 } 87 }
90 flags[Dirty::VertexFormat0 + index] = false; 88 flags[Dirty::VertexFormat0 + index] = false;
91 89
92 const auto attrib = maxwell3d.regs.vertex_attrib_format[index]; 90 const auto& attrib = maxwell3d->regs.vertex_attrib_format[index];
93 const auto gl_index = static_cast<GLuint>(index); 91 const auto gl_index = static_cast<GLuint>(index);
94 92
95 // Disable constant attributes. 93 // Disable constant attributes.
@@ -99,8 +97,8 @@ void RasterizerOpenGL::SyncVertexFormats() {
99 } 97 }
100 glEnableVertexAttribArray(gl_index); 98 glEnableVertexAttribArray(gl_index);
101 99
102 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || 100 if (attrib.type == Maxwell::VertexAttribute::Type::SInt ||
103 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { 101 attrib.type == Maxwell::VertexAttribute::Type::UInt) {
104 glVertexAttribIFormat(gl_index, attrib.ComponentCount(), 102 glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
105 MaxwellToGL::VertexFormat(attrib), attrib.offset); 103 MaxwellToGL::VertexFormat(attrib), attrib.offset);
106 } else { 104 } else {
@@ -113,13 +111,13 @@ void RasterizerOpenGL::SyncVertexFormats() {
113} 111}
114 112
115void RasterizerOpenGL::SyncVertexInstances() { 113void RasterizerOpenGL::SyncVertexInstances() {
116 auto& flags = maxwell3d.dirty.flags; 114 auto& flags = maxwell3d->dirty.flags;
117 if (!flags[Dirty::VertexInstances]) { 115 if (!flags[Dirty::VertexInstances]) {
118 return; 116 return;
119 } 117 }
120 flags[Dirty::VertexInstances] = false; 118 flags[Dirty::VertexInstances] = false;
121 119
122 const auto& regs = maxwell3d.regs; 120 const auto& regs = maxwell3d->regs;
123 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { 121 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
124 if (!flags[Dirty::VertexInstance0 + index]) { 122 if (!flags[Dirty::VertexInstance0 + index]) {
125 continue; 123 continue;
@@ -127,8 +125,8 @@ void RasterizerOpenGL::SyncVertexInstances() {
127 flags[Dirty::VertexInstance0 + index] = false; 125 flags[Dirty::VertexInstance0 + index] = false;
128 126
129 const auto gl_index = static_cast<GLuint>(index); 127 const auto gl_index = static_cast<GLuint>(index);
130 const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index); 128 const bool instancing_enabled = regs.vertex_stream_instances.IsInstancingEnabled(gl_index);
131 const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0; 129 const GLuint divisor = instancing_enabled ? regs.vertex_streams[index].frequency : 0;
132 glVertexBindingDivisor(gl_index, divisor); 130 glVertexBindingDivisor(gl_index, divisor);
133 } 131 }
134} 132}
@@ -140,36 +138,36 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
140 138
141void RasterizerOpenGL::Clear() { 139void RasterizerOpenGL::Clear() {
142 MICROPROFILE_SCOPE(OpenGL_Clears); 140 MICROPROFILE_SCOPE(OpenGL_Clears);
143 if (!maxwell3d.ShouldExecute()) { 141 if (!maxwell3d->ShouldExecute()) {
144 return; 142 return;
145 } 143 }
146 144
147 const auto& regs = maxwell3d.regs; 145 const auto& regs = maxwell3d->regs;
148 bool use_color{}; 146 bool use_color{};
149 bool use_depth{}; 147 bool use_depth{};
150 bool use_stencil{}; 148 bool use_stencil{};
151 149
152 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 150 if (regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
153 regs.clear_buffers.A) { 151 regs.clear_surface.A) {
154 use_color = true; 152 use_color = true;
155 153
156 const GLuint index = regs.clear_buffers.RT; 154 const GLuint index = regs.clear_surface.RT;
157 state_tracker.NotifyColorMask(index); 155 state_tracker.NotifyColorMask(index);
158 glColorMaski(index, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0, 156 glColorMaski(index, regs.clear_surface.R != 0, regs.clear_surface.G != 0,
159 regs.clear_buffers.B != 0, regs.clear_buffers.A != 0); 157 regs.clear_surface.B != 0, regs.clear_surface.A != 0);
160 158
161 // TODO(Rodrigo): Determine if clamping is used on clears 159 // TODO(Rodrigo): Determine if clamping is used on clears
162 SyncFragmentColorClampState(); 160 SyncFragmentColorClampState();
163 SyncFramebufferSRGB(); 161 SyncFramebufferSRGB();
164 } 162 }
165 if (regs.clear_buffers.Z) { 163 if (regs.clear_surface.Z) {
166 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 164 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
167 use_depth = true; 165 use_depth = true;
168 166
169 state_tracker.NotifyDepthMask(); 167 state_tracker.NotifyDepthMask();
170 glDepthMask(GL_TRUE); 168 glDepthMask(GL_TRUE);
171 } 169 }
172 if (regs.clear_buffers.S) { 170 if (regs.clear_surface.S) {
173 ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!"); 171 ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
174 use_stencil = true; 172 use_stencil = true;
175 } 173 }
@@ -186,16 +184,16 @@ void RasterizerOpenGL::Clear() {
186 texture_cache.UpdateRenderTargets(true); 184 texture_cache.UpdateRenderTargets(true);
187 state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); 185 state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
188 SyncViewport(); 186 SyncViewport();
189 if (regs.clear_flags.scissor) { 187 if (regs.clear_control.use_scissor) {
190 SyncScissorTest(); 188 SyncScissorTest();
191 } else { 189 } else {
192 state_tracker.NotifyScissor0(); 190 state_tracker.NotifyScissor0();
193 glDisablei(GL_SCISSOR_TEST, 0); 191 glDisablei(GL_SCISSOR_TEST, 0);
194 } 192 }
195 UNIMPLEMENTED_IF(regs.clear_flags.viewport); 193 UNIMPLEMENTED_IF(regs.clear_control.use_viewport_clip0);
196 194
197 if (use_color) { 195 if (use_color) {
198 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); 196 glClearBufferfv(GL_COLOR, regs.clear_surface.RT, regs.clear_color.data());
199 } 197 }
200 if (use_depth && use_stencil) { 198 if (use_depth && use_stencil) {
201 glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil); 199 glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil);
@@ -207,7 +205,7 @@ void RasterizerOpenGL::Clear() {
207 ++num_queued_commands; 205 ++num_queued_commands;
208} 206}
209 207
210void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 208void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
211 MICROPROFILE_SCOPE(OpenGL_Drawing); 209 MICROPROFILE_SCOPE(OpenGL_Drawing);
212 210
213 SCOPE_EXIT({ gpu.TickWork(); }); 211 SCOPE_EXIT({ gpu.TickWork(); });
@@ -217,22 +215,27 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
217 if (!pipeline) { 215 if (!pipeline) {
218 return; 216 return;
219 } 217 }
218
219 gpu.TickWork();
220
220 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 221 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
222 pipeline->SetEngine(maxwell3d, gpu_memory);
221 pipeline->Configure(is_indexed); 223 pipeline->Configure(is_indexed);
222 224
225 BindInlineIndexBuffer();
226
223 SyncState(); 227 SyncState();
224 228
225 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology); 229 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
226 BeginTransformFeedback(pipeline, primitive_mode); 230 BeginTransformFeedback(pipeline, primitive_mode);
227 231
228 const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance); 232 const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
229 const GLsizei num_instances = 233 const GLsizei num_instances = static_cast<GLsizei>(instance_count);
230 static_cast<GLsizei>(is_instanced ? maxwell3d.mme_draw.instance_count : 1);
231 if (is_indexed) { 234 if (is_indexed) {
232 const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vb_element_base); 235 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
233 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.index_array.count); 236 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
234 const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); 237 const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
235 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format); 238 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
236 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { 239 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
237 glDrawElements(primitive_mode, num_vertices, format, offset); 240 glDrawElements(primitive_mode, num_vertices, format, offset);
238 } else if (num_instances == 1 && base_instance == 0) { 241 } else if (num_instances == 1 && base_instance == 0) {
@@ -251,8 +254,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
251 base_instance); 254 base_instance);
252 } 255 }
253 } else { 256 } else {
254 const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vertex_buffer.first); 257 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.vertex_buffer.first);
255 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.vertex_buffer.count); 258 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.vertex_buffer.count);
256 if (num_instances == 1 && base_instance == 0) { 259 if (num_instances == 1 && base_instance == 0) {
257 glDrawArrays(primitive_mode, base_vertex, num_vertices); 260 glDrawArrays(primitive_mode, base_vertex, num_vertices);
258 } else if (base_instance == 0) { 261 } else if (base_instance == 0) {
@@ -273,8 +276,9 @@ void RasterizerOpenGL::DispatchCompute() {
273 if (!pipeline) { 276 if (!pipeline) {
274 return; 277 return;
275 } 278 }
279 pipeline->SetEngine(kepler_compute, gpu_memory);
276 pipeline->Configure(); 280 pipeline->Configure();
277 const auto& qmd{kepler_compute.launch_description}; 281 const auto& qmd{kepler_compute->launch_description};
278 glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); 282 glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z);
279 ++num_queued_commands; 283 ++num_queued_commands;
280 has_written_global_memory |= pipeline->WritesGlobalMemory(); 284 has_written_global_memory |= pipeline->WritesGlobalMemory();
@@ -359,7 +363,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
359 } 363 }
360} 364}
361 365
362void RasterizerOpenGL::SyncGuestHost() { 366void RasterizerOpenGL::InvalidateGPUCache() {
363 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 367 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
364 shader_cache.SyncGuestHost(); 368 shader_cache.SyncGuestHost();
365 { 369 {
@@ -380,40 +384,30 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) {
380 shader_cache.OnCPUWrite(addr, size); 384 shader_cache.OnCPUWrite(addr, size);
381} 385}
382 386
383void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { 387void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {
384 { 388 {
385 std::scoped_lock lock{texture_cache.mutex}; 389 std::scoped_lock lock{texture_cache.mutex};
386 texture_cache.UnmapGPUMemory(addr, size); 390 texture_cache.UnmapGPUMemory(as_id, addr, size);
387 } 391 }
388} 392}
389 393
390void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { 394void RasterizerOpenGL::SignalFence(std::function<void()>&& func) {
391 if (!gpu.IsAsync()) { 395 fence_manager.SignalFence(std::move(func));
392 gpu_memory.Write<u32>(addr, value); 396}
393 return; 397
394 } 398void RasterizerOpenGL::SyncOperation(std::function<void()>&& func) {
395 fence_manager.SignalSemaphore(addr, value); 399 fence_manager.SyncOperation(std::move(func));
396} 400}
397 401
398void RasterizerOpenGL::SignalSyncPoint(u32 value) { 402void RasterizerOpenGL::SignalSyncPoint(u32 value) {
399 if (!gpu.IsAsync()) {
400 gpu.IncrementSyncPoint(value);
401 return;
402 }
403 fence_manager.SignalSyncPoint(value); 403 fence_manager.SignalSyncPoint(value);
404} 404}
405 405
406void RasterizerOpenGL::SignalReference() { 406void RasterizerOpenGL::SignalReference() {
407 if (!gpu.IsAsync()) {
408 return;
409 }
410 fence_manager.SignalOrdering(); 407 fence_manager.SignalOrdering();
411} 408}
412 409
413void RasterizerOpenGL::ReleaseFences() { 410void RasterizerOpenGL::ReleaseFences() {
414 if (!gpu.IsAsync()) {
415 return;
416 }
417 fence_manager.WaitPendingFences(); 411 fence_manager.WaitPendingFences();
418} 412}
419 413
@@ -430,6 +424,7 @@ void RasterizerOpenGL::WaitForIdle() {
430} 424}
431 425
432void RasterizerOpenGL::FragmentBarrier() { 426void RasterizerOpenGL::FragmentBarrier() {
427 glTextureBarrier();
433 glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT); 428 glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT);
434} 429}
435 430
@@ -482,13 +477,13 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA()
482} 477}
483 478
484void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 479void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
485 std::span<u8> memory) { 480 std::span<const u8> memory) {
486 auto cpu_addr = gpu_memory.GpuToCpuAddress(address); 481 auto cpu_addr = gpu_memory->GpuToCpuAddress(address);
487 if (!cpu_addr) [[unlikely]] { 482 if (!cpu_addr) [[unlikely]] {
488 gpu_memory.WriteBlock(address, memory.data(), copy_size); 483 gpu_memory->WriteBlock(address, memory.data(), copy_size);
489 return; 484 return;
490 } 485 }
491 gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); 486 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
492 { 487 {
493 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 488 std::unique_lock<std::mutex> lock{buffer_cache.mutex};
494 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 489 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
@@ -551,8 +546,8 @@ void RasterizerOpenGL::SyncState() {
551} 546}
552 547
553void RasterizerOpenGL::SyncViewport() { 548void RasterizerOpenGL::SyncViewport() {
554 auto& flags = maxwell3d.dirty.flags; 549 auto& flags = maxwell3d->dirty.flags;
555 const auto& regs = maxwell3d.regs; 550 const auto& regs = maxwell3d->regs;
556 551
557 const bool rescale_viewports = flags[VideoCommon::Dirty::RescaleViewports]; 552 const bool rescale_viewports = flags[VideoCommon::Dirty::RescaleViewports];
558 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; 553 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports;
@@ -561,9 +556,9 @@ void RasterizerOpenGL::SyncViewport() {
561 if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) { 556 if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) {
562 flags[Dirty::FrontFace] = false; 557 flags[Dirty::FrontFace] = false;
563 558
564 GLenum mode = MaxwellToGL::FrontFace(regs.front_face); 559 GLenum mode = MaxwellToGL::FrontFace(regs.gl_front_face);
565 bool flip_faces = true; 560 bool flip_faces = true;
566 if (regs.screen_y_control.triangle_rast_flip != 0) { 561 if (regs.window_origin.flip_y != 0) {
567 flip_faces = !flip_faces; 562 flip_faces = !flip_faces;
568 } 563 }
569 if (regs.viewport_transform[0].scale_y < 0.0f) { 564 if (regs.viewport_transform[0].scale_y < 0.0f) {
@@ -588,14 +583,15 @@ void RasterizerOpenGL::SyncViewport() {
588 if (regs.viewport_transform[0].scale_y < 0.0f) { 583 if (regs.viewport_transform[0].scale_y < 0.0f) {
589 flip_y = !flip_y; 584 flip_y = !flip_y;
590 } 585 }
591 if (regs.screen_y_control.y_negate != 0) { 586 const bool lower_left{regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft};
587 if (lower_left) {
592 flip_y = !flip_y; 588 flip_y = !flip_y;
593 } 589 }
594 const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne; 590 const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne;
595 const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT; 591 const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
596 const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE; 592 const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE;
597 state_tracker.ClipControl(origin, depth); 593 state_tracker.ClipControl(origin, depth);
598 state_tracker.SetYNegate(regs.screen_y_control.y_negate != 0); 594 state_tracker.SetYNegate(lower_left);
599 } 595 }
600 const bool is_rescaling{texture_cache.IsRescaling()}; 596 const bool is_rescaling{texture_cache.IsRescaling()};
601 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 597 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
@@ -667,23 +663,29 @@ void RasterizerOpenGL::SyncViewport() {
667} 663}
668 664
669void RasterizerOpenGL::SyncDepthClamp() { 665void RasterizerOpenGL::SyncDepthClamp() {
670 auto& flags = maxwell3d.dirty.flags; 666 auto& flags = maxwell3d->dirty.flags;
671 if (!flags[Dirty::DepthClampEnabled]) { 667 if (!flags[Dirty::DepthClampEnabled]) {
672 return; 668 return;
673 } 669 }
674 flags[Dirty::DepthClampEnabled] = false; 670 flags[Dirty::DepthClampEnabled] = false;
675 671
676 oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0); 672 bool depth_clamp_disabled{maxwell3d->regs.viewport_clip_control.geometry_clip ==
673 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
674 maxwell3d->regs.viewport_clip_control.geometry_clip ==
675 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
676 maxwell3d->regs.viewport_clip_control.geometry_clip ==
677 Maxwell::ViewportClipControl::GeometryClip::FrustumZ};
678 oglEnable(GL_DEPTH_CLAMP, !depth_clamp_disabled);
677} 679}
678 680
679void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { 681void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
680 auto& flags = maxwell3d.dirty.flags; 682 auto& flags = maxwell3d->dirty.flags;
681 if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) { 683 if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) {
682 return; 684 return;
683 } 685 }
684 flags[Dirty::ClipDistances] = false; 686 flags[Dirty::ClipDistances] = false;
685 687
686 clip_mask &= maxwell3d.regs.clip_distance_enabled; 688 clip_mask &= maxwell3d->regs.user_clip_enable.raw;
687 if (clip_mask == last_clip_distance_mask) { 689 if (clip_mask == last_clip_distance_mask) {
688 return; 690 return;
689 } 691 }
@@ -699,15 +701,15 @@ void RasterizerOpenGL::SyncClipCoef() {
699} 701}
700 702
701void RasterizerOpenGL::SyncCullMode() { 703void RasterizerOpenGL::SyncCullMode() {
702 auto& flags = maxwell3d.dirty.flags; 704 auto& flags = maxwell3d->dirty.flags;
703 const auto& regs = maxwell3d.regs; 705 const auto& regs = maxwell3d->regs;
704 706
705 if (flags[Dirty::CullTest]) { 707 if (flags[Dirty::CullTest]) {
706 flags[Dirty::CullTest] = false; 708 flags[Dirty::CullTest] = false;
707 709
708 if (regs.cull_test_enabled) { 710 if (regs.gl_cull_test_enabled) {
709 glEnable(GL_CULL_FACE); 711 glEnable(GL_CULL_FACE);
710 glCullFace(MaxwellToGL::CullFace(regs.cull_face)); 712 glCullFace(MaxwellToGL::CullFace(regs.gl_cull_face));
711 } else { 713 } else {
712 glDisable(GL_CULL_FACE); 714 glDisable(GL_CULL_FACE);
713 } 715 }
@@ -715,23 +717,23 @@ void RasterizerOpenGL::SyncCullMode() {
715} 717}
716 718
717void RasterizerOpenGL::SyncPrimitiveRestart() { 719void RasterizerOpenGL::SyncPrimitiveRestart() {
718 auto& flags = maxwell3d.dirty.flags; 720 auto& flags = maxwell3d->dirty.flags;
719 if (!flags[Dirty::PrimitiveRestart]) { 721 if (!flags[Dirty::PrimitiveRestart]) {
720 return; 722 return;
721 } 723 }
722 flags[Dirty::PrimitiveRestart] = false; 724 flags[Dirty::PrimitiveRestart] = false;
723 725
724 if (maxwell3d.regs.primitive_restart.enabled) { 726 if (maxwell3d->regs.primitive_restart.enabled) {
725 glEnable(GL_PRIMITIVE_RESTART); 727 glEnable(GL_PRIMITIVE_RESTART);
726 glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index); 728 glPrimitiveRestartIndex(maxwell3d->regs.primitive_restart.index);
727 } else { 729 } else {
728 glDisable(GL_PRIMITIVE_RESTART); 730 glDisable(GL_PRIMITIVE_RESTART);
729 } 731 }
730} 732}
731 733
732void RasterizerOpenGL::SyncDepthTestState() { 734void RasterizerOpenGL::SyncDepthTestState() {
733 auto& flags = maxwell3d.dirty.flags; 735 auto& flags = maxwell3d->dirty.flags;
734 const auto& regs = maxwell3d.regs; 736 const auto& regs = maxwell3d->regs;
735 737
736 if (flags[Dirty::DepthMask]) { 738 if (flags[Dirty::DepthMask]) {
737 flags[Dirty::DepthMask] = false; 739 flags[Dirty::DepthMask] = false;
@@ -750,28 +752,28 @@ void RasterizerOpenGL::SyncDepthTestState() {
750} 752}
751 753
752void RasterizerOpenGL::SyncStencilTestState() { 754void RasterizerOpenGL::SyncStencilTestState() {
753 auto& flags = maxwell3d.dirty.flags; 755 auto& flags = maxwell3d->dirty.flags;
754 if (!flags[Dirty::StencilTest]) { 756 if (!flags[Dirty::StencilTest]) {
755 return; 757 return;
756 } 758 }
757 flags[Dirty::StencilTest] = false; 759 flags[Dirty::StencilTest] = false;
758 760
759 const auto& regs = maxwell3d.regs; 761 const auto& regs = maxwell3d->regs;
760 oglEnable(GL_STENCIL_TEST, regs.stencil_enable); 762 oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
761 763
762 glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), 764 glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_op.func),
763 regs.stencil_front_func_ref, regs.stencil_front_func_mask); 765 regs.stencil_front_ref, regs.stencil_front_func_mask);
764 glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), 766 glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op.fail),
765 MaxwellToGL::StencilOp(regs.stencil_front_op_zfail), 767 MaxwellToGL::StencilOp(regs.stencil_front_op.zfail),
766 MaxwellToGL::StencilOp(regs.stencil_front_op_zpass)); 768 MaxwellToGL::StencilOp(regs.stencil_front_op.zpass));
767 glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask); 769 glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask);
768 770
769 if (regs.stencil_two_side_enable) { 771 if (regs.stencil_two_side_enable) {
770 glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func), 772 glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func),
771 regs.stencil_back_func_ref, regs.stencil_back_func_mask); 773 regs.stencil_back_ref, regs.stencil_back_mask);
772 glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail), 774 glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail),
773 MaxwellToGL::StencilOp(regs.stencil_back_op_zfail), 775 MaxwellToGL::StencilOp(regs.stencil_back_op.zfail),
774 MaxwellToGL::StencilOp(regs.stencil_back_op_zpass)); 776 MaxwellToGL::StencilOp(regs.stencil_back_op.zpass));
775 glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask); 777 glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask);
776 } else { 778 } else {
777 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF); 779 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF);
@@ -781,24 +783,24 @@ void RasterizerOpenGL::SyncStencilTestState() {
781} 783}
782 784
783void RasterizerOpenGL::SyncRasterizeEnable() { 785void RasterizerOpenGL::SyncRasterizeEnable() {
784 auto& flags = maxwell3d.dirty.flags; 786 auto& flags = maxwell3d->dirty.flags;
785 if (!flags[Dirty::RasterizeEnable]) { 787 if (!flags[Dirty::RasterizeEnable]) {
786 return; 788 return;
787 } 789 }
788 flags[Dirty::RasterizeEnable] = false; 790 flags[Dirty::RasterizeEnable] = false;
789 791
790 oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0); 792 oglEnable(GL_RASTERIZER_DISCARD, maxwell3d->regs.rasterize_enable == 0);
791} 793}
792 794
793void RasterizerOpenGL::SyncPolygonModes() { 795void RasterizerOpenGL::SyncPolygonModes() {
794 auto& flags = maxwell3d.dirty.flags; 796 auto& flags = maxwell3d->dirty.flags;
795 if (!flags[Dirty::PolygonModes]) { 797 if (!flags[Dirty::PolygonModes]) {
796 return; 798 return;
797 } 799 }
798 flags[Dirty::PolygonModes] = false; 800 flags[Dirty::PolygonModes] = false;
799 801
800 const auto& regs = maxwell3d.regs; 802 const auto& regs = maxwell3d->regs;
801 if (regs.fill_rectangle) { 803 if (regs.fill_via_triangle_mode != Maxwell::FillViaTriangleMode::Disabled) {
802 if (!GLAD_GL_NV_fill_rectangle) { 804 if (!GLAD_GL_NV_fill_rectangle) {
803 LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); 805 LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
804 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 806 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -830,7 +832,7 @@ void RasterizerOpenGL::SyncPolygonModes() {
830} 832}
831 833
832void RasterizerOpenGL::SyncColorMask() { 834void RasterizerOpenGL::SyncColorMask() {
833 auto& flags = maxwell3d.dirty.flags; 835 auto& flags = maxwell3d->dirty.flags;
834 if (!flags[Dirty::ColorMasks]) { 836 if (!flags[Dirty::ColorMasks]) {
835 return; 837 return;
836 } 838 }
@@ -839,7 +841,7 @@ void RasterizerOpenGL::SyncColorMask() {
839 const bool force = flags[Dirty::ColorMaskCommon]; 841 const bool force = flags[Dirty::ColorMaskCommon];
840 flags[Dirty::ColorMaskCommon] = false; 842 flags[Dirty::ColorMaskCommon] = false;
841 843
842 const auto& regs = maxwell3d.regs; 844 const auto& regs = maxwell3d->regs;
843 if (regs.color_mask_common) { 845 if (regs.color_mask_common) {
844 if (!force && !flags[Dirty::ColorMask0]) { 846 if (!force && !flags[Dirty::ColorMask0]) {
845 return; 847 return;
@@ -864,30 +866,31 @@ void RasterizerOpenGL::SyncColorMask() {
864} 866}
865 867
866void RasterizerOpenGL::SyncMultiSampleState() { 868void RasterizerOpenGL::SyncMultiSampleState() {
867 auto& flags = maxwell3d.dirty.flags; 869 auto& flags = maxwell3d->dirty.flags;
868 if (!flags[Dirty::MultisampleControl]) { 870 if (!flags[Dirty::MultisampleControl]) {
869 return; 871 return;
870 } 872 }
871 flags[Dirty::MultisampleControl] = false; 873 flags[Dirty::MultisampleControl] = false;
872 874
873 const auto& regs = maxwell3d.regs; 875 const auto& regs = maxwell3d->regs;
874 oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); 876 oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.anti_alias_alpha_control.alpha_to_coverage);
875 oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); 877 oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.anti_alias_alpha_control.alpha_to_one);
876} 878}
877 879
878void RasterizerOpenGL::SyncFragmentColorClampState() { 880void RasterizerOpenGL::SyncFragmentColorClampState() {
879 auto& flags = maxwell3d.dirty.flags; 881 auto& flags = maxwell3d->dirty.flags;
880 if (!flags[Dirty::FragmentClampColor]) { 882 if (!flags[Dirty::FragmentClampColor]) {
881 return; 883 return;
882 } 884 }
883 flags[Dirty::FragmentClampColor] = false; 885 flags[Dirty::FragmentClampColor] = false;
884 886
885 glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); 887 glClampColor(GL_CLAMP_FRAGMENT_COLOR,
888 maxwell3d->regs.frag_color_clamp.AnyEnabled() ? GL_TRUE : GL_FALSE);
886} 889}
887 890
888void RasterizerOpenGL::SyncBlendState() { 891void RasterizerOpenGL::SyncBlendState() {
889 auto& flags = maxwell3d.dirty.flags; 892 auto& flags = maxwell3d->dirty.flags;
890 const auto& regs = maxwell3d.regs; 893 const auto& regs = maxwell3d->regs;
891 894
892 if (flags[Dirty::BlendColor]) { 895 if (flags[Dirty::BlendColor]) {
893 flags[Dirty::BlendColor] = false; 896 flags[Dirty::BlendColor] = false;
@@ -902,18 +905,18 @@ void RasterizerOpenGL::SyncBlendState() {
902 } 905 }
903 flags[Dirty::BlendStates] = false; 906 flags[Dirty::BlendStates] = false;
904 907
905 if (!regs.independent_blend_enable) { 908 if (!regs.blend_per_target_enabled) {
906 if (!regs.blend.enable[0]) { 909 if (!regs.blend.enable[0]) {
907 glDisable(GL_BLEND); 910 glDisable(GL_BLEND);
908 return; 911 return;
909 } 912 }
910 glEnable(GL_BLEND); 913 glEnable(GL_BLEND);
911 glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb), 914 glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source),
912 MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb), 915 MaxwellToGL::BlendFunc(regs.blend.color_dest),
913 MaxwellToGL::BlendFunc(regs.blend.factor_source_a), 916 MaxwellToGL::BlendFunc(regs.blend.alpha_source),
914 MaxwellToGL::BlendFunc(regs.blend.factor_dest_a)); 917 MaxwellToGL::BlendFunc(regs.blend.alpha_dest));
915 glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb), 918 glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.color_op),
916 MaxwellToGL::BlendEquation(regs.blend.equation_a)); 919 MaxwellToGL::BlendEquation(regs.blend.alpha_op));
917 return; 920 return;
918 } 921 }
919 922
@@ -932,35 +935,34 @@ void RasterizerOpenGL::SyncBlendState() {
932 } 935 }
933 glEnablei(GL_BLEND, static_cast<GLuint>(i)); 936 glEnablei(GL_BLEND, static_cast<GLuint>(i));
934 937
935 const auto& src = regs.independent_blend[i]; 938 const auto& src = regs.blend_per_target[i];
936 glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb), 939 glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.color_source),
937 MaxwellToGL::BlendFunc(src.factor_dest_rgb), 940 MaxwellToGL::BlendFunc(src.color_dest),
938 MaxwellToGL::BlendFunc(src.factor_source_a), 941 MaxwellToGL::BlendFunc(src.alpha_source),
939 MaxwellToGL::BlendFunc(src.factor_dest_a)); 942 MaxwellToGL::BlendFunc(src.alpha_dest));
940 glBlendEquationSeparatei(static_cast<GLuint>(i), 943 glBlendEquationSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendEquation(src.color_op),
941 MaxwellToGL::BlendEquation(src.equation_rgb), 944 MaxwellToGL::BlendEquation(src.alpha_op));
942 MaxwellToGL::BlendEquation(src.equation_a));
943 } 945 }
944} 946}
945 947
946void RasterizerOpenGL::SyncLogicOpState() { 948void RasterizerOpenGL::SyncLogicOpState() {
947 auto& flags = maxwell3d.dirty.flags; 949 auto& flags = maxwell3d->dirty.flags;
948 if (!flags[Dirty::LogicOp]) { 950 if (!flags[Dirty::LogicOp]) {
949 return; 951 return;
950 } 952 }
951 flags[Dirty::LogicOp] = false; 953 flags[Dirty::LogicOp] = false;
952 954
953 const auto& regs = maxwell3d.regs; 955 const auto& regs = maxwell3d->regs;
954 if (regs.logic_op.enable) { 956 if (regs.logic_op.enable) {
955 glEnable(GL_COLOR_LOGIC_OP); 957 glEnable(GL_COLOR_LOGIC_OP);
956 glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); 958 glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op));
957 } else { 959 } else {
958 glDisable(GL_COLOR_LOGIC_OP); 960 glDisable(GL_COLOR_LOGIC_OP);
959 } 961 }
960} 962}
961 963
962void RasterizerOpenGL::SyncScissorTest() { 964void RasterizerOpenGL::SyncScissorTest() {
963 auto& flags = maxwell3d.dirty.flags; 965 auto& flags = maxwell3d->dirty.flags;
964 if (!flags[Dirty::Scissors] && !flags[VideoCommon::Dirty::RescaleScissors]) { 966 if (!flags[Dirty::Scissors] && !flags[VideoCommon::Dirty::RescaleScissors]) {
965 return; 967 return;
966 } 968 }
@@ -969,7 +971,7 @@ void RasterizerOpenGL::SyncScissorTest() {
969 const bool force = flags[VideoCommon::Dirty::RescaleScissors]; 971 const bool force = flags[VideoCommon::Dirty::RescaleScissors];
970 flags[VideoCommon::Dirty::RescaleScissors] = false; 972 flags[VideoCommon::Dirty::RescaleScissors] = false;
971 973
972 const auto& regs = maxwell3d.regs; 974 const auto& regs = maxwell3d->regs;
973 975
974 const auto& resolution = Settings::values.resolution_info; 976 const auto& resolution = Settings::values.resolution_info;
975 const bool is_rescaling{texture_cache.IsRescaling()}; 977 const bool is_rescaling{texture_cache.IsRescaling()};
@@ -1005,39 +1007,39 @@ void RasterizerOpenGL::SyncScissorTest() {
1005} 1007}
1006 1008
1007void RasterizerOpenGL::SyncPointState() { 1009void RasterizerOpenGL::SyncPointState() {
1008 auto& flags = maxwell3d.dirty.flags; 1010 auto& flags = maxwell3d->dirty.flags;
1009 if (!flags[Dirty::PointSize]) { 1011 if (!flags[Dirty::PointSize]) {
1010 return; 1012 return;
1011 } 1013 }
1012 flags[Dirty::PointSize] = false; 1014 flags[Dirty::PointSize] = false;
1013 1015
1014 oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable); 1016 oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable);
1015 oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d.regs.vp_point_size.enable); 1017 oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.point_size_attribute.enabled);
1016 const bool is_rescaling{texture_cache.IsRescaling()}; 1018 const bool is_rescaling{texture_cache.IsRescaling()};
1017 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 1019 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
1018 glPointSize(std::max(1.0f, maxwell3d.regs.point_size * scale)); 1020 glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale));
1019} 1021}
1020 1022
1021void RasterizerOpenGL::SyncLineState() { 1023void RasterizerOpenGL::SyncLineState() {
1022 auto& flags = maxwell3d.dirty.flags; 1024 auto& flags = maxwell3d->dirty.flags;
1023 if (!flags[Dirty::LineWidth]) { 1025 if (!flags[Dirty::LineWidth]) {
1024 return; 1026 return;
1025 } 1027 }
1026 flags[Dirty::LineWidth] = false; 1028 flags[Dirty::LineWidth] = false;
1027 1029
1028 const auto& regs = maxwell3d.regs; 1030 const auto& regs = maxwell3d->regs;
1029 oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); 1031 oglEnable(GL_LINE_SMOOTH, regs.line_anti_alias_enable);
1030 glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); 1032 glLineWidth(regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased);
1031} 1033}
1032 1034
1033void RasterizerOpenGL::SyncPolygonOffset() { 1035void RasterizerOpenGL::SyncPolygonOffset() {
1034 auto& flags = maxwell3d.dirty.flags; 1036 auto& flags = maxwell3d->dirty.flags;
1035 if (!flags[Dirty::PolygonOffset]) { 1037 if (!flags[Dirty::PolygonOffset]) {
1036 return; 1038 return;
1037 } 1039 }
1038 flags[Dirty::PolygonOffset] = false; 1040 flags[Dirty::PolygonOffset] = false;
1039 1041
1040 const auto& regs = maxwell3d.regs; 1042 const auto& regs = maxwell3d->regs;
1041 oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable); 1043 oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
1042 oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable); 1044 oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
1043 oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable); 1045 oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
@@ -1045,19 +1047,19 @@ void RasterizerOpenGL::SyncPolygonOffset() {
1045 if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable || 1047 if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable ||
1046 regs.polygon_offset_point_enable) { 1048 regs.polygon_offset_point_enable) {
1047 // Hardware divides polygon offset units by two 1049 // Hardware divides polygon offset units by two
1048 glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f, 1050 glPolygonOffsetClamp(regs.slope_scale_depth_bias, regs.depth_bias / 2.0f,
1049 regs.polygon_offset_clamp); 1051 regs.depth_bias_clamp);
1050 } 1052 }
1051} 1053}
1052 1054
1053void RasterizerOpenGL::SyncAlphaTest() { 1055void RasterizerOpenGL::SyncAlphaTest() {
1054 auto& flags = maxwell3d.dirty.flags; 1056 auto& flags = maxwell3d->dirty.flags;
1055 if (!flags[Dirty::AlphaTest]) { 1057 if (!flags[Dirty::AlphaTest]) {
1056 return; 1058 return;
1057 } 1059 }
1058 flags[Dirty::AlphaTest] = false; 1060 flags[Dirty::AlphaTest] = false;
1059 1061
1060 const auto& regs = maxwell3d.regs; 1062 const auto& regs = maxwell3d->regs;
1061 if (regs.alpha_test_enabled) { 1063 if (regs.alpha_test_enabled) {
1062 glEnable(GL_ALPHA_TEST); 1064 glEnable(GL_ALPHA_TEST);
1063 glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref); 1065 glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
@@ -1067,25 +1069,25 @@ void RasterizerOpenGL::SyncAlphaTest() {
1067} 1069}
1068 1070
1069void RasterizerOpenGL::SyncFramebufferSRGB() { 1071void RasterizerOpenGL::SyncFramebufferSRGB() {
1070 auto& flags = maxwell3d.dirty.flags; 1072 auto& flags = maxwell3d->dirty.flags;
1071 if (!flags[Dirty::FramebufferSRGB]) { 1073 if (!flags[Dirty::FramebufferSRGB]) {
1072 return; 1074 return;
1073 } 1075 }
1074 flags[Dirty::FramebufferSRGB] = false; 1076 flags[Dirty::FramebufferSRGB] = false;
1075 1077
1076 oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb); 1078 oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d->regs.framebuffer_srgb);
1077} 1079}
1078 1080
1079void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) { 1081void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) {
1080 const auto& regs = maxwell3d.regs; 1082 const auto& regs = maxwell3d->regs;
1081 if (regs.tfb_enabled == 0) { 1083 if (regs.transform_feedback_enabled == 0) {
1082 return; 1084 return;
1083 } 1085 }
1084 program->ConfigureTransformFeedback(); 1086 program->ConfigureTransformFeedback();
1085 1087
1086 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || 1088 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
1087 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || 1089 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
1088 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); 1090 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
1089 UNIMPLEMENTED_IF(primitive_mode != GL_POINTS); 1091 UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
1090 1092
1091 // We may have to call BeginTransformFeedbackNV here since they seem to call different 1093 // We may have to call BeginTransformFeedbackNV here since they seem to call different
@@ -1096,11 +1098,58 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum
1096} 1098}
1097 1099
1098void RasterizerOpenGL::EndTransformFeedback() { 1100void RasterizerOpenGL::EndTransformFeedback() {
1099 if (maxwell3d.regs.tfb_enabled != 0) { 1101 if (maxwell3d->regs.transform_feedback_enabled != 0) {
1100 glEndTransformFeedback(); 1102 glEndTransformFeedback();
1101 } 1103 }
1102} 1104}
1103 1105
1106void RasterizerOpenGL::InitializeChannel(Tegra::Control::ChannelState& channel) {
1107 CreateChannel(channel);
1108 {
1109 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1110 texture_cache.CreateChannel(channel);
1111 buffer_cache.CreateChannel(channel);
1112 }
1113 shader_cache.CreateChannel(channel);
1114 query_cache.CreateChannel(channel);
1115 state_tracker.SetupTables(channel);
1116}
1117
1118void RasterizerOpenGL::BindChannel(Tegra::Control::ChannelState& channel) {
1119 const s32 channel_id = channel.bind_id;
1120 BindToChannel(channel_id);
1121 {
1122 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1123 texture_cache.BindToChannel(channel_id);
1124 buffer_cache.BindToChannel(channel_id);
1125 }
1126 shader_cache.BindToChannel(channel_id);
1127 query_cache.BindToChannel(channel_id);
1128 state_tracker.ChangeChannel(channel);
1129 state_tracker.InvalidateState();
1130}
1131
1132void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
1133 EraseChannel(channel_id);
1134 {
1135 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1136 texture_cache.EraseChannel(channel_id);
1137 buffer_cache.EraseChannel(channel_id);
1138 }
1139 shader_cache.EraseChannel(channel_id);
1140 query_cache.EraseChannel(channel_id);
1141}
1142
1143void RasterizerOpenGL::BindInlineIndexBuffer() {
1144 if (maxwell3d->inline_index_draw_indexes.empty()) {
1145 return;
1146 }
1147 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1148 auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
1149 buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
1150 buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
1151}
1152
1104AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} 1153AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
1105 1154
1106bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 1155bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 31a16fcba..793e0d608 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -12,6 +12,7 @@
12#include <glad/glad.h> 12#include <glad/glad.h>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "video_core/control/channel_state_cache.h"
15#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
16#include "video_core/rasterizer_accelerated.h" 17#include "video_core/rasterizer_accelerated.h"
17#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
@@ -58,7 +59,8 @@ private:
58 BufferCache& buffer_cache; 59 BufferCache& buffer_cache;
59}; 60};
60 61
61class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { 62class RasterizerOpenGL : public VideoCore::RasterizerAccelerated,
63 protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
62public: 64public:
63 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 65 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
64 Core::Memory::Memory& cpu_memory_, const Device& device_, 66 Core::Memory::Memory& cpu_memory_, const Device& device_,
@@ -66,7 +68,7 @@ public:
66 StateTracker& state_tracker_); 68 StateTracker& state_tracker_);
67 ~RasterizerOpenGL() override; 69 ~RasterizerOpenGL() override;
68 70
69 void Draw(bool is_indexed, bool is_instanced) override; 71 void Draw(bool is_indexed, u32 instance_count) override;
70 void Clear() override; 72 void Clear() override;
71 void DispatchCompute() override; 73 void DispatchCompute() override;
72 void ResetCounter(VideoCore::QueryType type) override; 74 void ResetCounter(VideoCore::QueryType type) override;
@@ -78,10 +80,11 @@ public:
78 bool MustFlushRegion(VAddr addr, u64 size) override; 80 bool MustFlushRegion(VAddr addr, u64 size) override;
79 void InvalidateRegion(VAddr addr, u64 size) override; 81 void InvalidateRegion(VAddr addr, u64 size) override;
80 void OnCPUWrite(VAddr addr, u64 size) override; 82 void OnCPUWrite(VAddr addr, u64 size) override;
81 void SyncGuestHost() override; 83 void InvalidateGPUCache() override;
82 void UnmapMemory(VAddr addr, u64 size) override; 84 void UnmapMemory(VAddr addr, u64 size) override;
83 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 85 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;
84 void SignalSemaphore(GPUVAddr addr, u32 value) override; 86 void SignalFence(std::function<void()>&& func) override;
87 void SyncOperation(std::function<void()>&& func) override;
85 void SignalSyncPoint(u32 value) override; 88 void SignalSyncPoint(u32 value) override;
86 void SignalReference() override; 89 void SignalReference() override;
87 void ReleaseFences() override; 90 void ReleaseFences() override;
@@ -96,7 +99,7 @@ public:
96 const Tegra::Engines::Fermi2D::Config& copy_config) override; 99 const Tegra::Engines::Fermi2D::Config& copy_config) override;
97 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 100 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
98 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 101 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
99 std::span<u8> memory) override; 102 std::span<const u8> memory) override;
100 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 103 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
101 u32 pixel_stride) override; 104 u32 pixel_stride) override;
102 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 105 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
@@ -107,6 +110,12 @@ public:
107 return num_queued_commands > 0; 110 return num_queued_commands > 0;
108 } 111 }
109 112
113 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
114
115 void BindChannel(Tegra::Control::ChannelState& channel) override;
116
117 void ReleaseChannel(s32 channel_id) override;
118
110private: 119private:
111 static constexpr size_t MAX_TEXTURES = 192; 120 static constexpr size_t MAX_TEXTURES = 192;
112 static constexpr size_t MAX_IMAGES = 48; 121 static constexpr size_t MAX_IMAGES = 48;
@@ -190,10 +199,9 @@ private:
190 /// End a transform feedback 199 /// End a transform feedback
191 void EndTransformFeedback(); 200 void EndTransformFeedback();
192 201
202 void BindInlineIndexBuffer();
203
193 Tegra::GPU& gpu; 204 Tegra::GPU& gpu;
194 Tegra::Engines::Maxwell3D& maxwell3d;
195 Tegra::Engines::KeplerCompute& kepler_compute;
196 Tegra::MemoryManager& gpu_memory;
197 205
198 const Device& device; 206 const Device& device;
199 ScreenInfo& screen_info; 207 ScreenInfo& screen_info;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7e76f679f..977709518 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -63,6 +63,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
63 Shader::RuntimeInfo info; 63 Shader::RuntimeInfo info;
64 if (previous_program) { 64 if (previous_program) {
65 info.previous_stage_stores = previous_program->info.stores; 65 info.previous_stage_stores = previous_program->info.stores;
66 info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
66 } else { 67 } else {
67 // Mark all stores as available for vertex shaders 68 // Mark all stores as available for vertex shaders
68 info.previous_stage_stores.mask.set(); 69 info.previous_stage_stores.mask.set();
@@ -78,11 +79,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
78 info.tess_clockwise = key.tessellation_clockwise != 0; 79 info.tess_clockwise = key.tessellation_clockwise != 0;
79 info.tess_primitive = [&key] { 80 info.tess_primitive = [&key] {
80 switch (key.tessellation_primitive) { 81 switch (key.tessellation_primitive) {
81 case Maxwell::TessellationPrimitive::Isolines: 82 case Maxwell::Tessellation::DomainType::Isolines:
82 return Shader::TessPrimitive::Isolines; 83 return Shader::TessPrimitive::Isolines;
83 case Maxwell::TessellationPrimitive::Triangles: 84 case Maxwell::Tessellation::DomainType::Triangles:
84 return Shader::TessPrimitive::Triangles; 85 return Shader::TessPrimitive::Triangles;
85 case Maxwell::TessellationPrimitive::Quads: 86 case Maxwell::Tessellation::DomainType::Quads:
86 return Shader::TessPrimitive::Quads; 87 return Shader::TessPrimitive::Quads;
87 } 88 }
88 ASSERT(false); 89 ASSERT(false);
@@ -90,11 +91,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
90 }(); 91 }();
91 info.tess_spacing = [&] { 92 info.tess_spacing = [&] {
92 switch (key.tessellation_spacing) { 93 switch (key.tessellation_spacing) {
93 case Maxwell::TessellationSpacing::Equal: 94 case Maxwell::Tessellation::Spacing::Integer:
94 return Shader::TessSpacing::Equal; 95 return Shader::TessSpacing::Equal;
95 case Maxwell::TessellationSpacing::FractionalOdd: 96 case Maxwell::Tessellation::Spacing::FractionalOdd:
96 return Shader::TessSpacing::FractionalOdd; 97 return Shader::TessSpacing::FractionalOdd;
97 case Maxwell::TessellationSpacing::FractionalEven: 98 case Maxwell::Tessellation::Spacing::FractionalEven:
98 return Shader::TessSpacing::FractionalEven; 99 return Shader::TessSpacing::FractionalEven;
99 } 100 }
100 ASSERT(false); 101 ASSERT(false);
@@ -139,28 +140,26 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
139} 140}
140 141
141void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { 142void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
142 std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { 143 std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
143 return VideoCommon::TransformFeedbackState::Layout{ 144 [](const auto& layout) {
144 .stream = layout.stream, 145 return VideoCommon::TransformFeedbackState::Layout{
145 .varying_count = layout.varying_count, 146 .stream = layout.stream,
146 .stride = layout.stride, 147 .varying_count = layout.varying_count,
147 }; 148 .stride = layout.stride,
148 }); 149 };
149 state.varyings = regs.tfb_varying_locs; 150 });
151 state.varyings = regs.stream_out_layout;
150} 152}
151} // Anonymous namespace 153} // Anonymous namespace
152 154
153ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, 155ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_,
154 Tegra::Engines::Maxwell3D& maxwell3d_, 156 const Device& device_, TextureCache& texture_cache_,
155 Tegra::Engines::KeplerCompute& kepler_compute_, 157 BufferCache& buffer_cache_, ProgramManager& program_manager_,
156 Tegra::MemoryManager& gpu_memory_, const Device& device_, 158 StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_)
157 TextureCache& texture_cache_, BufferCache& buffer_cache_, 159 : VideoCommon::ShaderCache{rasterizer_}, emu_window{emu_window_}, device{device_},
158 ProgramManager& program_manager_, StateTracker& state_tracker_, 160 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
159 VideoCore::ShaderNotify& shader_notify_) 161 state_tracker{state_tracker_}, shader_notify{shader_notify_},
160 : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, 162 use_asynchronous_shaders{device.UseAsynchronousShaders()},
161 emu_window{emu_window_}, device{device_}, texture_cache{texture_cache_},
162 buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_},
163 shader_notify{shader_notify_}, use_asynchronous_shaders{device.UseAsynchronousShaders()},
164 profile{ 163 profile{
165 .supported_spirv = 0x00010000, 164 .supported_spirv = 0x00010000,
166 165
@@ -310,16 +309,18 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
310 current_pipeline = nullptr; 309 current_pipeline = nullptr;
311 return nullptr; 310 return nullptr;
312 } 311 }
313 const auto& regs{maxwell3d.regs}; 312 const auto& regs{maxwell3d->regs};
314 graphics_key.raw = 0; 313 graphics_key.raw = 0;
315 graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); 314 graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
316 graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 315 graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0
317 ? regs.draw.topology.Value() 316 ? regs.draw.topology.Value()
318 : Maxwell::PrimitiveTopology{}); 317 : Maxwell::PrimitiveTopology{});
319 graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value()); 318 graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
320 graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value()); 319 graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
321 graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); 320 graphics_key.tessellation_clockwise.Assign(
322 graphics_key.xfb_enabled.Assign(regs.tfb_enabled != 0 ? 1 : 0); 321 regs.tessellation.params.output_primitives.Value() ==
322 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
323 graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
323 if (graphics_key.xfb_enabled) { 324 if (graphics_key.xfb_enabled) {
324 SetXfbState(graphics_key.xfb_state, regs); 325 SetXfbState(graphics_key.xfb_state, regs);
325 } 326 }
@@ -351,13 +352,13 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n
351 } 352 }
352 // If something is using depth, we can assume that games are not rendering anything which 353 // If something is using depth, we can assume that games are not rendering anything which
353 // will be used one time. 354 // will be used one time.
354 if (maxwell3d.regs.zeta_enable) { 355 if (maxwell3d->regs.zeta_enable) {
355 return nullptr; 356 return nullptr;
356 } 357 }
357 // If games are using a small index count, we can assume these are full screen quads. 358 // If games are using a small index count, we can assume these are full screen quads.
358 // Usually these shaders are only used once for building textures so we can assume they 359 // Usually these shaders are only used once for building textures so we can assume they
359 // can't be built async 360 // can't be built async
360 if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { 361 if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
361 return pipeline; 362 return pipeline;
362 } 363 }
363 return nullptr; 364 return nullptr;
@@ -368,7 +369,7 @@ ComputePipeline* ShaderCache::CurrentComputePipeline() {
368 if (!shader) { 369 if (!shader) {
369 return nullptr; 370 return nullptr;
370 } 371 }
371 const auto& qmd{kepler_compute.launch_description}; 372 const auto& qmd{kepler_compute->launch_description};
372 const ComputePipelineKey key{ 373 const ComputePipelineKey key{
373 .unique_hash = shader->unique_hash, 374 .unique_hash = shader->unique_hash,
374 .shared_memory_size = qmd.shared_alloc, 375 .shared_memory_size = qmd.shared_alloc,
@@ -480,9 +481,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
480 previous_program = &program; 481 previous_program = &program;
481 } 482 }
482 auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; 483 auto* const thread_worker{build_in_parallel ? workers.get() : nullptr};
483 return std::make_unique<GraphicsPipeline>( 484 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager,
484 device, texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker, 485 state_tracker, thread_worker, &shader_notify, sources,
485 thread_worker, &shader_notify, sources, sources_spirv, infos, key); 486 sources_spirv, infos, key);
486 487
487} catch (Shader::Exception& exception) { 488} catch (Shader::Exception& exception) {
488 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 489 LOG_ERROR(Render_OpenGL, "{}", exception.what());
@@ -491,9 +492,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
491 492
492std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline( 493std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
493 const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader) { 494 const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader) {
494 const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; 495 const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
495 const auto& qmd{kepler_compute.launch_description}; 496 const auto& qmd{kepler_compute->launch_description};
496 ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; 497 ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
497 env.SetCachedSize(shader->size_bytes); 498 env.SetCachedSize(shader->size_bytes);
498 499
499 main_pools.ReleaseContents(); 500 main_pools.ReleaseContents();
@@ -536,9 +537,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
536 break; 537 break;
537 } 538 }
538 539
539 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, gpu_memory, 540 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager,
540 kepler_compute, program_manager, program.info, code, 541 program.info, code, code_spirv);
541 code_spirv);
542} catch (Shader::Exception& exception) { 542} catch (Shader::Exception& exception) {
543 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 543 LOG_ERROR(Render_OpenGL, "{}", exception.what());
544 return nullptr; 544 return nullptr;
@@ -546,7 +546,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
546 546
547std::unique_ptr<ShaderWorker> ShaderCache::CreateWorkers() const { 547std::unique_ptr<ShaderWorker> ShaderCache::CreateWorkers() const {
548 return std::make_unique<ShaderWorker>(std::max(std::thread::hardware_concurrency(), 2U) - 1, 548 return std::make_unique<ShaderWorker>(std::max(std::thread::hardware_concurrency(), 2U) - 1,
549 "yuzu:ShaderBuilder", 549 "GlShaderBuilder",
550 [this] { return Context{emu_window}; }); 550 [this] { return Context{emu_window}; });
551} 551}
552 552
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index a14269dea..89f181fe3 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -30,12 +30,9 @@ using ShaderWorker = Common::StatefulThreadWorker<ShaderContext::Context>;
30class ShaderCache : public VideoCommon::ShaderCache { 30class ShaderCache : public VideoCommon::ShaderCache {
31public: 31public:
32 explicit ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, 32 explicit ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_,
33 Tegra::Engines::Maxwell3D& maxwell3d_, 33 const Device& device_, TextureCache& texture_cache_,
34 Tegra::Engines::KeplerCompute& kepler_compute_, 34 BufferCache& buffer_cache_, ProgramManager& program_manager_,
35 Tegra::MemoryManager& gpu_memory_, const Device& device_, 35 StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_);
36 TextureCache& texture_cache_, BufferCache& buffer_cache_,
37 ProgramManager& program_manager_, StateTracker& state_tracker_,
38 VideoCore::ShaderNotify& shader_notify_);
39 ~ShaderCache(); 36 ~ShaderCache();
40 37
41 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 38 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 912725ef7..a359f96f1 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -7,8 +7,8 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "video_core/control/channel_state.h"
10#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
11#include "video_core/gpu.h"
12#include "video_core/renderer_opengl/gl_state_tracker.h" 12#include "video_core/renderer_opengl/gl_state_tracker.h"
13 13
14#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) 14#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
@@ -38,12 +38,12 @@ void SetupDirtyColorMasks(Tables& tables) {
38void SetupDirtyVertexInstances(Tables& tables) { 38void SetupDirtyVertexInstances(Tables& tables) {
39 static constexpr std::size_t instance_base_offset = 3; 39 static constexpr std::size_t instance_base_offset = 3;
40 for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) { 40 for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
41 const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); 41 const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]);
42 const std::size_t instance_array_offset = array_offset + instance_base_offset; 42 const std::size_t instance_array_offset = array_offset + instance_base_offset;
43 tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i); 43 tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i);
44 tables[1][instance_array_offset] = VertexInstances; 44 tables[1][instance_array_offset] = VertexInstances;
45 45
46 const std::size_t instance_offset = OFF(instanced_arrays) + i; 46 const std::size_t instance_offset = OFF(vertex_stream_instances) + i;
47 tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i); 47 tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i);
48 tables[1][instance_offset] = VertexInstances; 48 tables[1][instance_offset] = VertexInstances;
49 } 49 }
@@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) {
70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); 70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); 71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
72 72
73 tables[0][OFF(viewport_transform_enabled)] = ViewportTransform; 73 tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform;
74 tables[1][OFF(viewport_transform_enabled)] = Viewports; 74 tables[1][OFF(viewport_scale_offset_enbled)] = Viewports;
75} 75}
76 76
77void SetupDirtyScissors(Tables& tables) { 77void SetupDirtyScissors(Tables& tables) {
@@ -88,7 +88,7 @@ void SetupDirtyPolygonModes(Tables& tables) {
88 88
89 tables[1][OFF(polygon_mode_front)] = PolygonModes; 89 tables[1][OFF(polygon_mode_front)] = PolygonModes;
90 tables[1][OFF(polygon_mode_back)] = PolygonModes; 90 tables[1][OFF(polygon_mode_back)] = PolygonModes;
91 tables[0][OFF(fill_rectangle)] = PolygonModes; 91 tables[0][OFF(fill_via_triangle_mode)] = PolygonModes;
92} 92}
93 93
94void SetupDirtyDepthTest(Tables& tables) { 94void SetupDirtyDepthTest(Tables& tables) {
@@ -100,11 +100,11 @@ void SetupDirtyDepthTest(Tables& tables) {
100 100
101void SetupDirtyStencilTest(Tables& tables) { 101void SetupDirtyStencilTest(Tables& tables) {
102 static constexpr std::array offsets = { 102 static constexpr std::array offsets = {
103 OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref), 103 OFF(stencil_enable), OFF(stencil_front_op.func), OFF(stencil_front_ref),
104 OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail), 104 OFF(stencil_front_func_mask), OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail),
105 OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable), 105 OFF(stencil_front_op.zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable),
106 OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask), 106 OFF(stencil_back_op.func), OFF(stencil_back_ref), OFF(stencil_back_func_mask),
107 OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass), 107 OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), OFF(stencil_back_op.zpass),
108 OFF(stencil_back_mask)}; 108 OFF(stencil_back_mask)};
109 for (const auto offset : offsets) { 109 for (const auto offset : offsets) {
110 tables[0][offset] = StencilTest; 110 tables[0][offset] = StencilTest;
@@ -121,15 +121,15 @@ void SetupDirtyAlphaTest(Tables& tables) {
121void SetupDirtyBlend(Tables& tables) { 121void SetupDirtyBlend(Tables& tables) {
122 FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor); 122 FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
123 123
124 tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled; 124 tables[0][OFF(blend_per_target_enabled)] = BlendIndependentEnabled;
125 125
126 for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) { 126 for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
127 const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]); 127 const std::size_t offset = OFF(blend_per_target) + i * NUM(blend_per_target[0]);
128 FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i); 128 FillBlock(tables[0], offset, NUM(blend_per_target[0]), BlendState0 + i);
129 129
130 tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i); 130 tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i);
131 } 131 }
132 FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates); 132 FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendStates);
133 FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates); 133 FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
134} 134}
135 135
@@ -142,13 +142,14 @@ void SetupDirtyPolygonOffset(Tables& tables) {
142 table[OFF(polygon_offset_fill_enable)] = PolygonOffset; 142 table[OFF(polygon_offset_fill_enable)] = PolygonOffset;
143 table[OFF(polygon_offset_line_enable)] = PolygonOffset; 143 table[OFF(polygon_offset_line_enable)] = PolygonOffset;
144 table[OFF(polygon_offset_point_enable)] = PolygonOffset; 144 table[OFF(polygon_offset_point_enable)] = PolygonOffset;
145 table[OFF(polygon_offset_factor)] = PolygonOffset; 145 table[OFF(slope_scale_depth_bias)] = PolygonOffset;
146 table[OFF(polygon_offset_units)] = PolygonOffset; 146 table[OFF(depth_bias)] = PolygonOffset;
147 table[OFF(polygon_offset_clamp)] = PolygonOffset; 147 table[OFF(depth_bias_clamp)] = PolygonOffset;
148} 148}
149 149
150void SetupDirtyMultisampleControl(Tables& tables) { 150void SetupDirtyMultisampleControl(Tables& tables) {
151 FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl); 151 FillBlock(tables[0], OFF(anti_alias_alpha_control), NUM(anti_alias_alpha_control),
152 MultisampleControl);
152} 153}
153 154
154void SetupDirtyRasterizeEnable(Tables& tables) { 155void SetupDirtyRasterizeEnable(Tables& tables) {
@@ -168,7 +169,7 @@ void SetupDirtyFragmentClampColor(Tables& tables) {
168} 169}
169 170
170void SetupDirtyPointSize(Tables& tables) { 171void SetupDirtyPointSize(Tables& tables) {
171 tables[0][OFF(vp_point_size)] = PointSize; 172 tables[0][OFF(point_size_attribute)] = PointSize;
172 tables[0][OFF(point_size)] = PointSize; 173 tables[0][OFF(point_size)] = PointSize;
173 tables[0][OFF(point_sprite_enable)] = PointSize; 174 tables[0][OFF(point_sprite_enable)] = PointSize;
174} 175}
@@ -176,35 +177,34 @@ void SetupDirtyPointSize(Tables& tables) {
176void SetupDirtyLineWidth(Tables& tables) { 177void SetupDirtyLineWidth(Tables& tables) {
177 tables[0][OFF(line_width_smooth)] = LineWidth; 178 tables[0][OFF(line_width_smooth)] = LineWidth;
178 tables[0][OFF(line_width_aliased)] = LineWidth; 179 tables[0][OFF(line_width_aliased)] = LineWidth;
179 tables[0][OFF(line_smooth_enable)] = LineWidth; 180 tables[0][OFF(line_anti_alias_enable)] = LineWidth;
180} 181}
181 182
182void SetupDirtyClipControl(Tables& tables) { 183void SetupDirtyClipControl(Tables& tables) {
183 auto& table = tables[0]; 184 auto& table = tables[0];
184 table[OFF(screen_y_control)] = ClipControl; 185 table[OFF(window_origin)] = ClipControl;
185 table[OFF(depth_mode)] = ClipControl; 186 table[OFF(depth_mode)] = ClipControl;
186} 187}
187 188
188void SetupDirtyDepthClampEnabled(Tables& tables) { 189void SetupDirtyDepthClampEnabled(Tables& tables) {
189 tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled; 190 tables[0][OFF(viewport_clip_control)] = DepthClampEnabled;
190} 191}
191 192
192void SetupDirtyMisc(Tables& tables) { 193void SetupDirtyMisc(Tables& tables) {
193 auto& table = tables[0]; 194 auto& table = tables[0];
194 195
195 table[OFF(clip_distance_enabled)] = ClipDistances; 196 table[OFF(user_clip_enable)] = ClipDistances;
196 197
197 table[OFF(front_face)] = FrontFace; 198 table[OFF(gl_front_face)] = FrontFace;
198 199
199 table[OFF(cull_test_enabled)] = CullTest; 200 table[OFF(gl_cull_test_enabled)] = CullTest;
200 table[OFF(cull_face)] = CullTest; 201 table[OFF(gl_cull_face)] = CullTest;
201} 202}
202 203
203} // Anonymous namespace 204} // Anonymous namespace
204 205
205StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} { 206void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
206 auto& dirty = gpu.Maxwell3D().dirty; 207 auto& tables{channel_state.maxwell_3d->dirty.tables};
207 auto& tables = dirty.tables;
208 SetupDirtyFlags(tables); 208 SetupDirtyFlags(tables);
209 SetupDirtyColorMasks(tables); 209 SetupDirtyColorMasks(tables);
210 SetupDirtyViewports(tables); 210 SetupDirtyViewports(tables);
@@ -230,4 +230,14 @@ StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags}
230 SetupDirtyMisc(tables); 230 SetupDirtyMisc(tables);
231} 231}
232 232
233void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
234 flags = &channel_state.maxwell_3d->dirty.flags;
235}
236
237void StateTracker::InvalidateState() {
238 flags->set();
239}
240
241StateTracker::StateTracker() : flags{&default_flags} {}
242
233} // namespace OpenGL 243} // 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 04e024f08..19bcf3f35 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -12,8 +12,10 @@
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13 13
14namespace Tegra { 14namespace Tegra {
15class GPU; 15namespace Control {
16struct ChannelState;
16} 17}
18} // namespace Tegra
17 19
18namespace OpenGL { 20namespace OpenGL {
19 21
@@ -83,7 +85,7 @@ static_assert(Last <= std::numeric_limits<u8>::max());
83 85
84class StateTracker { 86class StateTracker {
85public: 87public:
86 explicit StateTracker(Tegra::GPU& gpu); 88 explicit StateTracker();
87 89
88 void BindIndexBuffer(GLuint new_index_buffer) { 90 void BindIndexBuffer(GLuint new_index_buffer) {
89 if (index_buffer == new_index_buffer) { 91 if (index_buffer == new_index_buffer) {
@@ -121,94 +123,107 @@ public:
121 } 123 }
122 124
123 void NotifyScreenDrawVertexArray() { 125 void NotifyScreenDrawVertexArray() {
124 flags[OpenGL::Dirty::VertexFormats] = true; 126 (*flags)[OpenGL::Dirty::VertexFormats] = true;
125 flags[OpenGL::Dirty::VertexFormat0 + 0] = true; 127 (*flags)[OpenGL::Dirty::VertexFormat0 + 0] = true;
126 flags[OpenGL::Dirty::VertexFormat0 + 1] = true; 128 (*flags)[OpenGL::Dirty::VertexFormat0 + 1] = true;
127 129
128 flags[VideoCommon::Dirty::VertexBuffers] = true; 130 (*flags)[VideoCommon::Dirty::VertexBuffers] = true;
129 flags[VideoCommon::Dirty::VertexBuffer0] = true; 131 (*flags)[VideoCommon::Dirty::VertexBuffer0] = true;
130 132
131 flags[OpenGL::Dirty::VertexInstances] = true; 133 (*flags)[OpenGL::Dirty::VertexInstances] = true;
132 flags[OpenGL::Dirty::VertexInstance0 + 0] = true; 134 (*flags)[OpenGL::Dirty::VertexInstance0 + 0] = true;
133 flags[OpenGL::Dirty::VertexInstance0 + 1] = true; 135 (*flags)[OpenGL::Dirty::VertexInstance0 + 1] = true;
134 } 136 }
135 137
136 void NotifyPolygonModes() { 138 void NotifyPolygonModes() {
137 flags[OpenGL::Dirty::PolygonModes] = true; 139 (*flags)[OpenGL::Dirty::PolygonModes] = true;
138 flags[OpenGL::Dirty::PolygonModeFront] = true; 140 (*flags)[OpenGL::Dirty::PolygonModeFront] = true;
139 flags[OpenGL::Dirty::PolygonModeBack] = true; 141 (*flags)[OpenGL::Dirty::PolygonModeBack] = true;
140 } 142 }
141 143
142 void NotifyViewport0() { 144 void NotifyViewport0() {
143 flags[OpenGL::Dirty::Viewports] = true; 145 (*flags)[OpenGL::Dirty::Viewports] = true;
144 flags[OpenGL::Dirty::Viewport0] = true; 146 (*flags)[OpenGL::Dirty::Viewport0] = true;
145 } 147 }
146 148
147 void NotifyScissor0() { 149 void NotifyScissor0() {
148 flags[OpenGL::Dirty::Scissors] = true; 150 (*flags)[OpenGL::Dirty::Scissors] = true;
149 flags[OpenGL::Dirty::Scissor0] = true; 151 (*flags)[OpenGL::Dirty::Scissor0] = true;
150 } 152 }
151 153
152 void NotifyColorMask(size_t index) { 154 void NotifyColorMask(size_t index) {
153 flags[OpenGL::Dirty::ColorMasks] = true; 155 (*flags)[OpenGL::Dirty::ColorMasks] = true;
154 flags[OpenGL::Dirty::ColorMask0 + index] = true; 156 (*flags)[OpenGL::Dirty::ColorMask0 + index] = true;
155 } 157 }
156 158
157 void NotifyBlend0() { 159 void NotifyBlend0() {
158 flags[OpenGL::Dirty::BlendStates] = true; 160 (*flags)[OpenGL::Dirty::BlendStates] = true;
159 flags[OpenGL::Dirty::BlendState0] = true; 161 (*flags)[OpenGL::Dirty::BlendState0] = true;
160 } 162 }
161 163
162 void NotifyFramebuffer() { 164 void NotifyFramebuffer() {
163 flags[VideoCommon::Dirty::RenderTargets] = true; 165 (*flags)[VideoCommon::Dirty::RenderTargets] = true;
164 } 166 }
165 167
166 void NotifyFrontFace() { 168 void NotifyFrontFace() {
167 flags[OpenGL::Dirty::FrontFace] = true; 169 (*flags)[OpenGL::Dirty::FrontFace] = true;
168 } 170 }
169 171
170 void NotifyCullTest() { 172 void NotifyCullTest() {
171 flags[OpenGL::Dirty::CullTest] = true; 173 (*flags)[OpenGL::Dirty::CullTest] = true;
172 } 174 }
173 175
174 void NotifyDepthMask() { 176 void NotifyDepthMask() {
175 flags[OpenGL::Dirty::DepthMask] = true; 177 (*flags)[OpenGL::Dirty::DepthMask] = true;
176 } 178 }
177 179
178 void NotifyDepthTest() { 180 void NotifyDepthTest() {
179 flags[OpenGL::Dirty::DepthTest] = true; 181 (*flags)[OpenGL::Dirty::DepthTest] = true;
180 } 182 }
181 183
182 void NotifyStencilTest() { 184 void NotifyStencilTest() {
183 flags[OpenGL::Dirty::StencilTest] = true; 185 (*flags)[OpenGL::Dirty::StencilTest] = true;
184 } 186 }
185 187
186 void NotifyPolygonOffset() { 188 void NotifyPolygonOffset() {
187 flags[OpenGL::Dirty::PolygonOffset] = true; 189 (*flags)[OpenGL::Dirty::PolygonOffset] = true;
188 } 190 }
189 191
190 void NotifyRasterizeEnable() { 192 void NotifyRasterizeEnable() {
191 flags[OpenGL::Dirty::RasterizeEnable] = true; 193 (*flags)[OpenGL::Dirty::RasterizeEnable] = true;
192 } 194 }
193 195
194 void NotifyFramebufferSRGB() { 196 void NotifyFramebufferSRGB() {
195 flags[OpenGL::Dirty::FramebufferSRGB] = true; 197 (*flags)[OpenGL::Dirty::FramebufferSRGB] = true;
196 } 198 }
197 199
198 void NotifyLogicOp() { 200 void NotifyLogicOp() {
199 flags[OpenGL::Dirty::LogicOp] = true; 201 (*flags)[OpenGL::Dirty::LogicOp] = true;
200 } 202 }
201 203
202 void NotifyClipControl() { 204 void NotifyClipControl() {
203 flags[OpenGL::Dirty::ClipControl] = true; 205 (*flags)[OpenGL::Dirty::ClipControl] = true;
204 } 206 }
205 207
206 void NotifyAlphaTest() { 208 void NotifyAlphaTest() {
207 flags[OpenGL::Dirty::AlphaTest] = true; 209 (*flags)[OpenGL::Dirty::AlphaTest] = true;
208 } 210 }
209 211
212 void NotifyRange(u8 start, u8 end) {
213 for (auto flag = start; flag <= end; flag++) {
214 (*flags)[flag] = true;
215 }
216 }
217
218 void SetupTables(Tegra::Control::ChannelState& channel_state);
219
220 void ChangeChannel(Tegra::Control::ChannelState& channel_state);
221
222 void InvalidateState();
223
210private: 224private:
211 Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; 225 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
226 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags{};
212 227
213 GLuint framebuffer = 0; 228 GLuint framebuffer = 0;
214 GLuint index_buffer = 0; 229 GLuint index_buffer = 0;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 9a72d0d6d..e14f9b2db 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -87,7 +87,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
87 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 87 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
88 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB 88 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
89 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM 89 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
90 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM 90 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // G4R4_UNORM
91 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB 91 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB 92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
93 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB 93 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
@@ -99,6 +99,8 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
99 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM 99 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
100 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB 100 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
101 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM 101 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
102 {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM
103 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB
102 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM 104 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
103 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB 105 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
104 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM 106 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
@@ -124,51 +126,60 @@ inline const FormatTuple& GetFormatTuple(VideoCore::Surface::PixelFormat pixel_f
124 126
125inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { 127inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
126 switch (attrib.type) { 128 switch (attrib.type) {
127 case Maxwell::VertexAttribute::Type::UnsignedNorm: 129 case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
128 case Maxwell::VertexAttribute::Type::UnsignedScaled: 130 ASSERT_MSG(false, "Invalid vertex attribute type!");
129 case Maxwell::VertexAttribute::Type::UnsignedInt: 131 break;
132 case Maxwell::VertexAttribute::Type::UNorm:
133 case Maxwell::VertexAttribute::Type::UScaled:
134 case Maxwell::VertexAttribute::Type::UInt:
130 switch (attrib.size) { 135 switch (attrib.size) {
131 case Maxwell::VertexAttribute::Size::Size_8: 136 case Maxwell::VertexAttribute::Size::Size_R8:
132 case Maxwell::VertexAttribute::Size::Size_8_8: 137 case Maxwell::VertexAttribute::Size::Size_A8:
133 case Maxwell::VertexAttribute::Size::Size_8_8_8: 138 case Maxwell::VertexAttribute::Size::Size_R8_G8:
134 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 139 case Maxwell::VertexAttribute::Size::Size_G8_R8:
140 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
141 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
142 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
135 return GL_UNSIGNED_BYTE; 143 return GL_UNSIGNED_BYTE;
136 case Maxwell::VertexAttribute::Size::Size_16: 144 case Maxwell::VertexAttribute::Size::Size_R16:
137 case Maxwell::VertexAttribute::Size::Size_16_16: 145 case Maxwell::VertexAttribute::Size::Size_R16_G16:
138 case Maxwell::VertexAttribute::Size::Size_16_16_16: 146 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
139 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 147 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
140 return GL_UNSIGNED_SHORT; 148 return GL_UNSIGNED_SHORT;
141 case Maxwell::VertexAttribute::Size::Size_32: 149 case Maxwell::VertexAttribute::Size::Size_R32:
142 case Maxwell::VertexAttribute::Size::Size_32_32: 150 case Maxwell::VertexAttribute::Size::Size_R32_G32:
143 case Maxwell::VertexAttribute::Size::Size_32_32_32: 151 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
144 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 152 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
145 return GL_UNSIGNED_INT; 153 return GL_UNSIGNED_INT;
146 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 154 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
147 return GL_UNSIGNED_INT_2_10_10_10_REV; 155 return GL_UNSIGNED_INT_2_10_10_10_REV;
148 default: 156 default:
149 break; 157 break;
150 } 158 }
151 break; 159 break;
152 case Maxwell::VertexAttribute::Type::SignedNorm: 160 case Maxwell::VertexAttribute::Type::SNorm:
153 case Maxwell::VertexAttribute::Type::SignedScaled: 161 case Maxwell::VertexAttribute::Type::SScaled:
154 case Maxwell::VertexAttribute::Type::SignedInt: 162 case Maxwell::VertexAttribute::Type::SInt:
155 switch (attrib.size) { 163 switch (attrib.size) {
156 case Maxwell::VertexAttribute::Size::Size_8: 164 case Maxwell::VertexAttribute::Size::Size_R8:
157 case Maxwell::VertexAttribute::Size::Size_8_8: 165 case Maxwell::VertexAttribute::Size::Size_A8:
158 case Maxwell::VertexAttribute::Size::Size_8_8_8: 166 case Maxwell::VertexAttribute::Size::Size_R8_G8:
159 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 167 case Maxwell::VertexAttribute::Size::Size_G8_R8:
168 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
169 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
170 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
160 return GL_BYTE; 171 return GL_BYTE;
161 case Maxwell::VertexAttribute::Size::Size_16: 172 case Maxwell::VertexAttribute::Size::Size_R16:
162 case Maxwell::VertexAttribute::Size::Size_16_16: 173 case Maxwell::VertexAttribute::Size::Size_R16_G16:
163 case Maxwell::VertexAttribute::Size::Size_16_16_16: 174 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
164 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 175 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
165 return GL_SHORT; 176 return GL_SHORT;
166 case Maxwell::VertexAttribute::Size::Size_32: 177 case Maxwell::VertexAttribute::Size::Size_R32:
167 case Maxwell::VertexAttribute::Size::Size_32_32: 178 case Maxwell::VertexAttribute::Size::Size_R32_G32:
168 case Maxwell::VertexAttribute::Size::Size_32_32_32: 179 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
169 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 180 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
170 return GL_INT; 181 return GL_INT;
171 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 182 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
172 return GL_INT_2_10_10_10_REV; 183 return GL_INT_2_10_10_10_REV;
173 default: 184 default:
174 break; 185 break;
@@ -176,17 +187,17 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
176 break; 187 break;
177 case Maxwell::VertexAttribute::Type::Float: 188 case Maxwell::VertexAttribute::Type::Float:
178 switch (attrib.size) { 189 switch (attrib.size) {
179 case Maxwell::VertexAttribute::Size::Size_16: 190 case Maxwell::VertexAttribute::Size::Size_R16:
180 case Maxwell::VertexAttribute::Size::Size_16_16: 191 case Maxwell::VertexAttribute::Size::Size_R16_G16:
181 case Maxwell::VertexAttribute::Size::Size_16_16_16: 192 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
182 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 193 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
183 return GL_HALF_FLOAT; 194 return GL_HALF_FLOAT;
184 case Maxwell::VertexAttribute::Size::Size_32: 195 case Maxwell::VertexAttribute::Size::Size_R32:
185 case Maxwell::VertexAttribute::Size::Size_32_32: 196 case Maxwell::VertexAttribute::Size::Size_R32_G32:
186 case Maxwell::VertexAttribute::Size::Size_32_32_32: 197 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
187 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 198 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
188 return GL_FLOAT; 199 return GL_FLOAT;
189 case Maxwell::VertexAttribute::Size::Size_11_11_10: 200 case Maxwell::VertexAttribute::Size::Size_B10_G11_R11:
190 return GL_UNSIGNED_INT_10F_11F_11F_REV; 201 return GL_UNSIGNED_INT_10F_11F_11F_REV;
191 default: 202 default:
192 break; 203 break;
@@ -333,20 +344,20 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
333 344
334inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 345inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
335 switch (equation) { 346 switch (equation) {
336 case Maxwell::Blend::Equation::Add: 347 case Maxwell::Blend::Equation::Add_D3D:
337 case Maxwell::Blend::Equation::AddGL: 348 case Maxwell::Blend::Equation::Add_GL:
338 return GL_FUNC_ADD; 349 return GL_FUNC_ADD;
339 case Maxwell::Blend::Equation::Subtract: 350 case Maxwell::Blend::Equation::Subtract_D3D:
340 case Maxwell::Blend::Equation::SubtractGL: 351 case Maxwell::Blend::Equation::Subtract_GL:
341 return GL_FUNC_SUBTRACT; 352 return GL_FUNC_SUBTRACT;
342 case Maxwell::Blend::Equation::ReverseSubtract: 353 case Maxwell::Blend::Equation::ReverseSubtract_D3D:
343 case Maxwell::Blend::Equation::ReverseSubtractGL: 354 case Maxwell::Blend::Equation::ReverseSubtract_GL:
344 return GL_FUNC_REVERSE_SUBTRACT; 355 return GL_FUNC_REVERSE_SUBTRACT;
345 case Maxwell::Blend::Equation::Min: 356 case Maxwell::Blend::Equation::Min_D3D:
346 case Maxwell::Blend::Equation::MinGL: 357 case Maxwell::Blend::Equation::Min_GL:
347 return GL_MIN; 358 return GL_MIN;
348 case Maxwell::Blend::Equation::Max: 359 case Maxwell::Blend::Equation::Max_D3D:
349 case Maxwell::Blend::Equation::MaxGL: 360 case Maxwell::Blend::Equation::Max_GL:
350 return GL_MAX; 361 return GL_MAX;
351 } 362 }
352 UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); 363 UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation);
@@ -355,62 +366,62 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
355 366
356inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { 367inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
357 switch (factor) { 368 switch (factor) {
358 case Maxwell::Blend::Factor::Zero: 369 case Maxwell::Blend::Factor::Zero_D3D:
359 case Maxwell::Blend::Factor::ZeroGL: 370 case Maxwell::Blend::Factor::Zero_GL:
360 return GL_ZERO; 371 return GL_ZERO;
361 case Maxwell::Blend::Factor::One: 372 case Maxwell::Blend::Factor::One_D3D:
362 case Maxwell::Blend::Factor::OneGL: 373 case Maxwell::Blend::Factor::One_GL:
363 return GL_ONE; 374 return GL_ONE;
364 case Maxwell::Blend::Factor::SourceColor: 375 case Maxwell::Blend::Factor::SourceColor_D3D:
365 case Maxwell::Blend::Factor::SourceColorGL: 376 case Maxwell::Blend::Factor::SourceColor_GL:
366 return GL_SRC_COLOR; 377 return GL_SRC_COLOR;
367 case Maxwell::Blend::Factor::OneMinusSourceColor: 378 case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
368 case Maxwell::Blend::Factor::OneMinusSourceColorGL: 379 case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
369 return GL_ONE_MINUS_SRC_COLOR; 380 return GL_ONE_MINUS_SRC_COLOR;
370 case Maxwell::Blend::Factor::SourceAlpha: 381 case Maxwell::Blend::Factor::SourceAlpha_D3D:
371 case Maxwell::Blend::Factor::SourceAlphaGL: 382 case Maxwell::Blend::Factor::SourceAlpha_GL:
372 return GL_SRC_ALPHA; 383 return GL_SRC_ALPHA;
373 case Maxwell::Blend::Factor::OneMinusSourceAlpha: 384 case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
374 case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: 385 case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
375 return GL_ONE_MINUS_SRC_ALPHA; 386 return GL_ONE_MINUS_SRC_ALPHA;
376 case Maxwell::Blend::Factor::DestAlpha: 387 case Maxwell::Blend::Factor::DestAlpha_D3D:
377 case Maxwell::Blend::Factor::DestAlphaGL: 388 case Maxwell::Blend::Factor::DestAlpha_GL:
378 return GL_DST_ALPHA; 389 return GL_DST_ALPHA;
379 case Maxwell::Blend::Factor::OneMinusDestAlpha: 390 case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
380 case Maxwell::Blend::Factor::OneMinusDestAlphaGL: 391 case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
381 return GL_ONE_MINUS_DST_ALPHA; 392 return GL_ONE_MINUS_DST_ALPHA;
382 case Maxwell::Blend::Factor::DestColor: 393 case Maxwell::Blend::Factor::DestColor_D3D:
383 case Maxwell::Blend::Factor::DestColorGL: 394 case Maxwell::Blend::Factor::DestColor_GL:
384 return GL_DST_COLOR; 395 return GL_DST_COLOR;
385 case Maxwell::Blend::Factor::OneMinusDestColor: 396 case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
386 case Maxwell::Blend::Factor::OneMinusDestColorGL: 397 case Maxwell::Blend::Factor::OneMinusDestColor_GL:
387 return GL_ONE_MINUS_DST_COLOR; 398 return GL_ONE_MINUS_DST_COLOR;
388 case Maxwell::Blend::Factor::SourceAlphaSaturate: 399 case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
389 case Maxwell::Blend::Factor::SourceAlphaSaturateGL: 400 case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
390 return GL_SRC_ALPHA_SATURATE; 401 return GL_SRC_ALPHA_SATURATE;
391 case Maxwell::Blend::Factor::Source1Color: 402 case Maxwell::Blend::Factor::Source1Color_D3D:
392 case Maxwell::Blend::Factor::Source1ColorGL: 403 case Maxwell::Blend::Factor::Source1Color_GL:
393 return GL_SRC1_COLOR; 404 return GL_SRC1_COLOR;
394 case Maxwell::Blend::Factor::OneMinusSource1Color: 405 case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
395 case Maxwell::Blend::Factor::OneMinusSource1ColorGL: 406 case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
396 return GL_ONE_MINUS_SRC1_COLOR; 407 return GL_ONE_MINUS_SRC1_COLOR;
397 case Maxwell::Blend::Factor::Source1Alpha: 408 case Maxwell::Blend::Factor::Source1Alpha_D3D:
398 case Maxwell::Blend::Factor::Source1AlphaGL: 409 case Maxwell::Blend::Factor::Source1Alpha_GL:
399 return GL_SRC1_ALPHA; 410 return GL_SRC1_ALPHA;
400 case Maxwell::Blend::Factor::OneMinusSource1Alpha: 411 case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
401 case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: 412 case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
402 return GL_ONE_MINUS_SRC1_ALPHA; 413 return GL_ONE_MINUS_SRC1_ALPHA;
403 case Maxwell::Blend::Factor::ConstantColor: 414 case Maxwell::Blend::Factor::BlendFactor_D3D:
404 case Maxwell::Blend::Factor::ConstantColorGL: 415 case Maxwell::Blend::Factor::ConstantColor_GL:
405 return GL_CONSTANT_COLOR; 416 return GL_CONSTANT_COLOR;
406 case Maxwell::Blend::Factor::OneMinusConstantColor: 417 case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
407 case Maxwell::Blend::Factor::OneMinusConstantColorGL: 418 case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
408 return GL_ONE_MINUS_CONSTANT_COLOR; 419 return GL_ONE_MINUS_CONSTANT_COLOR;
409 case Maxwell::Blend::Factor::ConstantAlpha: 420 case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
410 case Maxwell::Blend::Factor::ConstantAlphaGL: 421 case Maxwell::Blend::Factor::ConstantAlpha_GL:
411 return GL_CONSTANT_ALPHA; 422 return GL_CONSTANT_ALPHA;
412 case Maxwell::Blend::Factor::OneMinusConstantAlpha: 423 case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
413 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 424 case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
414 return GL_ONE_MINUS_CONSTANT_ALPHA; 425 return GL_ONE_MINUS_CONSTANT_ALPHA;
415 } 426 }
416 UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); 427 UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor);
@@ -419,60 +430,60 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
419 430
420inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { 431inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
421 switch (comparison) { 432 switch (comparison) {
422 case Maxwell::ComparisonOp::Never: 433 case Maxwell::ComparisonOp::Never_D3D:
423 case Maxwell::ComparisonOp::NeverOld: 434 case Maxwell::ComparisonOp::Never_GL:
424 return GL_NEVER; 435 return GL_NEVER;
425 case Maxwell::ComparisonOp::Less: 436 case Maxwell::ComparisonOp::Less_D3D:
426 case Maxwell::ComparisonOp::LessOld: 437 case Maxwell::ComparisonOp::Less_GL:
427 return GL_LESS; 438 return GL_LESS;
428 case Maxwell::ComparisonOp::Equal: 439 case Maxwell::ComparisonOp::Equal_D3D:
429 case Maxwell::ComparisonOp::EqualOld: 440 case Maxwell::ComparisonOp::Equal_GL:
430 return GL_EQUAL; 441 return GL_EQUAL;
431 case Maxwell::ComparisonOp::LessEqual: 442 case Maxwell::ComparisonOp::LessEqual_D3D:
432 case Maxwell::ComparisonOp::LessEqualOld: 443 case Maxwell::ComparisonOp::LessEqual_GL:
433 return GL_LEQUAL; 444 return GL_LEQUAL;
434 case Maxwell::ComparisonOp::Greater: 445 case Maxwell::ComparisonOp::Greater_D3D:
435 case Maxwell::ComparisonOp::GreaterOld: 446 case Maxwell::ComparisonOp::Greater_GL:
436 return GL_GREATER; 447 return GL_GREATER;
437 case Maxwell::ComparisonOp::NotEqual: 448 case Maxwell::ComparisonOp::NotEqual_D3D:
438 case Maxwell::ComparisonOp::NotEqualOld: 449 case Maxwell::ComparisonOp::NotEqual_GL:
439 return GL_NOTEQUAL; 450 return GL_NOTEQUAL;
440 case Maxwell::ComparisonOp::GreaterEqual: 451 case Maxwell::ComparisonOp::GreaterEqual_D3D:
441 case Maxwell::ComparisonOp::GreaterEqualOld: 452 case Maxwell::ComparisonOp::GreaterEqual_GL:
442 return GL_GEQUAL; 453 return GL_GEQUAL;
443 case Maxwell::ComparisonOp::Always: 454 case Maxwell::ComparisonOp::Always_D3D:
444 case Maxwell::ComparisonOp::AlwaysOld: 455 case Maxwell::ComparisonOp::Always_GL:
445 return GL_ALWAYS; 456 return GL_ALWAYS;
446 } 457 }
447 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); 458 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
448 return GL_ALWAYS; 459 return GL_ALWAYS;
449} 460}
450 461
451inline GLenum StencilOp(Maxwell::StencilOp stencil) { 462inline GLenum StencilOp(Maxwell::StencilOp::Op stencil) {
452 switch (stencil) { 463 switch (stencil) {
453 case Maxwell::StencilOp::Keep: 464 case Maxwell::StencilOp::Op::Keep_D3D:
454 case Maxwell::StencilOp::KeepOGL: 465 case Maxwell::StencilOp::Op::Keep_GL:
455 return GL_KEEP; 466 return GL_KEEP;
456 case Maxwell::StencilOp::Zero: 467 case Maxwell::StencilOp::Op::Zero_D3D:
457 case Maxwell::StencilOp::ZeroOGL: 468 case Maxwell::StencilOp::Op::Zero_GL:
458 return GL_ZERO; 469 return GL_ZERO;
459 case Maxwell::StencilOp::Replace: 470 case Maxwell::StencilOp::Op::Replace_D3D:
460 case Maxwell::StencilOp::ReplaceOGL: 471 case Maxwell::StencilOp::Op::Replace_GL:
461 return GL_REPLACE; 472 return GL_REPLACE;
462 case Maxwell::StencilOp::Incr: 473 case Maxwell::StencilOp::Op::IncrSaturate_D3D:
463 case Maxwell::StencilOp::IncrOGL: 474 case Maxwell::StencilOp::Op::IncrSaturate_GL:
464 return GL_INCR; 475 return GL_INCR;
465 case Maxwell::StencilOp::Decr: 476 case Maxwell::StencilOp::Op::DecrSaturate_D3D:
466 case Maxwell::StencilOp::DecrOGL: 477 case Maxwell::StencilOp::Op::DecrSaturate_GL:
467 return GL_DECR; 478 return GL_DECR;
468 case Maxwell::StencilOp::Invert: 479 case Maxwell::StencilOp::Op::Invert_D3D:
469 case Maxwell::StencilOp::InvertOGL: 480 case Maxwell::StencilOp::Op::Invert_GL:
470 return GL_INVERT; 481 return GL_INVERT;
471 case Maxwell::StencilOp::IncrWrap: 482 case Maxwell::StencilOp::Op::Incr_D3D:
472 case Maxwell::StencilOp::IncrWrapOGL: 483 case Maxwell::StencilOp::Op::Incr_GL:
473 return GL_INCR_WRAP; 484 return GL_INCR_WRAP;
474 case Maxwell::StencilOp::DecrWrap: 485 case Maxwell::StencilOp::Op::Decr_D3D:
475 case Maxwell::StencilOp::DecrWrapOGL: 486 case Maxwell::StencilOp::Op::Decr_GL:
476 return GL_DECR_WRAP; 487 return GL_DECR_WRAP;
477 } 488 }
478 UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil); 489 UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil);
@@ -503,39 +514,39 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) {
503 return GL_BACK; 514 return GL_BACK;
504} 515}
505 516
506inline GLenum LogicOp(Maxwell::LogicOperation operation) { 517inline GLenum LogicOp(Maxwell::LogicOp::Op operation) {
507 switch (operation) { 518 switch (operation) {
508 case Maxwell::LogicOperation::Clear: 519 case Maxwell::LogicOp::Op::Clear:
509 return GL_CLEAR; 520 return GL_CLEAR;
510 case Maxwell::LogicOperation::And: 521 case Maxwell::LogicOp::Op::And:
511 return GL_AND; 522 return GL_AND;
512 case Maxwell::LogicOperation::AndReverse: 523 case Maxwell::LogicOp::Op::AndReverse:
513 return GL_AND_REVERSE; 524 return GL_AND_REVERSE;
514 case Maxwell::LogicOperation::Copy: 525 case Maxwell::LogicOp::Op::Copy:
515 return GL_COPY; 526 return GL_COPY;
516 case Maxwell::LogicOperation::AndInverted: 527 case Maxwell::LogicOp::Op::AndInverted:
517 return GL_AND_INVERTED; 528 return GL_AND_INVERTED;
518 case Maxwell::LogicOperation::NoOp: 529 case Maxwell::LogicOp::Op::NoOp:
519 return GL_NOOP; 530 return GL_NOOP;
520 case Maxwell::LogicOperation::Xor: 531 case Maxwell::LogicOp::Op::Xor:
521 return GL_XOR; 532 return GL_XOR;
522 case Maxwell::LogicOperation::Or: 533 case Maxwell::LogicOp::Op::Or:
523 return GL_OR; 534 return GL_OR;
524 case Maxwell::LogicOperation::Nor: 535 case Maxwell::LogicOp::Op::Nor:
525 return GL_NOR; 536 return GL_NOR;
526 case Maxwell::LogicOperation::Equiv: 537 case Maxwell::LogicOp::Op::Equiv:
527 return GL_EQUIV; 538 return GL_EQUIV;
528 case Maxwell::LogicOperation::Invert: 539 case Maxwell::LogicOp::Op::Invert:
529 return GL_INVERT; 540 return GL_INVERT;
530 case Maxwell::LogicOperation::OrReverse: 541 case Maxwell::LogicOp::Op::OrReverse:
531 return GL_OR_REVERSE; 542 return GL_OR_REVERSE;
532 case Maxwell::LogicOperation::CopyInverted: 543 case Maxwell::LogicOp::Op::CopyInverted:
533 return GL_COPY_INVERTED; 544 return GL_COPY_INVERTED;
534 case Maxwell::LogicOperation::OrInverted: 545 case Maxwell::LogicOp::Op::OrInverted:
535 return GL_OR_INVERTED; 546 return GL_OR_INVERTED;
536 case Maxwell::LogicOperation::Nand: 547 case Maxwell::LogicOp::Op::Nand:
537 return GL_NAND; 548 return GL_NAND;
538 case Maxwell::LogicOperation::Set: 549 case Maxwell::LogicOp::Op::Set:
539 return GL_SET; 550 return GL_SET;
540 } 551 }
541 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", operation); 552 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", operation);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 34f3f7a67..8bd5eba7e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -131,7 +131,7 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
131 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 131 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
132 std::unique_ptr<Core::Frontend::GraphicsContext> context_) 132 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
133 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, 133 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
134 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{gpu}, 134 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{},
135 program_manager{device}, 135 program_manager{device},
136 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { 136 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) {
137 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { 137 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 733b454de..f85ed8e5b 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -34,14 +34,15 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
34}; 34};
35 35
36void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { 36void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
37 std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { 37 std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
38 return VideoCommon::TransformFeedbackState::Layout{ 38 [](const auto& layout) {
39 .stream = layout.stream, 39 return VideoCommon::TransformFeedbackState::Layout{
40 .varying_count = layout.varying_count, 40 .stream = layout.stream,
41 .stride = layout.stride, 41 .varying_count = layout.varying_count,
42 }; 42 .stride = layout.stride,
43 }); 43 };
44 state.varyings = regs.tfb_varying_locs; 44 });
45 state.varyings = regs.stream_out_layout;
45} 46}
46} // Anonymous namespace 47} // Anonymous namespace
47 48
@@ -58,33 +59,38 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
58 raw1 = 0; 59 raw1 = 0;
59 extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); 60 extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0);
60 dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); 61 dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0);
61 xfb_enabled.Assign(regs.tfb_enabled != 0); 62 xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
62 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); 63 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
63 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); 64 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
64 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); 65 depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
66 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
67 regs.viewport_clip_control.geometry_clip ==
68 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
69 regs.viewport_clip_control.geometry_clip ==
70 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
65 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 71 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
66 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); 72 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
67 patch_control_points_minus_one.Assign(regs.patch_vertices - 1); 73 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
68 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); 74 tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
69 tessellation_spacing.Assign(static_cast<u32>(regs.tess_mode.spacing.Value())); 75 tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
70 tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); 76 tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
77 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
71 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 78 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
72 logic_op.Assign(PackLogicOp(regs.logic_op.operation)); 79 logic_op.Assign(PackLogicOp(regs.logic_op.op));
73 topology.Assign(regs.draw.topology); 80 topology.Assign(regs.draw.topology);
74 msaa_mode.Assign(regs.multisample_mode); 81 msaa_mode.Assign(regs.anti_alias_samples_mode);
75 82
76 raw2 = 0; 83 raw2 = 0;
77 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 84 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
78 const auto test_func = 85 const auto test_func =
79 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always; 86 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
80 alpha_test_func.Assign(PackComparisonOp(test_func)); 87 alpha_test_func.Assign(PackComparisonOp(test_func));
81 early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); 88 early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
82 depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0); 89 depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0);
83 depth_format.Assign(static_cast<u32>(regs.zeta.format)); 90 depth_format.Assign(static_cast<u32>(regs.zeta.format));
84 y_negate.Assign(regs.screen_y_control.y_negate != 0 ? 1 : 0); 91 y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
85 provoking_vertex_last.Assign(regs.provoking_vertex_last != 0 ? 1 : 0); 92 provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0);
86 conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); 93 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
87 smooth_lines.Assign(regs.line_smooth_enable != 0 ? 1 : 0);
88 94
89 for (size_t i = 0; i < regs.rt.size(); ++i) { 95 for (size_t i = 0; i < regs.rt.size(); ++i) {
90 color_formats[i] = static_cast<u8>(regs.rt[i].format); 96 color_formats[i] = static_cast<u8>(regs.rt[i].format);
@@ -116,8 +122,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
116 maxwell3d.dirty.flags[Dirty::VertexInput] = false; 122 maxwell3d.dirty.flags[Dirty::VertexInput] = false;
117 enabled_divisors = 0; 123 enabled_divisors = 0;
118 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 124 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
119 const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index); 125 const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index);
120 binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0; 126 binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0;
121 enabled_divisors |= (is_enabled ? u64{1} : 0) << index; 127 enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
122 } 128 }
123 for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { 129 for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
@@ -164,17 +170,17 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
164 170
165 // TODO: C++20 Use templated lambda to deduplicate code 171 // TODO: C++20 Use templated lambda to deduplicate code
166 172
167 if (!regs.independent_blend_enable) { 173 if (!regs.blend_per_target_enabled) {
168 const auto& src = regs.blend; 174 if (!regs.blend.enable[index]) {
169 if (!src.enable[index]) {
170 return; 175 return;
171 } 176 }
172 equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); 177 const auto& src = regs.blend;
173 equation_a.Assign(PackBlendEquation(src.equation_a)); 178 equation_rgb.Assign(PackBlendEquation(src.color_op));
174 factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); 179 equation_a.Assign(PackBlendEquation(src.alpha_op));
175 factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); 180 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
176 factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); 181 factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
177 factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); 182 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
183 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
178 enable.Assign(1); 184 enable.Assign(1);
179 return; 185 return;
180 } 186 }
@@ -182,34 +188,34 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
182 if (!regs.blend.enable[index]) { 188 if (!regs.blend.enable[index]) {
183 return; 189 return;
184 } 190 }
185 const auto& src = regs.independent_blend[index]; 191 const auto& src = regs.blend_per_target[index];
186 equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); 192 equation_rgb.Assign(PackBlendEquation(src.color_op));
187 equation_a.Assign(PackBlendEquation(src.equation_a)); 193 equation_a.Assign(PackBlendEquation(src.alpha_op));
188 factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); 194 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
189 factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); 195 factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
190 factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); 196 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
191 factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); 197 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
192 enable.Assign(1); 198 enable.Assign(1);
193} 199}
194 200
195void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { 201void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
196 u32 packed_front_face = PackFrontFace(regs.front_face); 202 u32 packed_front_face = PackFrontFace(regs.gl_front_face);
197 if (regs.screen_y_control.triangle_rast_flip != 0) { 203 if (regs.window_origin.flip_y != 0) {
198 // Flip front face 204 // Flip front face
199 packed_front_face = 1 - packed_front_face; 205 packed_front_face = 1 - packed_front_face;
200 } 206 }
201 207
202 raw1 = 0; 208 raw1 = 0;
203 raw2 = 0; 209 raw2 = 0;
204 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); 210 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
205 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); 211 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
206 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); 212 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
207 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); 213 front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func));
208 if (regs.stencil_two_side_enable) { 214 if (regs.stencil_two_side_enable) {
209 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); 215 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail));
210 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); 216 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail));
211 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); 217 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass));
212 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); 218 back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func));
213 } else { 219 } else {
214 back.action_stencil_fail.Assign(front.action_stencil_fail); 220 back.action_stencil_fail.Assign(front.action_stencil_fail);
215 back.action_depth_fail.Assign(front.action_depth_fail); 221 back.action_depth_fail.Assign(front.action_depth_fail);
@@ -222,9 +228,9 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
222 depth_test_enable.Assign(regs.depth_test_enable); 228 depth_test_enable.Assign(regs.depth_test_enable);
223 front_face.Assign(packed_front_face); 229 front_face.Assign(packed_front_face);
224 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); 230 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
225 cull_face.Assign(PackCullFace(regs.cull_face)); 231 cull_face.Assign(PackCullFace(regs.gl_cull_face));
226 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); 232 cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0);
227 std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) { 233 std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
228 return static_cast<u16>(array.stride.Value()); 234 return static_cast<u16>(array.stride.Value());
229 }); 235 });
230} 236}
@@ -251,41 +257,42 @@ Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcep
251 return static_cast<Maxwell::ComparisonOp>(packed + 1); 257 return static_cast<Maxwell::ComparisonOp>(packed + 1);
252} 258}
253 259
254u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept { 260u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp::Op op) noexcept {
255 switch (op) { 261 switch (op) {
256 case Maxwell::StencilOp::Keep: 262 case Maxwell::StencilOp::Op::Keep_D3D:
257 case Maxwell::StencilOp::KeepOGL: 263 case Maxwell::StencilOp::Op::Keep_GL:
258 return 0; 264 return 0;
259 case Maxwell::StencilOp::Zero: 265 case Maxwell::StencilOp::Op::Zero_D3D:
260 case Maxwell::StencilOp::ZeroOGL: 266 case Maxwell::StencilOp::Op::Zero_GL:
261 return 1; 267 return 1;
262 case Maxwell::StencilOp::Replace: 268 case Maxwell::StencilOp::Op::Replace_D3D:
263 case Maxwell::StencilOp::ReplaceOGL: 269 case Maxwell::StencilOp::Op::Replace_GL:
264 return 2; 270 return 2;
265 case Maxwell::StencilOp::Incr: 271 case Maxwell::StencilOp::Op::IncrSaturate_D3D:
266 case Maxwell::StencilOp::IncrOGL: 272 case Maxwell::StencilOp::Op::IncrSaturate_GL:
267 return 3; 273 return 3;
268 case Maxwell::StencilOp::Decr: 274 case Maxwell::StencilOp::Op::DecrSaturate_D3D:
269 case Maxwell::StencilOp::DecrOGL: 275 case Maxwell::StencilOp::Op::DecrSaturate_GL:
270 return 4; 276 return 4;
271 case Maxwell::StencilOp::Invert: 277 case Maxwell::StencilOp::Op::Invert_D3D:
272 case Maxwell::StencilOp::InvertOGL: 278 case Maxwell::StencilOp::Op::Invert_GL:
273 return 5; 279 return 5;
274 case Maxwell::StencilOp::IncrWrap: 280 case Maxwell::StencilOp::Op::Incr_D3D:
275 case Maxwell::StencilOp::IncrWrapOGL: 281 case Maxwell::StencilOp::Op::Incr_GL:
276 return 6; 282 return 6;
277 case Maxwell::StencilOp::DecrWrap: 283 case Maxwell::StencilOp::Op::Decr_D3D:
278 case Maxwell::StencilOp::DecrWrapOGL: 284 case Maxwell::StencilOp::Op::Decr_GL:
279 return 7; 285 return 7;
280 } 286 }
281 return 0; 287 return 0;
282} 288}
283 289
284Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { 290Maxwell::StencilOp::Op FixedPipelineState::UnpackStencilOp(u32 packed) noexcept {
285 static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero, 291 static constexpr std::array LUT = {
286 Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr, 292 Maxwell::StencilOp::Op::Keep_D3D, Maxwell::StencilOp::Op::Zero_D3D,
287 Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert, 293 Maxwell::StencilOp::Op::Replace_D3D, Maxwell::StencilOp::Op::IncrSaturate_D3D,
288 Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap}; 294 Maxwell::StencilOp::Op::DecrSaturate_D3D, Maxwell::StencilOp::Op::Invert_D3D,
295 Maxwell::StencilOp::Op::Incr_D3D, Maxwell::StencilOp::Op::Decr_D3D};
289 return LUT[packed]; 296 return LUT[packed];
290} 297}
291 298
@@ -318,30 +325,30 @@ Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept
318 return static_cast<Maxwell::PolygonMode>(packed + 0x1B00); 325 return static_cast<Maxwell::PolygonMode>(packed + 0x1B00);
319} 326}
320 327
321u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept { 328u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOp::Op op) noexcept {
322 return static_cast<u32>(op) - 0x1500; 329 return static_cast<u32>(op) - 0x1500;
323} 330}
324 331
325Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { 332Maxwell::LogicOp::Op FixedPipelineState::UnpackLogicOp(u32 packed) noexcept {
326 return static_cast<Maxwell::LogicOperation>(packed + 0x1500); 333 return static_cast<Maxwell::LogicOp::Op>(packed + 0x1500);
327} 334}
328 335
329u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept { 336u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept {
330 switch (equation) { 337 switch (equation) {
331 case Maxwell::Blend::Equation::Add: 338 case Maxwell::Blend::Equation::Add_D3D:
332 case Maxwell::Blend::Equation::AddGL: 339 case Maxwell::Blend::Equation::Add_GL:
333 return 0; 340 return 0;
334 case Maxwell::Blend::Equation::Subtract: 341 case Maxwell::Blend::Equation::Subtract_D3D:
335 case Maxwell::Blend::Equation::SubtractGL: 342 case Maxwell::Blend::Equation::Subtract_GL:
336 return 1; 343 return 1;
337 case Maxwell::Blend::Equation::ReverseSubtract: 344 case Maxwell::Blend::Equation::ReverseSubtract_D3D:
338 case Maxwell::Blend::Equation::ReverseSubtractGL: 345 case Maxwell::Blend::Equation::ReverseSubtract_GL:
339 return 2; 346 return 2;
340 case Maxwell::Blend::Equation::Min: 347 case Maxwell::Blend::Equation::Min_D3D:
341 case Maxwell::Blend::Equation::MinGL: 348 case Maxwell::Blend::Equation::Min_GL:
342 return 3; 349 return 3;
343 case Maxwell::Blend::Equation::Max: 350 case Maxwell::Blend::Equation::Max_D3D:
344 case Maxwell::Blend::Equation::MaxGL: 351 case Maxwell::Blend::Equation::Max_GL:
345 return 4; 352 return 4;
346 } 353 }
347 return 0; 354 return 0;
@@ -349,97 +356,99 @@ u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noe
349 356
350Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept { 357Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept {
351 static constexpr std::array LUT = { 358 static constexpr std::array LUT = {
352 Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract, 359 Maxwell::Blend::Equation::Add_D3D, Maxwell::Blend::Equation::Subtract_D3D,
353 Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min, 360 Maxwell::Blend::Equation::ReverseSubtract_D3D, Maxwell::Blend::Equation::Min_D3D,
354 Maxwell::Blend::Equation::Max}; 361 Maxwell::Blend::Equation::Max_D3D};
355 return LUT[packed]; 362 return LUT[packed];
356} 363}
357 364
358u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept { 365u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept {
359 switch (factor) { 366 switch (factor) {
360 case Maxwell::Blend::Factor::Zero: 367 case Maxwell::Blend::Factor::Zero_D3D:
361 case Maxwell::Blend::Factor::ZeroGL: 368 case Maxwell::Blend::Factor::Zero_GL:
362 return 0; 369 return 0;
363 case Maxwell::Blend::Factor::One: 370 case Maxwell::Blend::Factor::One_D3D:
364 case Maxwell::Blend::Factor::OneGL: 371 case Maxwell::Blend::Factor::One_GL:
365 return 1; 372 return 1;
366 case Maxwell::Blend::Factor::SourceColor: 373 case Maxwell::Blend::Factor::SourceColor_D3D:
367 case Maxwell::Blend::Factor::SourceColorGL: 374 case Maxwell::Blend::Factor::SourceColor_GL:
368 return 2; 375 return 2;
369 case Maxwell::Blend::Factor::OneMinusSourceColor: 376 case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
370 case Maxwell::Blend::Factor::OneMinusSourceColorGL: 377 case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
371 return 3; 378 return 3;
372 case Maxwell::Blend::Factor::SourceAlpha: 379 case Maxwell::Blend::Factor::SourceAlpha_D3D:
373 case Maxwell::Blend::Factor::SourceAlphaGL: 380 case Maxwell::Blend::Factor::SourceAlpha_GL:
374 return 4; 381 return 4;
375 case Maxwell::Blend::Factor::OneMinusSourceAlpha: 382 case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
376 case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: 383 case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
377 return 5; 384 return 5;
378 case Maxwell::Blend::Factor::DestAlpha: 385 case Maxwell::Blend::Factor::DestAlpha_D3D:
379 case Maxwell::Blend::Factor::DestAlphaGL: 386 case Maxwell::Blend::Factor::DestAlpha_GL:
380 return 6; 387 return 6;
381 case Maxwell::Blend::Factor::OneMinusDestAlpha: 388 case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
382 case Maxwell::Blend::Factor::OneMinusDestAlphaGL: 389 case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
383 return 7; 390 return 7;
384 case Maxwell::Blend::Factor::DestColor: 391 case Maxwell::Blend::Factor::DestColor_D3D:
385 case Maxwell::Blend::Factor::DestColorGL: 392 case Maxwell::Blend::Factor::DestColor_GL:
386 return 8; 393 return 8;
387 case Maxwell::Blend::Factor::OneMinusDestColor: 394 case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
388 case Maxwell::Blend::Factor::OneMinusDestColorGL: 395 case Maxwell::Blend::Factor::OneMinusDestColor_GL:
389 return 9; 396 return 9;
390 case Maxwell::Blend::Factor::SourceAlphaSaturate: 397 case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
391 case Maxwell::Blend::Factor::SourceAlphaSaturateGL: 398 case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
392 return 10; 399 return 10;
393 case Maxwell::Blend::Factor::Source1Color: 400 case Maxwell::Blend::Factor::Source1Color_D3D:
394 case Maxwell::Blend::Factor::Source1ColorGL: 401 case Maxwell::Blend::Factor::Source1Color_GL:
395 return 11; 402 return 11;
396 case Maxwell::Blend::Factor::OneMinusSource1Color: 403 case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
397 case Maxwell::Blend::Factor::OneMinusSource1ColorGL: 404 case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
398 return 12; 405 return 12;
399 case Maxwell::Blend::Factor::Source1Alpha: 406 case Maxwell::Blend::Factor::Source1Alpha_D3D:
400 case Maxwell::Blend::Factor::Source1AlphaGL: 407 case Maxwell::Blend::Factor::Source1Alpha_GL:
401 return 13; 408 return 13;
402 case Maxwell::Blend::Factor::OneMinusSource1Alpha: 409 case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
403 case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: 410 case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
404 return 14; 411 return 14;
405 case Maxwell::Blend::Factor::ConstantColor: 412 case Maxwell::Blend::Factor::BlendFactor_D3D:
406 case Maxwell::Blend::Factor::ConstantColorGL: 413 case Maxwell::Blend::Factor::ConstantColor_GL:
407 return 15; 414 return 15;
408 case Maxwell::Blend::Factor::OneMinusConstantColor: 415 case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
409 case Maxwell::Blend::Factor::OneMinusConstantColorGL: 416 case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
410 return 16; 417 return 16;
411 case Maxwell::Blend::Factor::ConstantAlpha: 418 case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
412 case Maxwell::Blend::Factor::ConstantAlphaGL: 419 case Maxwell::Blend::Factor::ConstantAlpha_GL:
413 return 17; 420 return 17;
414 case Maxwell::Blend::Factor::OneMinusConstantAlpha: 421 case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
415 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 422 case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
416 return 18; 423 return 18;
417 } 424 }
425 UNIMPLEMENTED_MSG("Unknown blend factor {}", static_cast<u32>(factor));
418 return 0; 426 return 0;
419} 427}
420 428
421Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept { 429Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept {
422 static constexpr std::array LUT = { 430 static constexpr std::array LUT = {
423 Maxwell::Blend::Factor::Zero, 431 Maxwell::Blend::Factor::Zero_D3D,
424 Maxwell::Blend::Factor::One, 432 Maxwell::Blend::Factor::One_D3D,
425 Maxwell::Blend::Factor::SourceColor, 433 Maxwell::Blend::Factor::SourceColor_D3D,
426 Maxwell::Blend::Factor::OneMinusSourceColor, 434 Maxwell::Blend::Factor::OneMinusSourceColor_D3D,
427 Maxwell::Blend::Factor::SourceAlpha, 435 Maxwell::Blend::Factor::SourceAlpha_D3D,
428 Maxwell::Blend::Factor::OneMinusSourceAlpha, 436 Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D,
429 Maxwell::Blend::Factor::DestAlpha, 437 Maxwell::Blend::Factor::DestAlpha_D3D,
430 Maxwell::Blend::Factor::OneMinusDestAlpha, 438 Maxwell::Blend::Factor::OneMinusDestAlpha_D3D,
431 Maxwell::Blend::Factor::DestColor, 439 Maxwell::Blend::Factor::DestColor_D3D,
432 Maxwell::Blend::Factor::OneMinusDestColor, 440 Maxwell::Blend::Factor::OneMinusDestColor_D3D,
433 Maxwell::Blend::Factor::SourceAlphaSaturate, 441 Maxwell::Blend::Factor::SourceAlphaSaturate_D3D,
434 Maxwell::Blend::Factor::Source1Color, 442 Maxwell::Blend::Factor::Source1Color_D3D,
435 Maxwell::Blend::Factor::OneMinusSource1Color, 443 Maxwell::Blend::Factor::OneMinusSource1Color_D3D,
436 Maxwell::Blend::Factor::Source1Alpha, 444 Maxwell::Blend::Factor::Source1Alpha_D3D,
437 Maxwell::Blend::Factor::OneMinusSource1Alpha, 445 Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D,
438 Maxwell::Blend::Factor::ConstantColor, 446 Maxwell::Blend::Factor::BlendFactor_D3D,
439 Maxwell::Blend::Factor::OneMinusConstantColor, 447 Maxwell::Blend::Factor::OneMinusBlendFactor_D3D,
440 Maxwell::Blend::Factor::ConstantAlpha, 448 Maxwell::Blend::Factor::BothSourceAlpha_D3D,
441 Maxwell::Blend::Factor::OneMinusConstantAlpha, 449 Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D,
442 }; 450 };
451 ASSERT(packed < LUT.size());
443 return LUT[packed]; 452 return LUT[packed];
444} 453}
445 454
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 9d60756e5..43441209c 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -21,8 +21,8 @@ struct FixedPipelineState {
21 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; 21 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
22 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; 22 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
23 23
24 static u32 PackStencilOp(Maxwell::StencilOp op) noexcept; 24 static u32 PackStencilOp(Maxwell::StencilOp::Op op) noexcept;
25 static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept; 25 static Maxwell::StencilOp::Op UnpackStencilOp(u32 packed) noexcept;
26 26
27 static u32 PackCullFace(Maxwell::CullFace cull) noexcept; 27 static u32 PackCullFace(Maxwell::CullFace cull) noexcept;
28 static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept; 28 static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept;
@@ -33,8 +33,8 @@ struct FixedPipelineState {
33 static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept; 33 static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept;
34 static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept; 34 static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept;
35 35
36 static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept; 36 static u32 PackLogicOp(Maxwell::LogicOp::Op op) noexcept;
37 static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept; 37 static Maxwell::LogicOp::Op UnpackLogicOp(u32 packed) noexcept;
38 38
39 static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept; 39 static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept;
40 static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept; 40 static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept;
@@ -113,15 +113,15 @@ struct FixedPipelineState {
113 BitField<Position + 6, 3, u32> action_depth_pass; 113 BitField<Position + 6, 3, u32> action_depth_pass;
114 BitField<Position + 9, 3, u32> test_func; 114 BitField<Position + 9, 3, u32> test_func;
115 115
116 Maxwell::StencilOp ActionStencilFail() const noexcept { 116 Maxwell::StencilOp::Op ActionStencilFail() const noexcept {
117 return UnpackStencilOp(action_stencil_fail); 117 return UnpackStencilOp(action_stencil_fail);
118 } 118 }
119 119
120 Maxwell::StencilOp ActionDepthFail() const noexcept { 120 Maxwell::StencilOp::Op ActionDepthFail() const noexcept {
121 return UnpackStencilOp(action_depth_fail); 121 return UnpackStencilOp(action_depth_fail);
122 } 122 }
123 123
124 Maxwell::StencilOp ActionDepthPass() const noexcept { 124 Maxwell::StencilOp::Op ActionDepthPass() const noexcept {
125 return UnpackStencilOp(action_depth_pass); 125 return UnpackStencilOp(action_depth_pass);
126 } 126 }
127 127
@@ -193,7 +193,6 @@ struct FixedPipelineState {
193 BitField<6, 5, u32> depth_format; 193 BitField<6, 5, u32> depth_format;
194 BitField<11, 1, u32> y_negate; 194 BitField<11, 1, u32> y_negate;
195 BitField<12, 1, u32> provoking_vertex_last; 195 BitField<12, 1, u32> provoking_vertex_last;
196 BitField<13, 1, u32> conservative_raster_enable;
197 BitField<14, 1, u32> smooth_lines; 196 BitField<14, 1, u32> smooth_lines;
198 }; 197 };
199 std::array<u8, Maxwell::NumRenderTargets> color_formats; 198 std::array<u8, Maxwell::NumRenderTargets> color_formats;
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index bdb71dc53..5c156087b 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -184,7 +184,7 @@ struct FormatTuple {
184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB 184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB 185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM 186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
187 {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM 187 {VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM
188 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB 188 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
189 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB 189 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
190 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB 190 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB
@@ -196,6 +196,8 @@ struct FormatTuple {
196 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM 196 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
197 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB 197 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
198 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM 198 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
199 {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM
200 {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB
199 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM 201 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
200 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB 202 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
201 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM 203 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
@@ -321,161 +323,182 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
321 Maxwell::VertexAttribute::Size size) { 323 Maxwell::VertexAttribute::Size size) {
322 const VkFormat format{([&]() { 324 const VkFormat format{([&]() {
323 switch (type) { 325 switch (type) {
324 case Maxwell::VertexAttribute::Type::UnsignedNorm: 326 case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
327 ASSERT_MSG(false, "Invalid vertex attribute type!");
328 break;
329 case Maxwell::VertexAttribute::Type::UNorm:
325 switch (size) { 330 switch (size) {
326 case Maxwell::VertexAttribute::Size::Size_8: 331 case Maxwell::VertexAttribute::Size::Size_R8:
332 case Maxwell::VertexAttribute::Size::Size_A8:
327 return VK_FORMAT_R8_UNORM; 333 return VK_FORMAT_R8_UNORM;
328 case Maxwell::VertexAttribute::Size::Size_8_8: 334 case Maxwell::VertexAttribute::Size::Size_R8_G8:
335 case Maxwell::VertexAttribute::Size::Size_G8_R8:
329 return VK_FORMAT_R8G8_UNORM; 336 return VK_FORMAT_R8G8_UNORM;
330 case Maxwell::VertexAttribute::Size::Size_8_8_8: 337 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
331 return VK_FORMAT_R8G8B8_UNORM; 338 return VK_FORMAT_R8G8B8_UNORM;
332 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 339 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
340 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
333 return VK_FORMAT_R8G8B8A8_UNORM; 341 return VK_FORMAT_R8G8B8A8_UNORM;
334 case Maxwell::VertexAttribute::Size::Size_16: 342 case Maxwell::VertexAttribute::Size::Size_R16:
335 return VK_FORMAT_R16_UNORM; 343 return VK_FORMAT_R16_UNORM;
336 case Maxwell::VertexAttribute::Size::Size_16_16: 344 case Maxwell::VertexAttribute::Size::Size_R16_G16:
337 return VK_FORMAT_R16G16_UNORM; 345 return VK_FORMAT_R16G16_UNORM;
338 case Maxwell::VertexAttribute::Size::Size_16_16_16: 346 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
339 return VK_FORMAT_R16G16B16_UNORM; 347 return VK_FORMAT_R16G16B16_UNORM;
340 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 348 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
341 return VK_FORMAT_R16G16B16A16_UNORM; 349 return VK_FORMAT_R16G16B16A16_UNORM;
342 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 350 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
343 return VK_FORMAT_A2B10G10R10_UNORM_PACK32; 351 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
344 default: 352 default:
345 break; 353 break;
346 } 354 }
347 break; 355 break;
348 case Maxwell::VertexAttribute::Type::SignedNorm: 356 case Maxwell::VertexAttribute::Type::SNorm:
349 switch (size) { 357 switch (size) {
350 case Maxwell::VertexAttribute::Size::Size_8: 358 case Maxwell::VertexAttribute::Size::Size_R8:
359 case Maxwell::VertexAttribute::Size::Size_A8:
351 return VK_FORMAT_R8_SNORM; 360 return VK_FORMAT_R8_SNORM;
352 case Maxwell::VertexAttribute::Size::Size_8_8: 361 case Maxwell::VertexAttribute::Size::Size_R8_G8:
362 case Maxwell::VertexAttribute::Size::Size_G8_R8:
353 return VK_FORMAT_R8G8_SNORM; 363 return VK_FORMAT_R8G8_SNORM;
354 case Maxwell::VertexAttribute::Size::Size_8_8_8: 364 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
355 return VK_FORMAT_R8G8B8_SNORM; 365 return VK_FORMAT_R8G8B8_SNORM;
356 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 366 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
367 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
357 return VK_FORMAT_R8G8B8A8_SNORM; 368 return VK_FORMAT_R8G8B8A8_SNORM;
358 case Maxwell::VertexAttribute::Size::Size_16: 369 case Maxwell::VertexAttribute::Size::Size_R16:
359 return VK_FORMAT_R16_SNORM; 370 return VK_FORMAT_R16_SNORM;
360 case Maxwell::VertexAttribute::Size::Size_16_16: 371 case Maxwell::VertexAttribute::Size::Size_R16_G16:
361 return VK_FORMAT_R16G16_SNORM; 372 return VK_FORMAT_R16G16_SNORM;
362 case Maxwell::VertexAttribute::Size::Size_16_16_16: 373 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
363 return VK_FORMAT_R16G16B16_SNORM; 374 return VK_FORMAT_R16G16B16_SNORM;
364 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 375 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
365 return VK_FORMAT_R16G16B16A16_SNORM; 376 return VK_FORMAT_R16G16B16A16_SNORM;
366 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 377 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
367 return VK_FORMAT_A2B10G10R10_SNORM_PACK32; 378 return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
368 default: 379 default:
369 break; 380 break;
370 } 381 }
371 break; 382 break;
372 case Maxwell::VertexAttribute::Type::UnsignedScaled: 383 case Maxwell::VertexAttribute::Type::UScaled:
373 switch (size) { 384 switch (size) {
374 case Maxwell::VertexAttribute::Size::Size_8: 385 case Maxwell::VertexAttribute::Size::Size_R8:
386 case Maxwell::VertexAttribute::Size::Size_A8:
375 return VK_FORMAT_R8_USCALED; 387 return VK_FORMAT_R8_USCALED;
376 case Maxwell::VertexAttribute::Size::Size_8_8: 388 case Maxwell::VertexAttribute::Size::Size_R8_G8:
389 case Maxwell::VertexAttribute::Size::Size_G8_R8:
377 return VK_FORMAT_R8G8_USCALED; 390 return VK_FORMAT_R8G8_USCALED;
378 case Maxwell::VertexAttribute::Size::Size_8_8_8: 391 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
379 return VK_FORMAT_R8G8B8_USCALED; 392 return VK_FORMAT_R8G8B8_USCALED;
380 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 393 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
394 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
381 return VK_FORMAT_R8G8B8A8_USCALED; 395 return VK_FORMAT_R8G8B8A8_USCALED;
382 case Maxwell::VertexAttribute::Size::Size_16: 396 case Maxwell::VertexAttribute::Size::Size_R16:
383 return VK_FORMAT_R16_USCALED; 397 return VK_FORMAT_R16_USCALED;
384 case Maxwell::VertexAttribute::Size::Size_16_16: 398 case Maxwell::VertexAttribute::Size::Size_R16_G16:
385 return VK_FORMAT_R16G16_USCALED; 399 return VK_FORMAT_R16G16_USCALED;
386 case Maxwell::VertexAttribute::Size::Size_16_16_16: 400 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
387 return VK_FORMAT_R16G16B16_USCALED; 401 return VK_FORMAT_R16G16B16_USCALED;
388 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 402 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
389 return VK_FORMAT_R16G16B16A16_USCALED; 403 return VK_FORMAT_R16G16B16A16_USCALED;
390 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 404 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
391 return VK_FORMAT_A2B10G10R10_USCALED_PACK32; 405 return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
392 default: 406 default:
393 break; 407 break;
394 } 408 }
395 break; 409 break;
396 case Maxwell::VertexAttribute::Type::SignedScaled: 410 case Maxwell::VertexAttribute::Type::SScaled:
397 switch (size) { 411 switch (size) {
398 case Maxwell::VertexAttribute::Size::Size_8: 412 case Maxwell::VertexAttribute::Size::Size_R8:
413 case Maxwell::VertexAttribute::Size::Size_A8:
399 return VK_FORMAT_R8_SSCALED; 414 return VK_FORMAT_R8_SSCALED;
400 case Maxwell::VertexAttribute::Size::Size_8_8: 415 case Maxwell::VertexAttribute::Size::Size_R8_G8:
416 case Maxwell::VertexAttribute::Size::Size_G8_R8:
401 return VK_FORMAT_R8G8_SSCALED; 417 return VK_FORMAT_R8G8_SSCALED;
402 case Maxwell::VertexAttribute::Size::Size_8_8_8: 418 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
403 return VK_FORMAT_R8G8B8_SSCALED; 419 return VK_FORMAT_R8G8B8_SSCALED;
404 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 420 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
421 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
405 return VK_FORMAT_R8G8B8A8_SSCALED; 422 return VK_FORMAT_R8G8B8A8_SSCALED;
406 case Maxwell::VertexAttribute::Size::Size_16: 423 case Maxwell::VertexAttribute::Size::Size_R16:
407 return VK_FORMAT_R16_SSCALED; 424 return VK_FORMAT_R16_SSCALED;
408 case Maxwell::VertexAttribute::Size::Size_16_16: 425 case Maxwell::VertexAttribute::Size::Size_R16_G16:
409 return VK_FORMAT_R16G16_SSCALED; 426 return VK_FORMAT_R16G16_SSCALED;
410 case Maxwell::VertexAttribute::Size::Size_16_16_16: 427 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
411 return VK_FORMAT_R16G16B16_SSCALED; 428 return VK_FORMAT_R16G16B16_SSCALED;
412 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 429 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
413 return VK_FORMAT_R16G16B16A16_SSCALED; 430 return VK_FORMAT_R16G16B16A16_SSCALED;
414 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 431 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
415 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; 432 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
416 default: 433 default:
417 break; 434 break;
418 } 435 }
419 break; 436 break;
420 case Maxwell::VertexAttribute::Type::UnsignedInt: 437 case Maxwell::VertexAttribute::Type::UInt:
421 switch (size) { 438 switch (size) {
422 case Maxwell::VertexAttribute::Size::Size_8: 439 case Maxwell::VertexAttribute::Size::Size_R8:
440 case Maxwell::VertexAttribute::Size::Size_A8:
423 return VK_FORMAT_R8_UINT; 441 return VK_FORMAT_R8_UINT;
424 case Maxwell::VertexAttribute::Size::Size_8_8: 442 case Maxwell::VertexAttribute::Size::Size_R8_G8:
443 case Maxwell::VertexAttribute::Size::Size_G8_R8:
425 return VK_FORMAT_R8G8_UINT; 444 return VK_FORMAT_R8G8_UINT;
426 case Maxwell::VertexAttribute::Size::Size_8_8_8: 445 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
427 return VK_FORMAT_R8G8B8_UINT; 446 return VK_FORMAT_R8G8B8_UINT;
428 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 447 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
448 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
429 return VK_FORMAT_R8G8B8A8_UINT; 449 return VK_FORMAT_R8G8B8A8_UINT;
430 case Maxwell::VertexAttribute::Size::Size_16: 450 case Maxwell::VertexAttribute::Size::Size_R16:
431 return VK_FORMAT_R16_UINT; 451 return VK_FORMAT_R16_UINT;
432 case Maxwell::VertexAttribute::Size::Size_16_16: 452 case Maxwell::VertexAttribute::Size::Size_R16_G16:
433 return VK_FORMAT_R16G16_UINT; 453 return VK_FORMAT_R16G16_UINT;
434 case Maxwell::VertexAttribute::Size::Size_16_16_16: 454 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
435 return VK_FORMAT_R16G16B16_UINT; 455 return VK_FORMAT_R16G16B16_UINT;
436 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 456 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
437 return VK_FORMAT_R16G16B16A16_UINT; 457 return VK_FORMAT_R16G16B16A16_UINT;
438 case Maxwell::VertexAttribute::Size::Size_32: 458 case Maxwell::VertexAttribute::Size::Size_R32:
439 return VK_FORMAT_R32_UINT; 459 return VK_FORMAT_R32_UINT;
440 case Maxwell::VertexAttribute::Size::Size_32_32: 460 case Maxwell::VertexAttribute::Size::Size_R32_G32:
441 return VK_FORMAT_R32G32_UINT; 461 return VK_FORMAT_R32G32_UINT;
442 case Maxwell::VertexAttribute::Size::Size_32_32_32: 462 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
443 return VK_FORMAT_R32G32B32_UINT; 463 return VK_FORMAT_R32G32B32_UINT;
444 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 464 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
445 return VK_FORMAT_R32G32B32A32_UINT; 465 return VK_FORMAT_R32G32B32A32_UINT;
446 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 466 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
447 return VK_FORMAT_A2B10G10R10_UINT_PACK32; 467 return VK_FORMAT_A2B10G10R10_UINT_PACK32;
448 default: 468 default:
449 break; 469 break;
450 } 470 }
451 break; 471 break;
452 case Maxwell::VertexAttribute::Type::SignedInt: 472 case Maxwell::VertexAttribute::Type::SInt:
453 switch (size) { 473 switch (size) {
454 case Maxwell::VertexAttribute::Size::Size_8: 474 case Maxwell::VertexAttribute::Size::Size_R8:
475 case Maxwell::VertexAttribute::Size::Size_A8:
455 return VK_FORMAT_R8_SINT; 476 return VK_FORMAT_R8_SINT;
456 case Maxwell::VertexAttribute::Size::Size_8_8: 477 case Maxwell::VertexAttribute::Size::Size_R8_G8:
478 case Maxwell::VertexAttribute::Size::Size_G8_R8:
457 return VK_FORMAT_R8G8_SINT; 479 return VK_FORMAT_R8G8_SINT;
458 case Maxwell::VertexAttribute::Size::Size_8_8_8: 480 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
459 return VK_FORMAT_R8G8B8_SINT; 481 return VK_FORMAT_R8G8B8_SINT;
460 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 482 case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
483 case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
461 return VK_FORMAT_R8G8B8A8_SINT; 484 return VK_FORMAT_R8G8B8A8_SINT;
462 case Maxwell::VertexAttribute::Size::Size_16: 485 case Maxwell::VertexAttribute::Size::Size_R16:
463 return VK_FORMAT_R16_SINT; 486 return VK_FORMAT_R16_SINT;
464 case Maxwell::VertexAttribute::Size::Size_16_16: 487 case Maxwell::VertexAttribute::Size::Size_R16_G16:
465 return VK_FORMAT_R16G16_SINT; 488 return VK_FORMAT_R16G16_SINT;
466 case Maxwell::VertexAttribute::Size::Size_16_16_16: 489 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
467 return VK_FORMAT_R16G16B16_SINT; 490 return VK_FORMAT_R16G16B16_SINT;
468 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 491 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
469 return VK_FORMAT_R16G16B16A16_SINT; 492 return VK_FORMAT_R16G16B16A16_SINT;
470 case Maxwell::VertexAttribute::Size::Size_32: 493 case Maxwell::VertexAttribute::Size::Size_R32:
471 return VK_FORMAT_R32_SINT; 494 return VK_FORMAT_R32_SINT;
472 case Maxwell::VertexAttribute::Size::Size_32_32: 495 case Maxwell::VertexAttribute::Size::Size_R32_G32:
473 return VK_FORMAT_R32G32_SINT; 496 return VK_FORMAT_R32G32_SINT;
474 case Maxwell::VertexAttribute::Size::Size_32_32_32: 497 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
475 return VK_FORMAT_R32G32B32_SINT; 498 return VK_FORMAT_R32G32B32_SINT;
476 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 499 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
477 return VK_FORMAT_R32G32B32A32_SINT; 500 return VK_FORMAT_R32G32B32A32_SINT;
478 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 501 case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
479 return VK_FORMAT_A2B10G10R10_SINT_PACK32; 502 return VK_FORMAT_A2B10G10R10_SINT_PACK32;
480 default: 503 default:
481 break; 504 break;
@@ -483,23 +506,23 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
483 break; 506 break;
484 case Maxwell::VertexAttribute::Type::Float: 507 case Maxwell::VertexAttribute::Type::Float:
485 switch (size) { 508 switch (size) {
486 case Maxwell::VertexAttribute::Size::Size_16: 509 case Maxwell::VertexAttribute::Size::Size_R16:
487 return VK_FORMAT_R16_SFLOAT; 510 return VK_FORMAT_R16_SFLOAT;
488 case Maxwell::VertexAttribute::Size::Size_16_16: 511 case Maxwell::VertexAttribute::Size::Size_R16_G16:
489 return VK_FORMAT_R16G16_SFLOAT; 512 return VK_FORMAT_R16G16_SFLOAT;
490 case Maxwell::VertexAttribute::Size::Size_16_16_16: 513 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
491 return VK_FORMAT_R16G16B16_SFLOAT; 514 return VK_FORMAT_R16G16B16_SFLOAT;
492 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 515 case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
493 return VK_FORMAT_R16G16B16A16_SFLOAT; 516 return VK_FORMAT_R16G16B16A16_SFLOAT;
494 case Maxwell::VertexAttribute::Size::Size_32: 517 case Maxwell::VertexAttribute::Size::Size_R32:
495 return VK_FORMAT_R32_SFLOAT; 518 return VK_FORMAT_R32_SFLOAT;
496 case Maxwell::VertexAttribute::Size::Size_32_32: 519 case Maxwell::VertexAttribute::Size::Size_R32_G32:
497 return VK_FORMAT_R32G32_SFLOAT; 520 return VK_FORMAT_R32G32_SFLOAT;
498 case Maxwell::VertexAttribute::Size::Size_32_32_32: 521 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
499 return VK_FORMAT_R32G32B32_SFLOAT; 522 return VK_FORMAT_R32G32B32_SFLOAT;
500 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 523 case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
501 return VK_FORMAT_R32G32B32A32_SFLOAT; 524 return VK_FORMAT_R32G32B32A32_SFLOAT;
502 case Maxwell::VertexAttribute::Size::Size_11_11_10: 525 case Maxwell::VertexAttribute::Size::Size_B10_G11_R11:
503 return VK_FORMAT_B10G11R11_UFLOAT_PACK32; 526 return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
504 default: 527 default:
505 break; 528 break;
@@ -519,29 +542,29 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
519 542
520VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { 543VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) {
521 switch (comparison) { 544 switch (comparison) {
522 case Maxwell::ComparisonOp::Never: 545 case Maxwell::ComparisonOp::Never_D3D:
523 case Maxwell::ComparisonOp::NeverOld: 546 case Maxwell::ComparisonOp::Never_GL:
524 return VK_COMPARE_OP_NEVER; 547 return VK_COMPARE_OP_NEVER;
525 case Maxwell::ComparisonOp::Less: 548 case Maxwell::ComparisonOp::Less_D3D:
526 case Maxwell::ComparisonOp::LessOld: 549 case Maxwell::ComparisonOp::Less_GL:
527 return VK_COMPARE_OP_LESS; 550 return VK_COMPARE_OP_LESS;
528 case Maxwell::ComparisonOp::Equal: 551 case Maxwell::ComparisonOp::Equal_D3D:
529 case Maxwell::ComparisonOp::EqualOld: 552 case Maxwell::ComparisonOp::Equal_GL:
530 return VK_COMPARE_OP_EQUAL; 553 return VK_COMPARE_OP_EQUAL;
531 case Maxwell::ComparisonOp::LessEqual: 554 case Maxwell::ComparisonOp::LessEqual_D3D:
532 case Maxwell::ComparisonOp::LessEqualOld: 555 case Maxwell::ComparisonOp::LessEqual_GL:
533 return VK_COMPARE_OP_LESS_OR_EQUAL; 556 return VK_COMPARE_OP_LESS_OR_EQUAL;
534 case Maxwell::ComparisonOp::Greater: 557 case Maxwell::ComparisonOp::Greater_D3D:
535 case Maxwell::ComparisonOp::GreaterOld: 558 case Maxwell::ComparisonOp::Greater_GL:
536 return VK_COMPARE_OP_GREATER; 559 return VK_COMPARE_OP_GREATER;
537 case Maxwell::ComparisonOp::NotEqual: 560 case Maxwell::ComparisonOp::NotEqual_D3D:
538 case Maxwell::ComparisonOp::NotEqualOld: 561 case Maxwell::ComparisonOp::NotEqual_GL:
539 return VK_COMPARE_OP_NOT_EQUAL; 562 return VK_COMPARE_OP_NOT_EQUAL;
540 case Maxwell::ComparisonOp::GreaterEqual: 563 case Maxwell::ComparisonOp::GreaterEqual_D3D:
541 case Maxwell::ComparisonOp::GreaterEqualOld: 564 case Maxwell::ComparisonOp::GreaterEqual_GL:
542 return VK_COMPARE_OP_GREATER_OR_EQUAL; 565 return VK_COMPARE_OP_GREATER_OR_EQUAL;
543 case Maxwell::ComparisonOp::Always: 566 case Maxwell::ComparisonOp::Always_D3D:
544 case Maxwell::ComparisonOp::AlwaysOld: 567 case Maxwell::ComparisonOp::Always_GL:
545 return VK_COMPARE_OP_ALWAYS; 568 return VK_COMPARE_OP_ALWAYS;
546 } 569 }
547 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); 570 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
@@ -561,31 +584,31 @@ VkIndexType IndexFormat(Maxwell::IndexFormat index_format) {
561 return {}; 584 return {};
562} 585}
563 586
564VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) { 587VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op) {
565 switch (stencil_op) { 588 switch (stencil_op) {
566 case Maxwell::StencilOp::Keep: 589 case Maxwell::StencilOp::Op::Keep_D3D:
567 case Maxwell::StencilOp::KeepOGL: 590 case Maxwell::StencilOp::Op::Keep_GL:
568 return VK_STENCIL_OP_KEEP; 591 return VK_STENCIL_OP_KEEP;
569 case Maxwell::StencilOp::Zero: 592 case Maxwell::StencilOp::Op::Zero_D3D:
570 case Maxwell::StencilOp::ZeroOGL: 593 case Maxwell::StencilOp::Op::Zero_GL:
571 return VK_STENCIL_OP_ZERO; 594 return VK_STENCIL_OP_ZERO;
572 case Maxwell::StencilOp::Replace: 595 case Maxwell::StencilOp::Op::Replace_D3D:
573 case Maxwell::StencilOp::ReplaceOGL: 596 case Maxwell::StencilOp::Op::Replace_GL:
574 return VK_STENCIL_OP_REPLACE; 597 return VK_STENCIL_OP_REPLACE;
575 case Maxwell::StencilOp::Incr: 598 case Maxwell::StencilOp::Op::IncrSaturate_D3D:
576 case Maxwell::StencilOp::IncrOGL: 599 case Maxwell::StencilOp::Op::IncrSaturate_GL:
577 return VK_STENCIL_OP_INCREMENT_AND_CLAMP; 600 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
578 case Maxwell::StencilOp::Decr: 601 case Maxwell::StencilOp::Op::DecrSaturate_D3D:
579 case Maxwell::StencilOp::DecrOGL: 602 case Maxwell::StencilOp::Op::DecrSaturate_GL:
580 return VK_STENCIL_OP_DECREMENT_AND_CLAMP; 603 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
581 case Maxwell::StencilOp::Invert: 604 case Maxwell::StencilOp::Op::Invert_D3D:
582 case Maxwell::StencilOp::InvertOGL: 605 case Maxwell::StencilOp::Op::Invert_GL:
583 return VK_STENCIL_OP_INVERT; 606 return VK_STENCIL_OP_INVERT;
584 case Maxwell::StencilOp::IncrWrap: 607 case Maxwell::StencilOp::Op::Incr_D3D:
585 case Maxwell::StencilOp::IncrWrapOGL: 608 case Maxwell::StencilOp::Op::Incr_GL:
586 return VK_STENCIL_OP_INCREMENT_AND_WRAP; 609 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
587 case Maxwell::StencilOp::DecrWrap: 610 case Maxwell::StencilOp::Op::Decr_D3D:
588 case Maxwell::StencilOp::DecrWrapOGL: 611 case Maxwell::StencilOp::Op::Decr_GL:
589 return VK_STENCIL_OP_DECREMENT_AND_WRAP; 612 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
590 } 613 }
591 UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil_op); 614 UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil_op);
@@ -594,20 +617,20 @@ VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) {
594 617
595VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) { 618VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) {
596 switch (equation) { 619 switch (equation) {
597 case Maxwell::Blend::Equation::Add: 620 case Maxwell::Blend::Equation::Add_D3D:
598 case Maxwell::Blend::Equation::AddGL: 621 case Maxwell::Blend::Equation::Add_GL:
599 return VK_BLEND_OP_ADD; 622 return VK_BLEND_OP_ADD;
600 case Maxwell::Blend::Equation::Subtract: 623 case Maxwell::Blend::Equation::Subtract_D3D:
601 case Maxwell::Blend::Equation::SubtractGL: 624 case Maxwell::Blend::Equation::Subtract_GL:
602 return VK_BLEND_OP_SUBTRACT; 625 return VK_BLEND_OP_SUBTRACT;
603 case Maxwell::Blend::Equation::ReverseSubtract: 626 case Maxwell::Blend::Equation::ReverseSubtract_D3D:
604 case Maxwell::Blend::Equation::ReverseSubtractGL: 627 case Maxwell::Blend::Equation::ReverseSubtract_GL:
605 return VK_BLEND_OP_REVERSE_SUBTRACT; 628 return VK_BLEND_OP_REVERSE_SUBTRACT;
606 case Maxwell::Blend::Equation::Min: 629 case Maxwell::Blend::Equation::Min_D3D:
607 case Maxwell::Blend::Equation::MinGL: 630 case Maxwell::Blend::Equation::Min_GL:
608 return VK_BLEND_OP_MIN; 631 return VK_BLEND_OP_MIN;
609 case Maxwell::Blend::Equation::Max: 632 case Maxwell::Blend::Equation::Max_D3D:
610 case Maxwell::Blend::Equation::MaxGL: 633 case Maxwell::Blend::Equation::Max_GL:
611 return VK_BLEND_OP_MAX; 634 return VK_BLEND_OP_MAX;
612 } 635 }
613 UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); 636 UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation);
@@ -616,62 +639,62 @@ VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) {
616 639
617VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor) { 640VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor) {
618 switch (factor) { 641 switch (factor) {
619 case Maxwell::Blend::Factor::Zero: 642 case Maxwell::Blend::Factor::Zero_D3D:
620 case Maxwell::Blend::Factor::ZeroGL: 643 case Maxwell::Blend::Factor::Zero_GL:
621 return VK_BLEND_FACTOR_ZERO; 644 return VK_BLEND_FACTOR_ZERO;
622 case Maxwell::Blend::Factor::One: 645 case Maxwell::Blend::Factor::One_D3D:
623 case Maxwell::Blend::Factor::OneGL: 646 case Maxwell::Blend::Factor::One_GL:
624 return VK_BLEND_FACTOR_ONE; 647 return VK_BLEND_FACTOR_ONE;
625 case Maxwell::Blend::Factor::SourceColor: 648 case Maxwell::Blend::Factor::SourceColor_D3D:
626 case Maxwell::Blend::Factor::SourceColorGL: 649 case Maxwell::Blend::Factor::SourceColor_GL:
627 return VK_BLEND_FACTOR_SRC_COLOR; 650 return VK_BLEND_FACTOR_SRC_COLOR;
628 case Maxwell::Blend::Factor::OneMinusSourceColor: 651 case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
629 case Maxwell::Blend::Factor::OneMinusSourceColorGL: 652 case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
630 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; 653 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
631 case Maxwell::Blend::Factor::SourceAlpha: 654 case Maxwell::Blend::Factor::SourceAlpha_D3D:
632 case Maxwell::Blend::Factor::SourceAlphaGL: 655 case Maxwell::Blend::Factor::SourceAlpha_GL:
633 return VK_BLEND_FACTOR_SRC_ALPHA; 656 return VK_BLEND_FACTOR_SRC_ALPHA;
634 case Maxwell::Blend::Factor::OneMinusSourceAlpha: 657 case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
635 case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: 658 case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
636 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; 659 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
637 case Maxwell::Blend::Factor::DestAlpha: 660 case Maxwell::Blend::Factor::DestAlpha_D3D:
638 case Maxwell::Blend::Factor::DestAlphaGL: 661 case Maxwell::Blend::Factor::DestAlpha_GL:
639 return VK_BLEND_FACTOR_DST_ALPHA; 662 return VK_BLEND_FACTOR_DST_ALPHA;
640 case Maxwell::Blend::Factor::OneMinusDestAlpha: 663 case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
641 case Maxwell::Blend::Factor::OneMinusDestAlphaGL: 664 case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
642 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; 665 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
643 case Maxwell::Blend::Factor::DestColor: 666 case Maxwell::Blend::Factor::DestColor_D3D:
644 case Maxwell::Blend::Factor::DestColorGL: 667 case Maxwell::Blend::Factor::DestColor_GL:
645 return VK_BLEND_FACTOR_DST_COLOR; 668 return VK_BLEND_FACTOR_DST_COLOR;
646 case Maxwell::Blend::Factor::OneMinusDestColor: 669 case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
647 case Maxwell::Blend::Factor::OneMinusDestColorGL: 670 case Maxwell::Blend::Factor::OneMinusDestColor_GL:
648 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; 671 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
649 case Maxwell::Blend::Factor::SourceAlphaSaturate: 672 case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
650 case Maxwell::Blend::Factor::SourceAlphaSaturateGL: 673 case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
651 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; 674 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
652 case Maxwell::Blend::Factor::Source1Color: 675 case Maxwell::Blend::Factor::Source1Color_D3D:
653 case Maxwell::Blend::Factor::Source1ColorGL: 676 case Maxwell::Blend::Factor::Source1Color_GL:
654 return VK_BLEND_FACTOR_SRC1_COLOR; 677 return VK_BLEND_FACTOR_SRC1_COLOR;
655 case Maxwell::Blend::Factor::OneMinusSource1Color: 678 case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
656 case Maxwell::Blend::Factor::OneMinusSource1ColorGL: 679 case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
657 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; 680 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
658 case Maxwell::Blend::Factor::Source1Alpha: 681 case Maxwell::Blend::Factor::Source1Alpha_D3D:
659 case Maxwell::Blend::Factor::Source1AlphaGL: 682 case Maxwell::Blend::Factor::Source1Alpha_GL:
660 return VK_BLEND_FACTOR_SRC1_ALPHA; 683 return VK_BLEND_FACTOR_SRC1_ALPHA;
661 case Maxwell::Blend::Factor::OneMinusSource1Alpha: 684 case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
662 case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: 685 case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
663 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; 686 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
664 case Maxwell::Blend::Factor::ConstantColor: 687 case Maxwell::Blend::Factor::BlendFactor_D3D:
665 case Maxwell::Blend::Factor::ConstantColorGL: 688 case Maxwell::Blend::Factor::ConstantColor_GL:
666 return VK_BLEND_FACTOR_CONSTANT_COLOR; 689 return VK_BLEND_FACTOR_CONSTANT_COLOR;
667 case Maxwell::Blend::Factor::OneMinusConstantColor: 690 case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
668 case Maxwell::Blend::Factor::OneMinusConstantColorGL: 691 case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
669 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; 692 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
670 case Maxwell::Blend::Factor::ConstantAlpha: 693 case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
671 case Maxwell::Blend::Factor::ConstantAlphaGL: 694 case Maxwell::Blend::Factor::ConstantAlpha_GL:
672 return VK_BLEND_FACTOR_CONSTANT_ALPHA; 695 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
673 case Maxwell::Blend::Factor::OneMinusConstantAlpha: 696 case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
674 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 697 case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
675 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; 698 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
676 } 699 }
677 UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); 700 UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor);
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 356d46292..6f65502d6 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -55,7 +55,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison);
55 55
56VkIndexType IndexFormat(Maxwell::IndexFormat index_format); 56VkIndexType IndexFormat(Maxwell::IndexFormat index_format);
57 57
58VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); 58VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op);
59 59
60VkBlendOp BlendEquation(Maxwell::Blend::Equation equation); 60VkBlendOp BlendEquation(Maxwell::Blend::Equation equation);
61 61
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7c78d0299..d8131232a 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -102,13 +102,13 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
103 surface(CreateSurface(instance, render_window)), 103 surface(CreateSurface(instance, render_window)),
104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
105 state_tracker(gpu), scheduler(device, state_tracker), 105 state_tracker(), scheduler(device, state_tracker),
106 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 106 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
107 render_window.GetFramebufferLayout().height, false), 107 render_window.GetFramebufferLayout().height, false),
108 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, 108 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler,
109 screen_info), 109 screen_info),
110 rasterizer(render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, device, 110 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
111 memory_allocator, state_tracker, scheduler) { 111 state_tracker, scheduler) {
112 Report(); 112 Report();
113} catch (const vk::Exception& exception) { 113} catch (const vk::Exception& exception) {
114 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); 114 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
@@ -142,7 +142,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
142 const auto recreate_swapchain = [&] { 142 const auto recreate_swapchain = [&] {
143 if (!has_been_recreated) { 143 if (!has_been_recreated) {
144 has_been_recreated = true; 144 has_been_recreated = true;
145 scheduler.WaitWorker(); 145 scheduler.Finish();
146 } 146 }
147 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 147 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
148 swapchain.Create(layout.width, layout.height, is_srgb); 148 swapchain.Create(layout.width, layout.height, is_srgb);
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 444c29f68..89426121f 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -145,6 +145,11 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
145 // Finish any pending renderpass 145 // Finish any pending renderpass
146 scheduler.RequestOutsideRenderPassOperationContext(); 146 scheduler.RequestOutsideRenderPassOperationContext();
147 147
148 if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) {
149 image_count = swapchain_images;
150 Recreate();
151 }
152
148 const std::size_t image_index = swapchain.GetImageIndex(); 153 const std::size_t image_index = swapchain.GetImageIndex();
149 154
150 scheduler.Wait(resource_ticks[image_index]); 155 scheduler.Wait(resource_ticks[image_index]);
@@ -448,15 +453,15 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE
448 453
449void BlitScreen::CreateStaticResources() { 454void BlitScreen::CreateStaticResources() {
450 CreateShaders(); 455 CreateShaders();
456 CreateSampler();
457}
458
459void BlitScreen::CreateDynamicResources() {
451 CreateSemaphores(); 460 CreateSemaphores();
452 CreateDescriptorPool(); 461 CreateDescriptorPool();
453 CreateDescriptorSetLayout(); 462 CreateDescriptorSetLayout();
454 CreateDescriptorSets(); 463 CreateDescriptorSets();
455 CreatePipelineLayout(); 464 CreatePipelineLayout();
456 CreateSampler();
457}
458
459void BlitScreen::CreateDynamicResources() {
460 CreateRenderPass(); 465 CreateRenderPass();
461 CreateFramebuffers(); 466 CreateFramebuffers();
462 CreateGraphicsPipeline(); 467 CreateGraphicsPipeline();
@@ -475,11 +480,15 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
475 fsr.reset(); 480 fsr.reset();
476 } 481 }
477 482
478 if (framebuffer.width == raw_width && framebuffer.height == raw_height && !raw_images.empty()) { 483 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
484 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
479 return; 485 return;
480 } 486 }
487
481 raw_width = framebuffer.width; 488 raw_width = framebuffer.width;
482 raw_height = framebuffer.height; 489 raw_height = framebuffer.height;
490 pixel_format = framebuffer.pixel_format;
491
483 ReleaseRawImages(); 492 ReleaseRawImages();
484 493
485 CreateStagingBuffer(framebuffer); 494 CreateStagingBuffer(framebuffer);
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index b8c67bef0..a2b73ec54 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -28,6 +28,10 @@ namespace VideoCore {
28class RasterizerInterface; 28class RasterizerInterface;
29} 29}
30 30
31namespace Service::android {
32enum class PixelFormat : u32;
33}
34
31namespace Vulkan { 35namespace Vulkan {
32 36
33struct ScreenInfo; 37struct ScreenInfo;
@@ -109,7 +113,7 @@ private:
109 MemoryAllocator& memory_allocator; 113 MemoryAllocator& memory_allocator;
110 Swapchain& swapchain; 114 Swapchain& swapchain;
111 Scheduler& scheduler; 115 Scheduler& scheduler;
112 const std::size_t image_count; 116 std::size_t image_count;
113 const ScreenInfo& screen_info; 117 const ScreenInfo& screen_info;
114 118
115 vk::ShaderModule vertex_shader; 119 vk::ShaderModule vertex_shader;
@@ -156,6 +160,7 @@ private:
156 160
157 u32 raw_width = 0; 161 u32 raw_width = 0;
158 u32 raw_height = 0; 162 u32 raw_height = 0;
163 Service::android::PixelFormat pixel_format{};
159 164
160 std::unique_ptr<FSR> fsr; 165 std::unique_ptr<FSR> fsr;
161}; 166};
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index f17a5ccd6..241d7573e 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -26,8 +26,6 @@
26 26
27namespace Vulkan { 27namespace Vulkan {
28 28
29using Tegra::Texture::SWIZZLE_TABLE;
30
31namespace { 29namespace {
32 30
33constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; 31constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 6447210e2..7906e11a8 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -126,8 +126,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
126 const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; 126 const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset};
127 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + 127 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() +
128 secondary_offset}; 128 secondary_offset};
129 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 129 const u32 lhs_raw{gpu_memory.Read<u32>(addr) << desc.shift_left};
130 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 130 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr) << desc.secondary_shift_left};
131 return TexturePair(lhs_raw | rhs_raw, via_header_index); 131 return TexturePair(lhs_raw | rhs_raw, via_header_index);
132 } 132 }
133 } 133 }
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index c249b34d4..0214b103a 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -11,11 +11,8 @@
11 11
12namespace Vulkan { 12namespace Vulkan {
13 13
14InnerFence::InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_) 14InnerFence::InnerFence(Scheduler& scheduler_, bool is_stubbed_)
15 : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {} 15 : FenceBase{is_stubbed_}, scheduler{scheduler_} {}
16
17InnerFence::InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_)
18 : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {}
19 16
20InnerFence::~InnerFence() = default; 17InnerFence::~InnerFence() = default;
21 18
@@ -48,12 +45,8 @@ FenceManager::FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::G
48 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, 45 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_},
49 scheduler{scheduler_} {} 46 scheduler{scheduler_} {}
50 47
51Fence FenceManager::CreateFence(u32 value, bool is_stubbed) { 48Fence FenceManager::CreateFence(bool is_stubbed) {
52 return std::make_shared<InnerFence>(scheduler, value, is_stubbed); 49 return std::make_shared<InnerFence>(scheduler, is_stubbed);
53}
54
55Fence FenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
56 return std::make_shared<InnerFence>(scheduler, addr, value, is_stubbed);
57} 50}
58 51
59void FenceManager::QueueFence(Fence& fence) { 52void FenceManager::QueueFence(Fence& fence) {
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 7c0bbd80a..7fe2afcd9 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -25,8 +25,7 @@ class Scheduler;
25 25
26class InnerFence : public VideoCommon::FenceBase { 26class InnerFence : public VideoCommon::FenceBase {
27public: 27public:
28 explicit InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_); 28 explicit InnerFence(Scheduler& scheduler_, bool is_stubbed_);
29 explicit InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_);
30 ~InnerFence(); 29 ~InnerFence();
31 30
32 void Queue(); 31 void Queue();
@@ -50,8 +49,7 @@ public:
50 QueryCache& query_cache, const Device& device, Scheduler& scheduler); 49 QueryCache& query_cache, const Device& device, Scheduler& scheduler);
51 50
52protected: 51protected:
53 Fence CreateFence(u32 value, bool is_stubbed) override; 52 Fence CreateFence(bool is_stubbed) override;
54 Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
55 void QueueFence(Fence& fence) override; 53 void QueueFence(Fence& fence) override;
56 bool IsFenceSignaled(Fence& fence) const override; 54 bool IsFenceSignaled(Fence& fence) const override;
57 void WaitFence(Fence& fence) override; 55 void WaitFence(Fence& fence) override;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 7eb623a7c..b4372a839 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -216,15 +216,14 @@ ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& m
216} // Anonymous namespace 216} // Anonymous namespace
217 217
218GraphicsPipeline::GraphicsPipeline( 218GraphicsPipeline::GraphicsPipeline(
219 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, Scheduler& scheduler_, 219 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
220 BufferCache& buffer_cache_, TextureCache& texture_cache_,
221 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 220 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
222 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 221 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
223 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, 222 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
224 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, 223 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
225 const std::array<const Shader::Info*, NUM_STAGES>& infos) 224 const std::array<const Shader::Info*, NUM_STAGES>& infos)
226 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, 225 : key{key_}, device{device_}, texture_cache{texture_cache_},
227 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, 226 buffer_cache{buffer_cache_}, scheduler{scheduler_},
228 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { 227 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} {
229 if (shader_notify) { 228 if (shader_notify) {
230 shader_notify->MarkShaderBuilding(); 229 shader_notify->MarkShaderBuilding();
@@ -289,8 +288,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
289 288
290 buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); 289 buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes);
291 290
292 const auto& regs{maxwell3d.regs}; 291 const auto& regs{maxwell3d->regs};
293 const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; 292 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
294 const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 293 const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
295 const Shader::Info& info{stage_infos[stage]}; 294 const Shader::Info& info{stage_infos[stage]};
296 buffer_cache.UnbindGraphicsStorageBuffers(stage); 295 buffer_cache.UnbindGraphicsStorageBuffers(stage);
@@ -303,7 +302,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
303 ++ssbo_index; 302 ++ssbo_index;
304 } 303 }
305 } 304 }
306 const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; 305 const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers};
307 const auto read_handle{[&](const auto& desc, u32 index) { 306 const auto read_handle{[&](const auto& desc, u32 index) {
308 ASSERT(cbufs[desc.cbuf_index].enabled); 307 ASSERT(cbufs[desc.cbuf_index].enabled);
309 const u32 index_offset{index << desc.size_shift}; 308 const u32 index_offset{index << desc.size_shift};
@@ -316,13 +315,14 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
316 const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; 315 const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
317 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + 316 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
318 second_offset}; 317 second_offset};
319 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 318 const u32 lhs_raw{gpu_memory->Read<u32>(addr) << desc.shift_left};
320 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 319 const u32 rhs_raw{gpu_memory->Read<u32>(separate_addr)
320 << desc.secondary_shift_left};
321 const u32 raw{lhs_raw | rhs_raw}; 321 const u32 raw{lhs_raw | rhs_raw};
322 return TexturePair(raw, via_header_index); 322 return TexturePair(raw, via_header_index);
323 } 323 }
324 } 324 }
325 return TexturePair(gpu_memory.Read<u32>(addr), via_header_index); 325 return TexturePair(gpu_memory->Read<u32>(addr), via_header_index);
326 }}; 326 }};
327 const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { 327 const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE {
328 for (u32 index = 0; index < desc.count; ++index) { 328 for (u32 index = 0; index < desc.count; ++index) {
@@ -680,15 +680,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
680 .lineStippleFactor = 0, 680 .lineStippleFactor = 0,
681 .lineStipplePattern = 0, 681 .lineStipplePattern = 0,
682 }; 682 };
683 VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{
684 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
685 .pNext = nullptr,
686 .flags = 0,
687 .conservativeRasterizationMode = key.state.conservative_raster_enable != 0
688 ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
689 : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
690 .extraPrimitiveOverestimationSize = 0.0f,
691 };
692 VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ 683 VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
693 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, 684 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
694 .pNext = nullptr, 685 .pNext = nullptr,
@@ -699,9 +690,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
699 if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) { 690 if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) {
700 line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state); 691 line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state);
701 } 692 }
702 if (device.IsExtConservativeRasterizationSupported()) {
703 conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster);
704 }
705 if (device.IsExtProvokingVertexSupported()) { 693 if (device.IsExtProvokingVertexSupported()) {
706 provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); 694 provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
707 } 695 }
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 8842f62d7..6bf577d25 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -70,15 +70,16 @@ class GraphicsPipeline {
70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; 70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
71 71
72public: 72public:
73 explicit GraphicsPipeline( 73 explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache,
74 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, 74 TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify,
75 Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, 75 const Device& device, DescriptorPool& descriptor_pool,
76 VideoCore::ShaderNotify* shader_notify, const Device& device, 76 UpdateDescriptorQueue& update_descriptor_queue,
77 DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, 77 Common::ThreadWorker* worker_thread,
78 Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, 78 PipelineStatistics* pipeline_statistics,
79 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, 79 RenderPassCache& render_pass_cache,
80 std::array<vk::ShaderModule, NUM_STAGES> stages, 80 const GraphicsPipelineCacheKey& key,
81 const std::array<const Shader::Info*, NUM_STAGES>& infos); 81 std::array<vk::ShaderModule, NUM_STAGES> stages,
82 const std::array<const Shader::Info*, NUM_STAGES>& infos);
82 83
83 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 84 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
84 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; 85 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
@@ -110,6 +111,11 @@ public:
110 return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl<Spec>(is_indexed); }; 111 return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl<Spec>(is_indexed); };
111 } 112 }
112 113
114 void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) {
115 maxwell3d = maxwell3d_;
116 gpu_memory = gpu_memory_;
117 }
118
113private: 119private:
114 template <typename Spec> 120 template <typename Spec>
115 void ConfigureImpl(bool is_indexed); 121 void ConfigureImpl(bool is_indexed);
@@ -122,8 +128,8 @@ private:
122 void Validate(); 128 void Validate();
123 129
124 const GraphicsPipelineCacheKey key; 130 const GraphicsPipelineCacheKey key;
125 Tegra::Engines::Maxwell3D& maxwell3d; 131 Tegra::Engines::Maxwell3D* maxwell3d;
126 Tegra::MemoryManager& gpu_memory; 132 Tegra::MemoryManager* gpu_memory;
127 const Device& device; 133 const Device& device;
128 TextureCache& texture_cache; 134 TextureCache& texture_cache;
129 BufferCache& buffer_cache; 135 BufferCache& buffer_cache;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 9708dc45e..13d5a1f67 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -62,29 +62,29 @@ auto MakeSpan(Container& container) {
62 62
63Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { 63Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
64 switch (comparison) { 64 switch (comparison) {
65 case Maxwell::ComparisonOp::Never: 65 case Maxwell::ComparisonOp::Never_D3D:
66 case Maxwell::ComparisonOp::NeverOld: 66 case Maxwell::ComparisonOp::Never_GL:
67 return Shader::CompareFunction::Never; 67 return Shader::CompareFunction::Never;
68 case Maxwell::ComparisonOp::Less: 68 case Maxwell::ComparisonOp::Less_D3D:
69 case Maxwell::ComparisonOp::LessOld: 69 case Maxwell::ComparisonOp::Less_GL:
70 return Shader::CompareFunction::Less; 70 return Shader::CompareFunction::Less;
71 case Maxwell::ComparisonOp::Equal: 71 case Maxwell::ComparisonOp::Equal_D3D:
72 case Maxwell::ComparisonOp::EqualOld: 72 case Maxwell::ComparisonOp::Equal_GL:
73 return Shader::CompareFunction::Equal; 73 return Shader::CompareFunction::Equal;
74 case Maxwell::ComparisonOp::LessEqual: 74 case Maxwell::ComparisonOp::LessEqual_D3D:
75 case Maxwell::ComparisonOp::LessEqualOld: 75 case Maxwell::ComparisonOp::LessEqual_GL:
76 return Shader::CompareFunction::LessThanEqual; 76 return Shader::CompareFunction::LessThanEqual;
77 case Maxwell::ComparisonOp::Greater: 77 case Maxwell::ComparisonOp::Greater_D3D:
78 case Maxwell::ComparisonOp::GreaterOld: 78 case Maxwell::ComparisonOp::Greater_GL:
79 return Shader::CompareFunction::Greater; 79 return Shader::CompareFunction::Greater;
80 case Maxwell::ComparisonOp::NotEqual: 80 case Maxwell::ComparisonOp::NotEqual_D3D:
81 case Maxwell::ComparisonOp::NotEqualOld: 81 case Maxwell::ComparisonOp::NotEqual_GL:
82 return Shader::CompareFunction::NotEqual; 82 return Shader::CompareFunction::NotEqual;
83 case Maxwell::ComparisonOp::GreaterEqual: 83 case Maxwell::ComparisonOp::GreaterEqual_D3D:
84 case Maxwell::ComparisonOp::GreaterEqualOld: 84 case Maxwell::ComparisonOp::GreaterEqual_GL:
85 return Shader::CompareFunction::GreaterThanEqual; 85 return Shader::CompareFunction::GreaterThanEqual;
86 case Maxwell::ComparisonOp::Always: 86 case Maxwell::ComparisonOp::Always_D3D:
87 case Maxwell::ComparisonOp::AlwaysOld: 87 case Maxwell::ComparisonOp::Always_GL:
88 return Shader::CompareFunction::Always; 88 return Shader::CompareFunction::Always;
89 } 89 }
90 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); 90 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
@@ -96,15 +96,18 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
96 return Shader::AttributeType::Disabled; 96 return Shader::AttributeType::Disabled;
97 } 97 }
98 switch (attr.Type()) { 98 switch (attr.Type()) {
99 case Maxwell::VertexAttribute::Type::SignedNorm: 99 case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
100 case Maxwell::VertexAttribute::Type::UnsignedNorm: 100 ASSERT_MSG(false, "Invalid vertex attribute type!");
101 case Maxwell::VertexAttribute::Type::UnsignedScaled: 101 return Shader::AttributeType::Disabled;
102 case Maxwell::VertexAttribute::Type::SignedScaled: 102 case Maxwell::VertexAttribute::Type::SNorm:
103 case Maxwell::VertexAttribute::Type::UNorm:
104 case Maxwell::VertexAttribute::Type::UScaled:
105 case Maxwell::VertexAttribute::Type::SScaled:
103 case Maxwell::VertexAttribute::Type::Float: 106 case Maxwell::VertexAttribute::Type::Float:
104 return Shader::AttributeType::Float; 107 return Shader::AttributeType::Float;
105 case Maxwell::VertexAttribute::Type::SignedInt: 108 case Maxwell::VertexAttribute::Type::SInt:
106 return Shader::AttributeType::SignedInt; 109 return Shader::AttributeType::SignedInt;
107 case Maxwell::VertexAttribute::Type::UnsignedInt: 110 case Maxwell::VertexAttribute::Type::UInt:
108 return Shader::AttributeType::UnsignedInt; 111 return Shader::AttributeType::UnsignedInt;
109 } 112 }
110 return Shader::AttributeType::Float; 113 return Shader::AttributeType::Float;
@@ -131,6 +134,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
131 Shader::RuntimeInfo info; 134 Shader::RuntimeInfo info;
132 if (previous_program) { 135 if (previous_program) {
133 info.previous_stage_stores = previous_program->info.stores; 136 info.previous_stage_stores = previous_program->info.stores;
137 info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
134 if (previous_program->is_geometry_passthrough) { 138 if (previous_program->is_geometry_passthrough) {
135 info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; 139 info.previous_stage_stores.mask |= previous_program->info.passthrough.mask;
136 } 140 }
@@ -162,16 +166,14 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
162 } 166 }
163 break; 167 break;
164 case Shader::Stage::TessellationEval: 168 case Shader::Stage::TessellationEval:
165 // We have to flip tessellation clockwise for some reason...
166 info.tess_clockwise = key.state.tessellation_clockwise == 0;
167 info.tess_primitive = [&key] { 169 info.tess_primitive = [&key] {
168 const u32 raw{key.state.tessellation_primitive.Value()}; 170 const u32 raw{key.state.tessellation_primitive.Value()};
169 switch (static_cast<Maxwell::TessellationPrimitive>(raw)) { 171 switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
170 case Maxwell::TessellationPrimitive::Isolines: 172 case Maxwell::Tessellation::DomainType::Isolines:
171 return Shader::TessPrimitive::Isolines; 173 return Shader::TessPrimitive::Isolines;
172 case Maxwell::TessellationPrimitive::Triangles: 174 case Maxwell::Tessellation::DomainType::Triangles:
173 return Shader::TessPrimitive::Triangles; 175 return Shader::TessPrimitive::Triangles;
174 case Maxwell::TessellationPrimitive::Quads: 176 case Maxwell::Tessellation::DomainType::Quads:
175 return Shader::TessPrimitive::Quads; 177 return Shader::TessPrimitive::Quads;
176 } 178 }
177 ASSERT(false); 179 ASSERT(false);
@@ -179,12 +181,12 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
179 }(); 181 }();
180 info.tess_spacing = [&] { 182 info.tess_spacing = [&] {
181 const u32 raw{key.state.tessellation_spacing}; 183 const u32 raw{key.state.tessellation_spacing};
182 switch (static_cast<Maxwell::TessellationSpacing>(raw)) { 184 switch (static_cast<Maxwell::Tessellation::Spacing>(raw)) {
183 case Maxwell::TessellationSpacing::Equal: 185 case Maxwell::Tessellation::Spacing::Integer:
184 return Shader::TessSpacing::Equal; 186 return Shader::TessSpacing::Equal;
185 case Maxwell::TessellationSpacing::FractionalOdd: 187 case Maxwell::Tessellation::Spacing::FractionalOdd:
186 return Shader::TessSpacing::FractionalOdd; 188 return Shader::TessSpacing::FractionalOdd;
187 case Maxwell::TessellationSpacing::FractionalEven: 189 case Maxwell::Tessellation::Spacing::FractionalEven:
188 return Shader::TessSpacing::FractionalEven; 190 return Shader::TessSpacing::FractionalEven;
189 } 191 }
190 ASSERT(false); 192 ASSERT(false);
@@ -259,20 +261,18 @@ bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) c
259 return std::memcmp(&rhs, this, Size()) == 0; 261 return std::memcmp(&rhs, this, Size()) == 0;
260} 262}
261 263
262PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, 264PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device_,
263 Tegra::Engines::KeplerCompute& kepler_compute_,
264 Tegra::MemoryManager& gpu_memory_, const Device& device_,
265 Scheduler& scheduler_, DescriptorPool& descriptor_pool_, 265 Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
266 UpdateDescriptorQueue& update_descriptor_queue_, 266 UpdateDescriptorQueue& update_descriptor_queue_,
267 RenderPassCache& render_pass_cache_, BufferCache& buffer_cache_, 267 RenderPassCache& render_pass_cache_, BufferCache& buffer_cache_,
268 TextureCache& texture_cache_, VideoCore::ShaderNotify& shader_notify_) 268 TextureCache& texture_cache_, VideoCore::ShaderNotify& shader_notify_)
269 : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, 269 : VideoCommon::ShaderCache{rasterizer_}, device{device_}, scheduler{scheduler_},
270 device{device_}, scheduler{scheduler_}, descriptor_pool{descriptor_pool_}, 270 descriptor_pool{descriptor_pool_}, update_descriptor_queue{update_descriptor_queue_},
271 update_descriptor_queue{update_descriptor_queue_}, render_pass_cache{render_pass_cache_}, 271 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_},
272 buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, 272 texture_cache{texture_cache_}, shader_notify{shader_notify_},
273 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 273 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
274 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "yuzu:PipelineBuilder"), 274 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
275 serialization_thread(1, "yuzu:PipelineSerialization") { 275 serialization_thread(1, "VkPipelineSerialization") {
276 const auto& float_control{device.FloatControlProperties()}; 276 const auto& float_control{device.FloatControlProperties()};
277 const VkDriverIdKHR driver_id{device.GetDriverID()}; 277 const VkDriverIdKHR driver_id{device.GetDriverID()};
278 profile = Shader::Profile{ 278 profile = Shader::Profile{
@@ -337,7 +337,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
337 current_pipeline = nullptr; 337 current_pipeline = nullptr;
338 return nullptr; 338 return nullptr;
339 } 339 }
340 graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported(), 340 graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(),
341 device.IsExtVertexInputDynamicStateSupported()); 341 device.IsExtVertexInputDynamicStateSupported());
342 342
343 if (current_pipeline) { 343 if (current_pipeline) {
@@ -357,7 +357,7 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() {
357 if (!shader) { 357 if (!shader) {
358 return nullptr; 358 return nullptr;
359 } 359 }
360 const auto& qmd{kepler_compute.launch_description}; 360 const auto& qmd{kepler_compute->launch_description};
361 const ComputePipelineCacheKey key{ 361 const ComputePipelineCacheKey key{
362 .unique_hash = shader->unique_hash, 362 .unique_hash = shader->unique_hash,
363 .shared_memory_size = qmd.shared_alloc, 363 .shared_memory_size = qmd.shared_alloc,
@@ -486,13 +486,13 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
486 } 486 }
487 // If something is using depth, we can assume that games are not rendering anything which 487 // If something is using depth, we can assume that games are not rendering anything which
488 // will be used one time. 488 // will be used one time.
489 if (maxwell3d.regs.zeta_enable) { 489 if (maxwell3d->regs.zeta_enable) {
490 return nullptr; 490 return nullptr;
491 } 491 }
492 // If games are using a small index count, we can assume these are full screen quads. 492 // If games are using a small index count, we can assume these are full screen quads.
493 // Usually these shaders are only used once for building textures so we can assume they 493 // Usually these shaders are only used once for building textures so we can assume they
494 // can't be built async 494 // can't be built async
495 if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { 495 if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
496 return pipeline; 496 return pipeline;
497 } 497 }
498 return nullptr; 498 return nullptr;
@@ -557,10 +557,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
557 previous_stage = &program; 557 previous_stage = &program;
558 } 558 }
559 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 559 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
560 return std::make_unique<GraphicsPipeline>( 560 return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache,
561 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, 561 &shader_notify, device, descriptor_pool,
562 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, 562 update_descriptor_queue, thread_worker, statistics,
563 std::move(modules), infos); 563 render_pass_cache, key, std::move(modules), infos);
564 564
565} catch (const Shader::Exception& exception) { 565} catch (const Shader::Exception& exception) {
566 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 566 LOG_ERROR(Render_Vulkan, "{}", exception.what());
@@ -592,9 +592,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
592 592
593std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 593std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
594 const ComputePipelineCacheKey& key, const ShaderInfo* shader) { 594 const ComputePipelineCacheKey& key, const ShaderInfo* shader) {
595 const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; 595 const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
596 const auto& qmd{kepler_compute.launch_description}; 596 const auto& qmd{kepler_compute->launch_description};
597 ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; 597 ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
598 env.SetCachedSize(shader->size_bytes); 598 env.SetCachedSize(shader->size_bytes);
599 599
600 main_pools.ReleaseContents(); 600 main_pools.ReleaseContents();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 127957dbf..61f9e9366 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -100,10 +100,8 @@ struct ShaderPools {
100 100
101class PipelineCache : public VideoCommon::ShaderCache { 101class PipelineCache : public VideoCommon::ShaderCache {
102public: 102public:
103 explicit PipelineCache(RasterizerVulkan& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, 103 explicit PipelineCache(RasterizerVulkan& rasterizer, const Device& device, Scheduler& scheduler,
104 Tegra::Engines::KeplerCompute& kepler_compute, 104 DescriptorPool& descriptor_pool,
105 Tegra::MemoryManager& gpu_memory, const Device& device,
106 Scheduler& scheduler, DescriptorPool& descriptor_pool,
107 UpdateDescriptorQueue& update_descriptor_queue, 105 UpdateDescriptorQueue& update_descriptor_queue,
108 RenderPassCache& render_pass_cache, BufferCache& buffer_cache, 106 RenderPassCache& render_pass_cache, BufferCache& buffer_cache,
109 TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_); 107 TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_);
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 2b859c6b8..4b15c0f85 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -59,16 +59,16 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
59 std::find_if(pools.begin(), pools.end(), [query_pool = query.first](vk::QueryPool& pool) { 59 std::find_if(pools.begin(), pools.end(), [query_pool = query.first](vk::QueryPool& pool) {
60 return query_pool == *pool; 60 return query_pool == *pool;
61 }); 61 });
62 ASSERT(it != std::end(pools));
63 62
64 const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); 63 if (it != std::end(pools)) {
65 usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false; 64 const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it);
65 usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false;
66 }
66} 67}
67 68
68QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, 69QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
69 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, 70 Scheduler& scheduler_)
70 const Device& device_, Scheduler& scheduler_) 71 : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_},
71 : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_},
72 query_pools{ 72 query_pools{
73 QueryPool{device_, scheduler_, QueryType::SamplesPassed}, 73 QueryPool{device_, scheduler_, QueryType::SamplesPassed},
74 } {} 74 } {}
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index b0d86c4f8..26762ee09 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -52,9 +52,8 @@ private:
52class QueryCache final 52class QueryCache final
53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
54public: 54public:
55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, 55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
56 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, 56 Scheduler& scheduler_);
57 const Device& device_, Scheduler& scheduler_);
58 ~QueryCache(); 57 ~QueryCache();
59 58
60 std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type); 59 std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index bf9f825f4..5af3c930b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -11,6 +11,7 @@
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/scope_exit.h" 12#include "common/scope_exit.h"
13#include "common/settings.h" 13#include "common/settings.h"
14#include "video_core/control/channel_state.h"
14#include "video_core/engines/kepler_compute.h" 15#include "video_core/engines/kepler_compute.h"
15#include "video_core/engines/maxwell_3d.h" 16#include "video_core/engines/maxwell_3d.h"
16#include "video_core/renderer_vulkan/blit_image.h" 17#include "video_core/renderer_vulkan/blit_image.h"
@@ -69,7 +70,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
69 const float width = conv(src.scale_x * 2.0f); 70 const float width = conv(src.scale_x * 2.0f);
70 float y = conv(src.translate_y - src.scale_y); 71 float y = conv(src.translate_y - src.scale_y);
71 float height = conv(src.scale_y * 2.0f); 72 float height = conv(src.scale_y * 2.0f);
72 bool y_negate = regs.screen_y_control.y_negate; 73 bool y_negate = regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft;
73 74
74 if (!device.IsNvViewportSwizzleSupported()) { 75 if (!device.IsNvViewportSwizzleSupported()) {
75 y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY); 76 y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY);
@@ -126,14 +127,13 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
126 return scissor; 127 return scissor;
127} 128}
128 129
129DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, 130DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) {
130 bool is_indexed) {
131 DrawParams params{ 131 DrawParams params{
132 .base_instance = regs.vb_base_instance, 132 .base_instance = regs.global_base_instance_index,
133 .num_instances = is_instanced ? num_instances : 1, 133 .num_instances = num_instances,
134 .base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first, 134 .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
135 .num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count, 135 .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
136 .first_index = is_indexed ? regs.index_array.first : 0, 136 .first_index = is_indexed ? regs.index_buffer.first : 0,
137 .is_indexed = is_indexed, 137 .is_indexed = is_indexed,
138 }; 138 };
139 if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { 139 if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
@@ -148,31 +148,25 @@ DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instan
148} // Anonymous namespace 148} // Anonymous namespace
149 149
150RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 150RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
151 Tegra::MemoryManager& gpu_memory_,
152 Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_, 151 Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_,
153 const Device& device_, MemoryAllocator& memory_allocator_, 152 const Device& device_, MemoryAllocator& memory_allocator_,
154 StateTracker& state_tracker_, Scheduler& scheduler_) 153 StateTracker& state_tracker_, Scheduler& scheduler_)
155 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, 154 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, screen_info{screen_info_}, device{device_},
156 gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, 155 memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_},
157 screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_},
158 state_tracker{state_tracker_}, scheduler{scheduler_},
159 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), 156 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
160 update_descriptor_queue(device, scheduler), 157 update_descriptor_queue(device, scheduler),
161 blit_image(device, scheduler, state_tracker, descriptor_pool), 158 blit_image(device, scheduler, state_tracker, descriptor_pool),
162 astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue,
163 memory_allocator),
164 render_pass_cache(device), texture_cache_runtime{device, scheduler, 159 render_pass_cache(device), texture_cache_runtime{device, scheduler,
165 memory_allocator, staging_pool, 160 memory_allocator, staging_pool,
166 blit_image, astc_decoder_pass, 161 blit_image, render_pass_cache,
167 render_pass_cache}, 162 descriptor_pool, update_descriptor_queue},
168 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), 163 texture_cache(texture_cache_runtime, *this),
169 buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, 164 buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool,
170 update_descriptor_queue, descriptor_pool), 165 update_descriptor_queue, descriptor_pool),
171 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), 166 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
172 pipeline_cache(*this, maxwell3d, kepler_compute, gpu_memory, device, scheduler, 167 pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue,
173 descriptor_pool, update_descriptor_queue, render_pass_cache, buffer_cache, 168 render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()),
174 texture_cache, gpu.ShaderNotify()), 169 query_cache{*this, device, scheduler}, accelerate_dma{buffer_cache},
175 query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache},
176 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), 170 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
177 wfi_event(device.GetLogical().CreateEvent()) { 171 wfi_event(device.GetLogical().CreateEvent()) {
178 scheduler.SetQueryCache(query_cache); 172 scheduler.SetQueryCache(query_cache);
@@ -180,7 +174,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
180 174
181RasterizerVulkan::~RasterizerVulkan() = default; 175RasterizerVulkan::~RasterizerVulkan() = default;
182 176
183void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { 177void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
184 MICROPROFILE_SCOPE(Vulkan_Drawing); 178 MICROPROFILE_SCOPE(Vulkan_Drawing);
185 179
186 SCOPE_EXIT({ gpu.TickWork(); }); 180 SCOPE_EXIT({ gpu.TickWork(); });
@@ -193,15 +187,19 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
193 return; 187 return;
194 } 188 }
195 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 189 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
190 // update engine as channel may be different.
191 pipeline->SetEngine(maxwell3d, gpu_memory);
196 pipeline->Configure(is_indexed); 192 pipeline->Configure(is_indexed);
197 193
194 BindInlineIndexBuffer();
195
198 BeginTransformFeedback(); 196 BeginTransformFeedback();
199 197
200 UpdateDynamicStates(); 198 UpdateDynamicStates();
201 199
202 const auto& regs{maxwell3d.regs}; 200 const auto& regs{maxwell3d->regs};
203 const u32 num_instances{maxwell3d.mme_draw.instance_count}; 201 const u32 num_instances{instance_count};
204 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)}; 202 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)};
205 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { 203 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
206 if (draw_params.is_indexed) { 204 if (draw_params.is_indexed) {
207 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, 205 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
@@ -218,18 +216,18 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
218void RasterizerVulkan::Clear() { 216void RasterizerVulkan::Clear() {
219 MICROPROFILE_SCOPE(Vulkan_Clearing); 217 MICROPROFILE_SCOPE(Vulkan_Clearing);
220 218
221 if (!maxwell3d.ShouldExecute()) { 219 if (!maxwell3d->ShouldExecute()) {
222 return; 220 return;
223 } 221 }
224 FlushWork(); 222 FlushWork();
225 223
226 query_cache.UpdateCounters(); 224 query_cache.UpdateCounters();
227 225
228 auto& regs = maxwell3d.regs; 226 auto& regs = maxwell3d->regs;
229 const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 227 const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
230 regs.clear_buffers.A; 228 regs.clear_surface.A;
231 const bool use_depth = regs.clear_buffers.Z; 229 const bool use_depth = regs.clear_surface.Z;
232 const bool use_stencil = regs.clear_buffers.S; 230 const bool use_stencil = regs.clear_surface.S;
233 if (!use_color && !use_depth && !use_stencil) { 231 if (!use_color && !use_depth && !use_stencil) {
234 return; 232 return;
235 } 233 }
@@ -248,9 +246,16 @@ void RasterizerVulkan::Clear() {
248 } 246 }
249 UpdateViewportsState(regs); 247 UpdateViewportsState(regs);
250 248
249 VkRect2D default_scissor;
250 default_scissor.offset.x = 0;
251 default_scissor.offset.y = 0;
252 default_scissor.extent.width = std::numeric_limits<s32>::max();
253 default_scissor.extent.height = std::numeric_limits<s32>::max();
254
251 VkClearRect clear_rect{ 255 VkClearRect clear_rect{
252 .rect = GetScissorState(regs, 0, up_scale, down_shift), 256 .rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift)
253 .baseArrayLayer = regs.clear_buffers.layer, 257 : default_scissor,
258 .baseArrayLayer = regs.clear_surface.layer,
254 .layerCount = 1, 259 .layerCount = 1,
255 }; 260 };
256 if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) { 261 if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) {
@@ -261,7 +266,7 @@ void RasterizerVulkan::Clear() {
261 .height = std::min(clear_rect.rect.extent.height, render_area.height), 266 .height = std::min(clear_rect.rect.extent.height, render_area.height),
262 }; 267 };
263 268
264 const u32 color_attachment = regs.clear_buffers.RT; 269 const u32 color_attachment = regs.clear_surface.RT;
265 if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { 270 if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
266 VkClearValue clear_value; 271 VkClearValue clear_value;
267 bool is_integer = false; 272 bool is_integer = false;
@@ -283,7 +288,8 @@ void RasterizerVulkan::Clear() {
283 break; 288 break;
284 } 289 }
285 if (!is_integer) { 290 if (!is_integer) {
286 std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); 291 std::memcpy(clear_value.color.float32, regs.clear_color.data(),
292 regs.clear_color.size() * sizeof(f32));
287 } else if (!is_signed) { 293 } else if (!is_signed) {
288 for (size_t i = 0; i < 4; i++) { 294 for (size_t i = 0; i < 4; i++) {
289 clear_value.color.uint32[i] = static_cast<u32>( 295 clear_value.color.uint32[i] = static_cast<u32>(
@@ -297,14 +303,19 @@ void RasterizerVulkan::Clear() {
297 } 303 }
298 } 304 }
299 305
300 scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { 306 if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B &&
301 const VkClearAttachment attachment{ 307 regs.clear_surface.A) {
302 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 308 scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
303 .colorAttachment = color_attachment, 309 const VkClearAttachment attachment{
304 .clearValue = clear_value, 310 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
305 }; 311 .colorAttachment = color_attachment,
306 cmdbuf.ClearAttachments(attachment, clear_rect); 312 .clearValue = clear_value,
307 }); 313 };
314 cmdbuf.ClearAttachments(attachment, clear_rect);
315 });
316 } else {
317 UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel");
318 }
308 } 319 }
309 320
310 if (!use_depth && !use_stencil) { 321 if (!use_depth && !use_stencil) {
@@ -339,9 +350,9 @@ void RasterizerVulkan::DispatchCompute() {
339 return; 350 return;
340 } 351 }
341 std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; 352 std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
342 pipeline->Configure(kepler_compute, gpu_memory, scheduler, buffer_cache, texture_cache); 353 pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache);
343 354
344 const auto& qmd{kepler_compute.launch_description}; 355 const auto& qmd{kepler_compute->launch_description};
345 const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; 356 const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z};
346 scheduler.RequestOutsideRenderPassOperationContext(); 357 scheduler.RequestOutsideRenderPassOperationContext();
347 scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); 358 scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); });
@@ -422,7 +433,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
422 } 433 }
423} 434}
424 435
425void RasterizerVulkan::SyncGuestHost() { 436void RasterizerVulkan::InvalidateGPUCache() {
426 pipeline_cache.SyncGuestHost(); 437 pipeline_cache.SyncGuestHost();
427 { 438 {
428 std::scoped_lock lock{buffer_cache.mutex}; 439 std::scoped_lock lock{buffer_cache.mutex};
@@ -442,40 +453,30 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) {
442 pipeline_cache.OnCPUWrite(addr, size); 453 pipeline_cache.OnCPUWrite(addr, size);
443} 454}
444 455
445void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { 456void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {
446 { 457 {
447 std::scoped_lock lock{texture_cache.mutex}; 458 std::scoped_lock lock{texture_cache.mutex};
448 texture_cache.UnmapGPUMemory(addr, size); 459 texture_cache.UnmapGPUMemory(as_id, addr, size);
449 } 460 }
450} 461}
451 462
452void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { 463void RasterizerVulkan::SignalFence(std::function<void()>&& func) {
453 if (!gpu.IsAsync()) { 464 fence_manager.SignalFence(std::move(func));
454 gpu_memory.Write<u32>(addr, value); 465}
455 return; 466
456 } 467void RasterizerVulkan::SyncOperation(std::function<void()>&& func) {
457 fence_manager.SignalSemaphore(addr, value); 468 fence_manager.SyncOperation(std::move(func));
458} 469}
459 470
460void RasterizerVulkan::SignalSyncPoint(u32 value) { 471void RasterizerVulkan::SignalSyncPoint(u32 value) {
461 if (!gpu.IsAsync()) {
462 gpu.IncrementSyncPoint(value);
463 return;
464 }
465 fence_manager.SignalSyncPoint(value); 472 fence_manager.SignalSyncPoint(value);
466} 473}
467 474
468void RasterizerVulkan::SignalReference() { 475void RasterizerVulkan::SignalReference() {
469 if (!gpu.IsAsync()) {
470 return;
471 }
472 fence_manager.SignalOrdering(); 476 fence_manager.SignalOrdering();
473} 477}
474 478
475void RasterizerVulkan::ReleaseFences() { 479void RasterizerVulkan::ReleaseFences() {
476 if (!gpu.IsAsync()) {
477 return;
478 }
479 fence_manager.WaitPendingFences(); 480 fence_manager.WaitPendingFences();
480} 481}
481 482
@@ -552,13 +553,13 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA()
552} 553}
553 554
554void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 555void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
555 std::span<u8> memory) { 556 std::span<const u8> memory) {
556 auto cpu_addr = gpu_memory.GpuToCpuAddress(address); 557 auto cpu_addr = gpu_memory->GpuToCpuAddress(address);
557 if (!cpu_addr) [[unlikely]] { 558 if (!cpu_addr) [[unlikely]] {
558 gpu_memory.WriteBlock(address, memory.data(), copy_size); 559 gpu_memory->WriteBlock(address, memory.data(), copy_size);
559 return; 560 return;
560 } 561 }
561 gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); 562 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
562 { 563 {
563 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 564 std::unique_lock<std::mutex> lock{buffer_cache.mutex};
564 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 565 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
@@ -627,7 +628,7 @@ bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64
627} 628}
628 629
629void RasterizerVulkan::UpdateDynamicStates() { 630void RasterizerVulkan::UpdateDynamicStates() {
630 auto& regs = maxwell3d.regs; 631 auto& regs = maxwell3d->regs;
631 UpdateViewportsState(regs); 632 UpdateViewportsState(regs);
632 UpdateScissorsState(regs); 633 UpdateScissorsState(regs);
633 UpdateDepthBias(regs); 634 UpdateDepthBias(regs);
@@ -651,24 +652,24 @@ void RasterizerVulkan::UpdateDynamicStates() {
651} 652}
652 653
653void RasterizerVulkan::BeginTransformFeedback() { 654void RasterizerVulkan::BeginTransformFeedback() {
654 const auto& regs = maxwell3d.regs; 655 const auto& regs = maxwell3d->regs;
655 if (regs.tfb_enabled == 0) { 656 if (regs.transform_feedback_enabled == 0) {
656 return; 657 return;
657 } 658 }
658 if (!device.IsExtTransformFeedbackSupported()) { 659 if (!device.IsExtTransformFeedbackSupported()) {
659 LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); 660 LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
660 return; 661 return;
661 } 662 }
662 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || 663 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
663 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || 664 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
664 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); 665 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
665 scheduler.Record( 666 scheduler.Record(
666 [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); 667 [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
667} 668}
668 669
669void RasterizerVulkan::EndTransformFeedback() { 670void RasterizerVulkan::EndTransformFeedback() {
670 const auto& regs = maxwell3d.regs; 671 const auto& regs = maxwell3d->regs;
671 if (regs.tfb_enabled == 0) { 672 if (regs.transform_feedback_enabled == 0) {
672 return; 673 return;
673 } 674 }
674 if (!device.IsExtTransformFeedbackSupported()) { 675 if (!device.IsExtTransformFeedbackSupported()) {
@@ -748,11 +749,11 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
748 if (!state_tracker.TouchDepthBias()) { 749 if (!state_tracker.TouchDepthBias()) {
749 return; 750 return;
750 } 751 }
751 float units = regs.polygon_offset_units / 2.0f; 752 float units = regs.depth_bias / 2.0f;
752 const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::S8_UINT_Z24_UNORM || 753 const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT ||
753 regs.zeta.format == Tegra::DepthFormat::D24X8_UNORM || 754 regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
754 regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM || 755 regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
755 regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM; 756 regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
756 if (is_d24 && !device.SupportsD24DepthBuffer()) { 757 if (is_d24 && !device.SupportsD24DepthBuffer()) {
757 // the base formulas can be obtained from here: 758 // the base formulas can be obtained from here:
758 // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias 759 // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
@@ -760,8 +761,8 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
760 static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127)); 761 static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
761 units = static_cast<float>(static_cast<double>(units) * rescale_factor); 762 units = static_cast<float>(static_cast<double>(units) * rescale_factor);
762 } 763 }
763 scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp, 764 scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
764 factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) { 765 factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) {
765 cmdbuf.SetDepthBias(constant, clamp, factor); 766 cmdbuf.SetDepthBias(constant, clamp, factor);
766 }); 767 });
767} 768}
@@ -791,8 +792,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
791 if (regs.stencil_two_side_enable) { 792 if (regs.stencil_two_side_enable) {
792 // Separate values per face 793 // Separate values per face
793 scheduler.Record( 794 scheduler.Record(
794 [front_ref = regs.stencil_front_func_ref, front_write_mask = regs.stencil_front_mask, 795 [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask,
795 front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_func_ref, 796 front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref,
796 back_write_mask = regs.stencil_back_mask, 797 back_write_mask = regs.stencil_back_mask,
797 back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { 798 back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
798 // Front face 799 // Front face
@@ -807,7 +808,7 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
807 }); 808 });
808 } else { 809 } else {
809 // Front face defines both faces 810 // Front face defines both faces
810 scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask, 811 scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask,
811 test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { 812 test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
812 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); 813 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
813 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); 814 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
@@ -820,7 +821,8 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) {
820 if (!state_tracker.TouchLineWidth()) { 821 if (!state_tracker.TouchLineWidth()) {
821 return; 822 return;
822 } 823 }
823 const float width = regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased; 824 const float width =
825 regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased;
824 scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); 826 scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); });
825} 827}
826 828
@@ -828,10 +830,10 @@ void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
828 if (!state_tracker.TouchCullMode()) { 830 if (!state_tracker.TouchCullMode()) {
829 return; 831 return;
830 } 832 }
831 scheduler.Record( 833 scheduler.Record([enabled = regs.gl_cull_test_enabled,
832 [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { 834 cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) {
833 cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); 835 cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
834 }); 836 });
835} 837}
836 838
837void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { 839void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -880,8 +882,8 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
880 return; 882 return;
881 } 883 }
882 884
883 VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); 885 VkFrontFace front_face = MaxwellToVK::FrontFace(regs.gl_front_face);
884 if (regs.screen_y_control.triangle_rast_flip != 0) { 886 if (regs.window_origin.flip_y != 0) {
885 front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE 887 front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
886 : VK_FRONT_FACE_CLOCKWISE; 888 : VK_FRONT_FACE_CLOCKWISE;
887 } 889 }
@@ -893,16 +895,16 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
893 if (!state_tracker.TouchStencilOp()) { 895 if (!state_tracker.TouchStencilOp()) {
894 return; 896 return;
895 } 897 }
896 const Maxwell::StencilOp fail = regs.stencil_front_op_fail; 898 const Maxwell::StencilOp::Op fail = regs.stencil_front_op.fail;
897 const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; 899 const Maxwell::StencilOp::Op zfail = regs.stencil_front_op.zfail;
898 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; 900 const Maxwell::StencilOp::Op zpass = regs.stencil_front_op.zpass;
899 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; 901 const Maxwell::ComparisonOp compare = regs.stencil_front_op.func;
900 if (regs.stencil_two_side_enable) { 902 if (regs.stencil_two_side_enable) {
901 // Separate stencil op per face 903 // Separate stencil op per face
902 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; 904 const Maxwell::StencilOp::Op back_fail = regs.stencil_back_op.fail;
903 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; 905 const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail;
904 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; 906 const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass;
905 const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; 907 const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func;
906 scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, 908 scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
907 back_compare](vk::CommandBuffer cmdbuf) { 909 back_compare](vk::CommandBuffer cmdbuf) {
908 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), 910 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
@@ -933,7 +935,7 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
933} 935}
934 936
935void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { 937void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
936 auto& dirty{maxwell3d.dirty.flags}; 938 auto& dirty{maxwell3d->dirty.flags};
937 if (!dirty[Dirty::VertexInput]) { 939 if (!dirty[Dirty::VertexInput]) {
938 return; 940 return;
939 } 941 }
@@ -974,15 +976,15 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
974 dirty[Dirty::VertexBinding0 + index] = false; 976 dirty[Dirty::VertexBinding0 + index] = false;
975 977
976 const u32 binding{static_cast<u32>(index)}; 978 const u32 binding{static_cast<u32>(index)};
977 const auto& input_binding{regs.vertex_array[binding]}; 979 const auto& input_binding{regs.vertex_streams[binding]};
978 const bool is_instanced{regs.instanced_arrays.IsInstancingEnabled(binding)}; 980 const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
979 bindings.push_back({ 981 bindings.push_back({
980 .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, 982 .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
981 .pNext = nullptr, 983 .pNext = nullptr,
982 .binding = binding, 984 .binding = binding,
983 .stride = input_binding.stride, 985 .stride = input_binding.stride,
984 .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, 986 .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
985 .divisor = is_instanced ? input_binding.divisor : 1, 987 .divisor = is_instanced ? input_binding.frequency : 1,
986 }); 988 });
987 } 989 }
988 scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { 990 scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
@@ -990,4 +992,54 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
990 }); 992 });
991} 993}
992 994
995void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
996 CreateChannel(channel);
997 {
998 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
999 texture_cache.CreateChannel(channel);
1000 buffer_cache.CreateChannel(channel);
1001 }
1002 pipeline_cache.CreateChannel(channel);
1003 query_cache.CreateChannel(channel);
1004 state_tracker.SetupTables(channel);
1005}
1006
1007void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
1008 const s32 channel_id = channel.bind_id;
1009 BindToChannel(channel_id);
1010 {
1011 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1012 texture_cache.BindToChannel(channel_id);
1013 buffer_cache.BindToChannel(channel_id);
1014 }
1015 pipeline_cache.BindToChannel(channel_id);
1016 query_cache.BindToChannel(channel_id);
1017 state_tracker.ChangeChannel(channel);
1018 state_tracker.InvalidateState();
1019}
1020
1021void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
1022 EraseChannel(channel_id);
1023 {
1024 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1025 texture_cache.EraseChannel(channel_id);
1026 buffer_cache.EraseChannel(channel_id);
1027 }
1028 pipeline_cache.EraseChannel(channel_id);
1029 query_cache.EraseChannel(channel_id);
1030}
1031
1032void RasterizerVulkan::BindInlineIndexBuffer() {
1033 if (maxwell3d->inline_index_draw_indexes.empty()) {
1034 return;
1035 }
1036 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1037 auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
1038 std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
1039 buffer_cache_runtime.BindIndexBuffer(
1040 maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
1041 maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
1042 static_cast<u32>(buffer.offset), data_count);
1043}
1044
993} // namespace Vulkan 1045} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 0370ea39b..b0bc306f5 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -8,6 +8,7 @@
8#include <boost/container/static_vector.hpp> 8#include <boost/container/static_vector.hpp>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/control/channel_state_cache.h"
11#include "video_core/engines/maxwell_dma.h" 12#include "video_core/engines/maxwell_dma.h"
12#include "video_core/rasterizer_accelerated.h" 13#include "video_core/rasterizer_accelerated.h"
13#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
@@ -54,16 +55,16 @@ private:
54 BufferCache& buffer_cache; 55 BufferCache& buffer_cache;
55}; 56};
56 57
57class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { 58class RasterizerVulkan final : public VideoCore::RasterizerAccelerated,
59 protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
58public: 60public:
59 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 61 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
60 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, 62 Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_,
61 ScreenInfo& screen_info_, const Device& device_, 63 const Device& device_, MemoryAllocator& memory_allocator_,
62 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, 64 StateTracker& state_tracker_, Scheduler& scheduler_);
63 Scheduler& scheduler_);
64 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
65 66
66 void Draw(bool is_indexed, bool is_instanced) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
67 void Clear() override; 68 void Clear() override;
68 void DispatchCompute() override; 69 void DispatchCompute() override;
69 void ResetCounter(VideoCore::QueryType type) override; 70 void ResetCounter(VideoCore::QueryType type) override;
@@ -75,10 +76,11 @@ public:
75 bool MustFlushRegion(VAddr addr, u64 size) override; 76 bool MustFlushRegion(VAddr addr, u64 size) override;
76 void InvalidateRegion(VAddr addr, u64 size) override; 77 void InvalidateRegion(VAddr addr, u64 size) override;
77 void OnCPUWrite(VAddr addr, u64 size) override; 78 void OnCPUWrite(VAddr addr, u64 size) override;
78 void SyncGuestHost() override; 79 void InvalidateGPUCache() override;
79 void UnmapMemory(VAddr addr, u64 size) override; 80 void UnmapMemory(VAddr addr, u64 size) override;
80 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 81 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;
81 void SignalSemaphore(GPUVAddr addr, u32 value) override; 82 void SignalFence(std::function<void()>&& func) override;
83 void SyncOperation(std::function<void()>&& func) override;
82 void SignalSyncPoint(u32 value) override; 84 void SignalSyncPoint(u32 value) override;
83 void SignalReference() override; 85 void SignalReference() override;
84 void ReleaseFences() override; 86 void ReleaseFences() override;
@@ -93,12 +95,18 @@ public:
93 const Tegra::Engines::Fermi2D::Config& copy_config) override; 95 const Tegra::Engines::Fermi2D::Config& copy_config) override;
94 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 96 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
95 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 97 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
96 std::span<u8> memory) override; 98 std::span<const u8> memory) override;
97 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 99 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
98 u32 pixel_stride) override; 100 u32 pixel_stride) override;
99 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 101 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
100 const VideoCore::DiskResourceLoadCallback& callback) override; 102 const VideoCore::DiskResourceLoadCallback& callback) override;
101 103
104 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
105
106 void BindChannel(Tegra::Control::ChannelState& channel) override;
107
108 void ReleaseChannel(s32 channel_id) override;
109
102private: 110private:
103 static constexpr size_t MAX_TEXTURES = 192; 111 static constexpr size_t MAX_TEXTURES = 192;
104 static constexpr size_t MAX_IMAGES = 48; 112 static constexpr size_t MAX_IMAGES = 48;
@@ -133,10 +141,9 @@ private:
133 141
134 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); 142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
135 143
144 void BindInlineIndexBuffer();
145
136 Tegra::GPU& gpu; 146 Tegra::GPU& gpu;
137 Tegra::MemoryManager& gpu_memory;
138 Tegra::Engines::Maxwell3D& maxwell3d;
139 Tegra::Engines::KeplerCompute& kepler_compute;
140 147
141 ScreenInfo& screen_info; 148 ScreenInfo& screen_info;
142 const Device& device; 149 const Device& device;
@@ -148,7 +155,6 @@ private:
148 DescriptorPool descriptor_pool; 155 DescriptorPool descriptor_pool;
149 UpdateDescriptorQueue update_descriptor_queue; 156 UpdateDescriptorQueue update_descriptor_queue;
150 BlitImageHelper blit_image; 157 BlitImageHelper blit_image;
151 ASTCDecoderPass astc_decoder_pass;
152 RenderPassCache render_pass_cache; 158 RenderPassCache render_pass_cache;
153 159
154 TextureCacheRuntime texture_cache_runtime; 160 TextureCacheRuntime texture_cache_runtime;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index a331ff37e..7934f2a51 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -136,9 +136,10 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) {
136} 136}
137 137
138void Scheduler::WorkerThread(std::stop_token stop_token) { 138void Scheduler::WorkerThread(std::stop_token stop_token) {
139 Common::SetCurrentThreadName("yuzu:VulkanWorker"); 139 Common::SetCurrentThreadName("VulkanWorker");
140 do { 140 do {
141 std::unique_ptr<CommandChunk> work; 141 std::unique_ptr<CommandChunk> work;
142 bool has_submit{false};
142 { 143 {
143 std::unique_lock lock{work_mutex}; 144 std::unique_lock lock{work_mutex};
144 if (work_queue.empty()) { 145 if (work_queue.empty()) {
@@ -150,9 +151,10 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
150 } 151 }
151 work = std::move(work_queue.front()); 152 work = std::move(work_queue.front());
152 work_queue.pop(); 153 work_queue.pop();
154
155 has_submit = work->HasSubmit();
156 work->ExecuteAll(current_cmdbuf);
153 } 157 }
154 const bool has_submit = work->HasSubmit();
155 work->ExecuteAll(current_cmdbuf);
156 if (has_submit) { 158 if (has_submit) {
157 AllocateWorkerCommandBuffer(); 159 AllocateWorkerCommandBuffer();
158 } 160 }
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index c04aad08f..929216749 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -144,7 +144,6 @@ private:
144 using FuncType = TypedCommand<T>; 144 using FuncType = TypedCommand<T>;
145 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); 145 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
146 146
147 recorded_counts++;
148 command_offset = Common::AlignUp(command_offset, alignof(FuncType)); 147 command_offset = Common::AlignUp(command_offset, alignof(FuncType));
149 if (command_offset > sizeof(data) - sizeof(FuncType)) { 148 if (command_offset > sizeof(data) - sizeof(FuncType)) {
150 return false; 149 return false;
@@ -166,7 +165,7 @@ private:
166 } 165 }
167 166
168 bool Empty() const { 167 bool Empty() const {
169 return recorded_counts == 0; 168 return command_offset == 0;
170 } 169 }
171 170
172 bool HasSubmit() const { 171 bool HasSubmit() const {
@@ -177,7 +176,6 @@ private:
177 Command* first = nullptr; 176 Command* first = nullptr;
178 Command* last = nullptr; 177 Command* last = nullptr;
179 178
180 size_t recorded_counts = 0;
181 size_t command_offset = 0; 179 size_t command_offset = 0;
182 bool submit = false; 180 bool submit = false;
183 alignas(std::max_align_t) std::array<u8, 0x8000> data{}; 181 alignas(std::max_align_t) std::array<u8, 0x8000> data{};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9ad096431..b87c3be66 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -7,9 +7,9 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "video_core/control/channel_state.h"
10#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
11#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
12#include "video_core/gpu.h"
13#include "video_core/renderer_vulkan/vk_state_tracker.h" 13#include "video_core/renderer_vulkan/vk_state_tracker.h"
14 14
15#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) 15#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
@@ -51,8 +51,8 @@ Flags MakeInvalidationFlags() {
51void SetupDirtyViewports(Tables& tables) { 51void SetupDirtyViewports(Tables& tables) {
52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); 52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); 53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
54 tables[0][OFF(viewport_transform_enabled)] = Viewports; 54 tables[0][OFF(viewport_scale_offset_enbled)] = Viewports;
55 tables[1][OFF(screen_y_control)] = Viewports; 55 tables[1][OFF(window_origin)] = Viewports;
56} 56}
57 57
58void SetupDirtyScissors(Tables& tables) { 58void SetupDirtyScissors(Tables& tables) {
@@ -61,9 +61,9 @@ void SetupDirtyScissors(Tables& tables) {
61 61
62void SetupDirtyDepthBias(Tables& tables) { 62void SetupDirtyDepthBias(Tables& tables) {
63 auto& table = tables[0]; 63 auto& table = tables[0];
64 table[OFF(polygon_offset_units)] = DepthBias; 64 table[OFF(depth_bias)] = DepthBias;
65 table[OFF(polygon_offset_clamp)] = DepthBias; 65 table[OFF(depth_bias_clamp)] = DepthBias;
66 table[OFF(polygon_offset_factor)] = DepthBias; 66 table[OFF(slope_scale_depth_bias)] = DepthBias;
67} 67}
68 68
69void SetupDirtyBlendConstants(Tables& tables) { 69void SetupDirtyBlendConstants(Tables& tables) {
@@ -77,10 +77,10 @@ void SetupDirtyDepthBounds(Tables& tables) {
77void SetupDirtyStencilProperties(Tables& tables) { 77void SetupDirtyStencilProperties(Tables& tables) {
78 auto& table = tables[0]; 78 auto& table = tables[0];
79 table[OFF(stencil_two_side_enable)] = StencilProperties; 79 table[OFF(stencil_two_side_enable)] = StencilProperties;
80 table[OFF(stencil_front_func_ref)] = StencilProperties; 80 table[OFF(stencil_front_ref)] = StencilProperties;
81 table[OFF(stencil_front_mask)] = StencilProperties; 81 table[OFF(stencil_front_mask)] = StencilProperties;
82 table[OFF(stencil_front_func_mask)] = StencilProperties; 82 table[OFF(stencil_front_func_mask)] = StencilProperties;
83 table[OFF(stencil_back_func_ref)] = StencilProperties; 83 table[OFF(stencil_back_ref)] = StencilProperties;
84 table[OFF(stencil_back_mask)] = StencilProperties; 84 table[OFF(stencil_back_mask)] = StencilProperties;
85 table[OFF(stencil_back_func_mask)] = StencilProperties; 85 table[OFF(stencil_back_func_mask)] = StencilProperties;
86} 86}
@@ -92,8 +92,8 @@ void SetupDirtyLineWidth(Tables& tables) {
92 92
93void SetupDirtyCullMode(Tables& tables) { 93void SetupDirtyCullMode(Tables& tables) {
94 auto& table = tables[0]; 94 auto& table = tables[0];
95 table[OFF(cull_face)] = CullMode; 95 table[OFF(gl_cull_face)] = CullMode;
96 table[OFF(cull_test_enabled)] = CullMode; 96 table[OFF(gl_cull_test_enabled)] = CullMode;
97} 97}
98 98
99void SetupDirtyDepthBoundsEnable(Tables& tables) { 99void SetupDirtyDepthBoundsEnable(Tables& tables) {
@@ -114,20 +114,20 @@ void SetupDirtyDepthCompareOp(Tables& tables) {
114 114
115void SetupDirtyFrontFace(Tables& tables) { 115void SetupDirtyFrontFace(Tables& tables) {
116 auto& table = tables[0]; 116 auto& table = tables[0];
117 table[OFF(front_face)] = FrontFace; 117 table[OFF(gl_front_face)] = FrontFace;
118 table[OFF(screen_y_control)] = FrontFace; 118 table[OFF(window_origin)] = FrontFace;
119} 119}
120 120
121void SetupDirtyStencilOp(Tables& tables) { 121void SetupDirtyStencilOp(Tables& tables) {
122 auto& table = tables[0]; 122 auto& table = tables[0];
123 table[OFF(stencil_front_op_fail)] = StencilOp; 123 table[OFF(stencil_front_op.fail)] = StencilOp;
124 table[OFF(stencil_front_op_zfail)] = StencilOp; 124 table[OFF(stencil_front_op.zfail)] = StencilOp;
125 table[OFF(stencil_front_op_zpass)] = StencilOp; 125 table[OFF(stencil_front_op.zpass)] = StencilOp;
126 table[OFF(stencil_front_func_func)] = StencilOp; 126 table[OFF(stencil_front_op.func)] = StencilOp;
127 table[OFF(stencil_back_op_fail)] = StencilOp; 127 table[OFF(stencil_back_op.fail)] = StencilOp;
128 table[OFF(stencil_back_op_zfail)] = StencilOp; 128 table[OFF(stencil_back_op.zfail)] = StencilOp;
129 table[OFF(stencil_back_op_zpass)] = StencilOp; 129 table[OFF(stencil_back_op.zpass)] = StencilOp;
130 table[OFF(stencil_back_func_func)] = StencilOp; 130 table[OFF(stencil_back_op.func)] = StencilOp;
131 131
132 // Table 0 is used by StencilProperties 132 // Table 0 is used by StencilProperties
133 tables[1][OFF(stencil_two_side_enable)] = StencilOp; 133 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
@@ -139,10 +139,10 @@ void SetupDirtyStencilTestEnable(Tables& tables) {
139 139
140void SetupDirtyBlending(Tables& tables) { 140void SetupDirtyBlending(Tables& tables) {
141 tables[0][OFF(color_mask_common)] = Blending; 141 tables[0][OFF(color_mask_common)] = Blending;
142 tables[0][OFF(independent_blend_enable)] = Blending; 142 tables[0][OFF(blend_per_target_enabled)] = Blending;
143 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); 143 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending);
144 FillBlock(tables[0], OFF(blend), NUM(blend), Blending); 144 FillBlock(tables[0], OFF(blend), NUM(blend), Blending);
145 FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending); 145 FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending);
146} 146}
147 147
148void SetupDirtyViewportSwizzles(Tables& tables) { 148void SetupDirtyViewportSwizzles(Tables& tables) {
@@ -166,17 +166,16 @@ void SetupDirtyVertexBindings(Tables& tables) {
166 static constexpr size_t divisor_offset = 3; 166 static constexpr size_t divisor_offset = 3;
167 for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { 167 for (size_t i = 0; i < Regs::NumVertexArrays; ++i) {
168 const u8 flag = static_cast<u8>(VertexBinding0 + i); 168 const u8 flag = static_cast<u8>(VertexBinding0 + i);
169 tables[0][OFF(instanced_arrays) + i] = VertexInput; 169 tables[0][OFF(vertex_stream_instances) + i] = VertexInput;
170 tables[1][OFF(instanced_arrays) + i] = flag; 170 tables[1][OFF(vertex_stream_instances) + i] = flag;
171 tables[0][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = VertexInput; 171 tables[0][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = VertexInput;
172 tables[1][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = flag; 172 tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag;
173 } 173 }
174} 174}
175} // Anonymous namespace 175} // Anonymous namespace
176 176
177StateTracker::StateTracker(Tegra::GPU& gpu) 177void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
178 : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} { 178 auto& tables{channel_state.maxwell_3d->dirty.tables};
179 auto& tables{gpu.Maxwell3D().dirty.tables};
180 SetupDirtyFlags(tables); 179 SetupDirtyFlags(tables);
181 SetupDirtyViewports(tables); 180 SetupDirtyViewports(tables);
182 SetupDirtyScissors(tables); 181 SetupDirtyScissors(tables);
@@ -199,4 +198,15 @@ StateTracker::StateTracker(Tegra::GPU& gpu)
199 SetupDirtyVertexBindings(tables); 198 SetupDirtyVertexBindings(tables);
200} 199}
201 200
201void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
202 flags = &channel_state.maxwell_3d->dirty.flags;
203}
204
205void StateTracker::InvalidateState() {
206 flags->set();
207}
208
209StateTracker::StateTracker()
210 : flags{&default_flags}, default_flags{}, invalidation_flags{MakeInvalidationFlags()} {}
211
202} // namespace Vulkan 212} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index a85bc1c10..2296dea60 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -10,6 +10,12 @@
10#include "video_core/dirty_flags.h" 10#include "video_core/dirty_flags.h"
11#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
12 12
13namespace Tegra {
14namespace Control {
15struct ChannelState;
16}
17} // namespace Tegra
18
13namespace Vulkan { 19namespace Vulkan {
14 20
15namespace Dirty { 21namespace Dirty {
@@ -53,19 +59,19 @@ class StateTracker {
53 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 59 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
54 60
55public: 61public:
56 explicit StateTracker(Tegra::GPU& gpu); 62 explicit StateTracker();
57 63
58 void InvalidateCommandBufferState() { 64 void InvalidateCommandBufferState() {
59 flags |= invalidation_flags; 65 (*flags) |= invalidation_flags;
60 current_topology = INVALID_TOPOLOGY; 66 current_topology = INVALID_TOPOLOGY;
61 } 67 }
62 68
63 void InvalidateViewports() { 69 void InvalidateViewports() {
64 flags[Dirty::Viewports] = true; 70 (*flags)[Dirty::Viewports] = true;
65 } 71 }
66 72
67 void InvalidateScissors() { 73 void InvalidateScissors() {
68 flags[Dirty::Scissors] = true; 74 (*flags)[Dirty::Scissors] = true;
69 } 75 }
70 76
71 bool TouchViewports() { 77 bool TouchViewports() {
@@ -139,16 +145,23 @@ public:
139 return has_changed; 145 return has_changed;
140 } 146 }
141 147
148 void SetupTables(Tegra::Control::ChannelState& channel_state);
149
150 void ChangeChannel(Tegra::Control::ChannelState& channel_state);
151
152 void InvalidateState();
153
142private: 154private:
143 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u); 155 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
144 156
145 bool Exchange(std::size_t id, bool new_value) const noexcept { 157 bool Exchange(std::size_t id, bool new_value) const noexcept {
146 const bool is_dirty = flags[id]; 158 const bool is_dirty = (*flags)[id];
147 flags[id] = new_value; 159 (*flags)[id] = new_value;
148 return is_dirty; 160 return is_dirty;
149 } 161 }
150 162
151 Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; 163 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
164 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags;
152 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 165 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
153 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; 166 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
154}; 167};
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index a69ae7725..706d9ba74 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -36,7 +36,8 @@ VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
36 // Mailbox (triple buffering) doesn't lock the application like fifo (vsync), 36 // Mailbox (triple buffering) doesn't lock the application like fifo (vsync),
37 // prefer it if vsync option is not selected 37 // prefer it if vsync option is not selected
38 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); 38 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
39 if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { 39 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless &&
40 found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) {
40 return VK_PRESENT_MODE_MAILBOX_KHR; 41 return VK_PRESENT_MODE_MAILBOX_KHR;
41 } 42 }
42 if (!Settings::values.use_speed_limit.GetValue()) { 43 if (!Settings::values.use_speed_limit.GetValue()) {
@@ -156,8 +157,16 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u3
156 present_mode = ChooseSwapPresentMode(present_modes); 157 present_mode = ChooseSwapPresentMode(present_modes);
157 158
158 u32 requested_image_count{capabilities.minImageCount + 1}; 159 u32 requested_image_count{capabilities.minImageCount + 1};
159 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { 160 // Ensure Tripple buffering if possible.
160 requested_image_count = capabilities.maxImageCount; 161 if (capabilities.maxImageCount > 0) {
162 if (requested_image_count > capabilities.maxImageCount) {
163 requested_image_count = capabilities.maxImageCount;
164 } else {
165 requested_image_count =
166 std::max(requested_image_count, std::min(3U, capabilities.maxImageCount));
167 }
168 } else {
169 requested_image_count = std::max(requested_image_count, 3U);
161 } 170 }
162 VkSwapchainCreateInfoKHR swapchain_ci{ 171 VkSwapchainCreateInfoKHR swapchain_ci{
163 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 172 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index caca79d79..853b80d8a 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -592,7 +592,7 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4
592 case PixelFormat::A5B5G5R1_UNORM: 592 case PixelFormat::A5B5G5R1_UNORM:
593 std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial); 593 std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
594 break; 594 break;
595 case PixelFormat::R4G4_UNORM: 595 case PixelFormat::G4R4_UNORM:
596 std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed); 596 std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
597 break; 597 break;
598 default: 598 default:
@@ -791,12 +791,17 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
791 MemoryAllocator& memory_allocator_, 791 MemoryAllocator& memory_allocator_,
792 StagingBufferPool& staging_buffer_pool_, 792 StagingBufferPool& staging_buffer_pool_,
793 BlitImageHelper& blit_image_helper_, 793 BlitImageHelper& blit_image_helper_,
794 ASTCDecoderPass& astc_decoder_pass_, 794 RenderPassCache& render_pass_cache_,
795 RenderPassCache& render_pass_cache_) 795 DescriptorPool& descriptor_pool,
796 UpdateDescriptorQueue& update_descriptor_queue)
796 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, 797 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
797 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, 798 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
798 astc_decoder_pass{astc_decoder_pass_}, render_pass_cache{render_pass_cache_}, 799 render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} {
799 resolution{Settings::values.resolution_info} {} 800 if (Settings::values.accelerate_astc) {
801 astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
802 update_descriptor_queue, memory_allocator);
803 }
804}
800 805
801void TextureCacheRuntime::Finish() { 806void TextureCacheRuntime::Finish() {
802 scheduler.Finish(); 807 scheduler.Finish();
@@ -1474,13 +1479,14 @@ bool Image::BlitScaleHelper(bool scale_up) {
1474 }; 1479 };
1475 const VkExtent2D extent{ 1480 const VkExtent2D extent{
1476 .width = std::max(scaled_width, info.size.width), 1481 .width = std::max(scaled_width, info.size.width),
1477 .height = std::max(scaled_height, info.size.width), 1482 .height = std::max(scaled_height, info.size.height),
1478 }; 1483 };
1479 1484
1480 auto* view_ptr = blit_view.get(); 1485 auto* view_ptr = blit_view.get();
1481 if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) { 1486 if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
1482 if (!blit_framebuffer) { 1487 if (!blit_framebuffer) {
1483 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, view_ptr, nullptr, extent); 1488 blit_framebuffer =
1489 std::make_unique<Framebuffer>(*runtime, view_ptr, nullptr, extent, scale_up);
1484 } 1490 }
1485 const auto color_view = blit_view->Handle(Shader::TextureType::Color2D); 1491 const auto color_view = blit_view->Handle(Shader::TextureType::Color2D);
1486 1492
@@ -1488,7 +1494,8 @@ bool Image::BlitScaleHelper(bool scale_up) {
1488 src_region, operation, BLIT_OPERATION); 1494 src_region, operation, BLIT_OPERATION);
1489 } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { 1495 } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1490 if (!blit_framebuffer) { 1496 if (!blit_framebuffer) {
1491 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); 1497 blit_framebuffer =
1498 std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent, scale_up);
1492 } 1499 }
1493 runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), blit_view->DepthView(), 1500 runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), blit_view->DepthView(),
1494 blit_view->StencilView(), dst_region, 1501 blit_view->StencilView(), dst_region,
@@ -1756,34 +1763,42 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1756 .width = key.size.width, 1763 .width = key.size.width,
1757 .height = key.size.height, 1764 .height = key.size.height,
1758 }} { 1765 }} {
1759 CreateFramebuffer(runtime, color_buffers, depth_buffer); 1766 CreateFramebuffer(runtime, color_buffers, depth_buffer, key.is_rescaled);
1760 if (runtime.device.HasDebuggingToolAttached()) { 1767 if (runtime.device.HasDebuggingToolAttached()) {
1761 framebuffer.SetObjectNameEXT(VideoCommon::Name(key).c_str()); 1768 framebuffer.SetObjectNameEXT(VideoCommon::Name(key).c_str());
1762 } 1769 }
1763} 1770}
1764 1771
1765Framebuffer::Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer, 1772Framebuffer::Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer,
1766 ImageView* depth_buffer, VkExtent2D extent) 1773 ImageView* depth_buffer, VkExtent2D extent, bool is_rescaled)
1767 : render_area{extent} { 1774 : render_area{extent} {
1768 std::array<ImageView*, NUM_RT> color_buffers{color_buffer}; 1775 std::array<ImageView*, NUM_RT> color_buffers{color_buffer};
1769 CreateFramebuffer(runtime, color_buffers, depth_buffer); 1776 CreateFramebuffer(runtime, color_buffers, depth_buffer, is_rescaled);
1770} 1777}
1771 1778
1772Framebuffer::~Framebuffer() = default; 1779Framebuffer::~Framebuffer() = default;
1773 1780
1774void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, 1781void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1775 std::span<ImageView*, NUM_RT> color_buffers, 1782 std::span<ImageView*, NUM_RT> color_buffers,
1776 ImageView* depth_buffer) { 1783 ImageView* depth_buffer, bool is_rescaled) {
1777 std::vector<VkImageView> attachments; 1784 std::vector<VkImageView> attachments;
1778 RenderPassKey renderpass_key{}; 1785 RenderPassKey renderpass_key{};
1779 s32 num_layers = 1; 1786 s32 num_layers = 1;
1780 1787
1788 const auto& resolution = runtime.resolution;
1789
1790 u32 width = std::numeric_limits<u32>::max();
1791 u32 height = std::numeric_limits<u32>::max();
1781 for (size_t index = 0; index < NUM_RT; ++index) { 1792 for (size_t index = 0; index < NUM_RT; ++index) {
1782 const ImageView* const color_buffer = color_buffers[index]; 1793 const ImageView* const color_buffer = color_buffers[index];
1783 if (!color_buffer) { 1794 if (!color_buffer) {
1784 renderpass_key.color_formats[index] = PixelFormat::Invalid; 1795 renderpass_key.color_formats[index] = PixelFormat::Invalid;
1785 continue; 1796 continue;
1786 } 1797 }
1798 width = std::min(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width)
1799 : color_buffer->size.width);
1800 height = std::min(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height)
1801 : color_buffer->size.height);
1787 attachments.push_back(color_buffer->RenderTarget()); 1802 attachments.push_back(color_buffer->RenderTarget());
1788 renderpass_key.color_formats[index] = color_buffer->format; 1803 renderpass_key.color_formats[index] = color_buffer->format;
1789 num_layers = std::max(num_layers, color_buffer->range.extent.layers); 1804 num_layers = std::max(num_layers, color_buffer->range.extent.layers);
@@ -1794,6 +1809,10 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1794 } 1809 }
1795 const size_t num_colors = attachments.size(); 1810 const size_t num_colors = attachments.size();
1796 if (depth_buffer) { 1811 if (depth_buffer) {
1812 width = std::min(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width)
1813 : depth_buffer->size.width);
1814 height = std::min(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height)
1815 : depth_buffer->size.height);
1797 attachments.push_back(depth_buffer->RenderTarget()); 1816 attachments.push_back(depth_buffer->RenderTarget());
1798 renderpass_key.depth_format = depth_buffer->format; 1817 renderpass_key.depth_format = depth_buffer->format;
1799 num_layers = std::max(num_layers, depth_buffer->range.extent.layers); 1818 num_layers = std::max(num_layers, depth_buffer->range.extent.layers);
@@ -1810,6 +1829,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1810 renderpass_key.samples = samples; 1829 renderpass_key.samples = samples;
1811 1830
1812 renderpass = runtime.render_pass_cache.Get(renderpass_key); 1831 renderpass = runtime.render_pass_cache.Get(renderpass_key);
1832 render_area.width = std::min(render_area.width, width);
1833 render_area.height = std::min(render_area.height, height);
1813 1834
1814 num_color_buffers = static_cast<u32>(num_colors); 1835 num_color_buffers = static_cast<u32>(num_colors);
1815 framebuffer = runtime.device.GetLogical().CreateFramebuffer({ 1836 framebuffer = runtime.device.GetLogical().CreateFramebuffer({
@@ -1829,7 +1850,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
1829 Image& image, const StagingBufferRef& map, 1850 Image& image, const StagingBufferRef& map,
1830 std::span<const VideoCommon::SwizzleParameters> swizzles) { 1851 std::span<const VideoCommon::SwizzleParameters> swizzles) {
1831 if (IsPixelFormatASTC(image.info.format)) { 1852 if (IsPixelFormatASTC(image.info.format)) {
1832 return astc_decoder_pass.Assemble(image, map, swizzles); 1853 return astc_decoder_pass->Assemble(image, map, swizzles);
1833 } 1854 }
1834 ASSERT(false); 1855 ASSERT(false);
1835} 1856}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 69f06ee7b..7ec0df134 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -6,6 +6,7 @@
6#include <span> 6#include <span>
7 7
8#include "shader_recompiler/shader_info.h" 8#include "shader_recompiler/shader_info.h"
9#include "video_core/renderer_vulkan/vk_compute_pass.h"
9#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
10#include "video_core/texture_cache/image_view_base.h" 11#include "video_core/texture_cache/image_view_base.h"
11#include "video_core/texture_cache/texture_cache_base.h" 12#include "video_core/texture_cache/texture_cache_base.h"
@@ -25,14 +26,15 @@ using VideoCommon::RenderTargets;
25using VideoCommon::SlotVector; 26using VideoCommon::SlotVector;
26using VideoCore::Surface::PixelFormat; 27using VideoCore::Surface::PixelFormat;
27 28
28class ASTCDecoderPass;
29class BlitImageHelper; 29class BlitImageHelper;
30class DescriptorPool;
30class Device; 31class Device;
31class Image; 32class Image;
32class ImageView; 33class ImageView;
33class Framebuffer; 34class Framebuffer;
34class RenderPassCache; 35class RenderPassCache;
35class StagingBufferPool; 36class StagingBufferPool;
37class UpdateDescriptorQueue;
36class Scheduler; 38class Scheduler;
37 39
38class TextureCacheRuntime { 40class TextureCacheRuntime {
@@ -41,8 +43,9 @@ public:
41 MemoryAllocator& memory_allocator_, 43 MemoryAllocator& memory_allocator_,
42 StagingBufferPool& staging_buffer_pool_, 44 StagingBufferPool& staging_buffer_pool_,
43 BlitImageHelper& blit_image_helper_, 45 BlitImageHelper& blit_image_helper_,
44 ASTCDecoderPass& astc_decoder_pass_, 46 RenderPassCache& render_pass_cache_,
45 RenderPassCache& render_pass_cache_); 47 DescriptorPool& descriptor_pool,
48 UpdateDescriptorQueue& update_descriptor_queue);
46 49
47 void Finish(); 50 void Finish();
48 51
@@ -97,8 +100,8 @@ public:
97 MemoryAllocator& memory_allocator; 100 MemoryAllocator& memory_allocator;
98 StagingBufferPool& staging_buffer_pool; 101 StagingBufferPool& staging_buffer_pool;
99 BlitImageHelper& blit_image_helper; 102 BlitImageHelper& blit_image_helper;
100 ASTCDecoderPass& astc_decoder_pass;
101 RenderPassCache& render_pass_cache; 103 RenderPassCache& render_pass_cache;
104 std::optional<ASTCDecoderPass> astc_decoder_pass;
102 const Settings::ResolutionScalingInfo& resolution; 105 const Settings::ResolutionScalingInfo& resolution;
103 106
104 constexpr static size_t indexing_slots = 8 * sizeof(size_t); 107 constexpr static size_t indexing_slots = 8 * sizeof(size_t);
@@ -268,7 +271,7 @@ public:
268 ImageView* depth_buffer, const VideoCommon::RenderTargets& key); 271 ImageView* depth_buffer, const VideoCommon::RenderTargets& key);
269 272
270 explicit Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer, 273 explicit Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer,
271 ImageView* depth_buffer, VkExtent2D extent); 274 ImageView* depth_buffer, VkExtent2D extent, bool is_rescaled);
272 275
273 ~Framebuffer(); 276 ~Framebuffer();
274 277
@@ -279,7 +282,8 @@ public:
279 Framebuffer& operator=(Framebuffer&&) = default; 282 Framebuffer& operator=(Framebuffer&&) = default;
280 283
281 void CreateFramebuffer(TextureCacheRuntime& runtime, 284 void CreateFramebuffer(TextureCacheRuntime& runtime,
282 std::span<ImageView*, NUM_RT> color_buffers, ImageView* depth_buffer); 285 std::span<ImageView*, NUM_RT> color_buffers, ImageView* depth_buffer,
286 bool is_rescaled = false);
283 287
284 [[nodiscard]] VkFramebuffer Handle() const noexcept { 288 [[nodiscard]] VkFramebuffer Handle() const noexcept {
285 return *framebuffer; 289 return *framebuffer;
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index 164e4ee0e..d9482371b 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -8,6 +8,7 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "shader_recompiler/frontend/maxwell/control_flow.h" 9#include "shader_recompiler/frontend/maxwell/control_flow.h"
10#include "shader_recompiler/object_pool.h" 10#include "shader_recompiler/object_pool.h"
11#include "video_core/control/channel_state.h"
11#include "video_core/dirty_flags.h" 12#include "video_core/dirty_flags.h"
12#include "video_core/engines/kepler_compute.h" 13#include "video_core/engines/kepler_compute.h"
13#include "video_core/engines/maxwell_3d.h" 14#include "video_core/engines/maxwell_3d.h"
@@ -33,29 +34,25 @@ void ShaderCache::SyncGuestHost() {
33 RemovePendingShaders(); 34 RemovePendingShaders();
34} 35}
35 36
36ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_, 37ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {}
37 Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_,
38 Tegra::Engines::KeplerCompute& kepler_compute_)
39 : gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, kepler_compute{kepler_compute_},
40 rasterizer{rasterizer_} {}
41 38
42bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) { 39bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
43 auto& dirty{maxwell3d.dirty.flags}; 40 auto& dirty{maxwell3d->dirty.flags};
44 if (!dirty[VideoCommon::Dirty::Shaders]) { 41 if (!dirty[VideoCommon::Dirty::Shaders]) {
45 return last_shaders_valid; 42 return last_shaders_valid;
46 } 43 }
47 dirty[VideoCommon::Dirty::Shaders] = false; 44 dirty[VideoCommon::Dirty::Shaders] = false;
48 45
49 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; 46 const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
50 for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) { 47 for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) {
51 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { 48 if (!maxwell3d->regs.IsShaderConfigEnabled(index)) {
52 unique_hashes[index] = 0; 49 unique_hashes[index] = 0;
53 continue; 50 continue;
54 } 51 }
55 const auto& shader_config{maxwell3d.regs.shader_config[index]}; 52 const auto& shader_config{maxwell3d->regs.pipelines[index]};
56 const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderProgram>(index)}; 53 const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderType>(index)};
57 const GPUVAddr shader_addr{base_addr + shader_config.offset}; 54 const GPUVAddr shader_addr{base_addr + shader_config.offset};
58 const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; 55 const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
59 if (!cpu_shader_addr) { 56 if (!cpu_shader_addr) {
60 LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); 57 LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
61 last_shaders_valid = false; 58 last_shaders_valid = false;
@@ -64,7 +61,7 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
64 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)}; 61 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
65 if (!shader_info) { 62 if (!shader_info) {
66 const u32 start_address{shader_config.offset}; 63 const u32 start_address{shader_config.offset};
67 GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address}; 64 GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, base_addr, start_address};
68 shader_info = MakeShaderInfo(env, *cpu_shader_addr); 65 shader_info = MakeShaderInfo(env, *cpu_shader_addr);
69 } 66 }
70 shader_infos[index] = shader_info; 67 shader_infos[index] = shader_info;
@@ -75,10 +72,10 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
75} 72}
76 73
77const ShaderInfo* ShaderCache::ComputeShader() { 74const ShaderInfo* ShaderCache::ComputeShader() {
78 const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; 75 const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
79 const auto& qmd{kepler_compute.launch_description}; 76 const auto& qmd{kepler_compute->launch_description};
80 const GPUVAddr shader_addr{program_base + qmd.program_start}; 77 const GPUVAddr shader_addr{program_base + qmd.program_start};
81 const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; 78 const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
82 if (!cpu_shader_addr) { 79 if (!cpu_shader_addr) {
83 LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); 80 LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
84 return nullptr; 81 return nullptr;
@@ -86,22 +83,22 @@ const ShaderInfo* ShaderCache::ComputeShader() {
86 if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) { 83 if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) {
87 return shader; 84 return shader;
88 } 85 }
89 ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; 86 ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
90 return MakeShaderInfo(env, *cpu_shader_addr); 87 return MakeShaderInfo(env, *cpu_shader_addr);
91} 88}
92 89
93void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result, 90void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result,
94 const std::array<u64, NUM_PROGRAMS>& unique_hashes) { 91 const std::array<u64, NUM_PROGRAMS>& unique_hashes) {
95 size_t env_index{}; 92 size_t env_index{};
96 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; 93 const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
97 for (size_t index = 0; index < NUM_PROGRAMS; ++index) { 94 for (size_t index = 0; index < NUM_PROGRAMS; ++index) {
98 if (unique_hashes[index] == 0) { 95 if (unique_hashes[index] == 0) {
99 continue; 96 continue;
100 } 97 }
101 const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderProgram>(index)}; 98 const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderType>(index)};
102 auto& env{result.envs[index]}; 99 auto& env{result.envs[index]};
103 const u32 start_address{maxwell3d.regs.shader_config[index].offset}; 100 const u32 start_address{maxwell3d->regs.pipelines[index].offset};
104 env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address}; 101 env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address};
105 env.SetCachedSize(shader_infos[index]->size_bytes); 102 env.SetCachedSize(shader_infos[index]->size_bytes);
106 result.env_ptrs[env_index++] = &env; 103 result.env_ptrs[env_index++] = &env;
107 } 104 }
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index f67cea8c4..a4391202d 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "video_core/control/channel_state_cache.h"
15#include "video_core/rasterizer_interface.h" 16#include "video_core/rasterizer_interface.h"
16#include "video_core/shader_environment.h" 17#include "video_core/shader_environment.h"
17 18
@@ -19,6 +20,10 @@ namespace Tegra {
19class MemoryManager; 20class MemoryManager;
20} 21}
21 22
23namespace Tegra::Control {
24struct ChannelState;
25}
26
22namespace VideoCommon { 27namespace VideoCommon {
23 28
24class GenericEnvironment; 29class GenericEnvironment;
@@ -28,7 +33,7 @@ struct ShaderInfo {
28 size_t size_bytes{}; 33 size_t size_bytes{};
29}; 34};
30 35
31class ShaderCache { 36class ShaderCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
32 static constexpr u64 YUZU_PAGEBITS = 14; 37 static constexpr u64 YUZU_PAGEBITS = 14;
33 static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS; 38 static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS;
34 39
@@ -71,9 +76,7 @@ protected:
71 } 76 }
72 }; 77 };
73 78
74 explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_, 79 explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_);
75 Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_,
76 Tegra::Engines::KeplerCompute& kepler_compute_);
77 80
78 /// @brief Update the hashes and information of shader stages 81 /// @brief Update the hashes and information of shader stages
79 /// @param unique_hashes Shader hashes to store into when a stage is enabled 82 /// @param unique_hashes Shader hashes to store into when a stage is enabled
@@ -88,10 +91,6 @@ protected:
88 void GetGraphicsEnvironments(GraphicsEnvironments& result, 91 void GetGraphicsEnvironments(GraphicsEnvironments& result,
89 const std::array<u64, NUM_PROGRAMS>& unique_hashes); 92 const std::array<u64, NUM_PROGRAMS>& unique_hashes);
90 93
91 Tegra::MemoryManager& gpu_memory;
92 Tegra::Engines::Maxwell3D& maxwell3d;
93 Tegra::Engines::KeplerCompute& kepler_compute;
94
95 std::array<const ShaderInfo*, NUM_PROGRAMS> shader_infos{}; 94 std::array<const ShaderInfo*, NUM_PROGRAMS> shader_infos{};
96 bool last_shaders_valid = false; 95 bool last_shaders_valid = false;
97 96
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index c903975fc..63bcf9337 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -252,34 +252,34 @@ Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u
252 252
253GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, 253GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
254 Tegra::MemoryManager& gpu_memory_, 254 Tegra::MemoryManager& gpu_memory_,
255 Maxwell::ShaderProgram program, GPUVAddr program_base_, 255 Maxwell::ShaderType program, GPUVAddr program_base_,
256 u32 start_address_) 256 u32 start_address_)
257 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { 257 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
258 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); 258 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
259 initial_offset = sizeof(sph); 259 initial_offset = sizeof(sph);
260 gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; 260 gp_passthrough_mask = maxwell3d->regs.post_vtg_shader_attrib_skip_mask;
261 switch (program) { 261 switch (program) {
262 case Maxwell::ShaderProgram::VertexA: 262 case Maxwell::ShaderType::VertexA:
263 stage = Shader::Stage::VertexA; 263 stage = Shader::Stage::VertexA;
264 stage_index = 0; 264 stage_index = 0;
265 break; 265 break;
266 case Maxwell::ShaderProgram::VertexB: 266 case Maxwell::ShaderType::VertexB:
267 stage = Shader::Stage::VertexB; 267 stage = Shader::Stage::VertexB;
268 stage_index = 0; 268 stage_index = 0;
269 break; 269 break;
270 case Maxwell::ShaderProgram::TesselationControl: 270 case Maxwell::ShaderType::TessellationInit:
271 stage = Shader::Stage::TessellationControl; 271 stage = Shader::Stage::TessellationControl;
272 stage_index = 1; 272 stage_index = 1;
273 break; 273 break;
274 case Maxwell::ShaderProgram::TesselationEval: 274 case Maxwell::ShaderType::Tessellation:
275 stage = Shader::Stage::TessellationEval; 275 stage = Shader::Stage::TessellationEval;
276 stage_index = 2; 276 stage_index = 2;
277 break; 277 break;
278 case Maxwell::ShaderProgram::Geometry: 278 case Maxwell::ShaderType::Geometry:
279 stage = Shader::Stage::Geometry; 279 stage = Shader::Stage::Geometry;
280 stage_index = 3; 280 stage_index = 3;
281 break; 281 break;
282 case Maxwell::ShaderProgram::Fragment: 282 case Maxwell::ShaderType::Pixel:
283 stage = Shader::Stage::Fragment; 283 stage = Shader::Stage::Fragment;
284 stage_index = 4; 284 stage_index = 4;
285 break; 285 break;
@@ -290,7 +290,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
290 const u64 local_size{sph.LocalMemorySize()}; 290 const u64 local_size{sph.LocalMemorySize()};
291 ASSERT(local_size <= std::numeric_limits<u32>::max()); 291 ASSERT(local_size <= std::numeric_limits<u32>::max());
292 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size; 292 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size;
293 texture_bound = maxwell3d->regs.tex_cb_index; 293 texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot;
294} 294}
295 295
296u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { 296u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
@@ -306,8 +306,9 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
306 306
307Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 307Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
308 const auto& regs{maxwell3d->regs}; 308 const auto& regs{maxwell3d->regs};
309 const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; 309 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
310 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle); 310 return ReadTextureTypeImpl(regs.tex_header.Address(), regs.tex_header.limit, via_header_index,
311 handle);
311} 312}
312 313
313u32 GraphicsEnvironment::ReadViewportTransformState() { 314u32 GraphicsEnvironment::ReadViewportTransformState() {
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index a0659fd7c..a05833f38 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -95,7 +95,7 @@ public:
95 explicit GraphicsEnvironment() = default; 95 explicit GraphicsEnvironment() = default;
96 explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, 96 explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
97 Tegra::MemoryManager& gpu_memory_, 97 Tegra::MemoryManager& gpu_memory_,
98 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program, 98 Tegra::Engines::Maxwell3D::Regs::ShaderType program,
99 GPUVAddr program_base_, u32 start_address_); 99 GPUVAddr program_base_, u32 start_address_);
100 100
101 ~GraphicsEnvironment() override = default; 101 ~GraphicsEnvironment() override = default;
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 079d5f028..6bd133d10 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -73,17 +73,17 @@ bool SurfaceTargetIsArray(SurfaceTarget target) {
73 73
74PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { 74PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
75 switch (format) { 75 switch (format) {
76 case Tegra::DepthFormat::S8_UINT_Z24_UNORM: 76 case Tegra::DepthFormat::Z24_UNORM_S8_UINT:
77 return PixelFormat::S8_UINT_D24_UNORM; 77 return PixelFormat::S8_UINT_D24_UNORM;
78 case Tegra::DepthFormat::D24S8_UNORM: 78 case Tegra::DepthFormat::S8Z24_UNORM:
79 return PixelFormat::D24_UNORM_S8_UINT; 79 return PixelFormat::D24_UNORM_S8_UINT;
80 case Tegra::DepthFormat::D32_FLOAT: 80 case Tegra::DepthFormat::Z32_FLOAT:
81 return PixelFormat::D32_FLOAT; 81 return PixelFormat::D32_FLOAT;
82 case Tegra::DepthFormat::D16_UNORM: 82 case Tegra::DepthFormat::Z16_UNORM:
83 return PixelFormat::D16_UNORM; 83 return PixelFormat::D16_UNORM;
84 case Tegra::DepthFormat::S8_UINT: 84 case Tegra::DepthFormat::S8_UINT:
85 return PixelFormat::S8_UINT; 85 return PixelFormat::S8_UINT;
86 case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: 86 case Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT:
87 return PixelFormat::D32_FLOAT_S8_UINT; 87 return PixelFormat::D32_FLOAT_S8_UINT;
88 default: 88 default:
89 UNIMPLEMENTED_MSG("Unimplemented format={}", format); 89 UNIMPLEMENTED_MSG("Unimplemented format={}", format);
@@ -117,9 +117,9 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
117 return PixelFormat::R32G32_UINT; 117 return PixelFormat::R32G32_UINT;
118 case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT: 118 case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT:
119 return PixelFormat::R16G16B16X16_FLOAT; 119 return PixelFormat::R16G16B16X16_FLOAT;
120 case Tegra::RenderTargetFormat::B8G8R8A8_UNORM: 120 case Tegra::RenderTargetFormat::A8R8G8B8_UNORM:
121 return PixelFormat::B8G8R8A8_UNORM; 121 return PixelFormat::B8G8R8A8_UNORM;
122 case Tegra::RenderTargetFormat::B8G8R8A8_SRGB: 122 case Tegra::RenderTargetFormat::A8R8G8B8_SRGB:
123 return PixelFormat::B8G8R8A8_SRGB; 123 return PixelFormat::B8G8R8A8_SRGB;
124 case Tegra::RenderTargetFormat::A2B10G10R10_UNORM: 124 case Tegra::RenderTargetFormat::A2B10G10R10_UNORM:
125 return PixelFormat::A2B10G10R10_UNORM; 125 return PixelFormat::A2B10G10R10_UNORM;
@@ -247,6 +247,8 @@ bool IsPixelFormatASTC(PixelFormat format) {
247 case PixelFormat::ASTC_2D_6X6_UNORM: 247 case PixelFormat::ASTC_2D_6X6_UNORM:
248 case PixelFormat::ASTC_2D_6X6_SRGB: 248 case PixelFormat::ASTC_2D_6X6_SRGB:
249 case PixelFormat::ASTC_2D_10X6_UNORM: 249 case PixelFormat::ASTC_2D_10X6_UNORM:
250 case PixelFormat::ASTC_2D_10X5_UNORM:
251 case PixelFormat::ASTC_2D_10X5_SRGB:
250 case PixelFormat::ASTC_2D_10X10_UNORM: 252 case PixelFormat::ASTC_2D_10X10_UNORM:
251 case PixelFormat::ASTC_2D_10X10_SRGB: 253 case PixelFormat::ASTC_2D_10X10_SRGB:
252 case PixelFormat::ASTC_2D_12X12_UNORM: 254 case PixelFormat::ASTC_2D_12X12_UNORM:
@@ -276,6 +278,7 @@ bool IsPixelFormatSRGB(PixelFormat format) {
276 case PixelFormat::ASTC_2D_5X5_SRGB: 278 case PixelFormat::ASTC_2D_5X5_SRGB:
277 case PixelFormat::ASTC_2D_10X8_SRGB: 279 case PixelFormat::ASTC_2D_10X8_SRGB:
278 case PixelFormat::ASTC_2D_6X6_SRGB: 280 case PixelFormat::ASTC_2D_6X6_SRGB:
281 case PixelFormat::ASTC_2D_10X5_SRGB:
279 case PixelFormat::ASTC_2D_10X10_SRGB: 282 case PixelFormat::ASTC_2D_10X10_SRGB:
280 case PixelFormat::ASTC_2D_12X12_SRGB: 283 case PixelFormat::ASTC_2D_12X12_SRGB:
281 case PixelFormat::ASTC_2D_8X6_SRGB: 284 case PixelFormat::ASTC_2D_8X6_SRGB:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 16273f185..57ca7f597 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -82,7 +82,7 @@ enum class PixelFormat {
82 BC3_SRGB, 82 BC3_SRGB,
83 BC7_SRGB, 83 BC7_SRGB,
84 A4B4G4R4_UNORM, 84 A4B4G4R4_UNORM,
85 R4G4_UNORM, 85 G4R4_UNORM,
86 ASTC_2D_4X4_SRGB, 86 ASTC_2D_4X4_SRGB,
87 ASTC_2D_8X8_SRGB, 87 ASTC_2D_8X8_SRGB,
88 ASTC_2D_8X5_SRGB, 88 ASTC_2D_8X5_SRGB,
@@ -94,6 +94,8 @@ enum class PixelFormat {
94 ASTC_2D_6X6_UNORM, 94 ASTC_2D_6X6_UNORM,
95 ASTC_2D_6X6_SRGB, 95 ASTC_2D_6X6_SRGB,
96 ASTC_2D_10X6_UNORM, 96 ASTC_2D_10X6_UNORM,
97 ASTC_2D_10X5_UNORM,
98 ASTC_2D_10X5_SRGB,
97 ASTC_2D_10X10_UNORM, 99 ASTC_2D_10X10_UNORM,
98 ASTC_2D_10X10_SRGB, 100 ASTC_2D_10X10_SRGB,
99 ASTC_2D_12X12_UNORM, 101 ASTC_2D_12X12_UNORM,
@@ -216,7 +218,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
216 4, // BC3_SRGB 218 4, // BC3_SRGB
217 4, // BC7_SRGB 219 4, // BC7_SRGB
218 1, // A4B4G4R4_UNORM 220 1, // A4B4G4R4_UNORM
219 1, // R4G4_UNORM 221 1, // G4R4_UNORM
220 4, // ASTC_2D_4X4_SRGB 222 4, // ASTC_2D_4X4_SRGB
221 8, // ASTC_2D_8X8_SRGB 223 8, // ASTC_2D_8X8_SRGB
222 8, // ASTC_2D_8X5_SRGB 224 8, // ASTC_2D_8X5_SRGB
@@ -228,6 +230,8 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
228 6, // ASTC_2D_6X6_UNORM 230 6, // ASTC_2D_6X6_UNORM
229 6, // ASTC_2D_6X6_SRGB 231 6, // ASTC_2D_6X6_SRGB
230 10, // ASTC_2D_10X6_UNORM 232 10, // ASTC_2D_10X6_UNORM
233 10, // ASTC_2D_10X5_UNORM
234 10, // ASTC_2D_10X5_SRGB
231 10, // ASTC_2D_10X10_UNORM 235 10, // ASTC_2D_10X10_UNORM
232 10, // ASTC_2D_10X10_SRGB 236 10, // ASTC_2D_10X10_SRGB
233 12, // ASTC_2D_12X12_UNORM 237 12, // ASTC_2D_12X12_UNORM
@@ -319,7 +323,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
319 4, // BC3_SRGB 323 4, // BC3_SRGB
320 4, // BC7_SRGB 324 4, // BC7_SRGB
321 1, // A4B4G4R4_UNORM 325 1, // A4B4G4R4_UNORM
322 1, // R4G4_UNORM 326 1, // G4R4_UNORM
323 4, // ASTC_2D_4X4_SRGB 327 4, // ASTC_2D_4X4_SRGB
324 8, // ASTC_2D_8X8_SRGB 328 8, // ASTC_2D_8X8_SRGB
325 5, // ASTC_2D_8X5_SRGB 329 5, // ASTC_2D_8X5_SRGB
@@ -331,6 +335,8 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
331 6, // ASTC_2D_6X6_UNORM 335 6, // ASTC_2D_6X6_UNORM
332 6, // ASTC_2D_6X6_SRGB 336 6, // ASTC_2D_6X6_SRGB
333 6, // ASTC_2D_10X6_UNORM 337 6, // ASTC_2D_10X6_UNORM
338 5, // ASTC_2D_10X5_UNORM
339 5, // ASTC_2D_10X5_SRGB
334 10, // ASTC_2D_10X10_UNORM 340 10, // ASTC_2D_10X10_UNORM
335 10, // ASTC_2D_10X10_SRGB 341 10, // ASTC_2D_10X10_SRGB
336 12, // ASTC_2D_12X12_UNORM 342 12, // ASTC_2D_12X12_UNORM
@@ -422,7 +428,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
422 128, // BC3_SRGB 428 128, // BC3_SRGB
423 128, // BC7_UNORM 429 128, // BC7_UNORM
424 16, // A4B4G4R4_UNORM 430 16, // A4B4G4R4_UNORM
425 8, // R4G4_UNORM 431 8, // G4R4_UNORM
426 128, // ASTC_2D_4X4_SRGB 432 128, // ASTC_2D_4X4_SRGB
427 128, // ASTC_2D_8X8_SRGB 433 128, // ASTC_2D_8X8_SRGB
428 128, // ASTC_2D_8X5_SRGB 434 128, // ASTC_2D_8X5_SRGB
@@ -434,6 +440,8 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
434 128, // ASTC_2D_6X6_UNORM 440 128, // ASTC_2D_6X6_UNORM
435 128, // ASTC_2D_6X6_SRGB 441 128, // ASTC_2D_6X6_SRGB
436 128, // ASTC_2D_10X6_UNORM 442 128, // ASTC_2D_10X6_UNORM
443 128, // ASTC_2D_10X5_UNORM
444 128, // ASTC_2D_10X5_SRGB
437 128, // ASTC_2D_10X10_UNORM 445 128, // ASTC_2D_10X10_UNORM
438 128, // ASTC_2D_10X10_SRGB 446 128, // ASTC_2D_10X10_SRGB
439 128, // ASTC_2D_12X12_UNORM 447 128, // ASTC_2D_12X12_UNORM
diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h
index b18e3838f..ee4240288 100644
--- a/src/video_core/texture_cache/descriptor_table.h
+++ b/src/video_core/texture_cache/descriptor_table.h
@@ -18,7 +18,7 @@ class DescriptorTable {
18public: 18public:
19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} 19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}
20 20
21 [[nodiscard]] bool Synchornize(GPUVAddr gpu_addr, u32 limit) { 21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { 22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) {
23 return false; 23 return false;
24 } 24 }
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 1412aa076..08aa8ca33 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -63,7 +63,7 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
63 case Hash(TextureFormat::A4B4G4R4, UNORM): 63 case Hash(TextureFormat::A4B4G4R4, UNORM):
64 return PixelFormat::A4B4G4R4_UNORM; 64 return PixelFormat::A4B4G4R4_UNORM;
65 case Hash(TextureFormat::G4R4, UNORM): 65 case Hash(TextureFormat::G4R4, UNORM):
66 return PixelFormat::R4G4_UNORM; 66 return PixelFormat::G4R4_UNORM;
67 case Hash(TextureFormat::A5B5G5R1, UNORM): 67 case Hash(TextureFormat::A5B5G5R1, UNORM):
68 return PixelFormat::A5B5G5R1_UNORM; 68 return PixelFormat::A5B5G5R1_UNORM;
69 case Hash(TextureFormat::R8, UNORM): 69 case Hash(TextureFormat::R8, UNORM):
@@ -150,6 +150,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
150 return PixelFormat::D24_UNORM_S8_UINT; 150 return PixelFormat::D24_UNORM_S8_UINT;
151 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): 151 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
152 return PixelFormat::D32_FLOAT_S8_UINT; 152 return PixelFormat::D32_FLOAT_S8_UINT;
153 case Hash(TextureFormat::R32_B24G8, FLOAT, UINT, UNORM, UNORM, LINEAR):
154 return PixelFormat::D32_FLOAT_S8_UINT;
153 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): 155 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR):
154 return PixelFormat::BC1_RGBA_UNORM; 156 return PixelFormat::BC1_RGBA_UNORM;
155 case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB): 157 case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB):
@@ -208,6 +210,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
208 return PixelFormat::ASTC_2D_6X6_SRGB; 210 return PixelFormat::ASTC_2D_6X6_SRGB;
209 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): 211 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
210 return PixelFormat::ASTC_2D_10X6_UNORM; 212 return PixelFormat::ASTC_2D_10X6_UNORM;
213 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR):
214 return PixelFormat::ASTC_2D_10X5_UNORM;
215 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB):
216 return PixelFormat::ASTC_2D_10X5_SRGB;
211 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): 217 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR):
212 return PixelFormat::ASTC_2D_10X10_UNORM; 218 return PixelFormat::ASTC_2D_10X10_UNORM;
213 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): 219 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index 95a572604..acc854715 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -153,8 +153,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
153 return "BC7_SRGB"; 153 return "BC7_SRGB";
154 case PixelFormat::A4B4G4R4_UNORM: 154 case PixelFormat::A4B4G4R4_UNORM:
155 return "A4B4G4R4_UNORM"; 155 return "A4B4G4R4_UNORM";
156 case PixelFormat::R4G4_UNORM: 156 case PixelFormat::G4R4_UNORM:
157 return "R4G4_UNORM"; 157 return "G4R4_UNORM";
158 case PixelFormat::ASTC_2D_4X4_SRGB: 158 case PixelFormat::ASTC_2D_4X4_SRGB:
159 return "ASTC_2D_4X4_SRGB"; 159 return "ASTC_2D_4X4_SRGB";
160 case PixelFormat::ASTC_2D_8X8_SRGB: 160 case PixelFormat::ASTC_2D_8X8_SRGB:
@@ -177,6 +177,10 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
177 return "ASTC_2D_6X6_SRGB"; 177 return "ASTC_2D_6X6_SRGB";
178 case PixelFormat::ASTC_2D_10X6_UNORM: 178 case PixelFormat::ASTC_2D_10X6_UNORM:
179 return "ASTC_2D_10X6_UNORM"; 179 return "ASTC_2D_10X6_UNORM";
180 case PixelFormat::ASTC_2D_10X5_UNORM:
181 return "ASTC_2D_10X5_UNORM";
182 case PixelFormat::ASTC_2D_10X5_SRGB:
183 return "ASTC_2D_10X5_SRGB";
180 case PixelFormat::ASTC_2D_10X10_UNORM: 184 case PixelFormat::ASTC_2D_10X10_UNORM:
181 return "ASTC_2D_10X10_UNORM"; 185 return "ASTC_2D_10X10_UNORM";
182 case PixelFormat::ASTC_2D_10X10_SRGB: 186 case PixelFormat::ASTC_2D_10X10_SRGB:
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index f61e09ac7..91512022f 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/div_ceil.h"
10#include "video_core/surface.h" 11#include "video_core/surface.h"
11#include "video_core/texture_cache/formatter.h" 12#include "video_core/texture_cache/formatter.h"
12#include "video_core/texture_cache/image_base.h" 13#include "video_core/texture_cache/image_base.h"
@@ -182,10 +183,6 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
182 }; 183 };
183 const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1; 184 const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1;
184 const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1; 185 const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1;
185 if (is_lhs_compressed && is_rhs_compressed) {
186 LOG_ERROR(HW_GPU, "Compressed to compressed image aliasing is not implemented");
187 return;
188 }
189 const s32 lhs_mips = lhs.info.resources.levels; 186 const s32 lhs_mips = lhs.info.resources.levels;
190 const s32 rhs_mips = rhs.info.resources.levels; 187 const s32 rhs_mips = rhs.info.resources.levels;
191 const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips); 188 const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips);
@@ -199,12 +196,12 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
199 Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level); 196 Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level);
200 Extent3D rhs_size = MipSize(rhs.info.size, mip_level); 197 Extent3D rhs_size = MipSize(rhs.info.size, mip_level);
201 if (is_lhs_compressed) { 198 if (is_lhs_compressed) {
202 lhs_size.width /= lhs_block.width; 199 lhs_size.width = Common::DivCeil(lhs_size.width, lhs_block.width);
203 lhs_size.height /= lhs_block.height; 200 lhs_size.height = Common::DivCeil(lhs_size.height, lhs_block.height);
204 } 201 }
205 if (is_rhs_compressed) { 202 if (is_rhs_compressed) {
206 rhs_size.width /= rhs_block.width; 203 rhs_size.width = Common::DivCeil(rhs_size.width, rhs_block.width);
207 rhs_size.height /= rhs_block.height; 204 rhs_size.height = Common::DivCeil(rhs_size.height, rhs_block.height);
208 } 205 }
209 const Extent3D copy_size{ 206 const Extent3D copy_size{
210 .width = std::min(lhs_size.width, rhs_size.width), 207 .width = std::min(lhs_size.width, rhs_size.width),
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 1f85ec9da..620565684 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -88,6 +88,9 @@ struct ImageBase {
88 u32 scale_rating = 0; 88 u32 scale_rating = 0;
89 u64 scale_tick = 0; 89 u64 scale_tick = 0;
90 bool has_scaled = false; 90 bool has_scaled = false;
91
92 size_t channel = 0;
93
91 ImageFlagBits flags = ImageFlagBits::CpuModified; 94 ImageFlagBits flags = ImageFlagBits::CpuModified;
92 95
93 GPUVAddr gpu_addr = 0; 96 GPUVAddr gpu_addr = 0;
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 6c073ee57..852ec2519 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <fmt/format.h>
5
4#include "common/assert.h" 6#include "common/assert.h"
5#include "video_core/surface.h" 7#include "video_core/surface.h"
6#include "video_core/texture_cache/format_lookup_table.h" 8#include "video_core/texture_cache/format_lookup_table.h"
@@ -12,6 +14,7 @@
12 14
13namespace VideoCommon { 15namespace VideoCommon {
14 16
17using Tegra::Engines::Maxwell3D;
15using Tegra::Texture::TextureType; 18using Tegra::Texture::TextureType;
16using Tegra::Texture::TICEntry; 19using Tegra::Texture::TICEntry;
17using VideoCore::Surface::PixelFormat; 20using VideoCore::Surface::PixelFormat;
@@ -107,12 +110,13 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
107 } 110 }
108} 111}
109 112
110ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept { 113ImageInfo::ImageInfo(const Maxwell3D::Regs& regs, size_t index) noexcept {
111 const auto& rt = regs.rt[index]; 114 const auto& rt = regs.rt[index];
112 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format); 115 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format);
113 rescaleable = false; 116 rescaleable = false;
114 if (rt.tile_mode.is_pitch_linear) { 117 if (rt.tile_mode.is_pitch_linear) {
115 ASSERT(rt.tile_mode.is_3d == 0); 118 ASSERT(rt.tile_mode.dim_control ==
119 Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray);
116 type = ImageType::Linear; 120 type = ImageType::Linear;
117 pitch = rt.width; 121 pitch = rt.width;
118 size = Extent3D{ 122 size = Extent3D{
@@ -124,15 +128,16 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index)
124 } 128 }
125 size.width = rt.width; 129 size.width = rt.width;
126 size.height = rt.height; 130 size.height = rt.height;
127 layer_stride = rt.layer_stride * 4; 131 layer_stride = rt.array_pitch * 4;
128 maybe_unaligned_layer_stride = layer_stride; 132 maybe_unaligned_layer_stride = layer_stride;
129 num_samples = NumSamples(regs.multisample_mode); 133 num_samples = NumSamples(regs.anti_alias_samples_mode);
130 block = Extent3D{ 134 block = Extent3D{
131 .width = rt.tile_mode.block_width, 135 .width = rt.tile_mode.block_width,
132 .height = rt.tile_mode.block_height, 136 .height = rt.tile_mode.block_height,
133 .depth = rt.tile_mode.block_depth, 137 .depth = rt.tile_mode.block_depth,
134 }; 138 };
135 if (rt.tile_mode.is_3d) { 139 if (rt.tile_mode.dim_control ==
140 Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) {
136 type = ImageType::e3D; 141 type = ImageType::e3D;
137 size.depth = rt.depth; 142 size.depth = rt.depth;
138 } else { 143 } else {
@@ -146,31 +151,37 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index)
146 151
147ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept { 152ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
148 format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format); 153 format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format);
149 size.width = regs.zeta_width; 154 size.width = regs.zeta_size.width;
150 size.height = regs.zeta_height; 155 size.height = regs.zeta_size.height;
151 rescaleable = false; 156 rescaleable = false;
152 resources.levels = 1; 157 resources.levels = 1;
153 layer_stride = regs.zeta.layer_stride * 4; 158 layer_stride = regs.zeta.array_pitch * 4;
154 maybe_unaligned_layer_stride = layer_stride; 159 maybe_unaligned_layer_stride = layer_stride;
155 num_samples = NumSamples(regs.multisample_mode); 160 num_samples = NumSamples(regs.anti_alias_samples_mode);
156 block = Extent3D{ 161 block = Extent3D{
157 .width = regs.zeta.tile_mode.block_width, 162 .width = regs.zeta.tile_mode.block_width,
158 .height = regs.zeta.tile_mode.block_height, 163 .height = regs.zeta.tile_mode.block_height,
159 .depth = regs.zeta.tile_mode.block_depth, 164 .depth = regs.zeta.tile_mode.block_depth,
160 }; 165 };
161 if (regs.zeta.tile_mode.is_pitch_linear) { 166 if (regs.zeta.tile_mode.is_pitch_linear) {
162 ASSERT(regs.zeta.tile_mode.is_3d == 0); 167 ASSERT(regs.zeta.tile_mode.dim_control ==
168 Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray);
163 type = ImageType::Linear; 169 type = ImageType::Linear;
164 pitch = size.width * BytesPerBlock(format); 170 pitch = size.width * BytesPerBlock(format);
165 } else if (regs.zeta.tile_mode.is_3d) { 171 } else if (regs.zeta.tile_mode.dim_control ==
172 Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) {
166 ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0); 173 ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0);
174 ASSERT(regs.zeta_size.dim_control ==
175 Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeOne);
167 type = ImageType::e3D; 176 type = ImageType::e3D;
168 size.depth = regs.zeta_depth; 177 size.depth = regs.zeta_size.depth;
169 } else { 178 } else {
179 ASSERT(regs.zeta_size.dim_control ==
180 Maxwell3D::Regs::ZetaSize::DimensionControl::DepthDefinesArray);
170 rescaleable = block.depth == 0; 181 rescaleable = block.depth == 0;
171 downscaleable = size.height > 512; 182 downscaleable = size.height > 512;
172 type = ImageType::e2D; 183 type = ImageType::e2D;
173 resources.layers = regs.zeta_depth; 184 resources.layers = regs.zeta_size.depth;
174 } 185 }
175} 186}
176 187
diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h
index da8ffe9ec..1efbd6507 100644
--- a/src/video_core/texture_cache/render_targets.h
+++ b/src/video_core/texture_cache/render_targets.h
@@ -26,6 +26,7 @@ struct RenderTargets {
26 ImageViewId depth_buffer_id{}; 26 ImageViewId depth_buffer_id{};
27 std::array<u8, NUM_RT> draw_buffers{}; 27 std::array<u8, NUM_RT> draw_buffers{};
28 Extent2D size{}; 28 Extent2D size{};
29 bool is_rescaled{};
29}; 30};
30 31
31} // namespace VideoCommon 32} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp
new file mode 100644
index 000000000..8a9a32f44
--- /dev/null
+++ b/src/video_core/texture_cache/texture_cache.cpp
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "video_core/control/channel_state_cache.inc"
5#include "video_core/texture_cache/texture_cache_base.h"
6
7namespace VideoCommon {
8
9TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept
10 : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory},
11 compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {}
12
13template class VideoCommon::ChannelSetupCaches<VideoCommon::TextureCacheChannelInfo>;
14
15} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 1dbe01bc0..8ef75fe73 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -7,6 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "video_core/control/channel_state.h"
10#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
11#include "video_core/engines/kepler_compute.h" 12#include "video_core/engines/kepler_compute.h"
12#include "video_core/texture_cache/image_view_base.h" 13#include "video_core/texture_cache/image_view_base.h"
@@ -29,12 +30,8 @@ using VideoCore::Surface::SurfaceType;
29using namespace Common::Literals; 30using namespace Common::Literals;
30 31
31template <class P> 32template <class P>
32TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, 33TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_)
33 Tegra::Engines::Maxwell3D& maxwell3d_, 34 : runtime{runtime_}, rasterizer{rasterizer_} {
34 Tegra::Engines::KeplerCompute& kepler_compute_,
35 Tegra::MemoryManager& gpu_memory_)
36 : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
37 kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_} {
38 // Configure null sampler 35 // Configure null sampler
39 TSCEntry sampler_descriptor{}; 36 TSCEntry sampler_descriptor{};
40 sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear); 37 sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear);
@@ -93,7 +90,7 @@ void TextureCache<P>::RunGarbageCollector() {
93 const auto copies = FullDownloadCopies(image.info); 90 const auto copies = FullDownloadCopies(image.info);
94 image.DownloadMemory(map, copies); 91 image.DownloadMemory(map, copies);
95 runtime.Finish(); 92 runtime.Finish();
96 SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 93 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
97 } 94 }
98 if (True(image.flags & ImageFlagBits::Tracked)) { 95 if (True(image.flags & ImageFlagBits::Tracked)) {
99 UntrackImage(image, image_id); 96 UntrackImage(image, image_id);
@@ -152,22 +149,24 @@ void TextureCache<P>::MarkModification(ImageId id) noexcept {
152template <class P> 149template <class P>
153template <bool has_blacklists> 150template <bool has_blacklists>
154void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) { 151void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) {
155 FillImageViews<has_blacklists>(graphics_image_table, graphics_image_view_ids, views); 152 FillImageViews<has_blacklists>(channel_state->graphics_image_table,
153 channel_state->graphics_image_view_ids, views);
156} 154}
157 155
158template <class P> 156template <class P>
159void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) { 157void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
160 FillImageViews<true>(compute_image_table, compute_image_view_ids, views); 158 FillImageViews<true>(channel_state->compute_image_table, channel_state->compute_image_view_ids,
159 views);
161} 160}
162 161
163template <class P> 162template <class P>
164typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { 163typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
165 if (index > graphics_sampler_table.Limit()) { 164 if (index > channel_state->graphics_sampler_table.Limit()) {
166 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); 165 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
167 return &slot_samplers[NULL_SAMPLER_ID]; 166 return &slot_samplers[NULL_SAMPLER_ID];
168 } 167 }
169 const auto [descriptor, is_new] = graphics_sampler_table.Read(index); 168 const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
170 SamplerId& id = graphics_sampler_ids[index]; 169 SamplerId& id = channel_state->graphics_sampler_ids[index];
171 if (is_new) { 170 if (is_new) {
172 id = FindSampler(descriptor); 171 id = FindSampler(descriptor);
173 } 172 }
@@ -176,12 +175,12 @@ typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
176 175
177template <class P> 176template <class P>
178typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { 177typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
179 if (index > compute_sampler_table.Limit()) { 178 if (index > channel_state->compute_sampler_table.Limit()) {
180 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); 179 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
181 return &slot_samplers[NULL_SAMPLER_ID]; 180 return &slot_samplers[NULL_SAMPLER_ID];
182 } 181 }
183 const auto [descriptor, is_new] = compute_sampler_table.Read(index); 182 const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
184 SamplerId& id = compute_sampler_ids[index]; 183 SamplerId& id = channel_state->compute_sampler_ids[index];
185 if (is_new) { 184 if (is_new) {
186 id = FindSampler(descriptor); 185 id = FindSampler(descriptor);
187 } 186 }
@@ -190,35 +189,38 @@ typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
190 189
191template <class P> 190template <class P>
192void TextureCache<P>::SynchronizeGraphicsDescriptors() { 191void TextureCache<P>::SynchronizeGraphicsDescriptors() {
193 using SamplerIndex = Tegra::Engines::Maxwell3D::Regs::SamplerIndex; 192 using SamplerBinding = Tegra::Engines::Maxwell3D::Regs::SamplerBinding;
194 const bool linked_tsc = maxwell3d.regs.sampler_index == SamplerIndex::ViaHeaderIndex; 193 const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding;
195 const u32 tic_limit = maxwell3d.regs.tic.limit; 194 const u32 tic_limit = maxwell3d->regs.tex_header.limit;
196 const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d.regs.tsc.limit; 195 const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit;
197 if (graphics_sampler_table.Synchornize(maxwell3d.regs.tsc.Address(), tsc_limit)) { 196 if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(),
198 graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); 197 tsc_limit)) {
198 channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
199 } 199 }
200 if (graphics_image_table.Synchornize(maxwell3d.regs.tic.Address(), tic_limit)) { 200 if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(),
201 graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); 201 tic_limit)) {
202 channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
202 } 203 }
203} 204}
204 205
205template <class P> 206template <class P>
206void TextureCache<P>::SynchronizeComputeDescriptors() { 207void TextureCache<P>::SynchronizeComputeDescriptors() {
207 const bool linked_tsc = kepler_compute.launch_description.linked_tsc; 208 const bool linked_tsc = kepler_compute->launch_description.linked_tsc;
208 const u32 tic_limit = kepler_compute.regs.tic.limit; 209 const u32 tic_limit = kepler_compute->regs.tic.limit;
209 const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute.regs.tsc.limit; 210 const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit;
210 const GPUVAddr tsc_gpu_addr = kepler_compute.regs.tsc.Address(); 211 const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address();
211 if (compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { 212 if (channel_state->compute_sampler_table.Synchronize(tsc_gpu_addr, tsc_limit)) {
212 compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); 213 channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
213 } 214 }
214 if (compute_image_table.Synchornize(kepler_compute.regs.tic.Address(), tic_limit)) { 215 if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(),
215 compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); 216 tic_limit)) {
217 channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
216 } 218 }
217} 219}
218 220
219template <class P> 221template <class P>
220bool TextureCache<P>::RescaleRenderTargets(bool is_clear) { 222bool TextureCache<P>::RescaleRenderTargets(bool is_clear) {
221 auto& flags = maxwell3d.dirty.flags; 223 auto& flags = maxwell3d->dirty.flags;
222 u32 scale_rating = 0; 224 u32 scale_rating = 0;
223 bool rescaled = false; 225 bool rescaled = false;
224 std::array<ImageId, NUM_RT> tmp_color_images{}; 226 std::array<ImageId, NUM_RT> tmp_color_images{};
@@ -315,7 +317,7 @@ bool TextureCache<P>::RescaleRenderTargets(bool is_clear) {
315template <class P> 317template <class P>
316void TextureCache<P>::UpdateRenderTargets(bool is_clear) { 318void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
317 using namespace VideoCommon::Dirty; 319 using namespace VideoCommon::Dirty;
318 auto& flags = maxwell3d.dirty.flags; 320 auto& flags = maxwell3d->dirty.flags;
319 if (!flags[Dirty::RenderTargets]) { 321 if (!flags[Dirty::RenderTargets]) {
320 for (size_t index = 0; index < NUM_RT; ++index) { 322 for (size_t index = 0; index < NUM_RT; ++index) {
321 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; 323 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
@@ -342,7 +344,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
342 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); 344 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
343 345
344 for (size_t index = 0; index < NUM_RT; ++index) { 346 for (size_t index = 0; index < NUM_RT; ++index) {
345 render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index)); 347 render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d->regs.rt_control.Map(index));
346 } 348 }
347 u32 up_scale = 1; 349 u32 up_scale = 1;
348 u32 down_shift = 0; 350 u32 down_shift = 0;
@@ -351,9 +353,10 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
351 down_shift = Settings::values.resolution_info.down_shift; 353 down_shift = Settings::values.resolution_info.down_shift;
352 } 354 }
353 render_targets.size = Extent2D{ 355 render_targets.size = Extent2D{
354 (maxwell3d.regs.render_area.width * up_scale) >> down_shift, 356 (maxwell3d->regs.surface_clip.width * up_scale) >> down_shift,
355 (maxwell3d.regs.render_area.height * up_scale) >> down_shift, 357 (maxwell3d->regs.surface_clip.height * up_scale) >> down_shift,
356 }; 358 };
359 render_targets.is_rescaled = is_rescaling;
357 360
358 flags[Dirty::DepthBiasGlobal] = true; 361 flags[Dirty::DepthBiasGlobal] = true;
359} 362}
@@ -439,7 +442,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
439template <class P> 442template <class P>
440void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 443void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
441 std::vector<ImageId> images; 444 std::vector<ImageId> images;
442 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { 445 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
443 if (!image.IsSafeDownload()) { 446 if (!image.IsSafeDownload()) {
444 return; 447 return;
445 } 448 }
@@ -458,7 +461,7 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
458 const auto copies = FullDownloadCopies(image.info); 461 const auto copies = FullDownloadCopies(image.info);
459 image.DownloadMemory(map, copies); 462 image.DownloadMemory(map, copies);
460 runtime.Finish(); 463 runtime.Finish();
461 SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 464 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
462 } 465 }
463} 466}
464 467
@@ -477,12 +480,20 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
477} 480}
478 481
479template <class P> 482template <class P>
480void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { 483void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) {
481 std::vector<ImageId> deleted_images; 484 std::vector<ImageId> deleted_images;
482 ForEachImageInRegionGPU(gpu_addr, size, 485 ForEachImageInRegionGPU(as_id, gpu_addr, size,
483 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 486 [&](ImageId id, Image&) { deleted_images.push_back(id); });
484 for (const ImageId id : deleted_images) { 487 for (const ImageId id : deleted_images) {
485 Image& image = slot_images[id]; 488 Image& image = slot_images[id];
489 if (True(image.flags & ImageFlagBits::CpuModified)) {
490 return;
491 }
492 image.flags |= ImageFlagBits::CpuModified;
493 if (True(image.flags & ImageFlagBits::Tracked)) {
494 UntrackImage(image, id);
495 }
496 /*
486 if (True(image.flags & ImageFlagBits::Remapped)) { 497 if (True(image.flags & ImageFlagBits::Remapped)) {
487 continue; 498 continue;
488 } 499 }
@@ -490,6 +501,7 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
490 if (True(image.flags & ImageFlagBits::Tracked)) { 501 if (True(image.flags & ImageFlagBits::Tracked)) {
491 UntrackImage(image, id); 502 UntrackImage(image, id);
492 } 503 }
504 */
493 } 505 }
494} 506}
495 507
@@ -655,7 +667,7 @@ void TextureCache<P>::PopAsyncFlushes() {
655 for (const ImageId image_id : download_ids) { 667 for (const ImageId image_id : download_ids) {
656 const ImageBase& image = slot_images[image_id]; 668 const ImageBase& image = slot_images[image_id];
657 const auto copies = FullDownloadCopies(image.info); 669 const auto copies = FullDownloadCopies(image.info);
658 SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, download_span); 670 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span);
659 download_map.offset += image.unswizzled_size_bytes; 671 download_map.offset += image.unswizzled_size_bytes;
660 download_span = download_span.subspan(image.unswizzled_size_bytes); 672 download_span = download_span.subspan(image.unswizzled_size_bytes);
661 } 673 }
@@ -714,26 +726,26 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
714 const GPUVAddr gpu_addr = image.gpu_addr; 726 const GPUVAddr gpu_addr = image.gpu_addr;
715 727
716 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { 728 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
717 gpu_memory.ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); 729 gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes());
718 const auto uploads = FullUploadSwizzles(image.info); 730 const auto uploads = FullUploadSwizzles(image.info);
719 runtime.AccelerateImageUpload(image, staging, uploads); 731 runtime.AccelerateImageUpload(image, staging, uploads);
720 } else if (True(image.flags & ImageFlagBits::Converted)) { 732 } else if (True(image.flags & ImageFlagBits::Converted)) {
721 std::vector<u8> unswizzled_data(image.unswizzled_size_bytes); 733 std::vector<u8> unswizzled_data(image.unswizzled_size_bytes);
722 auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, unswizzled_data); 734 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data);
723 ConvertImage(unswizzled_data, image.info, mapped_span, copies); 735 ConvertImage(unswizzled_data, image.info, mapped_span, copies);
724 image.UploadMemory(staging, copies); 736 image.UploadMemory(staging, copies);
725 } else { 737 } else {
726 const auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, mapped_span); 738 const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span);
727 image.UploadMemory(staging, copies); 739 image.UploadMemory(staging, copies);
728 } 740 }
729} 741}
730 742
731template <class P> 743template <class P>
732ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) { 744ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) {
733 if (!IsValidEntry(gpu_memory, config)) { 745 if (!IsValidEntry(*gpu_memory, config)) {
734 return NULL_IMAGE_VIEW_ID; 746 return NULL_IMAGE_VIEW_ID;
735 } 747 }
736 const auto [pair, is_new] = image_views.try_emplace(config); 748 const auto [pair, is_new] = channel_state->image_views.try_emplace(config);
737 ImageViewId& image_view_id = pair->second; 749 ImageViewId& image_view_id = pair->second;
738 if (is_new) { 750 if (is_new) {
739 image_view_id = CreateImageView(config); 751 image_view_id = CreateImageView(config);
@@ -777,9 +789,9 @@ ImageId TextureCache<P>::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_a
777template <class P> 789template <class P>
778ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, 790ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
779 RelaxedOptions options) { 791 RelaxedOptions options) {
780 std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 792 std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
781 if (!cpu_addr) { 793 if (!cpu_addr) {
782 cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); 794 cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info));
783 if (!cpu_addr) { 795 if (!cpu_addr) {
784 return ImageId{}; 796 return ImageId{};
785 } 797 }
@@ -860,7 +872,7 @@ void TextureCache<P>::InvalidateScale(Image& image) {
860 image.scale_tick = frame_tick + 1; 872 image.scale_tick = frame_tick + 1;
861 } 873 }
862 const std::span<const ImageViewId> image_view_ids = image.image_view_ids; 874 const std::span<const ImageViewId> image_view_ids = image.image_view_ids;
863 auto& dirty = maxwell3d.dirty.flags; 875 auto& dirty = maxwell3d->dirty.flags;
864 dirty[Dirty::RenderTargets] = true; 876 dirty[Dirty::RenderTargets] = true;
865 dirty[Dirty::ZetaBuffer] = true; 877 dirty[Dirty::ZetaBuffer] = true;
866 for (size_t rt = 0; rt < NUM_RT; ++rt) { 878 for (size_t rt = 0; rt < NUM_RT; ++rt) {
@@ -880,12 +892,15 @@ void TextureCache<P>::InvalidateScale(Image& image) {
880 } 892 }
881 image.image_view_ids.clear(); 893 image.image_view_ids.clear();
882 image.image_view_infos.clear(); 894 image.image_view_infos.clear();
883 if constexpr (ENABLE_VALIDATION) { 895 for (size_t c : active_channel_ids) {
884 std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); 896 auto& channel_info = channel_storage[c];
885 std::ranges::fill(compute_image_view_ids, CORRUPT_ID); 897 if constexpr (ENABLE_VALIDATION) {
898 std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID);
899 std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID);
900 }
901 channel_info.graphics_image_table.Invalidate();
902 channel_info.compute_image_table.Invalidate();
886 } 903 }
887 graphics_image_table.Invalidate();
888 compute_image_table.Invalidate();
889 has_deleted_images = true; 904 has_deleted_images = true;
890} 905}
891 906
@@ -929,10 +944,10 @@ bool TextureCache<P>::ScaleDown(Image& image) {
929template <class P> 944template <class P>
930ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, 945ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
931 RelaxedOptions options) { 946 RelaxedOptions options) {
932 std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 947 std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
933 if (!cpu_addr) { 948 if (!cpu_addr) {
934 const auto size = CalculateGuestSizeInBytes(info); 949 const auto size = CalculateGuestSizeInBytes(info);
935 cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size); 950 cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, size);
936 if (!cpu_addr) { 951 if (!cpu_addr) {
937 const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space; 952 const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space;
938 virtual_invalid_space += Common::AlignUp(size, 32); 953 virtual_invalid_space += Common::AlignUp(size, 32);
@@ -1050,7 +1065,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1050 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); 1065 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
1051 Image& new_image = slot_images[new_image_id]; 1066 Image& new_image = slot_images[new_image_id];
1052 1067
1053 if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { 1068 if (!gpu_memory->IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
1054 new_image.flags |= ImageFlagBits::Sparse; 1069 new_image.flags |= ImageFlagBits::Sparse;
1055 } 1070 }
1056 1071
@@ -1192,7 +1207,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
1192 if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) { 1207 if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) {
1193 return NULL_SAMPLER_ID; 1208 return NULL_SAMPLER_ID;
1194 } 1209 }
1195 const auto [pair, is_new] = samplers.try_emplace(config); 1210 const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
1196 if (is_new) { 1211 if (is_new) {
1197 pair->second = slot_samplers.insert(runtime, config); 1212 pair->second = slot_samplers.insert(runtime, config);
1198 } 1213 }
@@ -1201,7 +1216,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
1201 1216
1202template <class P> 1217template <class P>
1203ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { 1218ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
1204 const auto& regs = maxwell3d.regs; 1219 const auto& regs = maxwell3d->regs;
1205 if (index >= regs.rt_control.count) { 1220 if (index >= regs.rt_control.count) {
1206 return ImageViewId{}; 1221 return ImageViewId{};
1207 } 1222 }
@@ -1219,7 +1234,7 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
1219 1234
1220template <class P> 1235template <class P>
1221ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { 1236ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
1222 const auto& regs = maxwell3d.regs; 1237 const auto& regs = maxwell3d->regs;
1223 if (!regs.zeta_enable) { 1238 if (!regs.zeta_enable) {
1224 return ImageViewId{}; 1239 return ImageViewId{};
1225 } 1240 }
@@ -1316,11 +1331,17 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f
1316 1331
1317template <class P> 1332template <class P>
1318template <typename Func> 1333template <typename Func>
1319void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) { 1334void TextureCache<P>::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size,
1335 Func&& func) {
1320 using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; 1336 using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
1321 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; 1337 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
1322 boost::container::small_vector<ImageId, 8> images; 1338 boost::container::small_vector<ImageId, 8> images;
1323 ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { 1339 auto storage_id = getStorageID(as_id);
1340 if (!storage_id) {
1341 return;
1342 }
1343 auto& gpu_page_table = gpu_page_table_storage[*storage_id];
1344 ForEachGPUPage(gpu_addr, size, [this, gpu_page_table, &images, gpu_addr, size, func](u64 page) {
1324 const auto it = gpu_page_table.find(page); 1345 const auto it = gpu_page_table.find(page);
1325 if (it == gpu_page_table.end()) { 1346 if (it == gpu_page_table.end()) {
1326 if constexpr (BOOL_BREAK) { 1347 if constexpr (BOOL_BREAK) {
@@ -1403,9 +1424,9 @@ template <typename Func>
1403void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) { 1424void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) {
1404 using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type; 1425 using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type;
1405 static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>; 1426 static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
1406 const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); 1427 const auto segments = gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes);
1407 for (const auto& [gpu_addr, size] : segments) { 1428 for (const auto& [gpu_addr, size] : segments) {
1408 std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 1429 std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1409 ASSERT(cpu_addr); 1430 ASSERT(cpu_addr);
1410 if constexpr (RETURNS_BOOL) { 1431 if constexpr (RETURNS_BOOL) {
1411 if (func(gpu_addr, *cpu_addr, size)) { 1432 if (func(gpu_addr, *cpu_addr, size)) {
@@ -1448,8 +1469,9 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1448 } 1469 }
1449 image.lru_index = lru_cache.Insert(image_id, frame_tick); 1470 image.lru_index = lru_cache.Insert(image_id, frame_tick);
1450 1471
1451 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1472 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
1452 [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); 1473 (*channel_state->gpu_page_table)[page].push_back(image_id);
1474 });
1453 if (False(image.flags & ImageFlagBits::Sparse)) { 1475 if (False(image.flags & ImageFlagBits::Sparse)) {
1454 auto map_id = 1476 auto map_id =
1455 slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); 1477 slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id);
@@ -1480,9 +1502,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1480 image.flags &= ~ImageFlagBits::BadOverlap; 1502 image.flags &= ~ImageFlagBits::BadOverlap;
1481 lru_cache.Free(image.lru_index); 1503 lru_cache.Free(image.lru_index);
1482 const auto& clear_page_table = 1504 const auto& clear_page_table =
1483 [this, image_id]( 1505 [image_id](u64 page,
1484 u64 page, 1506 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>&
1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { 1507 selected_page_table) {
1486 const auto page_it = selected_page_table.find(page); 1508 const auto page_it = selected_page_table.find(page);
1487 if (page_it == selected_page_table.end()) { 1509 if (page_it == selected_page_table.end()) {
1488 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); 1510 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
@@ -1497,8 +1519,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1497 } 1519 }
1498 image_ids.erase(vector_it); 1520 image_ids.erase(vector_it);
1499 }; 1521 };
1500 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1522 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) {
1501 [this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); }); 1523 clear_page_table(page, (*channel_state->gpu_page_table));
1524 });
1502 if (False(image.flags & ImageFlagBits::Sparse)) { 1525 if (False(image.flags & ImageFlagBits::Sparse)) {
1503 const auto map_id = image.map_view_id; 1526 const auto map_id = image.map_view_id;
1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { 1527 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
@@ -1631,7 +1654,7 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1631 ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); 1654 ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered");
1632 1655
1633 // Mark render targets as dirty 1656 // Mark render targets as dirty
1634 auto& dirty = maxwell3d.dirty.flags; 1657 auto& dirty = maxwell3d->dirty.flags;
1635 dirty[Dirty::RenderTargets] = true; 1658 dirty[Dirty::RenderTargets] = true;
1636 dirty[Dirty::ZetaBuffer] = true; 1659 dirty[Dirty::ZetaBuffer] = true;
1637 for (size_t rt = 0; rt < NUM_RT; ++rt) { 1660 for (size_t rt = 0; rt < NUM_RT; ++rt) {
@@ -1681,24 +1704,30 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1681 if (alloc_images.empty()) { 1704 if (alloc_images.empty()) {
1682 image_allocs_table.erase(alloc_it); 1705 image_allocs_table.erase(alloc_it);
1683 } 1706 }
1684 if constexpr (ENABLE_VALIDATION) { 1707 for (size_t c : active_channel_ids) {
1685 std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); 1708 auto& channel_info = channel_storage[c];
1686 std::ranges::fill(compute_image_view_ids, CORRUPT_ID); 1709 if constexpr (ENABLE_VALIDATION) {
1710 std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID);
1711 std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID);
1712 }
1713 channel_info.graphics_image_table.Invalidate();
1714 channel_info.compute_image_table.Invalidate();
1687 } 1715 }
1688 graphics_image_table.Invalidate();
1689 compute_image_table.Invalidate();
1690 has_deleted_images = true; 1716 has_deleted_images = true;
1691} 1717}
1692 1718
1693template <class P> 1719template <class P>
1694void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> removed_views) { 1720void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> removed_views) {
1695 auto it = image_views.begin(); 1721 for (size_t c : active_channel_ids) {
1696 while (it != image_views.end()) { 1722 auto& channel_info = channel_storage[c];
1697 const auto found = std::ranges::find(removed_views, it->second); 1723 auto it = channel_info.image_views.begin();
1698 if (found != removed_views.end()) { 1724 while (it != channel_info.image_views.end()) {
1699 it = image_views.erase(it); 1725 const auto found = std::ranges::find(removed_views, it->second);
1700 } else { 1726 if (found != removed_views.end()) {
1701 ++it; 1727 it = channel_info.image_views.erase(it);
1728 } else {
1729 ++it;
1730 }
1702 } 1731 }
1703 } 1732 }
1704} 1733}
@@ -1729,6 +1758,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1729 boost::container::small_vector<const AliasedImage*, 1> aliased_images; 1758 boost::container::small_vector<const AliasedImage*, 1> aliased_images;
1730 Image& image = slot_images[image_id]; 1759 Image& image = slot_images[image_id];
1731 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); 1760 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);
1761 bool any_modified = True(image.flags & ImageFlagBits::GpuModified);
1732 u64 most_recent_tick = image.modification_tick; 1762 u64 most_recent_tick = image.modification_tick;
1733 for (const AliasedImage& aliased : image.aliased_images) { 1763 for (const AliasedImage& aliased : image.aliased_images) {
1734 ImageBase& aliased_image = slot_images[aliased.id]; 1764 ImageBase& aliased_image = slot_images[aliased.id];
@@ -1736,9 +1766,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1736 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); 1766 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
1737 aliased_images.push_back(&aliased); 1767 aliased_images.push_back(&aliased);
1738 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); 1768 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
1739 if (True(aliased_image.flags & ImageFlagBits::GpuModified)) { 1769 any_modified |= True(aliased_image.flags & ImageFlagBits::GpuModified);
1740 image.flags |= ImageFlagBits::GpuModified;
1741 }
1742 } 1770 }
1743 } 1771 }
1744 if (aliased_images.empty()) { 1772 if (aliased_images.empty()) {
@@ -1753,6 +1781,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1753 } 1781 }
1754 } 1782 }
1755 image.modification_tick = most_recent_tick; 1783 image.modification_tick = most_recent_tick;
1784 if (any_modified) {
1785 image.flags |= ImageFlagBits::GpuModified;
1786 }
1756 std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) { 1787 std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) {
1757 const ImageBase& lhs_image = slot_images[lhs->id]; 1788 const ImageBase& lhs_image = slot_images[lhs->id];
1758 const ImageBase& rhs_image = slot_images[rhs->id]; 1789 const ImageBase& rhs_image = slot_images[rhs->id];
@@ -1931,6 +1962,7 @@ std::pair<FramebufferId, ImageViewId> TextureCache<P>::RenderTargetFromImage(
1931 .color_buffer_ids = {color_view_id}, 1962 .color_buffer_ids = {color_view_id},
1932 .depth_buffer_id = depth_view_id, 1963 .depth_buffer_id = depth_view_id,
1933 .size = {extent.width >> samples_x, extent.height >> samples_y}, 1964 .size = {extent.width >> samples_x, extent.height >> samples_y},
1965 .is_rescaled = is_rescaled,
1934 }); 1966 });
1935 return {framebuffer_id, view_id}; 1967 return {framebuffer_id, view_id};
1936} 1968}
@@ -1943,13 +1975,13 @@ bool TextureCache<P>::IsFullClear(ImageViewId id) {
1943 const ImageViewBase& image_view = slot_image_views[id]; 1975 const ImageViewBase& image_view = slot_image_views[id];
1944 const ImageBase& image = slot_images[image_view.image_id]; 1976 const ImageBase& image = slot_images[image_view.image_id];
1945 const Extent3D size = image_view.size; 1977 const Extent3D size = image_view.size;
1946 const auto& regs = maxwell3d.regs; 1978 const auto& regs = maxwell3d->regs;
1947 const auto& scissor = regs.scissor_test[0]; 1979 const auto& scissor = regs.scissor_test[0];
1948 if (image.info.resources.levels > 1 || image.info.resources.layers > 1) { 1980 if (image.info.resources.levels > 1 || image.info.resources.layers > 1) {
1949 // Images with multiple resources can't be cleared in a single call 1981 // Images with multiple resources can't be cleared in a single call
1950 return false; 1982 return false;
1951 } 1983 }
1952 if (regs.clear_flags.scissor == 0) { 1984 if (regs.clear_control.use_scissor == 0) {
1953 // If scissor testing is disabled, the clear is always full 1985 // If scissor testing is disabled, the clear is always full
1954 return true; 1986 return true;
1955 } 1987 }
@@ -1958,4 +1990,19 @@ bool TextureCache<P>::IsFullClear(ImageViewId id) {
1958 scissor.max_y >= size.height; 1990 scissor.max_y >= size.height;
1959} 1991}
1960 1992
1993template <class P>
1994void TextureCache<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
1995 VideoCommon::ChannelSetupCaches<TextureCacheChannelInfo>::CreateChannel(channel);
1996 const auto it = channel_map.find(channel.bind_id);
1997 auto* this_state = &channel_storage[it->second];
1998 const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()];
1999 this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id];
2000}
2001
2002/// Bind a channel for execution.
2003template <class P>
2004void TextureCache<P>::OnGPUASRegister([[maybe_unused]] size_t map_id) {
2005 gpu_page_table_storage.emplace_back();
2006}
2007
1961} // namespace VideoCommon 2008} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 7e6c6cef2..2fa8445eb 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -1,8 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <deque>
7#include <limits>
6#include <mutex> 8#include <mutex>
7#include <span> 9#include <span>
8#include <type_traits> 10#include <type_traits>
@@ -11,9 +13,11 @@
11#include <queue> 13#include <queue>
12 14
13#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/hash.h"
14#include "common/literals.h" 17#include "common/literals.h"
15#include "common/lru_cache.h" 18#include "common/lru_cache.h"
16#include "video_core/compatible_formats.h" 19#include "video_core/compatible_formats.h"
20#include "video_core/control/channel_state_cache.h"
17#include "video_core/delayed_destruction_ring.h" 21#include "video_core/delayed_destruction_ring.h"
18#include "video_core/engines/fermi_2d.h" 22#include "video_core/engines/fermi_2d.h"
19#include "video_core/surface.h" 23#include "video_core/surface.h"
@@ -26,6 +30,10 @@
26#include "video_core/texture_cache/types.h" 30#include "video_core/texture_cache/types.h"
27#include "video_core/textures/texture.h" 31#include "video_core/textures/texture.h"
28 32
33namespace Tegra::Control {
34struct ChannelState;
35}
36
29namespace VideoCommon { 37namespace VideoCommon {
30 38
31using Tegra::Texture::SwizzleSource; 39using Tegra::Texture::SwizzleSource;
@@ -44,8 +52,35 @@ struct ImageViewInOut {
44 ImageViewId id{}; 52 ImageViewId id{};
45}; 53};
46 54
55using TextureCacheGPUMap = std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
56
57class TextureCacheChannelInfo : public ChannelInfo {
58public:
59 TextureCacheChannelInfo() = delete;
60 TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept;
61 TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete;
62 TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete;
63 TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default;
64 TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default;
65
66 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
67 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
68 std::vector<SamplerId> graphics_sampler_ids;
69 std::vector<ImageViewId> graphics_image_view_ids;
70
71 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
72 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
73 std::vector<SamplerId> compute_sampler_ids;
74 std::vector<ImageViewId> compute_image_view_ids;
75
76 std::unordered_map<TICEntry, ImageViewId> image_views;
77 std::unordered_map<TSCEntry, SamplerId> samplers;
78
79 TextureCacheGPUMap* gpu_page_table;
80};
81
47template <class P> 82template <class P>
48class TextureCache { 83class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelInfo> {
49 /// Address shift for caching images into a hash table 84 /// Address shift for caching images into a hash table
50 static constexpr u64 YUZU_PAGEBITS = 20; 85 static constexpr u64 YUZU_PAGEBITS = 20;
51 86
@@ -58,6 +93,8 @@ class TextureCache {
58 /// True when the API can provide info about the memory of the device. 93 /// True when the API can provide info about the memory of the device.
59 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 94 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
60 95
96 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()};
97
61 static constexpr s64 TARGET_THRESHOLD = 4_GiB; 98 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
62 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; 99 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
63 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; 100 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
@@ -77,16 +114,8 @@ class TextureCache {
77 PixelFormat src_format; 114 PixelFormat src_format;
78 }; 115 };
79 116
80 template <typename T>
81 struct IdentityHash {
82 [[nodiscard]] size_t operator()(T value) const noexcept {
83 return static_cast<size_t>(value);
84 }
85 };
86
87public: 117public:
88 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&, 118 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&);
89 Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
90 119
91 /// Notify the cache that a new frame has been queued 120 /// Notify the cache that a new frame has been queued
92 void TickFrame(); 121 void TickFrame();
@@ -142,7 +171,7 @@ public:
142 void UnmapMemory(VAddr cpu_addr, size_t size); 171 void UnmapMemory(VAddr cpu_addr, size_t size);
143 172
144 /// Remove images in a region 173 /// Remove images in a region
145 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); 174 void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
146 175
147 /// Blit an image with the given parameters 176 /// Blit an image with the given parameters
148 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 177 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
@@ -171,6 +200,9 @@ public:
171 200
172 [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept; 201 [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept;
173 202
203 /// Create channel state.
204 void CreateChannel(Tegra::Control::ChannelState& channel) final override;
205
174 std::mutex mutex; 206 std::mutex mutex;
175 207
176private: 208private:
@@ -205,6 +237,8 @@ private:
205 } 237 }
206 } 238 }
207 239
240 void OnGPUASRegister(size_t map_id) final override;
241
208 /// Runs the Garbage Collector. 242 /// Runs the Garbage Collector.
209 void RunGarbageCollector(); 243 void RunGarbageCollector();
210 244
@@ -273,7 +307,7 @@ private:
273 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); 307 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
274 308
275 template <typename Func> 309 template <typename Func>
276 void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); 310 void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func);
277 311
278 template <typename Func> 312 template <typename Func>
279 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); 313 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
@@ -338,31 +372,16 @@ private:
338 u64 GetScaledImageSizeBytes(ImageBase& image); 372 u64 GetScaledImageSizeBytes(ImageBase& image);
339 373
340 Runtime& runtime; 374 Runtime& runtime;
341 VideoCore::RasterizerInterface& rasterizer;
342 Tegra::Engines::Maxwell3D& maxwell3d;
343 Tegra::Engines::KeplerCompute& kepler_compute;
344 Tegra::MemoryManager& gpu_memory;
345 375
346 DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; 376 VideoCore::RasterizerInterface& rasterizer;
347 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; 377 std::deque<TextureCacheGPUMap> gpu_page_table_storage;
348 std::vector<SamplerId> graphics_sampler_ids;
349 std::vector<ImageViewId> graphics_image_view_ids;
350
351 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
352 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
353 std::vector<SamplerId> compute_sampler_ids;
354 std::vector<ImageViewId> compute_image_view_ids;
355 378
356 RenderTargets render_targets; 379 RenderTargets render_targets;
357 380
358 std::unordered_map<TICEntry, ImageViewId> image_views;
359 std::unordered_map<TSCEntry, SamplerId> samplers;
360 std::unordered_map<RenderTargets, FramebufferId> framebuffers; 381 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
361 382
362 std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table; 383 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
363 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table; 384 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table;
364 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
365
366 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; 385 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
367 386
368 VAddr virtual_invalid_space{}; 387 VAddr virtual_invalid_space{};
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 1820823b2..1223df5a0 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -517,7 +517,6 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; 517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block;
518 518
519 UNIMPLEMENTED_IF(info.tile_width_spacing > 0); 519 UNIMPLEMENTED_IF(info.tile_width_spacing > 0);
520
521 UNIMPLEMENTED_IF(copy.image_offset.x != 0); 520 UNIMPLEMENTED_IF(copy.image_offset.x != 0);
522 UNIMPLEMENTED_IF(copy.image_offset.y != 0); 521 UNIMPLEMENTED_IF(copy.image_offset.y != 0);
523 UNIMPLEMENTED_IF(copy.image_offset.z != 0); 522 UNIMPLEMENTED_IF(copy.image_offset.z != 0);
@@ -755,7 +754,7 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
755 if (address == 0) { 754 if (address == 0) {
756 return false; 755 return false;
757 } 756 }
758 if (address > (1ULL << 48)) { 757 if (address >= (1ULL << 40)) {
759 return false; 758 return false;
760 } 759 }
761 if (gpu_memory.GpuToCpuAddress(address).has_value()) { 760 if (gpu_memory.GpuToCpuAddress(address).has_value()) {
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b159494c5..69a32819a 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1413,7 +1413,7 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 b
1413static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) { 1413static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
1414 for (u32 j = 0; j < blockHeight; j++) { 1414 for (u32 j = 0; j < blockHeight; j++) {
1415 for (u32 i = 0; i < blockWidth; i++) { 1415 for (u32 i = 0; i < blockWidth; i++) {
1416 outBuf[j * blockWidth + i] = 0xFFFF00FF; 1416 outBuf[j * blockWidth + i] = 0x00000000;
1417 } 1417 }
1418 } 1418 }
1419} 1419}
@@ -1656,13 +1656,13 @@ void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height,
1656 const u32 cols = Common::DivideUp(width, block_width); 1656 const u32 cols = Common::DivideUp(width, block_width);
1657 1657
1658 Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2, 1658 Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2,
1659 "yuzu:ASTCDecompress"}; 1659 "ASTCDecompress"};
1660 1660
1661 for (u32 z = 0; z < depth; ++z) { 1661 for (u32 z = 0; z < depth; ++z) {
1662 const u32 depth_offset = z * height * width * 4; 1662 const u32 depth_offset = z * height * width * 4;
1663 for (u32 y_index = 0; y_index < rows; ++y_index) { 1663 for (u32 y_index = 0; y_index < rows; ++y_index) {
1664 auto decompress_stride = [data, width, height, depth, block_width, block_height, output, 1664 auto decompress_stride = [data, width, height, block_width, block_height, output, rows,
1665 rows, cols, z, depth_offset, y_index] { 1665 cols, z, depth_offset, y_index] {
1666 const u32 y = y_index * block_height; 1666 const u32 y = y_index * block_height;
1667 for (u32 x_index = 0; x_index < cols; ++x_index) { 1667 for (u32 x_index = 0; x_index < cols; ++x_index) {
1668 const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; 1668 const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 913f8ebcb..fd1a4b987 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -21,7 +21,7 @@ constexpr u32 pdep(u32 value) {
21 u32 m = mask; 21 u32 m = mask;
22 for (u32 bit = 1; m; bit += bit) { 22 for (u32 bit = 1; m; bit += bit) {
23 if (value & bit) 23 if (value & bit)
24 result |= m & -m; 24 result |= m & (~m + 1);
25 m &= m - 1; 25 m &= m - 1;
26 } 26 }
27 return result; 27 return result;
@@ -35,7 +35,7 @@ void incrpdep(u32& value) {
35 35
36template <bool TO_LINEAR, u32 BYTES_PER_PIXEL> 36template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
37void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth, 37void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
38 u32 block_height, u32 block_depth, u32 stride_alignment) { 38 u32 block_height, u32 block_depth, u32 stride) {
39 // The origin of the transformation can be configured here, leave it as zero as the current API 39 // The origin of the transformation can be configured here, leave it as zero as the current API
40 // doesn't expose it. 40 // doesn't expose it.
41 static constexpr u32 origin_x = 0; 41 static constexpr u32 origin_x = 0;
@@ -45,7 +45,6 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
45 // We can configure here a custom pitch 45 // We can configure here a custom pitch
46 // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. 46 // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
47 const u32 pitch = width * BYTES_PER_PIXEL; 47 const u32 pitch = width * BYTES_PER_PIXEL;
48 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * BYTES_PER_PIXEL;
49 48
50 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); 49 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
51 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 50 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
@@ -89,6 +88,69 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
89 } 88 }
90} 89}
91 90
91template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
92void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height,
93 u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 num_lines,
94 u32 block_height, u32 block_depth, u32 pitch_linear) {
95 // The origin of the transformation can be configured here, leave it as zero as the current API
96 // doesn't expose it.
97 static constexpr u32 origin_z = 0;
98
99 // We can configure here a custom pitch
100 // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
101 const u32 pitch = pitch_linear;
102 const u32 stride = Common::AlignUpLog2(width * BYTES_PER_PIXEL, GOB_SIZE_X_SHIFT);
103
104 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
105 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
106 const u32 slice_size =
107 Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size;
108
109 const u32 block_height_mask = (1U << block_height) - 1;
110 const u32 block_depth_mask = (1U << block_depth) - 1;
111 const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth;
112
113 u32 unprocessed_lines = num_lines;
114 u32 extent_y = std::min(num_lines, height - origin_y);
115
116 for (u32 slice = 0; slice < depth; ++slice) {
117 const u32 z = slice + origin_z;
118 const u32 offset_z = (z >> block_depth) * slice_size +
119 ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height));
120 const u32 lines_in_y = std::min(unprocessed_lines, extent_y);
121 for (u32 line = 0; line < lines_in_y; ++line) {
122 const u32 y = line + origin_y;
123 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(y);
124
125 const u32 block_y = y >> GOB_SIZE_Y_SHIFT;
126 const u32 offset_y = (block_y >> block_height) * block_size +
127 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
128
129 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
130 for (u32 column = 0; column < extent_x;
131 ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
132 const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
133 const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
134
135 const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
136 const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
137
138 const u32 unswizzled_offset =
139 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
140
141 u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
142 const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
143
144 std::memcpy(dst, src, BYTES_PER_PIXEL);
145 }
146 }
147 unprocessed_lines -= lines_in_y;
148 if (unprocessed_lines == 0) {
149 return;
150 }
151 }
152}
153
92template <bool TO_LINEAR> 154template <bool TO_LINEAR>
93void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, 155void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
94 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { 156 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
@@ -111,122 +173,39 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
111 } 173 }
112} 174}
113 175
114template <u32 BYTES_PER_PIXEL>
115void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
116 u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit,
117 u32 offset_x, u32 offset_y) {
118 const u32 block_height = 1U << block_height_bit;
119 const u32 image_width_in_gobs =
120 (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
121 for (u32 line = 0; line < subrect_height; ++line) {
122 const u32 dst_y = line + offset_y;
123 const u32 gob_address_y =
124 (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
125 ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
126
127 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(dst_y);
128 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(offset_x * BYTES_PER_PIXEL);
129 for (u32 x = 0; x < subrect_width;
130 ++x, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
131 const u32 dst_x = x + offset_x;
132 const u32 gob_address =
133 gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height;
134 const u32 swizzled_offset = gob_address + (swizzled_x | swizzled_y);
135 const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL;
136
137 const u8* const source_line = unswizzled_data + unswizzled_offset;
138 u8* const dest_addr = swizzled_data + swizzled_offset;
139 std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL);
140 }
141 }
142}
143
144template <u32 BYTES_PER_PIXEL>
145void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height,
146 u32 origin_x, u32 origin_y, u8* output, const u8* input) {
147 const u32 stride = width * BYTES_PER_PIXEL;
148 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
149 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
150
151 const u32 block_height_mask = (1U << block_height) - 1;
152 const u32 x_shift = GOB_SIZE_SHIFT + block_height;
153
154 for (u32 line = 0; line < line_count; ++line) {
155 const u32 src_y = line + origin_y;
156 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(src_y);
157
158 const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
159 const u32 src_offset_y = (block_y >> block_height) * block_size +
160 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
161
162 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
163 for (u32 column = 0; column < line_length_in;
164 ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
165 const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL;
166 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
167
168 const u32 swizzled_offset = src_offset_y + src_offset_x + (swizzled_x | swizzled_y);
169 const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
170
171 std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL);
172 }
173 }
174}
175
176template <u32 BYTES_PER_PIXEL>
177void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
178 u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output,
179 const u8* input) {
180 UNIMPLEMENTED_IF(origin_x > 0);
181 UNIMPLEMENTED_IF(origin_y > 0);
182
183 const u32 stride = width * BYTES_PER_PIXEL;
184 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
185 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
186
187 const u32 block_height_mask = (1U << block_height) - 1;
188 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
189
190 for (u32 line = 0; line < line_count; ++line) {
191 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(line);
192 const u32 block_y = line / GOB_SIZE_Y;
193 const u32 dst_offset_y =
194 (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
195
196 u32 swizzled_x = 0;
197 for (u32 x = 0; x < line_length_in; ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) {
198 const u32 dst_offset =
199 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + (swizzled_x | swizzled_y);
200 const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch;
201 std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL);
202 }
203 }
204}
205} // Anonymous namespace 176} // Anonymous namespace
206 177
207void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, 178void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
208 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, 179 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
209 u32 stride_alignment) { 180 u32 stride_alignment) {
181 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
182 const u32 new_bpp = std::min(4U, static_cast<u32>(std::countr_zero(width * bytes_per_pixel)));
183 width = (width * bytes_per_pixel) >> new_bpp;
184 bytes_per_pixel = 1U << new_bpp;
210 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, 185 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
211 stride_alignment); 186 stride);
212} 187}
213 188
214void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, 189void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
215 u32 height, u32 depth, u32 block_height, u32 block_depth, 190 u32 height, u32 depth, u32 block_height, u32 block_depth,
216 u32 stride_alignment) { 191 u32 stride_alignment) {
192 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
193 const u32 new_bpp = std::min(4U, static_cast<u32>(std::countr_zero(width * bytes_per_pixel)));
194 width = (width * bytes_per_pixel) >> new_bpp;
195 bytes_per_pixel = 1U << new_bpp;
217 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, 196 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
218 stride_alignment); 197 stride);
219} 198}
220 199
221void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 200void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
222 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, 201 u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y,
223 u32 block_height_bit, u32 offset_x, u32 offset_y) { 202 u32 block_height, u32 block_depth, u32 pitch_linear) {
224 switch (bytes_per_pixel) { 203 switch (bytes_per_pixel) {
225#define BPP_CASE(x) \ 204#define BPP_CASE(x) \
226 case x: \ 205 case x: \
227 return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \ 206 return SwizzleSubrectImpl<true, x>(output, input, width, height, depth, origin_x, \
228 swizzled_data, unswizzled_data, block_height_bit, offset_x, \ 207 origin_y, extent_x, extent_y, block_height, \
229 offset_y); 208 block_depth, pitch_linear);
230 BPP_CASE(1) 209 BPP_CASE(1)
231 BPP_CASE(2) 210 BPP_CASE(2)
232 BPP_CASE(3) 211 BPP_CASE(3)
@@ -241,13 +220,15 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
241 } 220 }
242} 221}
243 222
244void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, 223void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
245 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { 224 u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x,
225 u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear) {
246 switch (bytes_per_pixel) { 226 switch (bytes_per_pixel) {
247#define BPP_CASE(x) \ 227#define BPP_CASE(x) \
248 case x: \ 228 case x: \
249 return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \ 229 return SwizzleSubrectImpl<false, x>(output, input, width, height, depth, origin_x, \
250 origin_x, origin_y, output, input); 230 origin_y, extent_x, extent_y, block_height, \
231 block_depth, pitch_linear);
251 BPP_CASE(1) 232 BPP_CASE(1)
252 BPP_CASE(2) 233 BPP_CASE(2)
253 BPP_CASE(3) 234 BPP_CASE(3)
@@ -262,55 +243,6 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
262 } 243 }
263} 244}
264 245
265void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
266 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
267 u32 origin_y, u8* output, const u8* input) {
268 switch (bytes_per_pixel) {
269#define BPP_CASE(x) \
270 case x: \
271 return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \
272 block_height, block_depth, origin_x, origin_y, output, \
273 input);
274 BPP_CASE(1)
275 BPP_CASE(2)
276 BPP_CASE(3)
277 BPP_CASE(4)
278 BPP_CASE(6)
279 BPP_CASE(8)
280 BPP_CASE(12)
281 BPP_CASE(16)
282#undef BPP_CASE
283 default:
284 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
285 }
286}
287
288void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
289 const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
290 u8* swizzle_data) {
291 const u32 block_height = 1U << block_height_bit;
292 const u32 image_width_in_gobs{(width + GOB_SIZE_X - 1) / GOB_SIZE_X};
293 std::size_t count = 0;
294 for (std::size_t y = dst_y; y < height && count < copy_size; ++y) {
295 const std::size_t gob_address_y =
296 (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
297 ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
298 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(static_cast<u32>(y));
299 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(dst_x);
300 for (std::size_t x = dst_x; x < width && count < copy_size;
301 ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) {
302 const std::size_t gob_address =
303 gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height;
304 const std::size_t swizzled_offset = gob_address + (swizzled_x | swizzled_y);
305 const u8* source_line = source_data + count;
306 u8* dest_addr = swizzle_data + swizzled_offset;
307 count++;
308
309 *dest_addr = *source_line;
310 }
311 }
312}
313
314std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 246std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
315 u32 block_height, u32 block_depth) { 247 u32 block_height, u32 block_depth) {
316 if (tiled) { 248 if (tiled) {
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 31a11708f..e70407692 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -40,7 +40,6 @@ constexpr SwizzleTable MakeSwizzleTable() {
40 } 40 }
41 return table; 41 return table;
42} 42}
43constexpr SwizzleTable SWIZZLE_TABLE = MakeSwizzleTable();
44 43
45/// Unswizzles a block linear texture into linear memory. 44/// Unswizzles a block linear texture into linear memory.
46void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, 45void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
@@ -57,34 +56,14 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
57 u32 block_height, u32 block_depth); 56 u32 block_height, u32 block_depth);
58 57
59/// Copies an untiled subrectangle into a tiled surface. 58/// Copies an untiled subrectangle into a tiled surface.
60void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 59void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
61 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, 60 u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y,
62 u32 block_height_bit, u32 offset_x, u32 offset_y); 61 u32 block_height, u32 block_depth, u32 pitch_linear);
63 62
64/// Copies a tiled subrectangle into a linear surface. 63/// Copies a tiled subrectangle into a linear surface.
65void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, 64void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
66 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input); 65 u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x,
67 66 u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear);
68/// @brief Swizzles a 2D array of pixels into a 3D texture
69/// @param line_length_in Number of pixels per line
70/// @param line_count Number of lines
71/// @param pitch Number of bytes per line
72/// @param width Width of the swizzled texture
73/// @param height Height of the swizzled texture
74/// @param bytes_per_pixel Number of bytes used per pixel
75/// @param block_height Block height shift
76/// @param block_depth Block depth shift
77/// @param origin_x Column offset in pixels of the swizzled texture
78/// @param origin_y Row offset in pixels of the swizzled texture
79/// @param output Pointer to the pixels of the swizzled texture
80/// @param input Pointer to the 2D array of pixels used as input
81/// @pre input and output points to an array large enough to hold the number of bytes used
82void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
83 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
84 u32 origin_y, u8* output, const u8* input);
85
86void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
87 std::size_t copy_size, const u8* source_data, u8* swizzle_data);
88 67
89/// Obtains the offset of the gob for positions 'dst_x' & 'dst_y' 68/// Obtains the offset of the gob for positions 'dst_x' & 'dst_y'
90u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, 69u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
index 7e605981c..45071185a 100644
--- a/src/video_core/transform_feedback.cpp
+++ b/src/video_core/transform_feedback.cpp
@@ -15,51 +15,51 @@ namespace VideoCommon {
15std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 15std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
16 const TransformFeedbackState& state) { 16 const TransformFeedbackState& state) {
17 static constexpr std::array VECTORS{ 17 static constexpr std::array VECTORS{
18 28, // gl_Position 18 28U, // gl_Position
19 32, // Generic 0 19 32U, // Generic 0
20 36, // Generic 1 20 36U, // Generic 1
21 40, // Generic 2 21 40U, // Generic 2
22 44, // Generic 3 22 44U, // Generic 3
23 48, // Generic 4 23 48U, // Generic 4
24 52, // Generic 5 24 52U, // Generic 5
25 56, // Generic 6 25 56U, // Generic 6
26 60, // Generic 7 26 60U, // Generic 7
27 64, // Generic 8 27 64U, // Generic 8
28 68, // Generic 9 28 68U, // Generic 9
29 72, // Generic 10 29 72U, // Generic 10
30 76, // Generic 11 30 76U, // Generic 11
31 80, // Generic 12 31 80U, // Generic 12
32 84, // Generic 13 32 84U, // Generic 13
33 88, // Generic 14 33 88U, // Generic 14
34 92, // Generic 15 34 92U, // Generic 15
35 96, // Generic 16 35 96U, // Generic 16
36 100, // Generic 17 36 100U, // Generic 17
37 104, // Generic 18 37 104U, // Generic 18
38 108, // Generic 19 38 108U, // Generic 19
39 112, // Generic 20 39 112U, // Generic 20
40 116, // Generic 21 40 116U, // Generic 21
41 120, // Generic 22 41 120U, // Generic 22
42 124, // Generic 23 42 124U, // Generic 23
43 128, // Generic 24 43 128U, // Generic 24
44 132, // Generic 25 44 132U, // Generic 25
45 136, // Generic 26 45 136U, // Generic 26
46 140, // Generic 27 46 140U, // Generic 27
47 144, // Generic 28 47 144U, // Generic 28
48 148, // Generic 29 48 148U, // Generic 29
49 152, // Generic 30 49 152U, // Generic 30
50 156, // Generic 31 50 156U, // Generic 31
51 160, // gl_FrontColor 51 160U, // gl_FrontColor
52 164, // gl_FrontSecondaryColor 52 164U, // gl_FrontSecondaryColor
53 160, // gl_BackColor 53 160U, // gl_BackColor
54 164, // gl_BackSecondaryColor 54 164U, // gl_BackSecondaryColor
55 192, // gl_TexCoord[0] 55 192U, // gl_TexCoord[0]
56 196, // gl_TexCoord[1] 56 196U, // gl_TexCoord[1]
57 200, // gl_TexCoord[2] 57 200U, // gl_TexCoord[2]
58 204, // gl_TexCoord[3] 58 204U, // gl_TexCoord[3]
59 208, // gl_TexCoord[4] 59 208U, // gl_TexCoord[4]
60 212, // gl_TexCoord[5] 60 212U, // gl_TexCoord[5]
61 216, // gl_TexCoord[6] 61 216U, // gl_TexCoord[6]
62 220, // gl_TexCoord[7] 62 220U, // gl_TexCoord[7]
63 }; 63 };
64 std::vector<Shader::TransformFeedbackVarying> xfb(256); 64 std::vector<Shader::TransformFeedbackVarying> xfb(256);
65 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { 65 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) {
@@ -68,8 +68,20 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
68 const u32 varying_count = layout.varying_count; 68 const u32 varying_count = layout.varying_count;
69 u32 highest = 0; 69 u32 highest = 0;
70 for (u32 offset = 0; offset < varying_count; ++offset) { 70 for (u32 offset = 0; offset < varying_count; ++offset) {
71 const u32 base_offset = offset; 71 const auto get_attribute = [&locations](u32 index) -> u32 {
72 const u8 location = locations[offset]; 72 switch (index % 4) {
73 case 0:
74 return locations[index / 4].attribute0.Value();
75 case 1:
76 return locations[index / 4].attribute1.Value();
77 case 2:
78 return locations[index / 4].attribute2.Value();
79 case 3:
80 return locations[index / 4].attribute3.Value();
81 }
82 UNREACHABLE();
83 return 0;
84 };
73 85
74 UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream); 86 UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream);
75 Shader::TransformFeedbackVarying varying{ 87 Shader::TransformFeedbackVarying varying{
@@ -78,16 +90,18 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
78 .offset = offset * 4, 90 .offset = offset * 4,
79 .components = 1, 91 .components = 1,
80 }; 92 };
81 if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { 93 const u32 base_offset = offset;
82 UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); 94 const auto attribute{get_attribute(offset)};
95 if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) {
96 UNIMPLEMENTED_IF_MSG(attribute % 4 != 0, "Unaligned TFB {}", attribute);
83 97
84 const u8 base_index = location / 4; 98 const auto base_index = attribute / 4;
85 while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { 99 while (offset + 1 < varying_count && base_index == get_attribute(offset + 1) / 4) {
86 ++offset; 100 ++offset;
87 ++varying.components; 101 ++varying.components;
88 } 102 }
89 } 103 }
90 xfb[location] = varying; 104 xfb[attribute] = varying;
91 highest = std::max(highest, (base_offset + varying.components) * 4); 105 highest = std::max(highest, (base_offset + varying.components) * 4);
92 } 106 }
93 UNIMPLEMENTED_IF(highest != layout.stride); 107 UNIMPLEMENTED_IF(highest != layout.stride);
diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h
index a519adb59..d13eb16c3 100644
--- a/src/video_core/transform_feedback.h
+++ b/src/video_core/transform_feedback.h
@@ -19,7 +19,8 @@ struct TransformFeedbackState {
19 u32 stride; 19 u32 stride;
20 }; 20 };
21 std::array<Layout, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> layouts; 21 std::array<Layout, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> layouts;
22 std::array<std::array<u8, 128>, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> 22 std::array<std::array<Tegra::Engines::Maxwell3D::Regs::StreamOutLayout, 32>,
23 Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
23 varyings; 24 varyings;
24}; 25};
25 26
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 795f16bfb..1b3f493bd 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -519,9 +519,7 @@ public:
519 dld{rhs.dld} {} 519 dld{rhs.dld} {}
520 520
521 /// Assign an allocation transfering ownership from another allocation. 521 /// Assign an allocation transfering ownership from another allocation.
522 /// Releases any previously held allocation.
523 PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { 522 PoolAllocations& operator=(PoolAllocations&& rhs) noexcept {
524 Release();
525 allocations = std::move(rhs.allocations); 523 allocations = std::move(rhs.allocations);
526 num = rhs.num; 524 num = rhs.num;
527 device = rhs.device; 525 device = rhs.device;
@@ -530,11 +528,6 @@ public:
530 return *this; 528 return *this;
531 } 529 }
532 530
533 /// Destroys any held allocation.
534 ~PoolAllocations() {
535 Release();
536 }
537
538 /// Returns the number of allocations. 531 /// Returns the number of allocations.
539 std::size_t size() const noexcept { 532 std::size_t size() const noexcept {
540 return num; 533 return num;
@@ -557,19 +550,6 @@ public:
557 } 550 }
558 551
559private: 552private:
560 /// Destroys the held allocations if they exist.
561 void Release() noexcept {
562 if (!allocations) {
563 return;
564 }
565 const Span<AllocationType> span(allocations.get(), num);
566 const VkResult result = Free(device, pool, span, *dld);
567 // There's no way to report errors from a destructor.
568 if (result != VK_SUCCESS) {
569 std::terminate();
570 }
571 }
572
573 std::unique_ptr<AllocationType[]> allocations; 553 std::unique_ptr<AllocationType[]> allocations;
574 std::size_t num = 0; 554 std::size_t num = 0;
575 VkDevice device = nullptr; 555 VkDevice device = nullptr;
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 378804c08..12a7e4922 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -111,7 +111,8 @@ struct Client::Impl {
111 httplib::Error error; 111 httplib::Error error;
112 112
113 if (!cli->send(request, response, error)) { 113 if (!cli->send(request, response, error)) {
114 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 114 LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path,
115 httplib::to_string(error));
115 return WebResult{WebResult::Code::LibError, "Null response", ""}; 116 return WebResult{WebResult::Code::LibError, "Null response", ""};
116 } 117 }
117 118
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 1d8072243..12efdc216 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -291,7 +291,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
291 // Here, we check and validate the current configuration against all applicable parameters. 291 // Here, we check and validate the current configuration against all applicable parameters.
292 const auto num_connected_players = static_cast<int>( 292 const auto num_connected_players = static_cast<int>(
293 std::count_if(player_groupboxes.begin(), player_groupboxes.end(), 293 std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
294 [this](const QGroupBox* player) { return player->isChecked(); })); 294 [](const QGroupBox* player) { return player->isChecked(); }));
295 295
296 const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; 296 const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
297 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; 297 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui
index c8cb6bcf3..f5eccba70 100644
--- a/src/yuzu/applets/qt_controller.ui
+++ b/src/yuzu/applets/qt_controller.ui
@@ -2300,7 +2300,7 @@
2300 <item> 2300 <item>
2301 <widget class="QRadioButton" name="radioUndocked"> 2301 <widget class="QRadioButton" name="radioUndocked">
2302 <property name="text"> 2302 <property name="text">
2303 <string>Undocked</string> 2303 <string>Handheld</string>
2304 </property> 2304 </property>
2305 </widget> 2305 </widget>
2306 </item> 2306 </item>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d3fbdb09d..6acfb7b06 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -47,7 +47,7 @@ EmuThread::EmuThread(Core::System& system_) : system{system_} {}
47EmuThread::~EmuThread() = default; 47EmuThread::~EmuThread() = default;
48 48
49void EmuThread::run() { 49void EmuThread::run() {
50 std::string name = "yuzu:EmuControlThread"; 50 std::string name = "EmuControlThread";
51 MicroProfileOnThreadCreate(name.c_str()); 51 MicroProfileOnThreadCreate(name.c_str());
52 Common::SetCurrentThreadName(name.c_str()); 52 Common::SetCurrentThreadName(name.c_str());
53 53
@@ -120,8 +120,8 @@ void EmuThread::run() {
120 } 120 }
121 } 121 }
122 122
123 // Shutdown the core emulation 123 // Shutdown the main emulated process
124 system.Shutdown(); 124 system.ShutdownMainProcess();
125 125
126#if MICROPROFILE_ENABLED 126#if MICROPROFILE_ENABLED
127 MicroProfileOnThreadExit(); 127 MicroProfileOnThreadExit();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index a4ed68422..927dd1069 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -546,6 +546,7 @@ void Config::ReadDebuggingValues() {
546 ReadBasicSetting(Settings::values.use_auto_stub); 546 ReadBasicSetting(Settings::values.use_auto_stub);
547 ReadBasicSetting(Settings::values.enable_all_controllers); 547 ReadBasicSetting(Settings::values.enable_all_controllers);
548 ReadBasicSetting(Settings::values.create_crash_dumps); 548 ReadBasicSetting(Settings::values.create_crash_dumps);
549 ReadBasicSetting(Settings::values.perform_vulkan_check);
549 550
550 qt_config->endGroup(); 551 qt_config->endGroup();
551} 552}
@@ -818,6 +819,7 @@ void Config::ReadUIGamelistValues() {
818 qt_config->beginGroup(QStringLiteral("UIGameList")); 819 qt_config->beginGroup(QStringLiteral("UIGameList"));
819 820
820 ReadBasicSetting(UISettings::values.show_add_ons); 821 ReadBasicSetting(UISettings::values.show_add_ons);
822 ReadBasicSetting(UISettings::values.show_compat);
821 ReadBasicSetting(UISettings::values.game_icon_size); 823 ReadBasicSetting(UISettings::values.game_icon_size);
822 ReadBasicSetting(UISettings::values.folder_icon_size); 824 ReadBasicSetting(UISettings::values.folder_icon_size);
823 ReadBasicSetting(UISettings::values.row_1_text_id); 825 ReadBasicSetting(UISettings::values.row_1_text_id);
@@ -1162,6 +1164,7 @@ void Config::SaveDebuggingValues() {
1162 WriteBasicSetting(Settings::values.disable_macro_jit); 1164 WriteBasicSetting(Settings::values.disable_macro_jit);
1163 WriteBasicSetting(Settings::values.enable_all_controllers); 1165 WriteBasicSetting(Settings::values.enable_all_controllers);
1164 WriteBasicSetting(Settings::values.create_crash_dumps); 1166 WriteBasicSetting(Settings::values.create_crash_dumps);
1167 WriteBasicSetting(Settings::values.perform_vulkan_check);
1165 1168
1166 qt_config->endGroup(); 1169 qt_config->endGroup();
1167} 1170}
@@ -1412,6 +1415,7 @@ void Config::SaveUIGamelistValues() {
1412 qt_config->beginGroup(QStringLiteral("UIGameList")); 1415 qt_config->beginGroup(QStringLiteral("UIGameList"));
1413 1416
1414 WriteBasicSetting(UISettings::values.show_add_ons); 1417 WriteBasicSetting(UISettings::values.show_add_ons);
1418 WriteBasicSetting(UISettings::values.show_compat);
1415 WriteBasicSetting(UISettings::values.game_icon_size); 1419 WriteBasicSetting(UISettings::values.game_icon_size);
1416 WriteBasicSetting(UISettings::values.folder_icon_size); 1420 WriteBasicSetting(UISettings::values.folder_icon_size);
1417 WriteBasicSetting(UISettings::values.row_1_text_id); 1421 WriteBasicSetting(UISettings::values.row_1_text_id);
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 19b8b15ef..70cc6f84b 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -161,8 +161,8 @@ void ConfigureAudio::InitializeAudioSinkComboBox() {
161 ui->sink_combo_box->clear(); 161 ui->sink_combo_box->clear();
162 ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 162 ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
163 163
164 for (const char* id : AudioCore::Sink::GetSinkIDs()) { 164 for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
165 ui->sink_combo_box->addItem(QString::fromUtf8(id)); 165 ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length())));
166 } 166 }
167} 167}
168 168
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 622808e94..dacc75a20 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -77,6 +77,7 @@ void ConfigureDebug::SetConfiguration() {
77 ui->disable_loop_safety_checks->setChecked( 77 ui->disable_loop_safety_checks->setChecked(
78 Settings::values.disable_shader_loop_safety_checks.GetValue()); 78 Settings::values.disable_shader_loop_safety_checks.GetValue());
79 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); 79 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
80 ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
80 81
81#ifdef YUZU_USE_QT_WEB_ENGINE 82#ifdef YUZU_USE_QT_WEB_ENGINE
82 ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue()); 83 ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
@@ -117,6 +118,7 @@ void ConfigureDebug::ApplyConfiguration() {
117 ui->disable_loop_safety_checks->isChecked(); 118 ui->disable_loop_safety_checks->isChecked();
118 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 119 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
119 Settings::values.extended_logging = ui->extended_logging->isChecked(); 120 Settings::values.extended_logging = ui->extended_logging->isChecked();
121 Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked();
120 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked(); 122 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
121 Debugger::ToggleConsole(); 123 Debugger::ToggleConsole();
122 Common::Log::Filter filter; 124 Common::Log::Filter filter;
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 314d47af5..102c8c66c 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -313,6 +313,16 @@
313 </property> 313 </property>
314 </widget> 314 </widget>
315 </item> 315 </item>
316 <item row="3" column="0">
317 <widget class="QCheckBox" name="perform_vulkan_check">
318 <property name="toolTip">
319 <string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string>
320 </property>
321 <property name="text">
322 <string>Perform Startup Vulkan Check</string>
323 </property>
324 </widget>
325 </item>
316 </layout> 326 </layout>
317 </widget> 327 </widget>
318 </item> 328 </item>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 87e5d0f48..bd69d04a6 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -57,9 +57,10 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
57 UpdateBackgroundColorButton(new_bg_color); 57 UpdateBackgroundColorButton(new_bg_color);
58 }); 58 });
59 59
60 ui->api->setEnabled(!UISettings::values.has_broken_vulkan); 60 ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled());
61 ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan || 61 ui->api_widget->setEnabled(
62 Settings::IsConfiguringGlobal()); 62 (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) &&
63 ui->api_widget->isEnabled());
63 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 64 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
64 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 65 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
65} 66}
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 1e4f74704..fdbb33372 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -301,6 +301,11 @@
301 </item> 301 </item>
302 <item> 302 <item>
303 <property name="text"> 303 <property name="text">
304 <string>Force 16:10</string>
305 </property>
306 </item>
307 <item>
308 <property name="text">
304 <string>Stretch to Window</string> 309 <string>Stretch to Window</string>
305 </property> 310 </property>
306 </item> 311 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index cb55472c9..1db374d4a 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -163,10 +163,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
163 [this, input_subsystem, &hid_core] { 163 [this, input_subsystem, &hid_core] {
164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core); 164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
165 }); 165 });
166 connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, 166 connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] {
167 [this, input_subsystem, &hid_core] { 167 CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
168 CallConfigureDialog<ConfigureCamera>(*this, input_subsystem); 168 });
169 });
170 169
171 connect(ui->vibrationButton, &QPushButton::clicked, 170 connect(ui->vibrationButton, &QPushButton::clicked,
172 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); 171 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 48f71b53c..92e6da6ee 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -72,6 +72,7 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
72 72
73 // Force game list reload if any of the relevant settings are changed. 73 // Force game list reload if any of the relevant settings are changed.
74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); 74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
75 connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
75 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 76 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
76 &ConfigureUi::RequestGameListUpdate); 77 &ConfigureUi::RequestGameListUpdate);
77 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), 78 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -109,6 +110,7 @@ void ConfigureUi::ApplyConfiguration() {
109 UISettings::values.theme = 110 UISettings::values.theme =
110 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 111 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
111 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 112 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
113 UISettings::values.show_compat = ui->show_compat->isChecked();
112 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); 114 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
113 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); 115 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
114 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 116 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
@@ -129,6 +131,7 @@ void ConfigureUi::SetConfiguration() {
129 ui->language_combobox->setCurrentIndex( 131 ui->language_combobox->setCurrentIndex(
130 ui->language_combobox->findData(UISettings::values.language)); 132 ui->language_combobox->findData(UISettings::values.language));
131 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 133 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
134 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
132 ui->game_icon_size_combobox->setCurrentIndex( 135 ui->game_icon_size_combobox->setCurrentIndex(
133 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); 136 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
134 ui->folder_icon_size_combobox->setCurrentIndex( 137 ui->folder_icon_size_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index a50df7f6f..f0b719ba3 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -77,6 +77,13 @@
77 <item> 77 <item>
78 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 78 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
79 <item> 79 <item>
80 <widget class="QCheckBox" name="show_compat">
81 <property name="text">
82 <string>Show Compatibility List</string>
83 </property>
84 </widget>
85 </item>
86 <item>
80 <widget class="QCheckBox" name="show_add_ons"> 87 <widget class="QCheckBox" name="show_add_ons">
81 <property name="text"> 88 <property name="text">
82 <string>Show Add-Ons Column</string> 89 <string>Show Add-Ons Column</string>
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 807afbeb2..9bb69cab1 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -67,6 +67,8 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
67 profile_names.push_back(profile_name); 67 profile_names.push_back(profile_name);
68 } 68 }
69 69
70 std::stable_sort(profile_names.begin(), profile_names.end());
71
70 return profile_names; 72 return profile_names;
71} 73}
72 74
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b127badc2..d6adfca16 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -335,6 +335,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
335 RetranslateUI(); 335 RetranslateUI();
336 336
337 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 337 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
338 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
338 item_model->setSortRole(GameListItemPath::SortRole); 339 item_model->setSortRole(GameListItemPath::SortRole);
339 340
340 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); 341 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
@@ -786,6 +787,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
786 787
787 // Update the columns in case UISettings has changed 788 // Update the columns in case UISettings has changed
788 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 789 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
790 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
789 791
790 // Delete any rows that might already exist if we're repopulating 792 // Delete any rows that might already exist if we're repopulating
791 item_model->removeRows(0, item_model->rowCount()); 793 item_model->removeRows(0, item_model->rowCount());
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3c1bd19db..7b16d7f7e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cinttypes> 4#include <cinttypes>
5#include <clocale> 5#include <clocale>
6#include <cmath>
6#include <memory> 7#include <memory>
7#include <thread> 8#include <thread>
8#ifdef __APPLE__ 9#ifdef __APPLE__
@@ -105,12 +106,12 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
105#include "core/hle/kernel/k_process.h" 106#include "core/hle/kernel/k_process.h"
106#include "core/hle/service/am/am.h" 107#include "core/hle/service/am/am.h"
107#include "core/hle/service/filesystem/filesystem.h" 108#include "core/hle/service/filesystem/filesystem.h"
108#include "core/hle/service/nfp/nfp.h"
109#include "core/hle/service/sm/sm.h" 109#include "core/hle/service/sm/sm.h"
110#include "core/loader/loader.h" 110#include "core/loader/loader.h"
111#include "core/perf_stats.h" 111#include "core/perf_stats.h"
112#include "core/telemetry_session.h" 112#include "core/telemetry_session.h"
113#include "input_common/drivers/tas_input.h" 113#include "input_common/drivers/tas_input.h"
114#include "input_common/drivers/virtual_amiibo.h"
114#include "input_common/main.h" 115#include "input_common/main.h"
115#include "ui_main.h" 116#include "ui_main.h"
116#include "util/overlay_dialog.h" 117#include "util/overlay_dialog.h"
@@ -261,6 +262,18 @@ static QString PrettyProductName() {
261 return QSysInfo::prettyProductName(); 262 return QSysInfo::prettyProductName();
262} 263}
263 264
265#ifdef _WIN32
266static void OverrideWindowsFont() {
267 // Qt5 chooses these fonts on Windows and they have fairly ugly alphanumeric/cyrllic characters
268 // Asking to use "MS Shell Dlg 2" gives better other chars while leaving the Chinese Characters.
269 const QString startup_font = QApplication::font().family();
270 const QStringList ugly_fonts = {QStringLiteral("SimSun"), QStringLiteral("PMingLiU")};
271 if (ugly_fonts.contains(startup_font)) {
272 QApplication::setFont(QFont(QStringLiteral("MS Shell Dlg 2"), 9, QFont::Normal));
273 }
274}
275#endif
276
264bool GMainWindow::CheckDarkMode() { 277bool GMainWindow::CheckDarkMode() {
265#ifdef __linux__ 278#ifdef __linux__
266 const QPalette test_palette(qApp->palette()); 279 const QPalette test_palette(qApp->palette());
@@ -281,6 +294,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
281#ifdef __linux__ 294#ifdef __linux__
282 SetupSigInterrupts(); 295 SetupSigInterrupts();
283#endif 296#endif
297 system->Initialize();
284 298
285 Common::Log::Initialize(); 299 Common::Log::Initialize();
286 LoadTranslation(); 300 LoadTranslation();
@@ -899,8 +913,8 @@ void GMainWindow::InitializeWidgets() {
899 } 913 }
900 914
901 // TODO (flTobi): Add the widget when multiplayer is fully implemented 915 // TODO (flTobi): Add the widget when multiplayer is fully implemented
902 // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); 916 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
903 // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); 917 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
904 918
905 tas_label = new QLabel(); 919 tas_label = new QLabel();
906 tas_label->setObjectName(QStringLiteral("TASlabel")); 920 tas_label->setObjectName(QStringLiteral("TASlabel"));
@@ -1299,6 +1313,7 @@ void GMainWindow::ConnectMenuEvents() {
1299 &MultiplayerState::OnDirectConnectToRoom); 1313 &MultiplayerState::OnDirectConnectToRoom);
1300 connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state, 1314 connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state,
1301 &MultiplayerState::OnOpenNetworkRoom); 1315 &MultiplayerState::OnOpenNetworkRoom);
1316 connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
1302 1317
1303 // Tools 1318 // Tools
1304 connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, 1319 connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
@@ -1339,6 +1354,8 @@ void GMainWindow::UpdateMenuState() {
1339 } else { 1354 } else {
1340 ui->action_Pause->setText(tr("&Pause")); 1355 ui->action_Pause->setText(tr("&Pause"));
1341 } 1356 }
1357
1358 multiplayer_state->UpdateNotificationStatus();
1342} 1359}
1343 1360
1344void GMainWindow::OnDisplayTitleBars(bool show) { 1361void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -1879,6 +1896,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1879 case GameListOpenTarget::SaveData: { 1896 case GameListOpenTarget::SaveData: {
1880 open_target = tr("Save Data"); 1897 open_target = tr("Save Data");
1881 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); 1898 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
1899 auto vfs_nand_dir =
1900 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
1882 1901
1883 if (has_user_save) { 1902 if (has_user_save) {
1884 // User save data 1903 // User save data
@@ -1905,15 +1924,15 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1905 ASSERT(user_id); 1924 ASSERT(user_id);
1906 1925
1907 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 1926 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
1908 *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1927 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
1909 program_id, user_id->AsU128(), 0); 1928 FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0);
1910 1929
1911 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 1930 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
1912 } else { 1931 } else {
1913 // Device save data 1932 // Device save data
1914 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( 1933 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
1915 *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1934 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
1916 program_id, {}, 0); 1935 FileSys::SaveDataType::SaveData, program_id, {}, 0);
1917 1936
1918 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); 1937 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
1919 } 1938 }
@@ -2000,7 +2019,7 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
2000} 2019}
2001 2020
2002void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { 2021void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
2003 const QString entry_type = [this, type] { 2022 const QString entry_type = [type] {
2004 switch (type) { 2023 switch (type) {
2005 case InstalledEntryType::Game: 2024 case InstalledEntryType::Game:
2006 return tr("Contents"); 2025 return tr("Contents");
@@ -2097,7 +2116,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
2097 2116
2098void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, 2117void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
2099 const std::string& game_path) { 2118 const std::string& game_path) {
2100 const QString question = [this, target] { 2119 const QString question = [target] {
2101 switch (target) { 2120 switch (target) {
2102 case GameListRemoveTarget::GlShaderCache: 2121 case GameListRemoveTarget::GlShaderCache:
2103 return tr("Delete OpenGL Transferable Shader Cache?"); 2122 return tr("Delete OpenGL Transferable Shader Cache?");
@@ -2770,6 +2789,11 @@ void GMainWindow::OnExit() {
2770 OnStopGame(); 2789 OnStopGame();
2771} 2790}
2772 2791
2792void GMainWindow::OnSaveConfig() {
2793 system->ApplySettings();
2794 config->Save();
2795}
2796
2773void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 2797void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
2774 OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"), 2798 OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"),
2775 Qt::AlignLeft | Qt::AlignVCenter); 2799 Qt::AlignLeft | Qt::AlignVCenter);
@@ -3211,21 +3235,16 @@ void GMainWindow::OnLoadAmiibo() {
3211 return; 3235 return;
3212 } 3236 }
3213 3237
3214 Service::SM::ServiceManager& sm = system->ServiceManager(); 3238 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3215 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); 3239
3216 if (nfc == nullptr) { 3240 // Remove amiibo if one is connected
3217 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); 3241 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) {
3218 return; 3242 virtual_amiibo->CloseAmiibo();
3219 }
3220 const auto nfc_state = nfc->GetCurrentState();
3221 if (nfc_state == Service::NFP::DeviceState::TagFound ||
3222 nfc_state == Service::NFP::DeviceState::TagMounted) {
3223 nfc->CloseAmiibo();
3224 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3243 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3225 return; 3244 return;
3226 } 3245 }
3227 3246
3228 if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { 3247 if (virtual_amiibo->GetCurrentState() != InputCommon::VirtualAmiibo::State::WaitingForAmiibo) {
3229 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); 3248 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
3230 return; 3249 return;
3231 } 3250 }
@@ -3244,24 +3263,30 @@ void GMainWindow::OnLoadAmiibo() {
3244} 3263}
3245 3264
3246void GMainWindow::LoadAmiibo(const QString& filename) { 3265void GMainWindow::LoadAmiibo(const QString& filename) {
3247 Service::SM::ServiceManager& sm = system->ServiceManager(); 3266 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3248 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); 3267 const QString title = tr("Error loading Amiibo data");
3249 if (nfc == nullptr) {
3250 return;
3251 }
3252
3253 // Remove amiibo if one is connected 3268 // Remove amiibo if one is connected
3254 const auto nfc_state = nfc->GetCurrentState(); 3269 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) {
3255 if (nfc_state == Service::NFP::DeviceState::TagFound || 3270 virtual_amiibo->CloseAmiibo();
3256 nfc_state == Service::NFP::DeviceState::TagMounted) {
3257 nfc->CloseAmiibo();
3258 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3271 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3259 return; 3272 return;
3260 } 3273 }
3261 3274
3262 if (!nfc->LoadAmiibo(filename.toStdString())) { 3275 switch (virtual_amiibo->LoadAmiibo(filename.toStdString())) {
3263 QMessageBox::warning(this, tr("Error loading Amiibo data"), 3276 case InputCommon::VirtualAmiibo::Info::NotAnAmiibo:
3264 tr("Unable to load Amiibo data.")); 3277 QMessageBox::warning(this, title, tr("The selected file is not a valid amiibo"));
3278 break;
3279 case InputCommon::VirtualAmiibo::Info::UnableToLoad:
3280 QMessageBox::warning(this, title, tr("The selected file is already on use"));
3281 break;
3282 case InputCommon::VirtualAmiibo::Info::WrongDeviceState:
3283 QMessageBox::warning(this, title, tr("The current game is not looking for amiibos"));
3284 break;
3285 case InputCommon::VirtualAmiibo::Info::Unknown:
3286 QMessageBox::warning(this, title, tr("An unknown error occurred"));
3287 break;
3288 default:
3289 break;
3265 } 3290 }
3266} 3291}
3267 3292
@@ -3442,9 +3467,10 @@ void GMainWindow::UpdateStatusBar() {
3442 } 3467 }
3443 if (!Settings::values.use_speed_limit) { 3468 if (!Settings::values.use_speed_limit) {
3444 game_fps_label->setText( 3469 game_fps_label->setText(
3445 tr("Game: %1 FPS (Unlocked)").arg(results.average_game_fps, 0, 'f', 0)); 3470 tr("Game: %1 FPS (Unlocked)").arg(std::round(results.average_game_fps), 0, 'f', 0));
3446 } else { 3471 } else {
3447 game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); 3472 game_fps_label->setText(
3473 tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0));
3448 } 3474 }
3449 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); 3475 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
3450 3476
@@ -4086,7 +4112,8 @@ int main(int argc, char* argv[]) {
4086 } 4112 }
4087#endif 4113#endif
4088 4114
4089 if (StartupChecks(argv[0], &has_broken_vulkan)) { 4115 if (StartupChecks(argv[0], &has_broken_vulkan,
4116 Settings::values.perform_vulkan_check.GetValue())) {
4090 return 0; 4117 return 0;
4091 } 4118 }
4092 4119
@@ -4125,6 +4152,10 @@ int main(int argc, char* argv[]) {
4125 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4152 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4126 QApplication app(argc, argv); 4153 QApplication app(argc, argv);
4127 4154
4155#ifdef _WIN32
4156 OverrideWindowsFont();
4157#endif
4158
4128 // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021 4159 // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
4129 // so we can see if we get \u3008 instead 4160 // so we can see if we get \u3008 instead
4130 // TL;DR all other number formats are consecutive in unicode code points 4161 // TL;DR all other number formats are consecutive in unicode code points
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 716aef063..f7aa8e417 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -169,6 +169,7 @@ public slots:
169 void OnLoadComplete(); 169 void OnLoadComplete();
170 void OnExecuteProgram(std::size_t program_index); 170 void OnExecuteProgram(std::size_t program_index);
171 void OnExit(); 171 void OnExit();
172 void OnSaveConfig();
172 void ControllerSelectorReconfigureControllers( 173 void ControllerSelectorReconfigureControllers(
173 const Core::Frontend::ControllerParameters& parameters); 174 const Core::Frontend::ControllerParameters& parameters);
174 void SoftwareKeyboardInitialize( 175 void SoftwareKeyboardInitialize(
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index cdf31b417..e670acc30 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -55,7 +55,6 @@
55 <addaction name="separator"/> 55 <addaction name="separator"/>
56 <addaction name="menu_recent_files"/> 56 <addaction name="menu_recent_files"/>
57 <addaction name="separator"/> 57 <addaction name="separator"/>
58 <addaction name="separator"/>
59 <addaction name="action_Load_Amiibo"/> 58 <addaction name="action_Load_Amiibo"/>
60 <addaction name="separator"/> 59 <addaction name="separator"/>
61 <addaction name="action_Open_yuzu_Folder"/> 60 <addaction name="action_Open_yuzu_Folder"/>
@@ -120,6 +119,20 @@
120 <addaction name="menu_Reset_Window_Size"/> 119 <addaction name="menu_Reset_Window_Size"/>
121 <addaction name="menu_View_Debugging"/> 120 <addaction name="menu_View_Debugging"/>
122 </widget> 121 </widget>
122 <widget class="QMenu" name="menu_Multiplayer">
123 <property name="enabled">
124 <bool>true</bool>
125 </property>
126 <property name="title">
127 <string>&amp;Multiplayer</string>
128 </property>
129 <addaction name="action_View_Lobby"/>
130 <addaction name="action_Start_Room"/>
131 <addaction name="action_Connect_To_Room"/>
132 <addaction name="separator"/>
133 <addaction name="action_Show_Room"/>
134 <addaction name="action_Leave_Room"/>
135 </widget>
123 <widget class="QMenu" name="menu_Tools"> 136 <widget class="QMenu" name="menu_Tools">
124 <property name="title"> 137 <property name="title">
125 <string>&amp;Tools</string> 138 <string>&amp;Tools</string>
@@ -251,7 +264,7 @@
251 <bool>true</bool> 264 <bool>true</bool>
252 </property> 265 </property>
253 <property name="text"> 266 <property name="text">
254 <string>Browse Public Game Lobby</string> 267 <string>&amp;Browse Public Game Lobby</string>
255 </property> 268 </property>
256 </action> 269 </action>
257 <action name="action_Start_Room"> 270 <action name="action_Start_Room">
@@ -259,7 +272,7 @@
259 <bool>true</bool> 272 <bool>true</bool>
260 </property> 273 </property>
261 <property name="text"> 274 <property name="text">
262 <string>Create Room</string> 275 <string>&amp;Create Room</string>
263 </property> 276 </property>
264 </action> 277 </action>
265 <action name="action_Leave_Room"> 278 <action name="action_Leave_Room">
@@ -267,12 +280,12 @@
267 <bool>false</bool> 280 <bool>false</bool>
268 </property> 281 </property>
269 <property name="text"> 282 <property name="text">
270 <string>Leave Room</string> 283 <string>&amp;Leave Room</string>
271 </property> 284 </property>
272 </action> 285 </action>
273 <action name="action_Connect_To_Room"> 286 <action name="action_Connect_To_Room">
274 <property name="text"> 287 <property name="text">
275 <string>Direct Connect to Room</string> 288 <string>&amp;Direct Connect to Room</string>
276 </property> 289 </property>
277 </action> 290 </action>
278 <action name="action_Show_Room"> 291 <action name="action_Show_Room">
@@ -280,7 +293,7 @@
280 <bool>false</bool> 293 <bool>false</bool>
281 </property> 294 </property>
282 <property name="text"> 295 <property name="text">
283 <string>Show Current Room</string> 296 <string>&amp;Show Current Room</string>
284 </property> 297 </property>
285 </action> 298 </action>
286 <action name="action_Fullscreen"> 299 <action name="action_Fullscreen">
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 9e672f82e..dec9696c1 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -61,7 +61,10 @@ public:
61 61
62 /// Format the message using the players color 62 /// Format the message using the players color
63 QString GetPlayerChatMessage(u16 player) const { 63 QString GetPlayerChatMessage(u16 player) const {
64 auto color = player_color[player % 16]; 64 const bool is_dark_theme = QIcon::themeName().contains(QStringLiteral("dark")) ||
65 QIcon::themeName().contains(QStringLiteral("midnight"));
66 auto color =
67 is_dark_theme ? player_color_dark[player % 16] : player_color_default[player % 16];
65 QString name; 68 QString name;
66 if (username.isEmpty() || username == nickname) { 69 if (username.isEmpty() || username == nickname) {
67 name = nickname; 70 name = nickname;
@@ -84,9 +87,12 @@ public:
84 } 87 }
85 88
86private: 89private:
87 static constexpr std::array<const char*, 16> player_color = { 90 static constexpr std::array<const char*, 16> player_color_default = {
88 {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222", 91 {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222",
89 "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "FFFF00"}}; 92 "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "#FFFF00"}};
93 static constexpr std::array<const char*, 16> player_color_dark = {
94 {"#559AD1", "#4EC9A8", "#D69D85", "#C6C923", "#B975B5", "#D81F1F", "#7EAE39", "#4F8733",
95 "#F7CD8A", "#6FCACF", "#CE4897", "#8A2BE2", "#D2691E", "#9ACD32", "#FF7F50", "#152ccd"}};
90 static constexpr char ping_color[] = "#FFFF00"; 96 static constexpr char ping_color[] = "#FFFF00";
91 97
92 QString timestamp; 98 QString timestamp;
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index b34a8d004..caf34a414 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -97,8 +97,9 @@ void ClientRoomWindow::UpdateView() {
97 auto memberlist = member->GetMemberInformation(); 97 auto memberlist = member->GetMemberInformation();
98 ui->chat->SetPlayerList(memberlist); 98 ui->chat->SetPlayerList(memberlist);
99 const auto information = member->GetRoomInformation(); 99 const auto information = member->GetRoomInformation();
100 setWindowTitle(QString(tr("%1 (%2/%3 members) - connected")) 100 setWindowTitle(QString(tr("%1 - %2 (%3/%4 members) - connected"))
101 .arg(QString::fromStdString(information.name)) 101 .arg(QString::fromStdString(information.name))
102 .arg(QString::fromStdString(information.preferred_game.name))
102 .arg(memberlist.size()) 103 .arg(memberlist.size())
103 .arg(information.member_slots)); 104 .arg(information.member_slots));
104 ui->description->setText(QString::fromStdString(information.description)); 105 ui->description->setText(QString::fromStdString(information.description));
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 017063074..10bf0a4fb 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -106,6 +106,8 @@ void DirectConnectWindow::Connect() {
106 UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); 106 UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault();
107 } 107 }
108 108
109 emit SaveConfig();
110
109 // attempt to connect in a different thread 111 // attempt to connect in a different thread
110 QFuture<void> f = QtConcurrent::run([&] { 112 QFuture<void> f = QtConcurrent::run([&] {
111 if (auto room_member = room_network.GetRoomMember().lock()) { 113 if (auto room_member = room_network.GetRoomMember().lock()) {
diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h
index e39dd1e0d..b8f66cfb2 100644
--- a/src/yuzu/multiplayer/direct_connect.h
+++ b/src/yuzu/multiplayer/direct_connect.h
@@ -31,6 +31,7 @@ signals:
31 * connections that it might have. 31 * connections that it might have.
32 */ 32 */
33 void Closed(); 33 void Closed();
34 void SaveConfig();
34 35
35private slots: 36private slots:
36 void OnConnection(); 37 void OnConnection();
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index 0c6adfd04..a8faa5b24 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -232,6 +232,7 @@ void HostRoomWindow::Host() {
232 } 232 }
233 UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); 233 UISettings::values.multiplayer_room_description = ui->room_description->toPlainText();
234 ui->host->setEnabled(true); 234 ui->host->setEnabled(true);
235 emit SaveConfig();
235 close(); 236 close();
236 } 237 }
237} 238}
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h
index 034cb2eef..ae816e2e0 100644
--- a/src/yuzu/multiplayer/host_room.h
+++ b/src/yuzu/multiplayer/host_room.h
@@ -46,6 +46,9 @@ public:
46 void UpdateGameList(QStandardItemModel* list); 46 void UpdateGameList(QStandardItemModel* list);
47 void RetranslateUi(); 47 void RetranslateUi();
48 48
49signals:
50 void SaveConfig();
51
49private: 52private:
50 void Host(); 53 void Host();
51 std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const; 54 std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const;
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 107d40547..08c275696 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -7,6 +7,7 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/service/acc/profile_manager.h"
10#include "core/internal_network/network_interface.h" 11#include "core/internal_network/network_interface.h"
11#include "network/network.h" 12#include "network/network.h"
12#include "ui_lobby.h" 13#include "ui_lobby.h"
@@ -26,9 +27,9 @@
26Lobby::Lobby(QWidget* parent, QStandardItemModel* list, 27Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
27 std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_) 28 std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
28 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), 29 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
29 ui(std::make_unique<Ui::Lobby>()), 30 ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session),
30 announce_multiplayer_session(session), system{system_}, room_network{ 31 profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_},
31 system.GetRoomNetwork()} { 32 room_network{system.GetRoomNetwork()} {
32 ui->setupUi(this); 33 ui->setupUi(this);
33 34
34 // setup the watcher for background connections 35 // setup the watcher for background connections
@@ -60,9 +61,17 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
60 61
61 ui->nickname->setValidator(validation.GetNickname()); 62 ui->nickname->setValidator(validation.GetNickname());
62 ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); 63 ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue());
63 if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { 64
64 // Use yuzu Web Service user name as nickname by default 65 // Try find the best nickname by default
65 ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); 66 if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) {
67 if (!Settings::values.yuzu_username.GetValue().empty()) {
68 ui->nickname->setText(
69 QString::fromStdString(Settings::values.yuzu_username.GetValue()));
70 } else if (!GetProfileUsername().empty()) {
71 ui->nickname->setText(QString::fromStdString(GetProfileUsername()));
72 } else {
73 ui->nickname->setText(QStringLiteral("yuzu"));
74 }
66 } 75 }
67 76
68 // UI Buttons 77 // UI Buttons
@@ -76,12 +85,6 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
76 // Actions 85 // Actions
77 connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, 86 connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
78 &Lobby::OnRefreshLobby); 87 &Lobby::OnRefreshLobby);
79
80 // manually start a refresh when the window is opening
81 // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as
82 // part of the constructor, but offload the refresh until after the window shown. perhaps emit a
83 // refreshroomlist signal from places that open the lobby
84 RefreshLobby();
85} 88}
86 89
87Lobby::~Lobby() = default; 90Lobby::~Lobby() = default;
@@ -96,6 +99,7 @@ void Lobby::UpdateGameList(QStandardItemModel* list) {
96 } 99 }
97 if (proxy) 100 if (proxy)
98 proxy->UpdateGameList(game_list); 101 proxy->UpdateGameList(game_list);
102 ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder);
99} 103}
100 104
101void Lobby::RetranslateUi() { 105void Lobby::RetranslateUi() {
@@ -117,6 +121,11 @@ void Lobby::OnExpandRoom(const QModelIndex& index) {
117 121
118void Lobby::OnJoinRoom(const QModelIndex& source) { 122void Lobby::OnJoinRoom(const QModelIndex& source) {
119 if (!Network::GetSelectedNetworkInterface()) { 123 if (!Network::GetSelectedNetworkInterface()) {
124 LOG_INFO(WebService, "Automatically selected network interface for room network.");
125 Network::SelectFirstNetworkInterface();
126 }
127
128 if (!Network::GetSelectedNetworkInterface()) {
120 NetworkMessage::ErrorManager::ShowError( 129 NetworkMessage::ErrorManager::ShowError(
121 NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); 130 NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED);
122 return; 131 return;
@@ -197,16 +206,16 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
197 proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); 206 proxy->data(connection_index, LobbyItemHost::HostIPRole).toString();
198 UISettings::values.multiplayer_port = 207 UISettings::values.multiplayer_port =
199 proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); 208 proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
209 emit SaveConfig();
200} 210}
201 211
202void Lobby::ResetModel() { 212void Lobby::ResetModel() {
203 model->clear(); 213 model->clear();
204 model->insertColumns(0, Column::TOTAL); 214 model->insertColumns(0, Column::TOTAL);
205 model->setHeaderData(Column::EXPAND, Qt::Horizontal, QString(), Qt::DisplayRole); 215 model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole);
206 model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole); 216 model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole);
207 model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); 217 model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole);
208 model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); 218 model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole);
209 model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole);
210} 219}
211 220
212void Lobby::RefreshLobby() { 221void Lobby::RefreshLobby() {
@@ -229,6 +238,7 @@ void Lobby::OnRefreshLobby() {
229 for (int r = 0; r < game_list->rowCount(); ++r) { 238 for (int r = 0; r < game_list->rowCount(); ++r) {
230 auto index = game_list->index(r, 0); 239 auto index = game_list->index(r, 0);
231 auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); 240 auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong();
241
232 if (game_id != 0 && room.information.preferred_game.id == game_id) { 242 if (game_id != 0 && room.information.preferred_game.id == game_id) {
233 smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>(); 243 smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>();
234 } 244 }
@@ -243,17 +253,16 @@ void Lobby::OnRefreshLobby() {
243 members.append(var); 253 members.append(var);
244 } 254 }
245 255
246 auto first_item = new LobbyItem(); 256 auto first_item = new LobbyItemGame(
257 room.information.preferred_game.id,
258 QString::fromStdString(room.information.preferred_game.name), smdh_icon);
247 auto row = QList<QStandardItem*>({ 259 auto row = QList<QStandardItem*>({
248 first_item, 260 first_item,
249 new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)), 261 new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)),
250 new LobbyItemGame(room.information.preferred_game.id, 262 new LobbyItemMemberList(members, room.information.member_slots),
251 QString::fromStdString(room.information.preferred_game.name),
252 smdh_icon),
253 new LobbyItemHost(QString::fromStdString(room.information.host_username), 263 new LobbyItemHost(QString::fromStdString(room.information.host_username),
254 QString::fromStdString(room.ip), room.information.port, 264 QString::fromStdString(room.ip), room.information.port,
255 QString::fromStdString(room.verify_uid)), 265 QString::fromStdString(room.verify_uid)),
256 new LobbyItemMemberList(members, room.information.member_slots),
257 }); 266 });
258 model->appendRow(row); 267 model->appendRow(row);
259 // To make the rows expandable, add the member data as a child of the first column of the 268 // To make the rows expandable, add the member data as a child of the first column of the
@@ -283,6 +292,26 @@ void Lobby::OnRefreshLobby() {
283 ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true); 292 ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true);
284 } 293 }
285 } 294 }
295
296 ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder);
297}
298
299std::string Lobby::GetProfileUsername() {
300 const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
301 Service::Account::ProfileBase profile{};
302
303 if (!current_user.has_value()) {
304 return "";
305 }
306
307 if (!profile_manager->GetProfileBase(*current_user, profile)) {
308 return "";
309 }
310
311 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
312 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
313
314 return text;
286} 315}
287 316
288LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) 317LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list)
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index 2696aec21..300dad13e 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -24,6 +24,10 @@ namespace Core {
24class System; 24class System;
25} 25}
26 26
27namespace Service::Account {
28class ProfileManager;
29}
30
27/** 31/**
28 * Listing of all public games pulled from services. The lobby should be simple enough for users to 32 * Listing of all public games pulled from services. The lobby should be simple enough for users to
29 * find the game they want to play, and join it. 33 * find the game they want to play, and join it.
@@ -75,8 +79,11 @@ private slots:
75 79
76signals: 80signals:
77 void StateChanged(const Network::RoomMember::State&); 81 void StateChanged(const Network::RoomMember::State&);
82 void SaveConfig();
78 83
79private: 84private:
85 std::string GetProfileUsername();
86
80 /** 87 /**
81 * Removes all entries in the Lobby before refreshing. 88 * Removes all entries in the Lobby before refreshing.
82 */ 89 */
@@ -96,6 +103,7 @@ private:
96 103
97 QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher; 104 QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher;
98 std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; 105 std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
106 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
99 QFutureWatcher<void>* watcher; 107 QFutureWatcher<void>* watcher;
100 Validation validation; 108 Validation validation;
101 Core::System& system; 109 Core::System& system;
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h
index 8071cede4..068c95aca 100644
--- a/src/yuzu/multiplayer/lobby_p.h
+++ b/src/yuzu/multiplayer/lobby_p.h
@@ -11,11 +11,10 @@
11 11
12namespace Column { 12namespace Column {
13enum List { 13enum List {
14 EXPAND,
15 ROOM_NAME,
16 GAME_NAME, 14 GAME_NAME,
17 HOST, 15 ROOM_NAME,
18 MEMBER, 16 MEMBER,
17 HOST,
19 TOTAL, 18 TOTAL,
20}; 19};
21} 20}
@@ -91,6 +90,8 @@ public:
91 setData(game_name, GameNameRole); 90 setData(game_name, GameNameRole);
92 if (!smdh_icon.isNull()) { 91 if (!smdh_icon.isNull()) {
93 setData(smdh_icon, GameIconRole); 92 setData(smdh_icon, GameIconRole);
93 } else {
94 setData(QIcon::fromTheme(QStringLiteral("chip")).pixmap(32), GameIconRole);
94 } 95 }
95 } 96 }
96 97
@@ -98,7 +99,12 @@ public:
98 if (role == Qt::DecorationRole) { 99 if (role == Qt::DecorationRole) {
99 auto val = data(GameIconRole); 100 auto val = data(GameIconRole);
100 if (val.isValid()) { 101 if (val.isValid()) {
101 val = val.value<QPixmap>().scaled(16, 16, Qt::KeepAspectRatio); 102 val = val.value<QPixmap>().scaled(32, 32, Qt::KeepAspectRatio,
103 Qt::TransformationMode::SmoothTransformation);
104 } else {
105 auto blank_image = QPixmap(32, 32);
106 blank_image.fill(Qt::black);
107 val = blank_image;
102 } 108 }
103 return val; 109 return val;
104 } else if (role != Qt::DisplayRole) { 110 } else if (role != Qt::DisplayRole) {
@@ -191,8 +197,8 @@ public:
191 return LobbyItem::data(role); 197 return LobbyItem::data(role);
192 } 198 }
193 auto members = data(MemberListRole).toList(); 199 auto members = data(MemberListRole).toList();
194 return QStringLiteral("%1 / %2").arg(QString::number(members.size()), 200 return QStringLiteral("%1 / %2 ")
195 data(MaxPlayerRole).toString()); 201 .arg(QString::number(members.size()), data(MaxPlayerRole).toString());
196 } 202 }
197 203
198 bool operator<(const QStandardItem& other) const override { 204 bool operator<(const QStandardItem& other) const override {
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp
index 758b5b731..6d8f18274 100644
--- a/src/yuzu/multiplayer/message.cpp
+++ b/src/yuzu/multiplayer/message.cpp
@@ -49,9 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED(
49 QT_TR_NOOP("You do not have enough permission to perform this action.")); 49 QT_TR_NOOP("You do not have enough permission to perform this action."));
50const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( 50const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP(
51 "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); 51 "The user you are trying to kick/ban could not be found.\nThey may have left the room."));
52const ConnectionError ErrorManager::NO_INTERFACE_SELECTED( 52const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(QT_TR_NOOP(
53 QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and " 53 "No valid network interface is selected.\nPlease go to Configure -> System -> Network and "
54 "make a selection.")); 54 "make a selection."));
55 55
56static bool WarnMessage(const std::string& title, const std::string& text) { 56static bool WarnMessage(const std::string& title, const std::string& text) {
57 return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), 57 return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()),
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index 66e098296..285bb150d 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -44,9 +44,6 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
44 44
45 status_text = new ClickableLabel(this); 45 status_text = new ClickableLabel(this);
46 status_icon = new ClickableLabel(this); 46 status_icon = new ClickableLabel(this);
47 status_text->setToolTip(tr("Current connection status"));
48 status_text->setText(tr("Not Connected. Click here to find a room!"));
49 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
50 47
51 connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); 48 connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
52 connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); 49 connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
@@ -57,6 +54,8 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
57 HideNotification(); 54 HideNotification();
58 } 55 }
59 }); 56 });
57
58 retranslateUi();
60} 59}
61 60
62MultiplayerState::~MultiplayerState() = default; 61MultiplayerState::~MultiplayerState() = default;
@@ -90,14 +89,7 @@ void MultiplayerState::Close() {
90void MultiplayerState::retranslateUi() { 89void MultiplayerState::retranslateUi() {
91 status_text->setToolTip(tr("Current connection status")); 90 status_text->setToolTip(tr("Current connection status"));
92 91
93 if (current_state == Network::RoomMember::State::Uninitialized) { 92 UpdateNotificationStatus();
94 status_text->setText(tr("Not Connected. Click here to find a room!"));
95 } else if (current_state == Network::RoomMember::State::Joined ||
96 current_state == Network::RoomMember::State::Moderator) {
97 status_text->setText(tr("Connected"));
98 } else {
99 status_text->setText(tr("Not Connected"));
100 }
101 93
102 if (lobby) { 94 if (lobby) {
103 lobby->RetranslateUi(); 95 lobby->RetranslateUi();
@@ -113,21 +105,55 @@ void MultiplayerState::retranslateUi() {
113 } 105 }
114} 106}
115 107
108void MultiplayerState::SetNotificationStatus(NotificationStatus status) {
109 notification_status = status;
110 UpdateNotificationStatus();
111}
112
113void MultiplayerState::UpdateNotificationStatus() {
114 switch (notification_status) {
115 case NotificationStatus::Unitialized:
116 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
117 status_text->setText(tr("Not Connected. Click here to find a room!"));
118 leave_room->setEnabled(false);
119 show_room->setEnabled(false);
120 break;
121 case NotificationStatus::Disconnected:
122 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
123 status_text->setText(tr("Not Connected"));
124 leave_room->setEnabled(false);
125 show_room->setEnabled(false);
126 break;
127 case NotificationStatus::Connected:
128 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
129 status_text->setText(tr("Connected"));
130 leave_room->setEnabled(true);
131 show_room->setEnabled(true);
132 break;
133 case NotificationStatus::Notification:
134 status_icon->setPixmap(
135 QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
136 status_text->setText(tr("New Messages Received"));
137 leave_room->setEnabled(true);
138 show_room->setEnabled(true);
139 break;
140 }
141
142 // Clean up status bar if game is running
143 if (system.IsPoweredOn()) {
144 status_text->clear();
145 }
146}
147
116void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { 148void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) {
117 LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); 149 LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state));
118 if (state == Network::RoomMember::State::Joined || 150 if (state == Network::RoomMember::State::Joined ||
119 state == Network::RoomMember::State::Moderator) { 151 state == Network::RoomMember::State::Moderator) {
120 152
121 OnOpenNetworkRoom(); 153 OnOpenNetworkRoom();
122 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); 154 SetNotificationStatus(NotificationStatus::Connected);
123 status_text->setText(tr("Connected"));
124 leave_room->setEnabled(true);
125 show_room->setEnabled(true);
126 } else { 155 } else {
127 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); 156 SetNotificationStatus(NotificationStatus::Disconnected);
128 status_text->setText(tr("Not Connected"));
129 leave_room->setEnabled(false);
130 show_room->setEnabled(false);
131 } 157 }
132 158
133 current_state = state; 159 current_state = state;
@@ -185,6 +211,10 @@ void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) {
185 QMessageBox::Ok); 211 QMessageBox::Ok);
186} 212}
187 213
214void MultiplayerState::OnSaveConfig() {
215 emit SaveConfig();
216}
217
188void MultiplayerState::UpdateThemedIcons() { 218void MultiplayerState::UpdateThemedIcons() {
189 if (show_notification) { 219 if (show_notification) {
190 status_icon->setPixmap( 220 status_icon->setPixmap(
@@ -209,13 +239,16 @@ static void BringWidgetToFront(QWidget* widget) {
209void MultiplayerState::OnViewLobby() { 239void MultiplayerState::OnViewLobby() {
210 if (lobby == nullptr) { 240 if (lobby == nullptr) {
211 lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); 241 lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system);
242 connect(lobby, &Lobby::SaveConfig, this, &MultiplayerState::OnSaveConfig);
212 } 243 }
244 lobby->RefreshLobby();
213 BringWidgetToFront(lobby); 245 BringWidgetToFront(lobby);
214} 246}
215 247
216void MultiplayerState::OnCreateRoom() { 248void MultiplayerState::OnCreateRoom() {
217 if (host_room == nullptr) { 249 if (host_room == nullptr) {
218 host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); 250 host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system);
251 connect(host_room, &HostRoomWindow::SaveConfig, this, &MultiplayerState::OnSaveConfig);
219 } 252 }
220 BringWidgetToFront(host_room); 253 BringWidgetToFront(host_room);
221} 254}
@@ -235,7 +268,7 @@ bool MultiplayerState::OnCloseRoom() {
235 return true; 268 return true;
236 } 269 }
237 // Save ban list 270 // Save ban list
238 UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); 271 UISettings::values.multiplayer_ban_list = room->GetBanList();
239 272
240 room->Destroy(); 273 room->Destroy();
241 announce_multiplayer_session->Stop(); 274 announce_multiplayer_session->Stop();
@@ -249,14 +282,13 @@ void MultiplayerState::ShowNotification() {
249 return; // Do not show notification if the chat window currently has focus 282 return; // Do not show notification if the chat window currently has focus
250 show_notification = true; 283 show_notification = true;
251 QApplication::alert(nullptr); 284 QApplication::alert(nullptr);
252 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); 285 QApplication::beep();
253 status_text->setText(tr("New Messages Received")); 286 SetNotificationStatus(NotificationStatus::Notification);
254} 287}
255 288
256void MultiplayerState::HideNotification() { 289void MultiplayerState::HideNotification() {
257 show_notification = false; 290 show_notification = false;
258 status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); 291 SetNotificationStatus(NotificationStatus::Connected);
259 status_text->setText(tr("Connected"));
260} 292}
261 293
262void MultiplayerState::OnOpenNetworkRoom() { 294void MultiplayerState::OnOpenNetworkRoom() {
@@ -279,6 +311,8 @@ void MultiplayerState::OnOpenNetworkRoom() {
279void MultiplayerState::OnDirectConnectToRoom() { 311void MultiplayerState::OnDirectConnectToRoom() {
280 if (direct_connect == nullptr) { 312 if (direct_connect == nullptr) {
281 direct_connect = new DirectConnectWindow(system, this); 313 direct_connect = new DirectConnectWindow(system, this);
314 connect(direct_connect, &DirectConnectWindow::SaveConfig, this,
315 &MultiplayerState::OnSaveConfig);
282 } 316 }
283 BringWidgetToFront(direct_connect); 317 BringWidgetToFront(direct_connect);
284} 318}
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h
index c92496413..5d681c5c6 100644
--- a/src/yuzu/multiplayer/state.h
+++ b/src/yuzu/multiplayer/state.h
@@ -22,6 +22,13 @@ class MultiplayerState : public QWidget {
22 Q_OBJECT; 22 Q_OBJECT;
23 23
24public: 24public:
25 enum class NotificationStatus {
26 Unitialized,
27 Disconnected,
28 Connected,
29 Notification,
30 };
31
25 explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, 32 explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room,
26 QAction* show_room, Core::System& system_); 33 QAction* show_room, Core::System& system_);
27 ~MultiplayerState(); 34 ~MultiplayerState();
@@ -31,6 +38,10 @@ public:
31 */ 38 */
32 void Close(); 39 void Close();
33 40
41 void SetNotificationStatus(NotificationStatus state);
42
43 void UpdateNotificationStatus();
44
34 ClickableLabel* GetStatusText() const { 45 ClickableLabel* GetStatusText() const {
35 return status_text; 46 return status_text;
36 } 47 }
@@ -64,6 +75,7 @@ public slots:
64 void OnOpenNetworkRoom(); 75 void OnOpenNetworkRoom();
65 void OnDirectConnectToRoom(); 76 void OnDirectConnectToRoom();
66 void OnAnnounceFailed(const WebService::WebResult&); 77 void OnAnnounceFailed(const WebService::WebResult&);
78 void OnSaveConfig();
67 void UpdateThemedIcons(); 79 void UpdateThemedIcons();
68 void ShowNotification(); 80 void ShowNotification();
69 void HideNotification(); 81 void HideNotification();
@@ -72,6 +84,7 @@ signals:
72 void NetworkStateChanged(const Network::RoomMember::State&); 84 void NetworkStateChanged(const Network::RoomMember::State&);
73 void NetworkError(const Network::RoomMember::Error&); 85 void NetworkError(const Network::RoomMember::Error&);
74 void AnnounceFailed(const WebService::WebResult&); 86 void AnnounceFailed(const WebService::WebResult&);
87 void SaveConfig();
75 88
76private: 89private:
77 Lobby* lobby = nullptr; 90 Lobby* lobby = nullptr;
@@ -85,6 +98,7 @@ private:
85 QAction* show_room; 98 QAction* show_room;
86 std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; 99 std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
87 Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; 100 Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized;
101 NotificationStatus notification_status = NotificationStatus::Unitialized;
88 bool has_mod_perms = false; 102 bool has_mod_perms = false;
89 Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle; 103 Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle;
90 Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; 104 Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle;
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 29b87da05..6a91212e2 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -49,7 +49,7 @@ bool CheckEnvVars(bool* is_child) {
49 *is_child = true; 49 *is_child = true;
50 return false; 50 return false;
51 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { 51 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) {
52 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", 52 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n",
53 IS_CHILD_ENV_VAR, GetLastError()); 53 IS_CHILD_ENV_VAR, GetLastError());
54 return true; 54 return true;
55 } 55 }
@@ -57,67 +57,72 @@ bool CheckEnvVars(bool* is_child) {
57 return false; 57 return false;
58} 58}
59 59
60bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { 60bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check) {
61#ifdef _WIN32 61#ifdef _WIN32
62 // Set the startup variable for child processes 62 // Set the startup variable for child processes
63 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); 63 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
64 if (!env_var_set) { 64 if (!env_var_set) {
65 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", 65 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n",
66 STARTUP_CHECK_ENV_VAR, GetLastError()); 66 STARTUP_CHECK_ENV_VAR, GetLastError());
67 return false; 67 return false;
68 } 68 }
69 69
70 PROCESS_INFORMATION process_info; 70 if (perform_vulkan_check) {
71 std::memset(&process_info, '\0', sizeof(process_info)); 71 // Spawn child process that performs Vulkan check
72 72 PROCESS_INFORMATION process_info;
73 if (!SpawnChild(arg0, &process_info, 0)) { 73 std::memset(&process_info, '\0', sizeof(process_info));
74 return false; 74
75 } 75 if (!SpawnChild(arg0, &process_info, 0)) {
76 76 return false;
77 // Wait until the processs exits and get exit code from it 77 }
78 WaitForSingleObject(process_info.hProcess, INFINITE); 78
79 DWORD exit_code = STILL_ACTIVE; 79 // Wait until the processs exits and get exit code from it
80 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); 80 WaitForSingleObject(process_info.hProcess, INFINITE);
81 if (err == 0) { 81 DWORD exit_code = STILL_ACTIVE;
82 std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError()); 82 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
83 } 83 if (err == 0) {
84 84 std::fprintf(stderr, "GetExitCodeProcess failed with error %lu\n", GetLastError());
85 // Vulkan is broken if the child crashed (return value is not zero) 85 }
86 *has_broken_vulkan = (exit_code != 0); 86
87 87 // Vulkan is broken if the child crashed (return value is not zero)
88 if (CloseHandle(process_info.hProcess) == 0) { 88 *has_broken_vulkan = (exit_code != 0);
89 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); 89
90 } 90 if (CloseHandle(process_info.hProcess) == 0) {
91 if (CloseHandle(process_info.hThread) == 0) { 91 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError());
92 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); 92 }
93 if (CloseHandle(process_info.hThread) == 0) {
94 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError());
95 }
93 } 96 }
94 97
95 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { 98 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
96 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n", 99 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %lu\n",
97 STARTUP_CHECK_ENV_VAR, GetLastError()); 100 STARTUP_CHECK_ENV_VAR, GetLastError());
98 } 101 }
99 102
100#elif defined(YUZU_UNIX) 103#elif defined(YUZU_UNIX)
101 const pid_t pid = fork(); 104 if (perform_vulkan_check) {
102 if (pid == 0) { 105 const pid_t pid = fork();
103 CheckVulkan(); 106 if (pid == 0) {
104 return true; 107 CheckVulkan();
105 } else if (pid == -1) { 108 return true;
106 const int err = errno; 109 } else if (pid == -1) {
107 std::fprintf(stderr, "fork failed with error %d\n", err); 110 const int err = errno;
108 return false; 111 std::fprintf(stderr, "fork failed with error %d\n", err);
109 } 112 return false;
110 113 }
111 // Get exit code from child process 114
112 int status; 115 // Get exit code from child process
113 const int r_val = wait(&status); 116 int status;
114 if (r_val == -1) { 117 const int r_val = wait(&status);
115 const int err = errno; 118 if (r_val == -1) {
116 std::fprintf(stderr, "wait failed with error %d\n", err); 119 const int err = errno;
117 return false; 120 std::fprintf(stderr, "wait failed with error %d\n", err);
121 return false;
122 }
123 // Vulkan is broken if the child crashed (return value is not zero)
124 *has_broken_vulkan = (status != 0);
118 } 125 }
119 // Vulkan is broken if the child crashed (return value is not zero)
120 *has_broken_vulkan = (status != 0);
121#endif 126#endif
122 return false; 127 return false;
123} 128}
@@ -130,7 +135,8 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
130 startup_info.cb = sizeof(startup_info); 135 startup_info.cb = sizeof(startup_info);
131 136
132 char p_name[255]; 137 char p_name[255];
133 std::strncpy(p_name, arg0, 255); 138 std::strncpy(p_name, arg0, 254);
139 p_name[254] = '\0';
134 140
135 const bool process_created = CreateProcessA(nullptr, // lpApplicationName 141 const bool process_created = CreateProcessA(nullptr, // lpApplicationName
136 p_name, // lpCommandLine 142 p_name, // lpCommandLine
@@ -144,7 +150,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
144 pi // lpProcessInformation 150 pi // lpProcessInformation
145 ); 151 );
146 if (!process_created) { 152 if (!process_created) {
147 std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError()); 153 std::fprintf(stderr, "CreateProcessA failed with error %lu\n", GetLastError());
148 return false; 154 return false;
149 } 155 }
150 156
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index f2fc2d9d4..d8e563be6 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -13,7 +13,7 @@ constexpr char ENV_VAR_ENABLED_TEXT[] = "ON";
13 13
14void CheckVulkan(); 14void CheckVulkan();
15bool CheckEnvVars(bool* is_child); 15bool CheckEnvVars(bool* is_child);
16bool StartupChecks(const char* arg0, bool* has_broken_vulkan); 16bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check);
17 17
18#ifdef _WIN32 18#ifdef _WIN32
19bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags); 19bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags);
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index e12d414d9..4f5b2a99d 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -102,7 +102,7 @@ struct Values {
102 Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; 102 Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"};
103 103
104 // multiplayer settings 104 // multiplayer settings
105 Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; 105 Settings::Setting<QString> multiplayer_nickname{{}, "nickname"};
106 Settings::Setting<QString> multiplayer_ip{{}, "ip"}; 106 Settings::Setting<QString> multiplayer_ip{{}, "ip"};
107 Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; 107 Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"};
108 Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; 108 Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"};
@@ -129,6 +129,9 @@ struct Values {
129 Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; 129 Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"};
130 QVector<u64> favorited_ids; 130 QVector<u64> favorited_ids;
131 131
132 // Compatibility List
133 Settings::Setting<bool> show_compat{false, "show_compat"};
134
132 bool configuration_applied; 135 bool configuration_applied;
133 bool reset_to_defaults; 136 bool reset_to_defaults;
134 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; 137 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3a0f33cba..e16f79eb4 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -302,6 +302,8 @@ int main(int argc, char** argv) {
302 } 302 }
303 303
304 Core::System system{}; 304 Core::System system{};
305 system.Initialize();
306
305 InputCommon::InputSubsystem input_subsystem{}; 307 InputCommon::InputSubsystem input_subsystem{};
306 308
307 // Apply the command line arguments 309 // Apply the command line arguments
@@ -392,7 +394,7 @@ int main(int argc, char** argv) {
392 } 394 }
393 system.DetachDebugger(); 395 system.DetachDebugger();
394 void(system.Pause()); 396 void(system.Pause());
395 system.Shutdown(); 397 system.ShutdownMainProcess();
396 398
397 detached_tasks.WaitForAllTasks(); 399 detached_tasks.WaitForAllTasks();
398 return 0; 400 return 0;