summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/audio_core/CMakeLists.txt19
-rw-r--r--src/audio_core/renderer/command/resample/upsample.cpp97
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/address_space.h7
-rw-r--r--src/common/alignment.h18
-rw-r--r--src/common/atomic_helpers.h2
-rw-r--r--src/common/bit_util.h6
-rw-r--r--src/common/concepts.h6
-rw-r--r--src/common/demangle.cpp35
-rw-r--r--src/common/demangle.h12
-rw-r--r--src/common/div_ceil.h4
-rw-r--r--src/common/expected.h60
-rw-r--r--src/common/host_memory.cpp19
-rw-r--r--src/common/input.h71
-rw-r--r--src/common/intrusive_red_black_tree.h20
-rw-r--r--src/common/make_unique_for_overwrite.h8
-rw-r--r--src/common/polyfill_ranges.h8
-rw-r--r--src/common/polyfill_thread.h123
-rw-r--r--src/common/range_map.h139
-rw-r--r--src/common/settings.cpp14
-rw-r--r--src/common/settings.h30
-rw-r--r--src/common/tree.h74
-rw-r--r--src/common/vector_math.h16
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/arm/arm_interface.cpp22
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp4
-rw-r--r--src/core/core_timing.cpp42
-rw-r--r--src/core/core_timing.h9
-rw-r--r--src/core/debugger/gdbstub.cpp26
-rw-r--r--src/core/file_sys/vfs.cpp6
-rw-r--r--src/core/hid/emulated_console.cpp5
-rw-r--r--src/core/hid/emulated_controller.cpp333
-rw-r--r--src/core/hid/emulated_controller.h64
-rw-r--r--src/core/hid/emulated_devices.cpp46
-rw-r--r--src/core/hid/emulated_devices.h18
-rw-r--r--src/core/hid/input_converter.cpp12
-rw-r--r--src/core/hid/input_converter.h10
-rw-r--r--src/core/hle/kernel/k_auto_object.h20
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp35
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp6
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp8
-rw-r--r--src/core/hle/kernel/k_page_group.cpp121
-rw-r--r--src/core/hle/kernel/k_page_group.h163
-rw-r--r--src/core/hle/kernel/k_page_table.cpp640
-rw-r--r--src/core/hle/kernel/k_page_table.h93
-rw-r--r--src/core/hle/kernel/k_priority_queue.h54
-rw-r--r--src/core/hle/kernel/k_process.cpp40
-rw-r--r--src/core/hle/kernel/k_scoped_lock.h11
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp25
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h32
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp39
-rw-r--r--src/core/hle/kernel/memory_types.h3
-rw-r--r--src/core/hle/kernel/svc.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp18
-rw-r--r--src/core/hle/service/hid/hidbus.cpp24
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp8
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h4
-rw-r--r--src/core/hle/service/hid/irs.cpp18
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp7
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp7
-rw-r--r--src/core/hle/service/nifm/nifm.cpp88
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp8
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h2
-rw-r--r--src/core/internal_network/network.cpp6
-rw-r--r--src/core/memory.cpp4
-rw-r--r--src/dedicated_room/CMakeLists.txt2
-rw-r--r--src/input_common/CMakeLists.txt36
-rw-r--r--src/input_common/drivers/camera.cpp4
-rw-r--r--src/input_common/drivers/camera.h4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp10
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp677
-rw-r--r--src/input_common/drivers/joycon.h111
-rw-r--r--src/input_common/drivers/sdl_driver.cpp77
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/tas_input.cpp12
-rw-r--r--src/input_common/drivers/tas_input.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp11
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp575
-rw-r--r--src/input_common/helpers/joycon_driver.h150
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp218
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h82
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp316
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h201
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp136
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h114
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.h63
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h697
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp406
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h61
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp337
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h81
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp115
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.h38
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.h33
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp26
-rw-r--r--src/input_common/input_engine.cpp37
-rw-r--r--src/input_common/input_engine.h25
-rw-r--r--src/input_common/input_mapping.cpp2
-rw-r--r--src/input_common/input_poller.cpp78
-rw-r--r--src/input_common/input_poller.h11
-rw-r--r--src/input_common/main.cpp64
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp18
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp13
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp18
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp37
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp49
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp20
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp10
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp58
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp63
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h2
-rw-r--r--src/shader_recompiler/environment.h10
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.cpp6
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.h5
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp22
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h10
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc7
-rw-r--r--src/shader_recompiler/frontend/ir/type.h31
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp3
-rw-r--r--src/shader_recompiler/frontend/ir/value.h14
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp8
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp118
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp76
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp13
-rw-r--r--src/shader_recompiler/ir_opt/passes.h4
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp44
-rw-r--r--src/shader_recompiler/object_pool.h4
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h19
-rw-r--r--src/shader_recompiler/varying_state.h2
-rw-r--r--src/tests/CMakeLists.txt4
-rw-r--r--src/tests/common/bit_field.cpp2
-rw-r--r--src/tests/common/cityhash.cpp2
-rw-r--r--src/tests/common/fibers.cpp2
-rw-r--r--src/tests/common/host_memory.cpp2
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/tests/common/range_map.cpp70
-rw-r--r--src/tests/common/ring_buffer.cpp2
-rw-r--r--src/tests/common/scratch_buffer.cpp3
-rw-r--r--src/tests/common/unique_function.cpp2
-rw-r--r--src/tests/core/core_timing.cpp2
-rw-r--r--src/tests/core/internal_network/network.cpp2
-rw-r--r--src/tests/input_common/calibration_configuration_job.cpp2
-rw-r--r--src/tests/tests.cpp8
-rw-r--r--src/tests/video_core/buffer_base.cpp4
-rw-r--r--src/video_core/CMakeLists.txt10
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h96
-rw-r--r--src/video_core/cache_types.h24
-rw-r--r--src/video_core/dma_pusher.cpp42
-rw-r--r--src/video_core/dma_pusher.h2
-rw-r--r--src/video_core/engines/draw_manager.cpp62
-rw-r--r--src/video_core/engines/draw_manager.h45
-rw-r--r--src/video_core/engines/engine_interface.h24
-rw-r--r--src/video_core/engines/engine_upload.cpp2
-rw-r--r--src/video_core/engines/fermi_2d.cpp16
-rw-r--r--src/video_core/engines/fermi_2d.h3
-rw-r--r--src/video_core/engines/kepler_compute.cpp14
-rw-r--r--src/video_core/engines/kepler_compute.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp183
-rw-r--r--src/video_core/engines/maxwell_3d.h91
-rw-r--r--src/video_core/engines/maxwell_dma.cpp33
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/fsr.cpp148
-rw-r--r--src/video_core/fsr.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt35
-rw-r--r--src/video_core/host_shaders/blit_color_float.frag (renamed from src/video_core/host_shaders/vulkan_blit_color_float.frag)0
-rw-r--r--src/video_core/host_shaders/full_screen_triangle.vert13
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag108
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag9
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag9
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.vert10
-rw-r--r--src/video_core/host_shaders/vulkan_quad_indexed.comp6
-rw-r--r--src/video_core/host_shaders/vulkan_turbo_mode.comp29
-rw-r--r--src/video_core/invalidation_accumulator.h79
-rw-r--r--src/video_core/macro/macro.cpp19
-rw-r--r--src/video_core/macro/macro.h1
-rw-r--r--src/video_core/macro/macro_hle.cpp606
-rw-r--r--src/video_core/macro/macro_hle.h5
-rw-r--r--src/video_core/memory_manager.cpp180
-rw-r--r--src/video_core/memory_manager.h54
-rw-r--r--src/video_core/rasterizer_interface.h30
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp9
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h13
-rw-r--r--src/video_core/renderer_opengl/blit_image.cpp59
-rw-r--r--src/video_core/renderer_opengl/blit_image.h38
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp232
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h21
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp121
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h129
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp98
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp212
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h16
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp119
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h76
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp23
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp29
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp318
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp42
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp144
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp127
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp161
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp493
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h26
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp56
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp102
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h119
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.cpp222
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.h35
-rw-r--r--src/video_core/shader_environment.cpp58
-rw-r--r--src/video_core/shader_environment.h21
-rw-r--r--src/video_core/texture_cache/descriptor_table.h4
-rw-r--r--src/video_core/texture_cache/image_info.cpp4
-rw-r--r--src/video_core/texture_cache/samples_helper.h44
-rw-r--r--src/video_core/texture_cache/slot_vector.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h145
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h17
-rw-r--r--src/video_core/texture_cache/util.cpp20
-rw-r--r--src/video_core/texture_cache/util.h5
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1284
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h441
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.h5
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp47
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h102
-rw-r--r--src/yuzu/CMakeLists.txt1
-rw-r--r--src/yuzu/Info.plist2
-rw-r--r--src/yuzu/bootmanager.cpp19
-rw-r--r--src/yuzu/bootmanager.h4
-rw-r--r--src/yuzu/configuration/config.cpp61
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp10
-rw-r--r--src/yuzu/configuration/configuration_shared.h3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui15
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui17
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp22
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui20
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp38
-rw-r--r--src/yuzu/configuration/configure_input_player.h6
-rw-r--r--src/yuzu/configuration/configure_input_player.ui19
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp10
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp1
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp105
-rw-r--r--src/yuzu/configuration/configure_ringcon.h14
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui396
-rw-r--r--src/yuzu/configuration/configure_system.cpp42
-rw-r--r--src/yuzu/configuration/configure_system.h6
-rw-r--r--src/yuzu/configuration/configure_system.ui12
-rw-r--r--src/yuzu/configuration/configure_tas.cpp1
-rw-r--r--src/yuzu/configuration/input_profiles.cpp7
-rw-r--r--src/yuzu/debugger/controller.cpp15
-rw-r--r--src/yuzu/debugger/profiler.cpp5
-rw-r--r--src/yuzu/hotkeys.cpp6
-rw-r--r--src/yuzu/hotkeys.h1
-rw-r--r--src/yuzu/install_dialog.cpp1
-rw-r--r--src/yuzu/main.cpp74
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp21
-rw-r--r--src/yuzu/multiplayer/direct_connect.ui23
-rw-r--r--src/yuzu/multiplayer/validation.h25
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp2
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h10
318 files changed, 14038 insertions, 3793 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 140415474..c7283e82c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -161,7 +161,10 @@ add_subdirectory(video_core)
161add_subdirectory(network) 161add_subdirectory(network)
162add_subdirectory(input_common) 162add_subdirectory(input_common)
163add_subdirectory(shader_recompiler) 163add_subdirectory(shader_recompiler)
164add_subdirectory(dedicated_room) 164
165if (YUZU_ROOM)
166 add_subdirectory(dedicated_room)
167endif()
165 168
166if (YUZU_TESTS) 169if (YUZU_TESTS)
167 add_subdirectory(tests) 170 add_subdirectory(tests)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 420ba62e0..e7b595459 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -187,11 +187,7 @@ add_library(audio_core STATIC
187 renderer/voice/voice_info.cpp 187 renderer/voice/voice_info.cpp
188 renderer/voice/voice_info.h 188 renderer/voice/voice_info.h
189 renderer/voice/voice_state.h 189 renderer/voice/voice_state.h
190 sink/cubeb_sink.cpp
191 sink/cubeb_sink.h
192 sink/null_sink.h 190 sink/null_sink.h
193 sink/sdl2_sink.cpp
194 sink/sdl2_sink.h
195 sink/sink.h 191 sink/sink.h
196 sink/sink_details.cpp 192 sink/sink_details.cpp
197 sink/sink_details.h 193 sink/sink_details.h
@@ -222,11 +218,22 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
222 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) 218 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
223endif() 219endif()
224 220
225if(ENABLE_CUBEB) 221if (ENABLE_CUBEB)
222 target_sources(audio_core PRIVATE
223 sink/cubeb_sink.cpp
224 sink/cubeb_sink.h
225 )
226
226 target_link_libraries(audio_core PRIVATE cubeb::cubeb) 227 target_link_libraries(audio_core PRIVATE cubeb::cubeb)
227 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) 228 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
228endif() 229endif()
229if(ENABLE_SDL2) 230
231if (ENABLE_SDL2)
232 target_sources(audio_core PRIVATE
233 sink/sdl2_sink.cpp
234 sink/sdl2_sink.h
235 )
236
230 target_link_libraries(audio_core PRIVATE SDL2::SDL2) 237 target_link_libraries(audio_core PRIVATE SDL2::SDL2)
231 target_compile_definitions(audio_core PRIVATE HAVE_SDL2) 238 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
232endif() 239endif()
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp
index 6c3ff31f7..5f7db12ca 100644
--- a/src/audio_core/renderer/command/resample/upsample.cpp
+++ b/src/audio_core/renderer/command/resample/upsample.cpp
@@ -20,25 +20,25 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
20 const u32 target_sample_count, const u32 source_sample_count, 20 const u32 target_sample_count, const u32 source_sample_count,
21 UpsamplerState* state) { 21 UpsamplerState* state) {
22 constexpr u32 WindowSize = 10; 22 constexpr u32 WindowSize = 10;
23 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow1{ 23 constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc1{
24 51.93359375f, -18.80078125f, 9.73046875f, -5.33203125f, 2.84375f, 24 0.95376587f, -0.12872314f, 0.060028076f, -0.032470703f, 0.017669678f,
25 -1.41015625f, 0.62109375f, -0.2265625f, 0.0625f, -0.00390625f, 25 -0.009124756f, 0.004272461f, -0.001739502f, 0.000579834f, -0.000091552734f,
26 }; 26 };
27 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow2{ 27 constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc2{
28 105.35546875f, -24.52734375f, 11.9609375f, -6.515625f, 3.52734375f, 28 0.8230896f, -0.19161987f, 0.093444824f, -0.05090332f, 0.027557373f,
29 -1.796875f, 0.828125f, -0.32421875f, 0.1015625f, -0.015625f, 29 -0.014038086f, 0.0064697266f, -0.002532959f, 0.00079345703f, -0.00012207031f,
30 }; 30 };
31 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow3{ 31 constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc3{
32 122.08203125f, -16.47656250f, 7.68359375f, -4.15625000f, 2.26171875f, 32 0.6298828f, -0.19274902f, 0.09725952f, -0.05319214f, 0.028625488f,
33 -1.16796875f, 0.54687500f, -0.22265625f, 0.07421875f, -0.01171875f, 33 -0.014373779f, 0.006500244f, -0.0024719238f, 0.0007324219f, -0.000091552734f,
34 }; 34 };
35 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow4{ 35 constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc4{
36 23.73437500f, -9.62109375f, 5.07812500f, -2.78125000f, 1.46875000f, 36 0.4057312f, -0.1468811f, 0.07601929f, -0.041656494f, 0.022216797f,
37 -0.71484375f, 0.30859375f, -0.10546875f, 0.02734375f, 0.00000000f, 37 -0.011016846f, 0.004852295f, -0.0017700195f, 0.00048828125f, -0.000030517578f,
38 }; 38 };
39 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow5{ 39 constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc5{
40 80.62500000f, -24.67187500f, 12.44921875f, -6.80859375f, 3.66406250f, 40 0.1854248f, -0.075164795f, 0.03967285f, -0.021728516f, 0.011474609f,
41 -1.83984375f, 0.83203125f, -0.31640625f, 0.09375000f, -0.01171875f, 41 -0.005584717f, 0.0024108887f, -0.0008239746f, 0.00021362305f, 0.0f,
42 }; 42 };
43 43
44 if (!state->initialized) { 44 if (!state->initialized) {
@@ -91,52 +91,31 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
91 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); 91 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
92 }; 92 };
93 93
94 auto calculate_sample = [&state](std::span<const Common::FixedPoint<24, 8>> coeffs1, 94 auto calculate_sample = [&state](std::span<const Common::FixedPoint<17, 15>> coeffs1,
95 std::span<const Common::FixedPoint<24, 8>> coeffs2) -> s32 { 95 std::span<const Common::FixedPoint<17, 15>> coeffs2) -> s32 {
96 auto output_index{state->history_output_index}; 96 auto output_index{state->history_output_index};
97 auto start_pos{output_index - state->history_start_index + 1U}; 97 u64 result{0};
98 auto end_pos{10U};
99 98
100 if (start_pos < 10) { 99 for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) {
101 end_pos = start_pos; 100 result += static_cast<u64>(state->history[output_index].to_raw()) *
102 } 101 coeffs1[coeff_index].to_raw();
103
104 u64 prev_contrib{0};
105 u32 coeff_index{0};
106 for (; coeff_index < end_pos; coeff_index++, output_index--) {
107 prev_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
108 coeffs1[coeff_index].to_raw();
109 }
110 102
111 auto end_index{state->history_end_index}; 103 output_index = output_index == state->history_start_index ? state->history_end_index
112 for (; start_pos < 9; start_pos++, coeff_index++, end_index--) { 104 : output_index - 1;
113 prev_contrib += static_cast<u64>(state->history[end_index].to_raw()) *
114 coeffs1[coeff_index].to_raw();
115 } 105 }
116 106
117 output_index = 107 output_index =
118 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize); 108 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
119 start_pos = state->history_end_index - output_index + 1U;
120 end_pos = 10U;
121 109
122 if (start_pos < 10) { 110 for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) {
123 end_pos = start_pos; 111 result += static_cast<u64>(state->history[output_index].to_raw()) *
124 } 112 coeffs2[coeff_index].to_raw();
125
126 u64 next_contrib{0};
127 coeff_index = 0;
128 for (; coeff_index < end_pos; coeff_index++, output_index++) {
129 next_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
130 coeffs2[coeff_index].to_raw();
131 }
132 113
133 auto start_index{state->history_start_index}; 114 output_index = output_index == state->history_end_index ? state->history_start_index
134 for (; start_pos < 9; start_pos++, start_index++, coeff_index++) { 115 : output_index + 1;
135 next_contrib += static_cast<u64>(state->history[start_index].to_raw()) *
136 coeffs2[coeff_index].to_raw();
137 } 116 }
138 117
139 return static_cast<s32>(((prev_contrib >> 15) + (next_contrib >> 15)) >> 8); 118 return static_cast<s32>(result >> (8 + 15));
140 }; 119 };
141 120
142 switch (state->ratio.to_int_floor()) { 121 switch (state->ratio.to_int_floor()) {
@@ -150,23 +129,23 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
150 break; 129 break;
151 130
152 case 1: 131 case 1:
153 output[write_index] = calculate_sample(SincWindow3, SincWindow4); 132 output[write_index] = calculate_sample(WindowedSinc1, WindowedSinc5);
154 break; 133 break;
155 134
156 case 2: 135 case 2:
157 output[write_index] = calculate_sample(SincWindow2, SincWindow1); 136 output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
158 break; 137 break;
159 138
160 case 3: 139 case 3:
161 output[write_index] = calculate_sample(SincWindow5, SincWindow5); 140 output[write_index] = calculate_sample(WindowedSinc3, WindowedSinc3);
162 break; 141 break;
163 142
164 case 4: 143 case 4:
165 output[write_index] = calculate_sample(SincWindow1, SincWindow2); 144 output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
166 break; 145 break;
167 146
168 case 5: 147 case 5:
169 output[write_index] = calculate_sample(SincWindow4, SincWindow3); 148 output[write_index] = calculate_sample(WindowedSinc5, WindowedSinc1);
170 break; 149 break;
171 } 150 }
172 state->sample_index = static_cast<u8>((state->sample_index + 1) % 6); 151 state->sample_index = static_cast<u8>((state->sample_index + 1) % 6);
@@ -183,11 +162,11 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
183 break; 162 break;
184 163
185 case 1: 164 case 1:
186 output[write_index] = calculate_sample(SincWindow2, SincWindow1); 165 output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
187 break; 166 break;
188 167
189 case 2: 168 case 2:
190 output[write_index] = calculate_sample(SincWindow1, SincWindow2); 169 output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
191 break; 170 break;
192 } 171 }
193 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); 172 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
@@ -204,12 +183,12 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
204 break; 183 break;
205 184
206 case 1: 185 case 1:
207 output[write_index] = calculate_sample(SincWindow1, SincWindow2); 186 output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
208 break; 187 break;
209 188
210 case 2: 189 case 2:
211 increment(); 190 increment();
212 output[write_index] = calculate_sample(SincWindow2, SincWindow1); 191 output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
213 break; 192 break;
214 } 193 }
215 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3); 194 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eb05e46a8..9884a4a0b 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -38,6 +38,8 @@ add_library(common STATIC
38 common_precompiled_headers.h 38 common_precompiled_headers.h
39 common_types.h 39 common_types.h
40 concepts.h 40 concepts.h
41 demangle.cpp
42 demangle.h
41 div_ceil.h 43 div_ceil.h
42 dynamic_library.cpp 44 dynamic_library.cpp
43 dynamic_library.h 45 dynamic_library.h
@@ -97,6 +99,7 @@ add_library(common STATIC
97 point.h 99 point.h
98 precompiled_headers.h 100 precompiled_headers.h
99 quaternion.h 101 quaternion.h
102 range_map.h
100 reader_writer_queue.h 103 reader_writer_queue.h
101 ring_buffer.h 104 ring_buffer.h
102 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 105 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@@ -174,7 +177,7 @@ endif()
174create_target_directory_groups(common) 177create_target_directory_groups(common)
175 178
176target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 179target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
177target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) 180target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
178 181
179if (YUZU_USE_PRECOMPILED_HEADERS) 182if (YUZU_USE_PRECOMPILED_HEADERS)
180 target_precompile_headers(common PRIVATE precompiled_headers.h) 183 target_precompile_headers(common PRIVATE precompiled_headers.h)
diff --git a/src/common/address_space.h b/src/common/address_space.h
index 9222b2fdc..8683c23c3 100644
--- a/src/common/address_space.h
+++ b/src/common/address_space.h
@@ -12,7 +12,8 @@
12 12
13namespace Common { 13namespace Common {
14template <typename VaType, size_t AddressSpaceBits> 14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; 15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >=
16AddressSpaceBits;
16 17
17struct EmptyStruct {}; 18struct EmptyStruct {};
18 19
@@ -21,7 +22,7 @@ struct EmptyStruct {};
21 */ 22 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, 23template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
23 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> 24 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
24requires AddressSpaceValid<VaType, AddressSpaceBits> 25 requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap { 26class FlatAddressSpaceMap {
26public: 27public:
27 /// The maximum VA that this AS can technically reach 28 /// The maximum VA that this AS can technically reach
@@ -109,7 +110,7 @@ private:
109 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block 110 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
110 */ 111 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> 112template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits> 113 requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator 114class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { 115 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private: 116private:
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 7e897334b..fa715d497 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -10,7 +10,7 @@
10namespace Common { 10namespace Common {
11 11
12template <typename T> 12template <typename T>
13requires std::is_unsigned_v<T> 13 requires std::is_unsigned_v<T>
14[[nodiscard]] constexpr T AlignUp(T value, size_t size) { 14[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
15 auto mod{static_cast<T>(value % size)}; 15 auto mod{static_cast<T>(value % size)};
16 value -= mod; 16 value -= mod;
@@ -18,31 +18,31 @@ requires std::is_unsigned_v<T>
18} 18}
19 19
20template <typename T> 20template <typename T>
21requires std::is_unsigned_v<T> 21 requires std::is_unsigned_v<T>
22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { 22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); 23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
24} 24}
25 25
26template <typename T> 26template <typename T>
27requires std::is_unsigned_v<T> 27 requires std::is_unsigned_v<T>
28[[nodiscard]] constexpr T AlignDown(T value, size_t size) { 28[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
29 return static_cast<T>(value - value % size); 29 return static_cast<T>(value - value % size);
30} 30}
31 31
32template <typename T> 32template <typename T>
33requires std::is_unsigned_v<T> 33 requires std::is_unsigned_v<T>
34[[nodiscard]] constexpr bool Is4KBAligned(T value) { 34[[nodiscard]] constexpr bool Is4KBAligned(T value) {
35 return (value & 0xFFF) == 0; 35 return (value & 0xFFF) == 0;
36} 36}
37 37
38template <typename T> 38template <typename T>
39requires std::is_unsigned_v<T> 39 requires std::is_unsigned_v<T>
40[[nodiscard]] constexpr bool IsWordAligned(T value) { 40[[nodiscard]] constexpr bool IsWordAligned(T value) {
41 return (value & 0b11) == 0; 41 return (value & 0b11) == 0;
42} 42}
43 43
44template <typename T> 44template <typename T>
45requires std::is_integral_v<T> 45 requires std::is_integral_v<T>
46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { 46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
47 using U = typename std::make_unsigned_t<T>; 47 using U = typename std::make_unsigned_t<T>;
48 const U mask = static_cast<U>(alignment - 1); 48 const U mask = static_cast<U>(alignment - 1);
@@ -50,7 +50,7 @@ requires std::is_integral_v<T>
50} 50}
51 51
52template <typename T, typename U> 52template <typename T, typename U>
53requires std::is_integral_v<T> 53 requires std::is_integral_v<T>
54[[nodiscard]] constexpr T DivideUp(T x, U y) { 54[[nodiscard]] constexpr T DivideUp(T x, U y) {
55 return (x + (y - 1)) / y; 55 return (x + (y - 1)) / y;
56} 56}
@@ -73,11 +73,11 @@ public:
73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
74 74
75 [[nodiscard]] T* allocate(size_type n) { 75 [[nodiscard]] T* allocate(size_type n) {
76 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); 76 return static_cast<T*>(::operator new(n * sizeof(T), std::align_val_t{Align}));
77 } 77 }
78 78
79 void deallocate(T* p, size_type n) { 79 void deallocate(T* p, size_type n) {
80 ::operator delete (p, n * sizeof(T), std::align_val_t{Align}); 80 ::operator delete(p, n * sizeof(T), std::align_val_t{Align});
81 } 81 }
82 82
83 template <typename T2> 83 template <typename T2>
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index aef3b66a4..d997f10ba 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -75,7 +75,7 @@ extern "C" void AnnotateHappensAfter(const char*, int, void*);
75#if defined(AE_VCPP) || defined(AE_ICC) 75#if defined(AE_VCPP) || defined(AE_ICC)
76#define AE_FORCEINLINE __forceinline 76#define AE_FORCEINLINE __forceinline
77#elif defined(AE_GCC) 77#elif defined(AE_GCC)
78//#define AE_FORCEINLINE __attribute__((always_inline)) 78// #define AE_FORCEINLINE __attribute__((always_inline))
79#define AE_FORCEINLINE inline 79#define AE_FORCEINLINE inline
80#else 80#else
81#define AE_FORCEINLINE inline 81#define AE_FORCEINLINE inline
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index e4e6287f3..13368b439 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -45,19 +45,19 @@ template <typename T>
45} 45}
46 46
47template <typename T> 47template <typename T>
48requires std::is_unsigned_v<T> 48 requires std::is_unsigned_v<T>
49[[nodiscard]] constexpr bool IsPow2(T value) { 49[[nodiscard]] constexpr bool IsPow2(T value) {
50 return std::has_single_bit(value); 50 return std::has_single_bit(value);
51} 51}
52 52
53template <typename T> 53template <typename T>
54requires std::is_integral_v<T> 54 requires std::is_integral_v<T>
55[[nodiscard]] T NextPow2(T value) { 55[[nodiscard]] T NextPow2(T value) {
56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
57} 57}
58 58
59template <size_t bit_index, typename T> 59template <size_t bit_index, typename T>
60requires std::is_integral_v<T> 60 requires std::is_integral_v<T>
61[[nodiscard]] constexpr bool Bit(const T value) { 61[[nodiscard]] constexpr bool Bit(const T value) {
62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); 62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
63 return ((value >> bit_index) & T(1)) == T(1); 63 return ((value >> bit_index) & T(1)) == T(1);
diff --git a/src/common/concepts.h b/src/common/concepts.h
index a9acff3e7..61df1d32a 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -16,9 +16,9 @@ concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
16// is available on all supported platforms. 16// is available on all supported platforms.
17template <typename Derived, typename Base> 17template <typename Derived, typename Base>
18concept DerivedFrom = requires { 18concept DerivedFrom = requires {
19 std::is_base_of_v<Base, Derived>; 19 std::is_base_of_v<Base, Derived>;
20 std::is_convertible_v<const volatile Derived*, const volatile Base*>; 20 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
21}; 21 };
22 22
23// TODO: Replace with std::convertible_to when libc++ implements it. 23// TODO: Replace with std::convertible_to when libc++ implements it.
24template <typename From, typename To> 24template <typename From, typename To>
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
new file mode 100644
index 000000000..3310faf86
--- /dev/null
+++ b/src/common/demangle.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <llvm/Demangle/Demangle.h>
5
6#include "common/demangle.h"
7#include "common/scope_exit.h"
8
9namespace Common {
10
11std::string DemangleSymbol(const std::string& mangled) {
12 auto is_itanium = [](const std::string& name) -> bool {
13 // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
14 auto pos = name.find_first_not_of('_');
15 return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
16 };
17
18 if (mangled.empty()) {
19 return mangled;
20 }
21
22 char* demangled = nullptr;
23 SCOPE_EXIT({ std::free(demangled); });
24
25 if (is_itanium(mangled)) {
26 demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr);
27 }
28
29 if (!demangled) {
30 return mangled;
31 }
32 return demangled;
33}
34
35} // namespace Common
diff --git a/src/common/demangle.h b/src/common/demangle.h
new file mode 100644
index 000000000..f072d22f3
--- /dev/null
+++ b/src/common/demangle.h
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8namespace Common {
9
10std::string DemangleSymbol(const std::string& mangled);
11
12} // namespace Common
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index eebc279c2..c12477d42 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -10,14 +10,14 @@ namespace Common {
10 10
11/// Ceiled integer division. 11/// Ceiled integer division.
12template <typename N, typename D> 12template <typename N, typename D>
13requires std::is_integral_v<N> && std::is_unsigned_v<D> 13 requires std::is_integral_v<N> && std::is_unsigned_v<D>
14[[nodiscard]] constexpr N DivCeil(N number, D divisor) { 14[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); 15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
16} 16}
17 17
18/// Ceiled integer division with logarithmic divisor in base 2 18/// Ceiled integer division with logarithmic divisor in base 2
19template <typename N, typename D> 19template <typename N, typename D>
20requires std::is_integral_v<N> && std::is_unsigned_v<D> 20 requires std::is_integral_v<N> && std::is_unsigned_v<D>
21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { 21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); 22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
23} 23}
diff --git a/src/common/expected.h b/src/common/expected.h
index 6e6c86ee7..5fccfbcbd 100644
--- a/src/common/expected.h
+++ b/src/common/expected.h
@@ -64,7 +64,7 @@ struct no_init_t {
64 * Additionally, this requires E to be trivially destructible 64 * Additionally, this requires E to be trivially destructible
65 */ 65 */
66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> 66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
67requires std::is_trivially_destructible_v<E> 67 requires std::is_trivially_destructible_v<E>
68struct expected_storage_base { 68struct expected_storage_base {
69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
70 70
@@ -111,7 +111,7 @@ struct expected_storage_base {
111 * Additionally, this requires E to be trivially destructible 111 * Additionally, this requires E to be trivially destructible
112 */ 112 */
113template <typename T, typename E> 113template <typename T, typename E>
114requires std::is_trivially_destructible_v<E> 114 requires std::is_trivially_destructible_v<E>
115struct expected_storage_base<T, E, true> { 115struct expected_storage_base<T, E, true> {
116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
117 117
@@ -251,7 +251,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
251 * Additionally, this requires E to be trivially copy constructible 251 * Additionally, this requires E to be trivially copy constructible
252 */ 252 */
253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> 253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
254requires std::is_trivially_copy_constructible_v<E> 254 requires std::is_trivially_copy_constructible_v<E>
255struct expected_copy_base : expected_operations_base<T, E> { 255struct expected_copy_base : expected_operations_base<T, E> {
256 using expected_operations_base<T, E>::expected_operations_base; 256 using expected_operations_base<T, E>::expected_operations_base;
257}; 257};
@@ -261,7 +261,7 @@ struct expected_copy_base : expected_operations_base<T, E> {
261 * Additionally, this requires E to be trivially copy constructible 261 * Additionally, this requires E to be trivially copy constructible
262 */ 262 */
263template <typename T, typename E> 263template <typename T, typename E>
264requires std::is_trivially_copy_constructible_v<E> 264 requires std::is_trivially_copy_constructible_v<E>
265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { 265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
266 using expected_operations_base<T, E>::expected_operations_base; 266 using expected_operations_base<T, E>::expected_operations_base;
267 267
@@ -289,7 +289,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
289 * Additionally, this requires E to be trivially move constructible 289 * Additionally, this requires E to be trivially move constructible
290 */ 290 */
291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> 291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
292requires std::is_trivially_move_constructible_v<E> 292 requires std::is_trivially_move_constructible_v<E>
293struct expected_move_base : expected_copy_base<T, E> { 293struct expected_move_base : expected_copy_base<T, E> {
294 using expected_copy_base<T, E>::expected_copy_base; 294 using expected_copy_base<T, E>::expected_copy_base;
295}; 295};
@@ -299,7 +299,7 @@ struct expected_move_base : expected_copy_base<T, E> {
299 * Additionally, this requires E to be trivially move constructible 299 * Additionally, this requires E to be trivially move constructible
300 */ 300 */
301template <typename T, typename E> 301template <typename T, typename E>
302requires std::is_trivially_move_constructible_v<E> 302 requires std::is_trivially_move_constructible_v<E>
303struct expected_move_base<T, E, false> : expected_copy_base<T, E> { 303struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
304 using expected_copy_base<T, E>::expected_copy_base; 304 using expected_copy_base<T, E>::expected_copy_base;
305 305
@@ -330,9 +330,9 @@ template <typename T, typename E,
330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, 330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
331 std::is_trivially_copy_constructible<T>, 331 std::is_trivially_copy_constructible<T>,
332 std::is_trivially_destructible<T>>> 332 std::is_trivially_destructible<T>>>
333requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 333 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
334 std::is_trivially_copy_constructible<E>, 334 std::is_trivially_copy_constructible<E>,
335 std::is_trivially_destructible<E>> 335 std::is_trivially_destructible<E>>
336struct expected_copy_assign_base : expected_move_base<T, E> { 336struct expected_copy_assign_base : expected_move_base<T, E> {
337 using expected_move_base<T, E>::expected_move_base; 337 using expected_move_base<T, E>::expected_move_base;
338}; 338};
@@ -342,9 +342,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> {
342 * Additionally, this requires E to be trivially copy assignable 342 * Additionally, this requires E to be trivially copy assignable
343 */ 343 */
344template <typename T, typename E> 344template <typename T, typename E>
345requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 345 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
346 std::is_trivially_copy_constructible<E>, 346 std::is_trivially_copy_constructible<E>,
347 std::is_trivially_destructible<E>> 347 std::is_trivially_destructible<E>>
348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { 348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
349 using expected_move_base<T, E>::expected_move_base; 349 using expected_move_base<T, E>::expected_move_base;
350 350
@@ -371,9 +371,9 @@ template <typename T, typename E,
371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>, 371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
372 std::is_trivially_move_constructible<T>, 372 std::is_trivially_move_constructible<T>,
373 std::is_trivially_destructible<T>>> 373 std::is_trivially_destructible<T>>>
374requires std::conjunction_v<std::is_trivially_move_assignable<E>, 374 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
375 std::is_trivially_move_constructible<E>, 375 std::is_trivially_move_constructible<E>,
376 std::is_trivially_destructible<E>> 376 std::is_trivially_destructible<E>>
377struct expected_move_assign_base : expected_copy_assign_base<T, E> { 377struct expected_move_assign_base : expected_copy_assign_base<T, E> {
378 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 378 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
379}; 379};
@@ -383,9 +383,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> {
383 * Additionally, this requires E to be trivially move assignable 383 * Additionally, this requires E to be trivially move assignable
384 */ 384 */
385template <typename T, typename E> 385template <typename T, typename E>
386requires std::conjunction_v<std::is_trivially_move_assignable<E>, 386 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
387 std::is_trivially_move_constructible<E>, 387 std::is_trivially_move_constructible<E>,
388 std::is_trivially_destructible<E>> 388 std::is_trivially_destructible<E>>
389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { 389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
390 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 390 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
391 391
@@ -412,7 +412,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E>
412 */ 412 */
413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, 413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
414 bool EnableMove = std::is_move_constructible_v<T>> 414 bool EnableMove = std::is_move_constructible_v<T>>
415requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 415 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
416struct expected_delete_ctor_base { 416struct expected_delete_ctor_base {
417 expected_delete_ctor_base() = default; 417 expected_delete_ctor_base() = default;
418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -422,7 +422,7 @@ struct expected_delete_ctor_base {
422}; 422};
423 423
424template <typename T, typename E> 424template <typename T, typename E>
425requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 425 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
426struct expected_delete_ctor_base<T, E, true, false> { 426struct expected_delete_ctor_base<T, E, true, false> {
427 expected_delete_ctor_base() = default; 427 expected_delete_ctor_base() = default;
428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -432,7 +432,7 @@ struct expected_delete_ctor_base<T, E, true, false> {
432}; 432};
433 433
434template <typename T, typename E> 434template <typename T, typename E>
435requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 435 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
436struct expected_delete_ctor_base<T, E, false, true> { 436struct expected_delete_ctor_base<T, E, false, true> {
437 expected_delete_ctor_base() = default; 437 expected_delete_ctor_base() = default;
438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -442,7 +442,7 @@ struct expected_delete_ctor_base<T, E, false, true> {
442}; 442};
443 443
444template <typename T, typename E> 444template <typename T, typename E>
445requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 445 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
446struct expected_delete_ctor_base<T, E, false, false> { 446struct expected_delete_ctor_base<T, E, false, false> {
447 expected_delete_ctor_base() = default; 447 expected_delete_ctor_base() = default;
448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -460,8 +460,8 @@ template <
460 typename T, typename E, 460 typename T, typename E,
461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, 461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> 462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
463requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 463 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
464 std::is_copy_assignable<E>, std::is_move_assignable<E>> 464 std::is_copy_assignable<E>, std::is_move_assignable<E>>
465struct expected_delete_assign_base { 465struct expected_delete_assign_base {
466 expected_delete_assign_base() = default; 466 expected_delete_assign_base() = default;
467 expected_delete_assign_base(const expected_delete_assign_base&) = default; 467 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -471,8 +471,8 @@ struct expected_delete_assign_base {
471}; 471};
472 472
473template <typename T, typename E> 473template <typename T, typename E>
474requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 474 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
475 std::is_copy_assignable<E>, std::is_move_assignable<E>> 475 std::is_copy_assignable<E>, std::is_move_assignable<E>>
476struct expected_delete_assign_base<T, E, true, false> { 476struct expected_delete_assign_base<T, E, true, false> {
477 expected_delete_assign_base() = default; 477 expected_delete_assign_base() = default;
478 expected_delete_assign_base(const expected_delete_assign_base&) = default; 478 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -482,8 +482,8 @@ struct expected_delete_assign_base<T, E, true, false> {
482}; 482};
483 483
484template <typename T, typename E> 484template <typename T, typename E>
485requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 485 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
486 std::is_copy_assignable<E>, std::is_move_assignable<E>> 486 std::is_copy_assignable<E>, std::is_move_assignable<E>>
487struct expected_delete_assign_base<T, E, false, true> { 487struct expected_delete_assign_base<T, E, false, true> {
488 expected_delete_assign_base() = default; 488 expected_delete_assign_base() = default;
489 expected_delete_assign_base(const expected_delete_assign_base&) = default; 489 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -493,8 +493,8 @@ struct expected_delete_assign_base<T, E, false, true> {
493}; 493};
494 494
495template <typename T, typename E> 495template <typename T, typename E>
496requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 496 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
497 std::is_copy_assignable<E>, std::is_move_assignable<E>> 497 std::is_copy_assignable<E>, std::is_move_assignable<E>>
498struct expected_delete_assign_base<T, E, false, false> { 498struct expected_delete_assign_base<T, E, false, false> {
499 expected_delete_assign_base() = default; 499 expected_delete_assign_base() = default;
500 expected_delete_assign_base(const expected_delete_assign_base&) = default; 500 expected_delete_assign_base(const expected_delete_assign_base&) = default;
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 909f6cf3f..611c7d1a3 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -393,12 +393,27 @@ public:
393 } 393 }
394 394
395 // Virtual memory initialization 395 // Virtual memory initialization
396 virtual_base = static_cast<u8*>( 396#if defined(__FreeBSD__)
397 mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); 397 virtual_base =
398 static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
399 MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0));
400 if (virtual_base == MAP_FAILED) {
401 virtual_base = static_cast<u8*>(
402 mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
403 if (virtual_base == MAP_FAILED) {
404 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
405 throw std::bad_alloc{};
406 }
407 }
408#else
409 virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
410 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
398 if (virtual_base == MAP_FAILED) { 411 if (virtual_base == MAP_FAILED) {
399 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); 412 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
400 throw std::bad_alloc{}; 413 throw std::bad_alloc{};
401 } 414 }
415 madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
416#endif
402 417
403 good = true; 418 good = true;
404 } 419 }
diff --git a/src/common/input.h b/src/common/input.h
index fc14fd7bf..d61cd7ca8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -51,6 +51,8 @@ enum class PollingMode {
51 NFC, 51 NFC,
52 // Enable infrared camera polling 52 // Enable infrared camera polling
53 IR, 53 IR,
54 // Enable ring controller polling
55 Ring,
54}; 56};
55 57
56enum class CameraFormat { 58enum class CameraFormat {
@@ -62,21 +64,22 @@ enum class CameraFormat {
62 None, 64 None,
63}; 65};
64 66
65// Vibration reply from the controller 67// Different results that can happen from a device request
66enum class VibrationError { 68enum class DriverResult {
67 None, 69 Success,
70 WrongReply,
71 Timeout,
72 UnsupportedControllerType,
73 HandleInUse,
74 ErrorReadingData,
75 ErrorWritingData,
76 NoDeviceDetected,
77 InvalidHandle,
68 NotSupported, 78 NotSupported,
69 Disabled, 79 Disabled,
70 Unknown, 80 Unknown,
71}; 81};
72 82
73// Polling mode reply from the controller
74enum class PollingError {
75 None,
76 NotSupported,
77 Unknown,
78};
79
80// Nfc reply from the controller 83// Nfc reply from the controller
81enum class NfcState { 84enum class NfcState {
82 Success, 85 Success,
@@ -90,13 +93,6 @@ enum class NfcState {
90 Unknown, 93 Unknown,
91}; 94};
92 95
93// Ir camera reply from the controller
94enum class CameraError {
95 None,
96 NotSupported,
97 Unknown,
98};
99
100// Hint for amplification curve to be used 96// Hint for amplification curve to be used
101enum class VibrationAmplificationType { 97enum class VibrationAmplificationType {
102 Linear, 98 Linear,
@@ -190,6 +186,8 @@ struct TouchStatus {
190struct BodyColorStatus { 186struct BodyColorStatus {
191 u32 body{}; 187 u32 body{};
192 u32 buttons{}; 188 u32 buttons{};
189 u32 left_grip{};
190 u32 right_grip{};
193}; 191};
194 192
195// HD rumble data 193// HD rumble data
@@ -228,17 +226,31 @@ enum class ButtonNames {
228 Engine, 226 Engine,
229 // This will display the button by value instead of the button name 227 // This will display the button by value instead of the button name
230 Value, 228 Value,
229
230 // Joycon button names
231 ButtonLeft, 231 ButtonLeft,
232 ButtonRight, 232 ButtonRight,
233 ButtonDown, 233 ButtonDown,
234 ButtonUp, 234 ButtonUp,
235 TriggerZ,
236 TriggerR,
237 TriggerL,
238 ButtonA, 235 ButtonA,
239 ButtonB, 236 ButtonB,
240 ButtonX, 237 ButtonX,
241 ButtonY, 238 ButtonY,
239 ButtonPlus,
240 ButtonMinus,
241 ButtonHome,
242 ButtonCapture,
243 ButtonStickL,
244 ButtonStickR,
245 TriggerL,
246 TriggerZL,
247 TriggerSL,
248 TriggerR,
249 TriggerZR,
250 TriggerSR,
251
252 // GC button names
253 TriggerZ,
242 ButtonStart, 254 ButtonStart,
243 255
244 // DS4 button names 256 // DS4 button names
@@ -292,9 +304,6 @@ class InputDevice {
292public: 304public:
293 virtual ~InputDevice() = default; 305 virtual ~InputDevice() = default;
294 306
295 // Request input device to update if necessary
296 virtual void SoftUpdate() {}
297
298 // Force input device to update data regardless of the current state 307 // Force input device to update data regardless of the current state
299 virtual void ForceUpdate() {} 308 virtual void ForceUpdate() {}
300 309
@@ -319,22 +328,24 @@ class OutputDevice {
319public: 328public:
320 virtual ~OutputDevice() = default; 329 virtual ~OutputDevice() = default;
321 330
322 virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} 331 virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) {
332 return DriverResult::NotSupported;
333 }
323 334
324 virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { 335 virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
325 return VibrationError::NotSupported; 336 return DriverResult::NotSupported;
326 } 337 }
327 338
328 virtual bool IsVibrationEnabled() { 339 virtual bool IsVibrationEnabled() {
329 return false; 340 return false;
330 } 341 }
331 342
332 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 343 virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
333 return PollingError::NotSupported; 344 return DriverResult::NotSupported;
334 } 345 }
335 346
336 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 347 virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
337 return CameraError::NotSupported; 348 return DriverResult::NotSupported;
338 } 349 }
339 350
340 virtual NfcState SupportsNfc() const { 351 virtual NfcState SupportsNfc() const {
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 93046615e..5f6b34e82 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -242,19 +242,21 @@ public:
242 242
243template <typename T> 243template <typename T>
244concept HasRedBlackKeyType = requires { 244concept HasRedBlackKeyType = requires {
245 { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; 245 {
246}; 246 std::is_same<typename T::RedBlackKeyType, void>::value
247 } -> std::convertible_to<bool>;
248 };
247 249
248namespace impl { 250namespace impl {
249 251
250 template <typename T, typename Default> 252template <typename T, typename Default>
251 consteval auto* GetRedBlackKeyType() { 253consteval auto* GetRedBlackKeyType() {
252 if constexpr (HasRedBlackKeyType<T>) { 254 if constexpr (HasRedBlackKeyType<T>) {
253 return static_cast<typename T::RedBlackKeyType*>(nullptr); 255 return static_cast<typename T::RedBlackKeyType*>(nullptr);
254 } else { 256 } else {
255 return static_cast<Default*>(nullptr); 257 return static_cast<Default*>(nullptr);
256 }
257 } 258 }
259}
258 260
259} // namespace impl 261} // namespace impl
260 262
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h
index c7413cf51..17f81bba4 100644
--- a/src/common/make_unique_for_overwrite.h
+++ b/src/common/make_unique_for_overwrite.h
@@ -9,17 +9,19 @@
9namespace Common { 9namespace Common {
10 10
11template <class T> 11template <class T>
12requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { 12 requires(!std::is_array_v<T>)
13std::unique_ptr<T> make_unique_for_overwrite() {
13 return std::unique_ptr<T>(new T); 14 return std::unique_ptr<T>(new T);
14} 15}
15 16
16template <class T> 17template <class T>
17requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { 18 requires std::is_unbounded_array_v<T>
19std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
18 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); 20 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
19} 21}
20 22
21template <class T, class... Args> 23template <class T, class... Args>
22requires std::is_bounded_array_v<T> 24 requires std::is_bounded_array_v<T>
23void make_unique_for_overwrite(Args&&...) = delete; 25void make_unique_for_overwrite(Args&&...) = delete;
24 26
25} // namespace Common 27} // namespace Common
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
index ca44bfaef..512dbcbcb 100644
--- a/src/common/polyfill_ranges.h
+++ b/src/common/polyfill_ranges.h
@@ -18,9 +18,9 @@ namespace ranges {
18 18
19template <typename T> 19template <typename T>
20concept range = requires(T& t) { 20concept range = requires(T& t) {
21 begin(t); 21 begin(t);
22 end(t); 22 end(t);
23}; 23 };
24 24
25template <typename T> 25template <typename T>
26concept input_range = range<T>; 26concept input_range = range<T>;
@@ -421,7 +421,7 @@ struct generate_fn {
421 } 421 }
422 422
423 template <typename R, std::copy_constructible F> 423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R> 424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { 425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); 426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 } 427 }
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index 5a8d1ce08..b5ef055db 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -11,6 +11,8 @@
11 11
12#ifdef __cpp_lib_jthread 12#ifdef __cpp_lib_jthread
13 13
14#include <chrono>
15#include <condition_variable>
14#include <stop_token> 16#include <stop_token>
15#include <thread> 17#include <thread>
16 18
@@ -21,23 +23,36 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred)); 23 cv.wait(lock, token, std::move(pred));
22} 24}
23 25
26template <typename Rep, typename Period>
27bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
28 std::condition_variable_any cv;
29 std::mutex m;
30
31 // Perform the timed wait.
32 std::unique_lock lk{m};
33 return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
34}
35
24} // namespace Common 36} // namespace Common
25 37
26#else 38#else
27 39
28#include <atomic> 40#include <atomic>
41#include <chrono>
42#include <condition_variable>
29#include <functional> 43#include <functional>
30#include <list> 44#include <map>
31#include <memory> 45#include <memory>
32#include <mutex> 46#include <mutex>
33#include <optional> 47#include <optional>
34#include <thread> 48#include <thread>
35#include <type_traits> 49#include <type_traits>
50#include <utility>
36 51
37namespace std { 52namespace std {
38namespace polyfill { 53namespace polyfill {
39 54
40using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
41 56
42class stop_state { 57class stop_state {
43public: 58public:
@@ -45,61 +60,69 @@ public:
45 ~stop_state() = default; 60 ~stop_state() = default;
46 61
47 bool request_stop() { 62 bool request_stop() {
48 stop_state_callbacks callbacks; 63 unique_lock lk{m_lock};
49 64
50 { 65 if (m_stop_requested) {
51 scoped_lock lk{m_lock}; 66 // Already set, nothing to do.
67 return false;
68 }
52 69
53 if (m_stop_requested.load()) { 70 // Mark stop requested.
54 // Already set, nothing to do 71 m_stop_requested = true;
55 return false;
56 }
57 72
58 // Set as requested 73 while (!m_callbacks.empty()) {
59 m_stop_requested = true; 74 // Get an iterator to the first element.
75 const auto it = m_callbacks.begin();
60 76
61 // Copy callback list 77 // Move the callback function out of the map.
62 callbacks = m_callbacks; 78 function<void()> f;
63 } 79 swap(it->second, f);
80
81 // Erase the now-empty map element.
82 m_callbacks.erase(it);
64 83
65 for (auto callback : callbacks) { 84 // Run the callback.
66 callback(); 85 if (f) {
86 f();
87 }
67 } 88 }
68 89
69 return true; 90 return true;
70 } 91 }
71 92
72 bool stop_requested() const { 93 bool stop_requested() const {
73 return m_stop_requested.load(); 94 unique_lock lk{m_lock};
95 return m_stop_requested;
74 } 96 }
75 97
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) { 98 stop_state_callback insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{}; 99 unique_lock lk{m_lock};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86 100
87 if (should_run) { 101 if (m_stop_requested) {
88 f(); 102 // Stop already requested. Don't insert anything,
103 // just run the callback synchronously.
104 if (f) {
105 f();
106 }
107 return 0;
89 } 108 }
90 109
110 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f));
91 return ret; 113 return ret;
92 } 114 }
93 115
94 void remove_callback(stop_state_callbacks::const_iterator it) { 116 void remove_callback(stop_state_callback cb) {
95 scoped_lock lk{m_lock}; 117 unique_lock lk{m_lock};
96 m_callbacks.erase(it); 118 m_callbacks.erase(cb);
97 } 119 }
98 120
99private: 121private:
100 mutex m_lock; 122 mutable recursive_mutex m_lock;
101 atomic<bool> m_stop_requested; 123 map<stop_state_callback, function<void()>> m_callbacks;
102 stop_state_callbacks m_callbacks; 124 stop_state_callback m_next_callback{0};
125 bool m_stop_requested{false};
103}; 126};
104 127
105} // namespace polyfill 128} // namespace polyfill
@@ -190,7 +213,7 @@ public:
190 using callback_type = Callback; 213 using callback_type = Callback;
191 214
192 template <typename C> 215 template <typename C>
193 requires constructible_from<Callback, C> 216 requires constructible_from<Callback, C>
194 explicit stop_callback(const stop_token& st, 217 explicit stop_callback(const stop_token& st,
195 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 218 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
196 : m_stop_state(st.m_stop_state) { 219 : m_stop_state(st.m_stop_state) {
@@ -199,7 +222,7 @@ public:
199 } 222 }
200 } 223 }
201 template <typename C> 224 template <typename C>
202 requires constructible_from<Callback, C> 225 requires constructible_from<Callback, C>
203 explicit stop_callback(stop_token&& st, 226 explicit stop_callback(stop_token&& st,
204 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 227 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
205 : m_stop_state(move(st.m_stop_state)) { 228 : m_stop_state(move(st.m_stop_state)) {
@@ -209,7 +232,7 @@ public:
209 } 232 }
210 ~stop_callback() { 233 ~stop_callback() {
211 if (m_stop_state && m_callback) { 234 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback); 235 m_stop_state->remove_callback(m_callback);
213 } 236 }
214 } 237 }
215 238
@@ -220,7 +243,7 @@ public:
220 243
221private: 244private:
222 shared_ptr<polyfill::stop_state> m_stop_state; 245 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback; 246 polyfill::stop_state_callback m_callback;
224}; 247};
225 248
226template <typename Callback> 249template <typename Callback>
@@ -318,6 +341,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); }); 341 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319} 342}
320 343
344template <typename Rep, typename Period>
345bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
346 if (token.stop_requested()) {
347 return false;
348 }
349
350 bool stop_requested = false;
351 std::condition_variable cv;
352 std::mutex m;
353
354 std::stop_callback cb(token, [&] {
355 // Wake up the waiting thread.
356 std::unique_lock lk{m};
357 stop_requested = true;
358 cv.notify_one();
359 });
360
361 // Perform the timed wait.
362 std::unique_lock lk{m};
363 return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
364}
365
321} // namespace Common 366} // namespace Common
322 367
323#endif 368#endif
diff --git a/src/common/range_map.h b/src/common/range_map.h
new file mode 100644
index 000000000..79c7ef547
--- /dev/null
+++ b/src/common/range_map.h
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <map>
7#include <type_traits>
8
9#include "common/common_types.h"
10
11namespace Common {
12
13template <typename KeyTBase, typename ValueT>
14class RangeMap {
15private:
16 using KeyT =
17 std::conditional_t<std::is_signed_v<KeyTBase>, KeyTBase, std::make_signed_t<KeyTBase>>;
18
19public:
20 explicit RangeMap(ValueT null_value_) : null_value{null_value_} {
21 container.emplace(std::numeric_limits<KeyT>::min(), null_value);
22 };
23 ~RangeMap() = default;
24
25 void Map(KeyTBase address, KeyTBase address_end, ValueT value) {
26 KeyT new_address = static_cast<KeyT>(address);
27 KeyT new_address_end = static_cast<KeyT>(address_end);
28 if (new_address < 0) {
29 new_address = 0;
30 }
31 if (new_address_end < 0) {
32 new_address_end = 0;
33 }
34 InternalMap(new_address, new_address_end, value);
35 }
36
37 void Unmap(KeyTBase address, KeyTBase address_end) {
38 Map(address, address_end, null_value);
39 }
40
41 [[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const {
42 const KeyT new_address = static_cast<KeyT>(address);
43 if (new_address < 0) {
44 return 0;
45 }
46 return ContinousSizeInternal(new_address);
47 }
48
49 [[nodiscard]] ValueT GetValueAt(KeyT address) const {
50 const KeyT new_address = static_cast<KeyT>(address);
51 if (new_address < 0) {
52 return null_value;
53 }
54 return GetValueInternal(new_address);
55 }
56
57private:
58 using MapType = std::map<KeyT, ValueT>;
59 using IteratorType = typename MapType::iterator;
60 using ConstIteratorType = typename MapType::const_iterator;
61
62 size_t ContinousSizeInternal(KeyT address) const {
63 const auto it = GetFirstElementBeforeOrOn(address);
64 if (it == container.end() || it->second == null_value) {
65 return 0;
66 }
67 const auto it_end = std::next(it);
68 if (it_end == container.end()) {
69 return std::numeric_limits<KeyT>::max() - address;
70 }
71 return it_end->first - address;
72 }
73
74 ValueT GetValueInternal(KeyT address) const {
75 const auto it = GetFirstElementBeforeOrOn(address);
76 if (it == container.end()) {
77 return null_value;
78 }
79 return it->second;
80 }
81
82 ConstIteratorType GetFirstElementBeforeOrOn(KeyT address) const {
83 auto it = container.lower_bound(address);
84 if (it == container.begin()) {
85 return it;
86 }
87 if (it != container.end() && (it->first == address)) {
88 return it;
89 }
90 --it;
91 return it;
92 }
93
94 ValueT GetFirstValueWithin(KeyT address) {
95 auto it = container.lower_bound(address);
96 if (it == container.begin()) {
97 return it->second;
98 }
99 if (it == container.end()) [[unlikely]] { // this would be a bug
100 return null_value;
101 }
102 --it;
103 return it->second;
104 }
105
106 ValueT GetLastValueWithin(KeyT address) {
107 auto it = container.upper_bound(address);
108 if (it == container.end()) {
109 return null_value;
110 }
111 if (it == container.begin()) [[unlikely]] { // this would be a bug
112 return it->second;
113 }
114 --it;
115 return it->second;
116 }
117
118 void InternalMap(KeyT address, KeyT address_end, ValueT value) {
119 const bool must_add_start = GetFirstValueWithin(address) != value;
120 const ValueT last_value = GetLastValueWithin(address_end);
121 const bool must_add_end = last_value != value;
122 auto it = container.lower_bound(address);
123 const auto it_end = container.upper_bound(address_end);
124 while (it != it_end) {
125 it = container.erase(it);
126 }
127 if (must_add_start) {
128 container.emplace(address, value);
129 }
130 if (must_add_end) {
131 container.emplace(address_end, last_value);
132 }
133 }
134
135 ValueT null_value;
136 MapType container;
137};
138
139} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 149e621f9..b1a2aa8b2 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -129,6 +129,10 @@ void UpdateRescalingInfo() {
129 info.up_scale = 1; 129 info.up_scale = 1;
130 info.down_shift = 0; 130 info.down_shift = 0;
131 break; 131 break;
132 case ResolutionSetup::Res3_2X:
133 info.up_scale = 3;
134 info.down_shift = 1;
135 break;
132 case ResolutionSetup::Res2X: 136 case ResolutionSetup::Res2X:
133 info.up_scale = 2; 137 info.up_scale = 2;
134 info.down_shift = 0; 138 info.down_shift = 0;
@@ -149,6 +153,14 @@ void UpdateRescalingInfo() {
149 info.up_scale = 6; 153 info.up_scale = 6;
150 info.down_shift = 0; 154 info.down_shift = 0;
151 break; 155 break;
156 case ResolutionSetup::Res7X:
157 info.up_scale = 7;
158 info.down_shift = 0;
159 break;
160 case ResolutionSetup::Res8X:
161 info.up_scale = 8;
162 info.down_shift = 0;
163 break;
152 default: 164 default:
153 ASSERT(false); 165 ASSERT(false);
154 info.up_scale = 1; 166 info.up_scale = 1;
@@ -185,6 +197,7 @@ void RestoreGlobalState(bool is_powered_on) {
185 // Renderer 197 // Renderer
186 values.fsr_sharpening_slider.SetGlobal(true); 198 values.fsr_sharpening_slider.SetGlobal(true);
187 values.renderer_backend.SetGlobal(true); 199 values.renderer_backend.SetGlobal(true);
200 values.renderer_force_max_clock.SetGlobal(true);
188 values.vulkan_device.SetGlobal(true); 201 values.vulkan_device.SetGlobal(true);
189 values.aspect_ratio.SetGlobal(true); 202 values.aspect_ratio.SetGlobal(true);
190 values.max_anisotropy.SetGlobal(true); 203 values.max_anisotropy.SetGlobal(true);
@@ -200,6 +213,7 @@ void RestoreGlobalState(bool is_powered_on) {
200 values.use_asynchronous_shaders.SetGlobal(true); 213 values.use_asynchronous_shaders.SetGlobal(true);
201 values.use_fast_gpu_time.SetGlobal(true); 214 values.use_fast_gpu_time.SetGlobal(true);
202 values.use_pessimistic_flushes.SetGlobal(true); 215 values.use_pessimistic_flushes.SetGlobal(true);
216 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
203 values.bg_red.SetGlobal(true); 217 values.bg_red.SetGlobal(true);
204 values.bg_green.SetGlobal(true); 218 values.bg_green.SetGlobal(true);
205 values.bg_blue.SetGlobal(true); 219 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 6b199af93..64db66f37 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -56,11 +56,14 @@ enum class ResolutionSetup : u32 {
56 Res1_2X = 0, 56 Res1_2X = 0,
57 Res3_4X = 1, 57 Res3_4X = 1,
58 Res1X = 2, 58 Res1X = 2,
59 Res2X = 3, 59 Res3_2X = 3,
60 Res3X = 4, 60 Res2X = 4,
61 Res4X = 5, 61 Res3X = 5,
62 Res5X = 6, 62 Res4X = 6,
63 Res6X = 7, 63 Res5X = 7,
64 Res6X = 8,
65 Res7X = 9,
66 Res8X = 10,
64}; 67};
65 68
66enum class ScalingFilter : u32 { 69enum class ScalingFilter : u32 {
@@ -128,7 +131,8 @@ public:
128 * @param default_val Intial value of the setting, and default value of the setting 131 * @param default_val Intial value of the setting, and default value of the setting
129 * @param name Label for the setting 132 * @param name Label for the setting
130 */ 133 */
131 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) 134 explicit Setting(const Type& default_val, const std::string& name)
135 requires(!ranged)
132 : value{default_val}, default_value{default_val}, label{name} {} 136 : value{default_val}, default_value{default_val}, label{name} {}
133 virtual ~Setting() = default; 137 virtual ~Setting() = default;
134 138
@@ -141,7 +145,8 @@ public:
141 * @param name Label for the setting 145 * @param name Label for the setting
142 */ 146 */
143 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 147 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
144 const std::string& name) requires(ranged) 148 const std::string& name)
149 requires(ranged)
145 : value{default_val}, 150 : value{default_val},
146 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} 151 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
147 152
@@ -229,7 +234,8 @@ public:
229 * @param default_val Intial value of the setting, and default value of the setting 234 * @param default_val Intial value of the setting, and default value of the setting
230 * @param name Label for the setting 235 * @param name Label for the setting
231 */ 236 */
232 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) 237 explicit SwitchableSetting(const Type& default_val, const std::string& name)
238 requires(!ranged)
233 : Setting<Type>{default_val, name} {} 239 : Setting<Type>{default_val, name} {}
234 virtual ~SwitchableSetting() = default; 240 virtual ~SwitchableSetting() = default;
235 241
@@ -242,7 +248,8 @@ public:
242 * @param name Label for the setting 248 * @param name Label for the setting
243 */ 249 */
244 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, 250 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
245 const std::string& name) requires(ranged) 251 const std::string& name)
252 requires(ranged)
246 : Setting<Type, true>{default_val, min_val, max_val, name} {} 253 : Setting<Type, true>{default_val, min_val, max_val, name} {}
247 254
248 /** 255 /**
@@ -415,6 +422,7 @@ struct Values {
415 // Renderer 422 // Renderer
416 SwitchableSetting<RendererBackend, true> renderer_backend{ 423 SwitchableSetting<RendererBackend, true> renderer_backend{
417 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; 424 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
425 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
418 Setting<bool> renderer_debug{false, "debug"}; 426 Setting<bool> renderer_debug{false, "debug"};
419 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 427 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
420 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 428 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -451,6 +459,8 @@ struct Values {
451 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 459 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
452 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 460 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
453 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; 461 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
462 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
463 "use_vulkan_driver_pipeline_cache"};
454 464
455 SwitchableSetting<u8> bg_red{0, "bg_red"}; 465 SwitchableSetting<u8> bg_red{0, "bg_red"};
456 SwitchableSetting<u8> bg_green{0, "bg_green"}; 466 SwitchableSetting<u8> bg_green{0, "bg_green"};
@@ -477,6 +487,7 @@ struct Values {
477 487
478 Setting<bool> enable_raw_input{false, "enable_raw_input"}; 488 Setting<bool> enable_raw_input{false, "enable_raw_input"};
479 Setting<bool> controller_navigation{true, "controller_navigation"}; 489 Setting<bool> controller_navigation{true, "controller_navigation"};
490 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
480 491
481 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; 492 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
482 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 493 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
@@ -531,6 +542,7 @@ struct Values {
531 Setting<bool> reporting_services{false, "reporting_services"}; 542 Setting<bool> reporting_services{false, "reporting_services"};
532 Setting<bool> quest_flag{false, "quest_flag"}; 543 Setting<bool> quest_flag{false, "quest_flag"};
533 Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; 544 Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
545 Setting<bool> disable_macro_hle{false, "disable_macro_hle"};
534 Setting<bool> extended_logging{false, "extended_logging"}; 546 Setting<bool> extended_logging{false, "extended_logging"};
535 Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; 547 Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
536 Setting<bool> use_auto_stub{false, "use_auto_stub"}; 548 Setting<bool> use_auto_stub{false, "use_auto_stub"};
diff --git a/src/common/tree.h b/src/common/tree.h
index f77859209..f4fc43de3 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -103,12 +103,12 @@ concept IsRBEntry = CheckRBEntry<T>::value;
103 103
104template <typename T> 104template <typename T>
105concept HasRBEntry = requires(T& t, const T& ct) { 105concept HasRBEntry = requires(T& t, const T& ct) {
106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; 106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; 107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
108}; 108 };
109 109
110template <typename T> 110template <typename T>
111requires HasRBEntry<T> 111 requires HasRBEntry<T>
112class RBHead { 112class RBHead {
113private: 113private:
114 T* m_rbh_root = nullptr; 114 T* m_rbh_root = nullptr;
@@ -130,90 +130,90 @@ public:
130}; 130};
131 131
132template <typename T> 132template <typename T>
133requires HasRBEntry<T> 133 requires HasRBEntry<T>
134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { 134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
135 return t->GetRBEntry(); 135 return t->GetRBEntry();
136} 136}
137template <typename T> 137template <typename T>
138requires HasRBEntry<T> 138 requires HasRBEntry<T>
139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { 139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
140 return t->GetRBEntry(); 140 return t->GetRBEntry();
141} 141}
142 142
143template <typename T> 143template <typename T>
144requires HasRBEntry<T> 144 requires HasRBEntry<T>
145[[nodiscard]] constexpr T* RB_LEFT(T* t) { 145[[nodiscard]] constexpr T* RB_LEFT(T* t) {
146 return RB_ENTRY(t).Left(); 146 return RB_ENTRY(t).Left();
147} 147}
148template <typename T> 148template <typename T>
149requires HasRBEntry<T> 149 requires HasRBEntry<T>
150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) { 150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
151 return RB_ENTRY(t).Left(); 151 return RB_ENTRY(t).Left();
152} 152}
153 153
154template <typename T> 154template <typename T>
155requires HasRBEntry<T> 155 requires HasRBEntry<T>
156[[nodiscard]] constexpr T* RB_RIGHT(T* t) { 156[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
157 return RB_ENTRY(t).Right(); 157 return RB_ENTRY(t).Right();
158} 158}
159template <typename T> 159template <typename T>
160requires HasRBEntry<T> 160 requires HasRBEntry<T>
161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { 161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
162 return RB_ENTRY(t).Right(); 162 return RB_ENTRY(t).Right();
163} 163}
164 164
165template <typename T> 165template <typename T>
166requires HasRBEntry<T> 166 requires HasRBEntry<T>
167[[nodiscard]] constexpr T* RB_PARENT(T* t) { 167[[nodiscard]] constexpr T* RB_PARENT(T* t) {
168 return RB_ENTRY(t).Parent(); 168 return RB_ENTRY(t).Parent();
169} 169}
170template <typename T> 170template <typename T>
171requires HasRBEntry<T> 171 requires HasRBEntry<T>
172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) { 172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
173 return RB_ENTRY(t).Parent(); 173 return RB_ENTRY(t).Parent();
174} 174}
175 175
176template <typename T> 176template <typename T>
177requires HasRBEntry<T> 177 requires HasRBEntry<T>
178constexpr void RB_SET_LEFT(T* t, T* e) { 178constexpr void RB_SET_LEFT(T* t, T* e) {
179 RB_ENTRY(t).SetLeft(e); 179 RB_ENTRY(t).SetLeft(e);
180} 180}
181template <typename T> 181template <typename T>
182requires HasRBEntry<T> 182 requires HasRBEntry<T>
183constexpr void RB_SET_RIGHT(T* t, T* e) { 183constexpr void RB_SET_RIGHT(T* t, T* e) {
184 RB_ENTRY(t).SetRight(e); 184 RB_ENTRY(t).SetRight(e);
185} 185}
186template <typename T> 186template <typename T>
187requires HasRBEntry<T> 187 requires HasRBEntry<T>
188constexpr void RB_SET_PARENT(T* t, T* e) { 188constexpr void RB_SET_PARENT(T* t, T* e) {
189 RB_ENTRY(t).SetParent(e); 189 RB_ENTRY(t).SetParent(e);
190} 190}
191 191
192template <typename T> 192template <typename T>
193requires HasRBEntry<T> 193 requires HasRBEntry<T>
194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { 194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
195 return RB_ENTRY(t).IsBlack(); 195 return RB_ENTRY(t).IsBlack();
196} 196}
197template <typename T> 197template <typename T>
198requires HasRBEntry<T> 198 requires HasRBEntry<T>
199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) { 199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
200 return RB_ENTRY(t).IsRed(); 200 return RB_ENTRY(t).IsRed();
201} 201}
202 202
203template <typename T> 203template <typename T>
204requires HasRBEntry<T> 204 requires HasRBEntry<T>
205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { 205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
206 return RB_ENTRY(t).Color(); 206 return RB_ENTRY(t).Color();
207} 207}
208 208
209template <typename T> 209template <typename T>
210requires HasRBEntry<T> 210 requires HasRBEntry<T>
211constexpr void RB_SET_COLOR(T* t, RBColor c) { 211constexpr void RB_SET_COLOR(T* t, RBColor c) {
212 RB_ENTRY(t).SetColor(c); 212 RB_ENTRY(t).SetColor(c);
213} 213}
214 214
215template <typename T> 215template <typename T>
216requires HasRBEntry<T> 216 requires HasRBEntry<T>
217constexpr void RB_SET(T* elm, T* parent) { 217constexpr void RB_SET(T* elm, T* parent) {
218 auto& rb_entry = RB_ENTRY(elm); 218 auto& rb_entry = RB_ENTRY(elm);
219 rb_entry.SetParent(parent); 219 rb_entry.SetParent(parent);
@@ -223,14 +223,14 @@ constexpr void RB_SET(T* elm, T* parent) {
223} 223}
224 224
225template <typename T> 225template <typename T>
226requires HasRBEntry<T> 226 requires HasRBEntry<T>
227constexpr void RB_SET_BLACKRED(T* black, T* red) { 227constexpr void RB_SET_BLACKRED(T* black, T* red) {
228 RB_SET_COLOR(black, RBColor::RB_BLACK); 228 RB_SET_COLOR(black, RBColor::RB_BLACK);
229 RB_SET_COLOR(red, RBColor::RB_RED); 229 RB_SET_COLOR(red, RBColor::RB_RED);
230} 230}
231 231
232template <typename T> 232template <typename T>
233requires HasRBEntry<T> 233 requires HasRBEntry<T>
234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { 234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
235 tmp = RB_RIGHT(elm); 235 tmp = RB_RIGHT(elm);
236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { 236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
@@ -252,7 +252,7 @@ constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
252} 252}
253 253
254template <typename T> 254template <typename T>
255requires HasRBEntry<T> 255 requires HasRBEntry<T>
256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { 256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
257 tmp = RB_LEFT(elm); 257 tmp = RB_LEFT(elm);
258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { 258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
@@ -274,7 +274,7 @@ constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
274} 274}
275 275
276template <typename T> 276template <typename T>
277requires HasRBEntry<T> 277 requires HasRBEntry<T>
278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { 278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
279 T* tmp; 279 T* tmp;
280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { 280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
@@ -358,7 +358,7 @@ constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
358} 358}
359 359
360template <typename T> 360template <typename T>
361requires HasRBEntry<T> 361 requires HasRBEntry<T>
362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { 362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
363 T* child = nullptr; 363 T* child = nullptr;
364 T* parent = nullptr; 364 T* parent = nullptr;
@@ -451,7 +451,7 @@ constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
451} 451}
452 452
453template <typename T> 453template <typename T>
454requires HasRBEntry<T> 454 requires HasRBEntry<T>
455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { 455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
456 T *parent = nullptr, *tmp = nullptr; 456 T *parent = nullptr, *tmp = nullptr;
457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { 457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
@@ -499,7 +499,7 @@ constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
499} 499}
500 500
501template <typename T, typename Compare> 501template <typename T, typename Compare>
502requires HasRBEntry<T> 502 requires HasRBEntry<T>
503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { 503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
504 T* parent = nullptr; 504 T* parent = nullptr;
505 T* tmp = head.Root(); 505 T* tmp = head.Root();
@@ -534,7 +534,7 @@ constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
534} 534}
535 535
536template <typename T, typename Compare> 536template <typename T, typename Compare>
537requires HasRBEntry<T> 537 requires HasRBEntry<T>
538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { 538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
539 T* tmp = head.Root(); 539 T* tmp = head.Root();
540 540
@@ -553,7 +553,7 @@ constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
553} 553}
554 554
555template <typename T, typename Compare> 555template <typename T, typename Compare>
556requires HasRBEntry<T> 556 requires HasRBEntry<T>
557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { 557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
558 T* tmp = head.Root(); 558 T* tmp = head.Root();
559 T* res = nullptr; 559 T* res = nullptr;
@@ -574,7 +574,7 @@ constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
574} 574}
575 575
576template <typename T, typename U, typename Compare> 576template <typename T, typename U, typename Compare>
577requires HasRBEntry<T> 577 requires HasRBEntry<T>
578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
579 T* tmp = head.Root(); 579 T* tmp = head.Root();
580 580
@@ -593,7 +593,7 @@ constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
593} 593}
594 594
595template <typename T, typename U, typename Compare> 595template <typename T, typename U, typename Compare>
596requires HasRBEntry<T> 596 requires HasRBEntry<T>
597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
598 T* tmp = head.Root(); 598 T* tmp = head.Root();
599 T* res = nullptr; 599 T* res = nullptr;
@@ -614,7 +614,7 @@ constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
614} 614}
615 615
616template <typename T, typename Compare> 616template <typename T, typename Compare>
617requires HasRBEntry<T> 617 requires HasRBEntry<T>
618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { 618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
619 T* tmp = head.Root(); 619 T* tmp = head.Root();
620 620
@@ -631,7 +631,7 @@ constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
631} 631}
632 632
633template <typename T, typename U, typename Compare> 633template <typename T, typename U, typename Compare>
634requires HasRBEntry<T> 634 requires HasRBEntry<T>
635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { 635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
636 T* tmp = head.Root(); 636 T* tmp = head.Root();
637 637
@@ -648,7 +648,7 @@ constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
648} 648}
649 649
650template <typename T> 650template <typename T>
651requires HasRBEntry<T> 651 requires HasRBEntry<T>
652constexpr T* RB_NEXT(T* elm) { 652constexpr T* RB_NEXT(T* elm) {
653 if (RB_RIGHT(elm)) { 653 if (RB_RIGHT(elm)) {
654 elm = RB_RIGHT(elm); 654 elm = RB_RIGHT(elm);
@@ -669,7 +669,7 @@ constexpr T* RB_NEXT(T* elm) {
669} 669}
670 670
671template <typename T> 671template <typename T>
672requires HasRBEntry<T> 672 requires HasRBEntry<T>
673constexpr T* RB_PREV(T* elm) { 673constexpr T* RB_PREV(T* elm) {
674 if (RB_LEFT(elm)) { 674 if (RB_LEFT(elm)) {
675 elm = RB_LEFT(elm); 675 elm = RB_LEFT(elm);
@@ -690,7 +690,7 @@ constexpr T* RB_PREV(T* elm) {
690} 690}
691 691
692template <typename T> 692template <typename T>
693requires HasRBEntry<T> 693 requires HasRBEntry<T>
694constexpr T* RB_MIN(RBHead<T>& head) { 694constexpr T* RB_MIN(RBHead<T>& head) {
695 T* tmp = head.Root(); 695 T* tmp = head.Root();
696 T* parent = nullptr; 696 T* parent = nullptr;
@@ -704,7 +704,7 @@ constexpr T* RB_MIN(RBHead<T>& head) {
704} 704}
705 705
706template <typename T> 706template <typename T>
707requires HasRBEntry<T> 707 requires HasRBEntry<T>
708constexpr T* RB_MAX(RBHead<T>& head) { 708constexpr T* RB_MAX(RBHead<T>& head) {
709 T* tmp = head.Root(); 709 T* tmp = head.Root();
710 T* parent = nullptr; 710 T* parent = nullptr;
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index e62eeea2e..0e2095c45 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -348,9 +348,7 @@ public:
348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
349// component names (x<->r) and permutations (xy<->yx) 349// component names (x<->r) and permutations (xy<->yx)
350#define _DEFINE_SWIZZLER2(a, b, name) \ 350#define _DEFINE_SWIZZLER2(a, b, name) \
351 [[nodiscard]] constexpr Vec2<T> name() const { \ 351 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
352 return Vec2<T>(a, b); \
353 }
354#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 352#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
355 _DEFINE_SWIZZLER2(a, b, a##b); \ 353 _DEFINE_SWIZZLER2(a, b, a##b); \
356 _DEFINE_SWIZZLER2(a, b, a2##b2); \ 354 _DEFINE_SWIZZLER2(a, b, a2##b2); \
@@ -543,9 +541,7 @@ public:
543// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 541// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
544// permutations (xy<->yx) 542// permutations (xy<->yx)
545#define _DEFINE_SWIZZLER2(a, b, name) \ 543#define _DEFINE_SWIZZLER2(a, b, name) \
546 [[nodiscard]] constexpr Vec2<T> name() const { \ 544 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
547 return Vec2<T>(a, b); \
548 }
549#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 545#define DEFINE_SWIZZLER2_COMP1(a, a2) \
550 _DEFINE_SWIZZLER2(a, a, a##a); \ 546 _DEFINE_SWIZZLER2(a, a, a##a); \
551 _DEFINE_SWIZZLER2(a, a, a2##a2) 547 _DEFINE_SWIZZLER2(a, a, a2##a2)
@@ -570,9 +566,7 @@ public:
570#undef _DEFINE_SWIZZLER2 566#undef _DEFINE_SWIZZLER2
571 567
572#define _DEFINE_SWIZZLER3(a, b, c, name) \ 568#define _DEFINE_SWIZZLER3(a, b, c, name) \
573 [[nodiscard]] constexpr Vec3<T> name() const { \ 569 [[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
574 return Vec3<T>(a, b, c); \
575 }
576#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 570#define DEFINE_SWIZZLER3_COMP1(a, a2) \
577 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ 571 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \
578 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) 572 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
@@ -641,8 +635,8 @@ template <typename T>
641 635
642// linear interpolation via float: 0.0=begin, 1.0=end 636// linear interpolation via float: 0.0=begin, 1.0=end
643template <typename X> 637template <typename X>
644[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) 638[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
645 Lerp(const X& begin, const X& end, const float t) { 639 const float t) {
646 return begin * (1.f - t) + end * t; 640 return begin * (1.f - t) + end * t;
647} 641}
648 642
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0252c8c31..5afdeb5ff 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -226,6 +226,7 @@ add_library(core STATIC
226 hle/kernel/k_page_buffer.h 226 hle/kernel/k_page_buffer.h
227 hle/kernel/k_page_heap.cpp 227 hle/kernel/k_page_heap.cpp
228 hle/kernel/k_page_heap.h 228 hle/kernel/k_page_heap.h
229 hle/kernel/k_page_group.cpp
229 hle/kernel/k_page_group.h 230 hle/kernel/k_page_group.h
230 hle/kernel/k_page_table.cpp 231 hle/kernel/k_page_table.cpp
231 hle/kernel/k_page_table.h 232 hle/kernel/k_page_table.h
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2df7b0ee8..8aa7b9641 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,14 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifndef _MSC_VER
5#include <cxxabi.h>
6#endif
7
8#include <map> 4#include <map>
9#include <optional> 5#include <optional>
6
10#include "common/bit_field.h" 7#include "common/bit_field.h"
11#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/demangle.h"
12#include "common/logging/log.h" 10#include "common/logging/log.h"
13#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
14#include "core/arm/symbols.h" 12#include "core/arm/symbols.h"
@@ -71,20 +69,8 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
71 const auto symbol_set = symbols.find(entry.module); 69 const auto symbol_set = symbols.find(entry.module);
72 if (symbol_set != symbols.end()) { 70 if (symbol_set != symbols.end()) {
73 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); 71 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
74 if (symbol.has_value()) { 72 if (symbol) {
75#ifdef _MSC_VER 73 entry.name = Common::DemangleSymbol(*symbol);
76 // TODO(DarkLordZach): Add demangling of symbol names.
77 entry.name = *symbol;
78#else
79 int status{-1};
80 char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
81 if (status == 0 && demangled != nullptr) {
82 entry.name = demangled;
83 std::free(demangled);
84 } else {
85 entry.name = *symbol;
86 }
87#endif
88 } 74 }
89 } 75 }
90 } 76 }
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 947747d36..2a7570073 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -229,7 +229,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
229 config.enable_cycle_counting = true; 229 config.enable_cycle_counting = true;
230 230
231 // Code cache size 231 // Code cache size
232#ifdef ARCHITECTURE_arm64
233 config.code_cache_size = 128_MiB;
234#else
232 config.code_cache_size = 512_MiB; 235 config.code_cache_size = 512_MiB;
236#endif
233 237
234 // Allow memory fault handling to work 238 // Allow memory fault handling to work
235 if (system.DebuggerEnabled()) { 239 if (system.DebuggerEnabled()) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 3df943df7..7229fdc2a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -288,7 +288,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
288 config.enable_cycle_counting = true; 288 config.enable_cycle_counting = true;
289 289
290 // Code cache size 290 // Code cache size
291#ifdef ARCHITECTURE_arm64
292 config.code_cache_size = 128_MiB;
293#else
291 config.code_cache_size = 512_MiB; 294 config.code_cache_size = 512_MiB;
295#endif
292 296
293 // Allow memory fault handling to work 297 // Allow memory fault handling to work
294 if (system.DebuggerEnabled()) { 298 if (system.DebuggerEnabled()) {
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0e7b5f943..6bac6722f 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -142,16 +142,24 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
142} 142}
143 143
144void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, 144void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
145 std::uintptr_t user_data) { 145 std::uintptr_t user_data, bool wait) {
146 std::scoped_lock scope{basic_lock}; 146 {
147 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 147 std::scoped_lock lk{basic_lock};
148 return e.type.lock().get() == event_type.get() && e.user_data == user_data; 148 const auto itr =
149 }); 149 std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
150 150 return e.type.lock().get() == event_type.get() && e.user_data == user_data;
151 // Removing random items breaks the invariant so we have to re-establish it. 151 });
152 if (itr != event_queue.end()) { 152
153 event_queue.erase(itr, event_queue.end()); 153 // Removing random items breaks the invariant so we have to re-establish it.
154 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 154 if (itr != event_queue.end()) {
155 event_queue.erase(itr, event_queue.end());
156 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
157 }
158 }
159
160 // Force any in-progress events to finish
161 if (wait) {
162 std::scoped_lock lk{advance_lock};
155 } 163 }
156} 164}
157 165
@@ -190,20 +198,6 @@ u64 CoreTiming::GetClockTicks() const {
190 return CpuCyclesToClockCycles(ticks); 198 return CpuCyclesToClockCycles(ticks);
191} 199}
192 200
193void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
194 std::scoped_lock lock{basic_lock};
195
196 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
197 return e.type.lock().get() == event_type.get();
198 });
199
200 // Removing random items breaks the invariant so we have to re-establish it.
201 if (itr != event_queue.end()) {
202 event_queue.erase(itr, event_queue.end());
203 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
204 }
205}
206
207std::optional<s64> CoreTiming::Advance() { 201std::optional<s64> CoreTiming::Advance() {
208 std::scoped_lock lock{advance_lock, basic_lock}; 202 std::scoped_lock lock{advance_lock, basic_lock};
209 global_timer = GetGlobalTimeNs().count(); 203 global_timer = GetGlobalTimeNs().count();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index b5925193c..da366637b 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -98,10 +98,13 @@ public:
98 const std::shared_ptr<EventType>& event_type, 98 const std::shared_ptr<EventType>& event_type,
99 std::uintptr_t user_data = 0, bool absolute_time = false); 99 std::uintptr_t user_data = 0, bool absolute_time = false);
100 100
101 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); 101 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
102 bool wait = true);
102 103
103 /// We only permit one event of each type in the queue at a time. 104 void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
104 void RemoveEvent(const std::shared_ptr<EventType>& event_type); 105 std::uintptr_t user_data) {
106 UnscheduleEvent(event_type, user_data, false);
107 }
105 108
106 void AddTicks(u64 ticks_to_add); 109 void AddTicks(u64 ticks_to_add);
107 110
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index a64a9ac64..9c02b7b31 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -11,6 +11,7 @@
11#include "common/hex_util.h" 11#include "common/hex_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "common/settings.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
16#include "core/debugger/gdbstub.h" 17#include "core/debugger/gdbstub.h"
@@ -731,7 +732,25 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
731 auto* process = system.CurrentProcess(); 732 auto* process = system.CurrentProcess();
732 auto& page_table = process->PageTable(); 733 auto& page_table = process->PageTable();
733 734
734 if (command_str == "get info") { 735 const char* commands = "Commands:\n"
736 " get fastmem\n"
737 " get info\n"
738 " get mappings\n";
739
740 if (command_str == "get fastmem") {
741 if (Settings::IsFastmemEnabled()) {
742 const auto& impl = page_table.PageTableImpl();
743 const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
744 const auto region_bits = impl.current_address_space_width_in_bits;
745 const auto region_size = 1ULL << region_bits;
746
747 reply = fmt::format("Region bits: {}\n"
748 "Host address: {:#x} - {:#x}\n",
749 region_bits, region, region + region_size - 1);
750 } else {
751 reply = "Fastmem is not enabled.\n";
752 }
753 } else if (command_str == "get info") {
735 Loader::AppLoader::Modules modules; 754 Loader::AppLoader::Modules modules;
736 system.GetAppLoader().ReadNSOModules(modules); 755 system.GetAppLoader().ReadNSOModules(modules);
737 756
@@ -787,9 +806,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
787 cur_addr = next_address; 806 cur_addr = next_address;
788 } 807 }
789 } else if (command_str == "help") { 808 } else if (command_str == "help") {
790 reply = "Commands:\n get info\n get mappings\n"; 809 reply = commands;
791 } else { 810 } else {
792 reply = "Unknown command.\nCommands:\n get info\n get mappings\n"; 811 reply = "Unknown command.\n";
812 reply += commands;
793 } 813 }
794 814
795 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; 815 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 0f6618b31..639842401 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -194,9 +194,9 @@ std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset)
194 194
195std::string VfsFile::GetFullPath() const { 195std::string VfsFile::GetFullPath() const {
196 if (GetContainingDirectory() == nullptr) 196 if (GetContainingDirectory() == nullptr)
197 return "/" + GetName(); 197 return '/' + GetName();
198 198
199 return GetContainingDirectory()->GetFullPath() + "/" + GetName(); 199 return GetContainingDirectory()->GetFullPath() + '/' + GetName();
200} 200}
201 201
202VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { 202VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
@@ -435,7 +435,7 @@ std::string VfsDirectory::GetFullPath() const {
435 if (IsRoot()) 435 if (IsRoot())
436 return GetName(); 436 return GetName();
437 437
438 return GetParentDirectory()->GetFullPath() + "/" + GetName(); 438 return GetParentDirectory()->GetFullPath() + '/' + GetName();
439} 439}
440 440
441bool ReadOnlyVfsDirectory::IsWritable() const { 441bool ReadOnlyVfsDirectory::IsWritable() const {
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index 30c2e9d17..1c91bbe40 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -40,6 +40,11 @@ void EmulatedConsole::SetTouchParams() {
40 touch_params[index++] = std::move(touchscreen_param); 40 touch_params[index++] = std::move(touchscreen_param);
41 } 41 }
42 42
43 if (Settings::values.touch_from_button_maps.empty()) {
44 LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
45 return;
46 }
47
43 const auto button_index = 48 const auto button_index =
44 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); 49 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
45 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons; 50 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index f238d6ccd..0e06468da 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <common/scope_exit.h>
5 6
6#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
7#include "common/thread.h" 8#include "common/thread.h"
@@ -11,6 +12,11 @@
11namespace Core::HID { 12namespace Core::HID {
12constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
13constexpr s32 HID_TRIGGER_MAX = 0x7fff; 14constexpr s32 HID_TRIGGER_MAX = 0x7fff;
15// Use a common UUID for TAS and Virtual Gamepad
16constexpr Common::UUID TAS_UUID =
17 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
18constexpr Common::UUID VIRTUAL_UUID =
19 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
14 20
15EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} 21EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
16 22
@@ -88,6 +94,7 @@ void EmulatedController::ReloadFromSettings() {
88 motion_params[index] = Common::ParamPackage(player.motions[index]); 94 motion_params[index] = Common::ParamPackage(player.motions[index]);
89 } 95 }
90 96
97 controller.color_values = {};
91 controller.colors_state.fullkey = { 98 controller.colors_state.fullkey = {
92 .body = GetNpadColor(player.body_color_left), 99 .body = GetNpadColor(player.body_color_left),
93 .button = GetNpadColor(player.button_color_left), 100 .button = GetNpadColor(player.button_color_left),
@@ -101,6 +108,8 @@ void EmulatedController::ReloadFromSettings() {
101 .button = GetNpadColor(player.button_color_right), 108 .button = GetNpadColor(player.button_color_right),
102 }; 109 };
103 110
111 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
112
104 // Other or debug controller should always be a pro controller 113 // Other or debug controller should always be a pro controller
105 if (npad_id_type != NpadIdType::Other) { 114 if (npad_id_type != NpadIdType::Other) {
106 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); 115 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -127,18 +136,28 @@ void EmulatedController::LoadDevices() {
127 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; 136 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
128 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; 137 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
129 138
139 color_params[LeftIndex] = left_joycon;
140 color_params[RightIndex] = right_joycon;
141 color_params[LeftIndex].Set("color", true);
142 color_params[RightIndex].Set("color", true);
143
130 battery_params[LeftIndex] = left_joycon; 144 battery_params[LeftIndex] = left_joycon;
131 battery_params[RightIndex] = right_joycon; 145 battery_params[RightIndex] = right_joycon;
132 battery_params[LeftIndex].Set("battery", true); 146 battery_params[LeftIndex].Set("battery", true);
133 battery_params[RightIndex].Set("battery", true); 147 battery_params[RightIndex].Set("battery", true);
134 148
135 camera_params = Common::ParamPackage{"engine:camera,camera:1"}; 149 camera_params[0] = right_joycon;
136 nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; 150 camera_params[0].Set("camera", true);
151 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
152 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
153 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
154 nfc_params[1] = right_joycon;
155 nfc_params[1].Set("nfc", true);
137 156
138 output_params[LeftIndex] = left_joycon; 157 output_params[LeftIndex] = left_joycon;
139 output_params[RightIndex] = right_joycon; 158 output_params[RightIndex] = right_joycon;
140 output_params[2] = camera_params; 159 output_params[2] = camera_params[1];
141 output_params[3] = nfc_params; 160 output_params[3] = nfc_params[0];
142 output_params[LeftIndex].Set("output", true); 161 output_params[LeftIndex].Set("output", true);
143 output_params[RightIndex].Set("output", true); 162 output_params[RightIndex].Set("output", true);
144 output_params[2].Set("output", true); 163 output_params[2].Set("output", true);
@@ -154,8 +173,11 @@ void EmulatedController::LoadDevices() {
154 Common::Input::CreateInputDevice); 173 Common::Input::CreateInputDevice);
155 std::ranges::transform(battery_params, battery_devices.begin(), 174 std::ranges::transform(battery_params, battery_devices.begin(),
156 Common::Input::CreateInputDevice); 175 Common::Input::CreateInputDevice);
157 camera_devices = Common::Input::CreateInputDevice(camera_params); 176 std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
158 nfc_devices = Common::Input::CreateInputDevice(nfc_params); 177 std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
178 std::ranges::transform(ring_params, ring_analog_devices.begin(),
179 Common::Input::CreateInputDevice);
180 std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
159 std::ranges::transform(output_params, output_devices.begin(), 181 std::ranges::transform(output_params, output_devices.begin(),
160 Common::Input::CreateOutputDevice); 182 Common::Input::CreateOutputDevice);
161 183
@@ -210,6 +232,13 @@ void EmulatedController::LoadTASParams() {
210 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 232 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
211 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); 233 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
212 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); 234 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
235
236 // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates
237 // making sure they play back in the game as originally written down in the script file
238 tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f);
239 tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
240 tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
241 tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
213} 242}
214 243
215void EmulatedController::LoadVirtualGamepadParams() { 244void EmulatedController::LoadVirtualGamepadParams() {
@@ -310,6 +339,19 @@ void EmulatedController::ReloadInput() {
310 battery_devices[index]->ForceUpdate(); 339 battery_devices[index]->ForceUpdate();
311 } 340 }
312 341
342 for (std::size_t index = 0; index < color_devices.size(); ++index) {
343 if (!color_devices[index]) {
344 continue;
345 }
346 color_devices[index]->SetCallback({
347 .on_change =
348 [this, index](const Common::Input::CallbackStatus& callback) {
349 SetColors(callback, index);
350 },
351 });
352 color_devices[index]->ForceUpdate();
353 }
354
313 for (std::size_t index = 0; index < motion_devices.size(); ++index) { 355 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
314 if (!motion_devices[index]) { 356 if (!motion_devices[index]) {
315 continue; 357 continue;
@@ -323,27 +365,38 @@ void EmulatedController::ReloadInput() {
323 motion_devices[index]->ForceUpdate(); 365 motion_devices[index]->ForceUpdate();
324 } 366 }
325 367
326 if (camera_devices) { 368 for (std::size_t index = 0; index < camera_devices.size(); ++index) {
327 camera_devices->SetCallback({ 369 if (!camera_devices[index]) {
370 continue;
371 }
372 camera_devices[index]->SetCallback({
328 .on_change = 373 .on_change =
329 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, 374 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
330 }); 375 });
331 camera_devices->ForceUpdate(); 376 camera_devices[index]->ForceUpdate();
332 } 377 }
333 378
334 if (nfc_devices) { 379 for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
335 if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { 380 if (!ring_analog_devices[index]) {
336 nfc_devices->SetCallback({ 381 continue;
337 .on_change =
338 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
339 });
340 nfc_devices->ForceUpdate();
341 } 382 }
383 ring_analog_devices[index]->SetCallback({
384 .on_change =
385 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
386 });
387 ring_analog_devices[index]->ForceUpdate();
342 } 388 }
343 389
344 // Use a common UUID for TAS 390 for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
345 static constexpr Common::UUID TAS_UUID = Common::UUID{ 391 if (!nfc_devices[index]) {
346 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 392 continue;
393 }
394 nfc_devices[index]->SetCallback({
395 .on_change =
396 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
397 });
398 nfc_devices[index]->ForceUpdate();
399 }
347 400
348 // Register TAS devices. No need to force update 401 // Register TAS devices. No need to force update
349 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { 402 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
@@ -370,10 +423,6 @@ void EmulatedController::ReloadInput() {
370 }); 423 });
371 } 424 }
372 425
373 // Use a common UUID for Virtual Gamepad
374 static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{
375 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
376
377 // Register virtual devices. No need to force update 426 // Register virtual devices. No need to force update
378 for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { 427 for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
379 if (!virtual_button_devices[index]) { 428 if (!virtual_button_devices[index]) {
@@ -416,6 +465,9 @@ void EmulatedController::UnloadInput() {
416 for (auto& battery : battery_devices) { 465 for (auto& battery : battery_devices) {
417 battery.reset(); 466 battery.reset();
418 } 467 }
468 for (auto& color : color_devices) {
469 color.reset();
470 }
419 for (auto& output : output_devices) { 471 for (auto& output : output_devices) {
420 output.reset(); 472 output.reset();
421 } 473 }
@@ -431,8 +483,15 @@ void EmulatedController::UnloadInput() {
431 for (auto& stick : virtual_stick_devices) { 483 for (auto& stick : virtual_stick_devices) {
432 stick.reset(); 484 stick.reset();
433 } 485 }
434 camera_devices.reset(); 486 for (auto& camera : camera_devices) {
435 nfc_devices.reset(); 487 camera.reset();
488 }
489 for (auto& ring : ring_analog_devices) {
490 ring.reset();
491 }
492 for (auto& nfc : nfc_devices) {
493 nfc.reset();
494 }
436} 495}
437 496
438void EmulatedController::EnableConfiguration() { 497void EmulatedController::EnableConfiguration() {
@@ -444,6 +503,11 @@ void EmulatedController::EnableConfiguration() {
444void EmulatedController::DisableConfiguration() { 503void EmulatedController::DisableConfiguration() {
445 is_configuring = false; 504 is_configuring = false;
446 505
506 // Get Joycon colors before turning on the controller
507 for (const auto& color_device : color_devices) {
508 color_device->ForceUpdate();
509 }
510
447 // Apply temporary npad type to the real controller 511 // Apply temporary npad type to the real controller
448 if (tmp_npad_type != npad_type) { 512 if (tmp_npad_type != npad_type) {
449 if (is_connected) { 513 if (is_connected) {
@@ -497,6 +561,9 @@ void EmulatedController::SaveCurrentConfig() {
497 for (std::size_t index = 0; index < player.motions.size(); ++index) { 561 for (std::size_t index = 0; index < player.motions.size(); ++index) {
498 player.motions[index] = motion_params[index].Serialize(); 562 player.motions[index] = motion_params[index].Serialize();
499 } 563 }
564 if (npad_id_type == NpadIdType::Player1) {
565 Settings::values.ringcon_analogs = ring_params[0].Serialize();
566 }
500} 567}
501 568
502void EmulatedController::RestoreConfig() { 569void EmulatedController::RestoreConfig() {
@@ -768,12 +835,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
768 if (index >= controller.stick_values.size()) { 835 if (index >= controller.stick_values.size()) {
769 return; 836 return;
770 } 837 }
771 std::unique_lock lock{mutex}; 838 auto trigger_guard =
839 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
840 std::scoped_lock lock{mutex};
772 const auto stick_value = TransformToStick(callback); 841 const auto stick_value = TransformToStick(callback);
773 842
774 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 843 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
775 if (controller.stick_values[index].uuid != uuid) { 844 if (controller.stick_values[index].uuid != uuid) {
776 if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) { 845 const bool is_tas = uuid == TAS_UUID;
846 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
847 trigger_guard.Cancel();
848 return;
849 }
850 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
851 !stick_value.right) {
852 trigger_guard.Cancel();
777 return; 853 return;
778 } 854 }
779 } 855 }
@@ -784,8 +860,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
784 if (is_configuring) { 860 if (is_configuring) {
785 controller.analog_stick_state.left = {}; 861 controller.analog_stick_state.left = {};
786 controller.analog_stick_state.right = {}; 862 controller.analog_stick_state.right = {};
787 lock.unlock();
788 TriggerOnChange(ControllerTriggerType::Stick, false);
789 return; 863 return;
790 } 864 }
791 865
@@ -810,9 +884,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
810 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); 884 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
811 break; 885 break;
812 } 886 }
813
814 lock.unlock();
815 TriggerOnChange(ControllerTriggerType::Stick, true);
816} 887}
817 888
818void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, 889void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
@@ -820,7 +891,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
820 if (index >= controller.trigger_values.size()) { 891 if (index >= controller.trigger_values.size()) {
821 return; 892 return;
822 } 893 }
823 std::unique_lock lock{mutex}; 894 auto trigger_guard =
895 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
896 std::scoped_lock lock{mutex};
824 const auto trigger_value = TransformToTrigger(callback); 897 const auto trigger_value = TransformToTrigger(callback);
825 898
826 // Only read trigger values that have the same uuid or are pressed once 899 // Only read trigger values that have the same uuid or are pressed once
@@ -836,13 +909,12 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
836 if (is_configuring) { 909 if (is_configuring) {
837 controller.gc_trigger_state.left = 0; 910 controller.gc_trigger_state.left = 0;
838 controller.gc_trigger_state.right = 0; 911 controller.gc_trigger_state.right = 0;
839 lock.unlock();
840 TriggerOnChange(ControllerTriggerType::Trigger, false);
841 return; 912 return;
842 } 913 }
843 914
844 // Only GC controllers have analog triggers 915 // Only GC controllers have analog triggers
845 if (npad_type != NpadStyleIndex::GameCube) { 916 if (npad_type != NpadStyleIndex::GameCube) {
917 trigger_guard.Cancel();
846 return; 918 return;
847 } 919 }
848 920
@@ -859,9 +931,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
859 controller.npad_button_state.zr.Assign(trigger.pressed.value); 931 controller.npad_button_state.zr.Assign(trigger.pressed.value);
860 break; 932 break;
861 } 933 }
862
863 lock.unlock();
864 TriggerOnChange(ControllerTriggerType::Trigger, true);
865} 934}
866 935
867void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, 936void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
@@ -869,7 +938,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
869 if (index >= controller.motion_values.size()) { 938 if (index >= controller.motion_values.size()) {
870 return; 939 return;
871 } 940 }
872 std::unique_lock lock{mutex}; 941 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
942 std::scoped_lock lock{mutex};
873 auto& raw_status = controller.motion_values[index].raw_status; 943 auto& raw_status = controller.motion_values[index].raw_status;
874 auto& emulated = controller.motion_values[index].emulated; 944 auto& emulated = controller.motion_values[index].emulated;
875 945
@@ -890,8 +960,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
890 force_update_motion = raw_status.force_update; 960 force_update_motion = raw_status.force_update;
891 961
892 if (is_configuring) { 962 if (is_configuring) {
893 lock.unlock();
894 TriggerOnChange(ControllerTriggerType::Motion, false);
895 return; 963 return;
896 } 964 }
897 965
@@ -901,9 +969,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
901 motion.rotation = emulated.GetRotations(); 969 motion.rotation = emulated.GetRotations();
902 motion.orientation = emulated.GetOrientation(); 970 motion.orientation = emulated.GetOrientation();
903 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 971 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
972}
904 973
905 lock.unlock(); 974void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
906 TriggerOnChange(ControllerTriggerType::Motion, true); 975 std::size_t index) {
976 if (index >= controller.color_values.size()) {
977 return;
978 }
979 auto trigger_guard =
980 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
981 std::scoped_lock lock{mutex};
982 controller.color_values[index] = TransformToColor(callback);
983
984 if (is_configuring) {
985 return;
986 }
987
988 if (controller.color_values[index].body == 0) {
989 trigger_guard.Cancel();
990 return;
991 }
992
993 controller.colors_state.fullkey = {
994 .body = GetNpadColor(controller.color_values[index].body),
995 .button = GetNpadColor(controller.color_values[index].buttons),
996 };
997 if (npad_type == NpadStyleIndex::ProController) {
998 controller.colors_state.left = {
999 .body = GetNpadColor(controller.color_values[index].left_grip),
1000 .button = GetNpadColor(controller.color_values[index].buttons),
1001 };
1002 controller.colors_state.right = {
1003 .body = GetNpadColor(controller.color_values[index].right_grip),
1004 .button = GetNpadColor(controller.color_values[index].buttons),
1005 };
1006 } else {
1007 switch (index) {
1008 case LeftIndex:
1009 controller.colors_state.left = {
1010 .body = GetNpadColor(controller.color_values[index].body),
1011 .button = GetNpadColor(controller.color_values[index].buttons),
1012 };
1013 break;
1014 case RightIndex:
1015 controller.colors_state.right = {
1016 .body = GetNpadColor(controller.color_values[index].body),
1017 .button = GetNpadColor(controller.color_values[index].buttons),
1018 };
1019 break;
1020 }
1021 }
907} 1022}
908 1023
909void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, 1024void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
@@ -911,12 +1026,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
911 if (index >= controller.battery_values.size()) { 1026 if (index >= controller.battery_values.size()) {
912 return; 1027 return;
913 } 1028 }
914 std::unique_lock lock{mutex}; 1029 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
1030 std::scoped_lock lock{mutex};
915 controller.battery_values[index] = TransformToBattery(callback); 1031 controller.battery_values[index] = TransformToBattery(callback);
916 1032
917 if (is_configuring) { 1033 if (is_configuring) {
918 lock.unlock();
919 TriggerOnChange(ControllerTriggerType::Battery, false);
920 return; 1034 return;
921 } 1035 }
922 1036
@@ -972,18 +1086,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
972 }; 1086 };
973 break; 1087 break;
974 } 1088 }
975
976 lock.unlock();
977 TriggerOnChange(ControllerTriggerType::Battery, true);
978} 1089}
979 1090
980void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { 1091void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
981 std::unique_lock lock{mutex}; 1092 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
1093 std::scoped_lock lock{mutex};
982 controller.camera_values = TransformToCamera(callback); 1094 controller.camera_values = TransformToCamera(callback);
983 1095
984 if (is_configuring) { 1096 if (is_configuring) {
985 lock.unlock();
986 TriggerOnChange(ControllerTriggerType::IrSensor, false);
987 return; 1097 return;
988 } 1098 }
989 1099
@@ -991,18 +1101,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
991 controller.camera_state.format = 1101 controller.camera_state.format =
992 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); 1102 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
993 controller.camera_state.data = controller.camera_values.data; 1103 controller.camera_state.data = controller.camera_values.data;
1104}
994 1105
995 lock.unlock(); 1106void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
996 TriggerOnChange(ControllerTriggerType::IrSensor, true); 1107 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
1108 std::scoped_lock lock{mutex};
1109 const auto force_value = TransformToStick(callback);
1110
1111 controller.ring_analog_value = force_value.x;
1112
1113 if (is_configuring) {
1114 return;
1115 }
1116
1117 controller.ring_analog_state.force = force_value.x.value;
997} 1118}
998 1119
999void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { 1120void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1000 std::unique_lock lock{mutex}; 1121 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
1122 std::scoped_lock lock{mutex};
1001 controller.nfc_values = TransformToNfc(callback); 1123 controller.nfc_values = TransformToNfc(callback);
1002 1124
1003 if (is_configuring) { 1125 if (is_configuring) {
1004 lock.unlock();
1005 TriggerOnChange(ControllerTriggerType::Nfc, false);
1006 return; 1126 return;
1007 } 1127 }
1008 1128
@@ -1010,9 +1130,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1010 controller.nfc_values.state, 1130 controller.nfc_values.state,
1011 controller.nfc_values.data, 1131 controller.nfc_values.data,
1012 }; 1132 };
1013
1014 lock.unlock();
1015 TriggerOnChange(ControllerTriggerType::Nfc, true);
1016} 1133}
1017 1134
1018bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1135bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1044,7 +1161,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1044 .type = type, 1161 .type = type,
1045 }; 1162 };
1046 return output_devices[device_index]->SetVibration(status) == 1163 return output_devices[device_index]->SetVibration(status) ==
1047 Common::Input::VibrationError::None; 1164 Common::Input::DriverResult::Success;
1048} 1165}
1049 1166
1050bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1167bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
@@ -1066,16 +1183,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1066 return output_devices[device_index]->IsVibrationEnabled(); 1183 return output_devices[device_index]->IsVibrationEnabled();
1067} 1184}
1068 1185
1069bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 1186Common::Input::DriverResult EmulatedController::SetPollingMode(
1070 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); 1187 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1071 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1188 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1189
1190 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1191 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1072 auto& nfc_output_device = output_devices[3]; 1192 auto& nfc_output_device = output_devices[3];
1073 1193
1074 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); 1194 if (device_index == EmulatedDeviceIndex::LeftIndex) {
1075 const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); 1195 return left_output_device->SetPollingMode(polling_mode);
1196 }
1197
1198 if (device_index == EmulatedDeviceIndex::RightIndex) {
1199 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1200 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1201
1202 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1203 return virtual_nfc_result;
1204 }
1205 return mapped_nfc_result;
1206 }
1076 1207
1077 return virtual_nfc_result == Common::Input::PollingError::None || 1208 left_output_device->SetPollingMode(polling_mode);
1078 mapped_nfc_result == Common::Input::PollingError::None; 1209 right_output_device->SetPollingMode(polling_mode);
1210 nfc_output_device->SetPollingMode(polling_mode);
1211 return Common::Input::DriverResult::Success;
1079} 1212}
1080 1213
1081bool EmulatedController::SetCameraFormat( 1214bool EmulatedController::SetCameraFormat(
@@ -1086,13 +1219,22 @@ bool EmulatedController::SetCameraFormat(
1086 auto& camera_output_device = output_devices[2]; 1219 auto& camera_output_device = output_devices[2];
1087 1220
1088 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1221 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1089 camera_format)) == Common::Input::CameraError::None) { 1222 camera_format)) == Common::Input::DriverResult::Success) {
1090 return true; 1223 return true;
1091 } 1224 }
1092 1225
1093 // Fallback to Qt camera if native device doesn't have support 1226 // Fallback to Qt camera if native device doesn't have support
1094 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1227 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1095 camera_format)) == Common::Input::CameraError::None; 1228 camera_format)) == Common::Input::DriverResult::Success;
1229}
1230
1231Common::ParamPackage EmulatedController::GetRingParam() const {
1232 return ring_params[0];
1233}
1234
1235void EmulatedController::SetRingParam(Common::ParamPackage param) {
1236 ring_params[0] = std::move(param);
1237 ReloadInput();
1096} 1238}
1097 1239
1098bool EmulatedController::HasNfc() const { 1240bool EmulatedController::HasNfc() const {
@@ -1246,39 +1388,35 @@ void EmulatedController::Connect(bool use_temporary_value) {
1246 return; 1388 return;
1247 } 1389 }
1248 1390
1249 std::unique_lock lock{mutex}; 1391 auto trigger_guard =
1392 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
1393 std::scoped_lock lock{mutex};
1250 if (is_configuring) { 1394 if (is_configuring) {
1251 tmp_is_connected = true; 1395 tmp_is_connected = true;
1252 lock.unlock();
1253 TriggerOnChange(ControllerTriggerType::Connected, false);
1254 return; 1396 return;
1255 } 1397 }
1256 1398
1257 if (is_connected) { 1399 if (is_connected) {
1400 trigger_guard.Cancel();
1258 return; 1401 return;
1259 } 1402 }
1260 is_connected = true; 1403 is_connected = true;
1261
1262 lock.unlock();
1263 TriggerOnChange(ControllerTriggerType::Connected, true);
1264} 1404}
1265 1405
1266void EmulatedController::Disconnect() { 1406void EmulatedController::Disconnect() {
1267 std::unique_lock lock{mutex}; 1407 auto trigger_guard =
1408 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
1409 std::scoped_lock lock{mutex};
1268 if (is_configuring) { 1410 if (is_configuring) {
1269 tmp_is_connected = false; 1411 tmp_is_connected = false;
1270 lock.unlock();
1271 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1272 return; 1412 return;
1273 } 1413 }
1274 1414
1275 if (!is_connected) { 1415 if (!is_connected) {
1416 trigger_guard.Cancel();
1276 return; 1417 return;
1277 } 1418 }
1278 is_connected = false; 1419 is_connected = false;
1279
1280 lock.unlock();
1281 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1282} 1420}
1283 1421
1284bool EmulatedController::IsConnected(bool get_temporary_value) const { 1422bool EmulatedController::IsConnected(bool get_temporary_value) const {
@@ -1303,19 +1441,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1303} 1441}
1304 1442
1305void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1443void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1306 std::unique_lock lock{mutex}; 1444 auto trigger_guard =
1445 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
1446 std::scoped_lock lock{mutex};
1307 1447
1308 if (is_configuring) { 1448 if (is_configuring) {
1309 if (tmp_npad_type == npad_type_) { 1449 if (tmp_npad_type == npad_type_) {
1450 trigger_guard.Cancel();
1310 return; 1451 return;
1311 } 1452 }
1312 tmp_npad_type = npad_type_; 1453 tmp_npad_type = npad_type_;
1313 lock.unlock();
1314 TriggerOnChange(ControllerTriggerType::Type, false);
1315 return; 1454 return;
1316 } 1455 }
1317 1456
1318 if (npad_type == npad_type_) { 1457 if (npad_type == npad_type_) {
1458 trigger_guard.Cancel();
1319 return; 1459 return;
1320 } 1460 }
1321 if (is_connected) { 1461 if (is_connected) {
@@ -1323,9 +1463,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1323 NpadIdTypeToIndex(npad_id_type)); 1463 NpadIdTypeToIndex(npad_id_type));
1324 } 1464 }
1325 npad_type = npad_type_; 1465 npad_type = npad_type_;
1326
1327 lock.unlock();
1328 TriggerOnChange(ControllerTriggerType::Type, true);
1329} 1466}
1330 1467
1331LedPattern EmulatedController::GetLedPattern() const { 1468LedPattern EmulatedController::GetLedPattern() const {
@@ -1386,6 +1523,10 @@ CameraValues EmulatedController::GetCameraValues() const {
1386 return controller.camera_values; 1523 return controller.camera_values;
1387} 1524}
1388 1525
1526RingAnalogValue EmulatedController::GetRingSensorValues() const {
1527 return controller.ring_analog_value;
1528}
1529
1389HomeButtonState EmulatedController::GetHomeButtons() const { 1530HomeButtonState EmulatedController::GetHomeButtons() const {
1390 std::scoped_lock lock{mutex}; 1531 std::scoped_lock lock{mutex};
1391 if (is_configuring) { 1532 if (is_configuring) {
@@ -1419,22 +1560,12 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1419} 1560}
1420 1561
1421AnalogSticks EmulatedController::GetSticks() const { 1562AnalogSticks EmulatedController::GetSticks() const {
1422 std::unique_lock lock{mutex}; 1563 std::scoped_lock lock{mutex};
1423 1564
1424 if (is_configuring) { 1565 if (is_configuring) {
1425 return {}; 1566 return {};
1426 } 1567 }
1427 1568
1428 // Some drivers like stick from buttons need constant refreshing
1429 for (auto& device : stick_devices) {
1430 if (!device) {
1431 continue;
1432 }
1433 lock.unlock();
1434 device->SoftUpdate();
1435 lock.lock();
1436 }
1437
1438 return controller.analog_stick_state; 1569 return controller.analog_stick_state;
1439} 1570}
1440 1571
@@ -1479,6 +1610,10 @@ const CameraState& EmulatedController::GetCamera() const {
1479 return controller.camera_state; 1610 return controller.camera_state;
1480} 1611}
1481 1612
1613RingSensorForce EmulatedController::GetRingSensorForce() const {
1614 return controller.ring_analog_state;
1615}
1616
1482const NfcState& EmulatedController::GetNfc() const { 1617const NfcState& EmulatedController::GetNfc() const {
1483 std::scoped_lock lock{mutex}; 1618 std::scoped_lock lock{mutex};
1484 return controller.nfc_state; 1619 return controller.nfc_state;
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index a398543a6..3ac77b2b5 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -35,19 +35,27 @@ using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; 35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices = 36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; 37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using ColorDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
38using BatteryDevices = 40using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 41 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; 42using CameraDevices =
41using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; 43 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
44using RingAnalogDevices =
45 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
46using NfcDevices =
47 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
42using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; 48using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
43 49
44using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 50using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
45using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 51using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
46using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; 52using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
47using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 53using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
54using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 55using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49using CameraParams = Common::ParamPackage; 56using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
50using NfcParams = Common::ParamPackage; 57using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
58using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
51using OutputParams = std::array<Common::ParamPackage, output_devices_size>; 59using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
52 60
53using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 61using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
@@ -58,6 +66,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
58using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 66using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
59using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 67using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
60using CameraValues = Common::Input::CameraStatus; 68using CameraValues = Common::Input::CameraStatus;
69using RingAnalogValue = Common::Input::AnalogStatus;
61using NfcValues = Common::Input::NfcStatus; 70using NfcValues = Common::Input::NfcStatus;
62using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 71using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
63 72
@@ -84,6 +93,10 @@ struct CameraState {
84 std::size_t sample{}; 93 std::size_t sample{};
85}; 94};
86 95
96struct RingSensorForce {
97 f32 force;
98};
99
87struct NfcState { 100struct NfcState {
88 Common::Input::NfcState state{}; 101 Common::Input::NfcState state{};
89 std::vector<u8> data{}; 102 std::vector<u8> data{};
@@ -116,6 +129,7 @@ struct ControllerStatus {
116 BatteryValues battery_values{}; 129 BatteryValues battery_values{};
117 VibrationValues vibration_values{}; 130 VibrationValues vibration_values{};
118 CameraValues camera_values{}; 131 CameraValues camera_values{};
132 RingAnalogValue ring_analog_value{};
119 NfcValues nfc_values{}; 133 NfcValues nfc_values{};
120 134
121 // Data for HID serices 135 // Data for HID serices
@@ -129,6 +143,7 @@ struct ControllerStatus {
129 ControllerColors colors_state{}; 143 ControllerColors colors_state{};
130 BatteryLevelState battery_state{}; 144 BatteryLevelState battery_state{};
131 CameraState camera_state{}; 145 CameraState camera_state{};
146 RingSensorForce ring_analog_state{};
132 NfcState nfc_state{}; 147 NfcState nfc_state{};
133}; 148};
134 149
@@ -141,6 +156,7 @@ enum class ControllerTriggerType {
141 Battery, 156 Battery,
142 Vibration, 157 Vibration,
143 IrSensor, 158 IrSensor,
159 RingController,
144 Nfc, 160 Nfc,
145 Connected, 161 Connected,
146 Disconnected, 162 Disconnected,
@@ -294,6 +310,9 @@ public:
294 /// Returns the latest camera status from the controller with parameters 310 /// Returns the latest camera status from the controller with parameters
295 CameraValues GetCameraValues() const; 311 CameraValues GetCameraValues() const;
296 312
313 /// Returns the latest status of analog input from the ring sensor with parameters
314 RingAnalogValue GetRingSensorValues() const;
315
297 /// Returns the latest status of button input for the hid::HomeButton service 316 /// Returns the latest status of button input for the hid::HomeButton service
298 HomeButtonState GetHomeButtons() const; 317 HomeButtonState GetHomeButtons() const;
299 318
@@ -324,6 +343,9 @@ public:
324 /// Returns the latest camera status from the controller 343 /// Returns the latest camera status from the controller
325 const CameraState& GetCamera() const; 344 const CameraState& GetCamera() const;
326 345
346 /// Returns the latest ringcon force sensor value
347 RingSensorForce GetRingSensorForce() const;
348
327 /// Returns the latest ntag status from the controller 349 /// Returns the latest ntag status from the controller
328 const NfcState& GetNfc() const; 350 const NfcState& GetNfc() const;
329 351
@@ -341,10 +363,12 @@ public:
341 363
342 /** 364 /**
343 * Sets the desired data to be polled from a controller 365 * Sets the desired data to be polled from a controller
366 * @param device_index index of the controller to set the polling mode
344 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. 367 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
345 * @return true if SetPollingMode was successfull 368 * @return driver result from this command
346 */ 369 */
347 bool SetPollingMode(Common::Input::PollingMode polling_mode); 370 Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
371 Common::Input::PollingMode polling_mode);
348 372
349 /** 373 /**
350 * Sets the desired camera format to be polled from a controller 374 * Sets the desired camera format to be polled from a controller
@@ -353,6 +377,15 @@ public:
353 */ 377 */
354 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); 378 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
355 379
380 // Returns the current mapped ring device
381 Common::ParamPackage GetRingParam() const;
382
383 /**
384 * Updates the current mapped ring device
385 * @param param ParamPackage with ring sensor data to be mapped
386 */
387 void SetRingParam(Common::ParamPackage param);
388
356 /// Returns true if the device has nfc support 389 /// Returns true if the device has nfc support
357 bool HasNfc() const; 390 bool HasNfc() const;
358 391
@@ -433,9 +466,16 @@ private:
433 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); 466 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
434 467
435 /** 468 /**
469 * Updates the color status of the controller
470 * @param callback A CallbackStatus containing the color status
471 * @param index color ID of the to be updated
472 */
473 void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
474
475 /**
436 * Updates the battery status of the controller 476 * Updates the battery status of the controller
437 * @param callback A CallbackStatus containing the battery status 477 * @param callback A CallbackStatus containing the battery status
438 * @param index Button ID of the to be updated 478 * @param index battery ID of the to be updated
439 */ 479 */
440 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); 480 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
441 481
@@ -446,6 +486,12 @@ private:
446 void SetCamera(const Common::Input::CallbackStatus& callback); 486 void SetCamera(const Common::Input::CallbackStatus& callback);
447 487
448 /** 488 /**
489 * Updates the ring analog sensor status of the ring controller
490 * @param callback A CallbackStatus containing the force status
491 */
492 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
493
494 /**
449 * Updates the nfc status of the controller 495 * Updates the nfc status of the controller
450 * @param callback A CallbackStatus containing the nfc status 496 * @param callback A CallbackStatus containing the nfc status
451 */ 497 */
@@ -484,7 +530,9 @@ private:
484 ControllerMotionParams motion_params; 530 ControllerMotionParams motion_params;
485 TriggerParams trigger_params; 531 TriggerParams trigger_params;
486 BatteryParams battery_params; 532 BatteryParams battery_params;
533 ColorParams color_params;
487 CameraParams camera_params; 534 CameraParams camera_params;
535 RingAnalogParams ring_params;
488 NfcParams nfc_params; 536 NfcParams nfc_params;
489 OutputParams output_params; 537 OutputParams output_params;
490 538
@@ -493,7 +541,9 @@ private:
493 ControllerMotionDevices motion_devices; 541 ControllerMotionDevices motion_devices;
494 TriggerDevices trigger_devices; 542 TriggerDevices trigger_devices;
495 BatteryDevices battery_devices; 543 BatteryDevices battery_devices;
544 ColorDevices color_devices;
496 CameraDevices camera_devices; 545 CameraDevices camera_devices;
546 RingAnalogDevices ring_analog_devices;
497 NfcDevices nfc_devices; 547 NfcDevices nfc_devices;
498 OutputDevices output_devices; 548 OutputDevices output_devices;
499 549
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index e421828d2..836f32c0f 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -14,7 +14,6 @@ EmulatedDevices::EmulatedDevices() = default;
14EmulatedDevices::~EmulatedDevices() = default; 14EmulatedDevices::~EmulatedDevices() = default;
15 15
16void EmulatedDevices::ReloadFromSettings() { 16void EmulatedDevices::ReloadFromSettings() {
17 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 17 ReloadInput();
19} 18}
20 19
@@ -66,8 +65,6 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 65 key_index++;
67 } 66 }
68 67
69 ring_analog_device = Common::Input::CreateInputDevice(ring_params);
70
71 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 68 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
72 if (!mouse_button_devices[index]) { 69 if (!mouse_button_devices[index]) {
73 continue; 70 continue;
@@ -122,13 +119,6 @@ void EmulatedDevices::ReloadInput() {
122 }, 119 },
123 }); 120 });
124 } 121 }
125
126 if (ring_analog_device) {
127 ring_analog_device->SetCallback({
128 .on_change =
129 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
130 });
131 }
132} 122}
133 123
134void EmulatedDevices::UnloadInput() { 124void EmulatedDevices::UnloadInput() {
@@ -145,7 +135,6 @@ void EmulatedDevices::UnloadInput() {
145 for (auto& button : keyboard_modifier_devices) { 135 for (auto& button : keyboard_modifier_devices) {
146 button.reset(); 136 button.reset();
147 } 137 }
148 ring_analog_device.reset();
149} 138}
150 139
151void EmulatedDevices::EnableConfiguration() { 140void EmulatedDevices::EnableConfiguration() {
@@ -165,7 +154,6 @@ void EmulatedDevices::SaveCurrentConfig() {
165 if (!is_configuring) { 154 if (!is_configuring) {
166 return; 155 return;
167 } 156 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
169} 157}
170 158
171void EmulatedDevices::RestoreConfig() { 159void EmulatedDevices::RestoreConfig() {
@@ -175,15 +163,6 @@ void EmulatedDevices::RestoreConfig() {
175 ReloadFromSettings(); 163 ReloadFromSettings();
176} 164}
177 165
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 166void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
188 std::size_t index) { 167 std::size_t index) {
189 if (index >= device_status.keyboard_values.size()) { 168 if (index >= device_status.keyboard_values.size()) {
@@ -430,23 +409,6 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
430 TriggerOnChange(DeviceTriggerType::Mouse); 409 TriggerOnChange(DeviceTriggerType::Mouse);
431} 410}
432 411
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
450KeyboardValues EmulatedDevices::GetKeyboardValues() const { 412KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex}; 413 std::scoped_lock lock{mutex};
452 return device_status.keyboard_values; 414 return device_status.keyboard_values;
@@ -462,10 +424,6 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
462 return device_status.mouse_button_values; 424 return device_status.mouse_button_values;
463} 425}
464 426
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
469KeyboardKey EmulatedDevices::GetKeyboard() const { 427KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex}; 428 std::scoped_lock lock{mutex};
471 return device_status.keyboard_state; 429 return device_status.keyboard_state;
@@ -491,10 +449,6 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
491 return device_status.mouse_wheel_state; 449 return device_status.mouse_wheel_state;
492} 450}
493 451
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 452void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex}; 453 std::scoped_lock lock{callback_mutex};
500 for (const auto& poller_pair : callback_list) { 454 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 4cdbf9dc6..76f9150df 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,11 +26,9 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
30 29
31using MouseButtonParams = 30using MouseButtonParams =
32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
34 32
35using KeyboardValues = 33using KeyboardValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -41,17 +39,12 @@ using MouseButtonValues =
41using MouseAnalogValues = 39using MouseAnalogValues =
42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
43using MouseStickValue = Common::Input::TouchStatus; 41using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
45 42
46struct MousePosition { 43struct MousePosition {
47 f32 x; 44 f32 x;
48 f32 y; 45 f32 y;
49}; 46};
50 47
51struct RingSensorForce {
52 f32 force;
53};
54
55struct DeviceStatus { 48struct DeviceStatus {
56 // Data from input_common 49 // Data from input_common
57 KeyboardValues keyboard_values{}; 50 KeyboardValues keyboard_values{};
@@ -59,7 +52,6 @@ struct DeviceStatus {
59 MouseButtonValues mouse_button_values{}; 52 MouseButtonValues mouse_button_values{};
60 MouseAnalogValues mouse_analog_values{}; 53 MouseAnalogValues mouse_analog_values{};
61 MouseStickValue mouse_stick_value{}; 54 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
63 55
64 // Data for HID serices 56 // Data for HID serices
65 KeyboardKey keyboard_state{}; 57 KeyboardKey keyboard_state{};
@@ -67,7 +59,6 @@ struct DeviceStatus {
67 MouseButton mouse_button_state{}; 59 MouseButton mouse_button_state{};
68 MousePosition mouse_position_state{}; 60 MousePosition mouse_position_state{};
69 AnalogStickState mouse_wheel_state{}; 61 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
71}; 62};
72 63
73enum class DeviceTriggerType { 64enum class DeviceTriggerType {
@@ -138,9 +129,6 @@ public:
138 /// Returns the latest status of button input from the mouse with parameters 129 /// Returns the latest status of button input from the mouse with parameters
139 MouseButtonValues GetMouseButtonsValues() const; 130 MouseButtonValues GetMouseButtonsValues() const;
140 131
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
144 /// Returns the latest status of button input from the keyboard 132 /// Returns the latest status of button input from the keyboard
145 KeyboardKey GetKeyboard() const; 133 KeyboardKey GetKeyboard() const;
146 134
@@ -156,9 +144,6 @@ public:
156 /// Returns the latest mouse wheel change 144 /// Returns the latest mouse wheel change
157 AnalogStickState GetMouseWheel() const; 145 AnalogStickState GetMouseWheel() const;
158 146
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
162 /** 147 /**
163 * Adds a callback to the list of events 148 * Adds a callback to the list of events
164 * @param update_callback InterfaceUpdateCallback that will be triggered 149 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -224,14 +209,11 @@ private:
224 209
225 bool is_configuring{false}; 210 bool is_configuring{false};
226 211
227 RingAnalogParams ring_params;
228
229 KeyboardDevices keyboard_devices; 212 KeyboardDevices keyboard_devices;
230 KeyboardModifierDevices keyboard_modifier_devices; 213 KeyboardModifierDevices keyboard_modifier_devices;
231 MouseButtonDevices mouse_button_devices; 214 MouseButtonDevices mouse_button_devices;
232 MouseAnalogDevices mouse_analog_devices; 215 MouseAnalogDevices mouse_analog_devices;
233 MouseStickDevice mouse_stick_device; 216 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
235 217
236 mutable std::mutex mutex; 218 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex; 219 mutable std::mutex callback_mutex;
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 502692875..3f7b8c090 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -304,6 +304,18 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
304 return nfc; 304 return nfc;
305} 305}
306 306
307Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
308 switch (callback.type) {
309 case Common::Input::InputType::Color:
310 return callback.color_status;
311 break;
312 default:
313 LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
314 return {};
315 break;
316 }
317}
318
307void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 319void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
308 const auto& properties = analog.properties; 320 const auto& properties = analog.properties;
309 float& raw_value = analog.raw_value; 321 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index b7eb6e660..c51c03e57 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -88,11 +88,19 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
88 * Converts raw input data into a valid nfc status. 88 * Converts raw input data into a valid nfc status.
89 * 89 *
90 * @param callback Supported callbacks: Nfc. 90 * @param callback Supported callbacks: Nfc.
91 * @return A valid CameraObject object. 91 * @return A valid data tag vector.
92 */ 92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); 93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94 94
95/** 95/**
96 * Converts raw input data into a valid color status.
97 *
98 * @param callback Supported callbacks: Color.
99 * @return A valid Color object.
100 */
101Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);
102
103/**
96 * Converts raw analog data into a valid analog value 104 * Converts raw analog data into a valid analog value
97 * @param analog An analog object containing raw data and properties 105 * @param analog An analog object containing raw data and properties
98 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 106 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 2827763d5..e8118c2b8 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -24,9 +24,7 @@ private:
24 friend class ::Kernel::KClassTokenGenerator; \ 24 friend class ::Kernel::KClassTokenGenerator; \
25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \ 25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
26 static constexpr inline const char* const TypeName = #CLASS; \ 26 static constexpr inline const char* const TypeName = #CLASS; \
27 static constexpr inline ClassTokenType ClassToken() { \ 27 static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken<CLASS>; } \
28 return ::Kernel::ClassToken<CLASS>; \
29 } \
30 \ 28 \
31public: \ 29public: \
32 YUZU_NON_COPYABLE(CLASS); \ 30 YUZU_NON_COPYABLE(CLASS); \
@@ -37,15 +35,9 @@ public:
37 constexpr ClassTokenType Token = ClassToken(); \ 35 constexpr ClassTokenType Token = ClassToken(); \
38 return TypeObj(TypeName, Token); \ 36 return TypeObj(TypeName, Token); \
39 } \ 37 } \
40 static constexpr const char* GetStaticTypeName() { \ 38 static constexpr const char* GetStaticTypeName() { return TypeName; } \
41 return TypeName; \ 39 virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); } \
42 } \ 40 virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); } \
43 virtual TypeObj GetTypeObj() ATTRIBUTE { \
44 return GetStaticTypeObj(); \
45 } \
46 virtual const char* GetTypeName() ATTRIBUTE { \
47 return GetStaticTypeName(); \
48 } \
49 \ 41 \
50private: \ 42private: \
51 constexpr bool operator!=(const TypeObj& rhs) 43 constexpr bool operator!=(const TypeObj& rhs)
@@ -245,8 +237,8 @@ public:
245 } 237 }
246 238
247 template <typename U> 239 template <typename U>
248 requires(std::derived_from<T, U> || 240 requires(std::derived_from<T, U> || std::derived_from<U, T>)
249 std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) { 241 constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
250 if constexpr (std::derived_from<U, T>) { 242 if constexpr (std::derived_from<U, T>) {
251 // Upcast. 243 // Upcast.
252 m_obj = rhs.m_obj; 244 m_obj = rhs.m_obj;
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 4b1c134d4..884eba001 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -27,13 +27,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
27 auto& page_table = m_owner->PageTable(); 27 auto& page_table = m_owner->PageTable();
28 28
29 // Construct the page group. 29 // Construct the page group.
30 m_page_group = {}; 30 m_page_group.emplace(kernel, page_table.GetBlockInfoManager());
31 31
32 // Lock the memory. 32 // Lock the memory.
33 R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) 33 R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size))
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) {
37 std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize()); 37 std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
38 } 38 }
39 39
@@ -51,12 +51,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
51void KCodeMemory::Finalize() { 51void KCodeMemory::Finalize() {
52 // Unlock. 52 // Unlock.
53 if (!m_is_mapped && !m_is_owner_mapped) { 53 if (!m_is_mapped && !m_is_owner_mapped) {
54 const size_t size = m_page_group.GetNumPages() * PageSize; 54 const size_t size = m_page_group->GetNumPages() * PageSize;
55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); 55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group);
56 } 56 }
57 57
58 // Close the page group. 58 // Close the page group.
59 m_page_group = {}; 59 m_page_group->Close();
60 m_page_group->Finalize();
60 61
61 // Close our reference to our owner. 62 // Close our reference to our owner.
62 m_owner->Close(); 63 m_owner->Close();
@@ -64,7 +65,7 @@ void KCodeMemory::Finalize() {
64 65
65Result KCodeMemory::Map(VAddr address, size_t size) { 66Result KCodeMemory::Map(VAddr address, size_t size) {
66 // Validate the size. 67 // Validate the size.
67 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 68 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
68 69
69 // Lock ourselves. 70 // Lock ourselves.
70 KScopedLightLock lk(m_lock); 71 KScopedLightLock lk(m_lock);
@@ -73,8 +74,8 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
73 R_UNLESS(!m_is_mapped, ResultInvalidState); 74 R_UNLESS(!m_is_mapped, ResultInvalidState);
74 75
75 // Map the memory. 76 // Map the memory.
76 R_TRY(kernel.CurrentProcess()->PageTable().MapPages( 77 R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup(
77 address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
78 79
79 // Mark ourselves as mapped. 80 // Mark ourselves as mapped.
80 m_is_mapped = true; 81 m_is_mapped = true;
@@ -84,14 +85,14 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
84 85
85Result KCodeMemory::Unmap(VAddr address, size_t size) { 86Result KCodeMemory::Unmap(VAddr address, size_t size) {
86 // Validate the size. 87 // Validate the size.
87 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 88 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
88 89
89 // Lock ourselves. 90 // Lock ourselves.
90 KScopedLightLock lk(m_lock); 91 KScopedLightLock lk(m_lock);
91 92
92 // Unmap the memory. 93 // Unmap the memory.
93 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group, 94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group,
94 KMemoryState::CodeOut)); 95 KMemoryState::CodeOut));
95 96
96 // Mark ourselves as unmapped. 97 // Mark ourselves as unmapped.
97 m_is_mapped = false; 98 m_is_mapped = false;
@@ -101,7 +102,7 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
101 102
102Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { 103Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
103 // Validate the size. 104 // Validate the size.
104 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 105 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
105 106
106 // Lock ourselves. 107 // Lock ourselves.
107 KScopedLightLock lk(m_lock); 108 KScopedLightLock lk(m_lock);
@@ -124,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
124 } 125 }
125 126
126 // Map the memory. 127 // Map the memory.
127 R_TRY( 128 R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
128 m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm)); 129 k_perm));
129 130
130 // Mark ourselves as mapped. 131 // Mark ourselves as mapped.
131 m_is_owner_mapped = true; 132 m_is_owner_mapped = true;
@@ -135,13 +136,13 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
135 136
136Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { 137Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
137 // Validate the size. 138 // Validate the size.
138 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 139 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
139 140
140 // Lock ourselves. 141 // Lock ourselves.
141 KScopedLightLock lk(m_lock); 142 KScopedLightLock lk(m_lock);
142 143
143 // Unmap the memory. 144 // Unmap the memory.
144 R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode)); 145 R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));
145 146
146 // Mark ourselves as unmapped. 147 // Mark ourselves as unmapped.
147 m_is_owner_mapped = false; 148 m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2e7e1436a..5b260b385 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <optional>
7
6#include "common/common_types.h" 8#include "common/common_types.h"
7#include "core/device_memory.h" 9#include "core/device_memory.h"
8#include "core/hle/kernel/k_auto_object.h" 10#include "core/hle/kernel/k_auto_object.h"
@@ -49,11 +51,11 @@ public:
49 return m_address; 51 return m_address;
50 } 52 }
51 size_t GetSize() const { 53 size_t GetSize() const {
52 return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0; 54 return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
53 } 55 }
54 56
55private: 57private:
56 KPageGroup m_page_group{}; 58 std::optional<KPageGroup> m_page_group{};
57 KProcess* m_owner{}; 59 KProcess* m_owner{};
58 VAddr m_address{}; 60 VAddr m_address{};
59 KLightLock m_lock; 61 KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..0c6b20db3 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -171,7 +171,7 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value)
171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); 171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
172 172
173 // Update the lock. 173 // Update the lock.
174 cur_thread->SetAddressKey(addr, value); 174 cur_thread->SetUserAddressKey(addr, value);
175 owner_thread->AddWaiter(cur_thread); 175 owner_thread->AddWaiter(cur_thread);
176 176
177 // Begin waiting. 177 // Begin waiting.
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 6bba79ea0..4dcd53821 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -18,7 +18,8 @@ void KHardwareTimer::Initialize() {
18} 18}
19 19
20void KHardwareTimer::Finalize() { 20void KHardwareTimer::Finalize() {
21 this->DisableInterrupt(); 21 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
22 m_wakeup_time = std::numeric_limits<s64>::max();
22 m_event_type.reset(); 23 m_event_type.reset();
23} 24}
24 25
@@ -59,7 +60,8 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
59} 60}
60 61
61void KHardwareTimer::DisableInterrupt() { 62void KHardwareTimer::DisableInterrupt() {
62 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); 63 m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
64 reinterpret_cast<uintptr_t>(this));
63 m_wakeup_time = std::numeric_limits<s64>::max(); 65 m_wakeup_time = std::numeric_limits<s64>::max();
64} 66}
65 67
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 43185320d..d791acbe3 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -68,7 +68,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
68 68
69 // Add the current thread as a waiter on the owner. 69 // Add the current thread as a waiter on the owner.
70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); 70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
71 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 71 cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
72 owner_thread->AddWaiter(cur_thread); 72 owner_thread->AddWaiter(cur_thread);
73 73
74 // Begin waiting to hold the lock. 74 // Begin waiting to hold the lock.
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index fd6e1d3e6..17fa1a6ed 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -67,9 +67,9 @@ constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + 67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
68 KernelSlabHeapSize + KernelPageBufferHeapSize; 68 KernelSlabHeapSize + KernelPageBufferHeapSize;
69 69
70constexpr bool IsKernelAddressKey(VAddr key) { 70//! NB: Use KThread::GetAddressKeyIsKernel().
71 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; 71//! See explanation for deviation of GetAddressKey.
72} 72bool IsKernelAddressKey(VAddr key) = delete;
73 73
74constexpr bool IsKernelAddress(VAddr address) { 74constexpr bool IsKernelAddress(VAddr address) {
75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; 75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index bd33571da..cd6ea388e 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -223,7 +223,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
223 223
224 // Ensure that we don't leave anything un-freed. 224 // Ensure that we don't leave anything un-freed.
225 ON_RESULT_FAILURE { 225 ON_RESULT_FAILURE {
226 for (const auto& it : out->Nodes()) { 226 for (const auto& it : *out) {
227 auto& manager = this->GetManager(it.GetAddress()); 227 auto& manager = this->GetManager(it.GetAddress());
228 const size_t node_num_pages = std::min<u64>( 228 const size_t node_num_pages = std::min<u64>(
229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); 229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
@@ -285,7 +285,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
285 m_has_optimized_process[static_cast<size_t>(pool)], true)); 285 m_has_optimized_process[static_cast<size_t>(pool)], true));
286 286
287 // Open the first reference to the pages. 287 // Open the first reference to the pages.
288 for (const auto& block : out->Nodes()) { 288 for (const auto& block : *out) {
289 PAddr cur_address = block.GetAddress(); 289 PAddr cur_address = block.GetAddress();
290 size_t remaining_pages = block.GetNumPages(); 290 size_t remaining_pages = block.GetNumPages();
291 while (remaining_pages > 0) { 291 while (remaining_pages > 0) {
@@ -335,7 +335,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
335 // Perform optimized memory tracking, if we should. 335 // Perform optimized memory tracking, if we should.
336 if (optimized) { 336 if (optimized) {
337 // Iterate over the allocated blocks. 337 // Iterate over the allocated blocks.
338 for (const auto& block : out->Nodes()) { 338 for (const auto& block : *out) {
339 // Get the block extents. 339 // Get the block extents.
340 const PAddr block_address = block.GetAddress(); 340 const PAddr block_address = block.GetAddress();
341 const size_t block_pages = block.GetNumPages(); 341 const size_t block_pages = block.GetNumPages();
@@ -391,7 +391,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
391 } 391 }
392 } else { 392 } else {
393 // Set all the allocated memory. 393 // Set all the allocated memory.
394 for (const auto& block : out->Nodes()) { 394 for (const auto& block : *out) {
395 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, 395 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
396 block.GetSize()); 396 block.GetSize());
397 } 397 }
diff --git a/src/core/hle/kernel/k_page_group.cpp b/src/core/hle/kernel/k_page_group.cpp
new file mode 100644
index 000000000..d8c644a33
--- /dev/null
+++ b/src/core/hle/kernel/k_page_group.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_dynamic_resource_manager.h"
5#include "core/hle/kernel/k_memory_manager.h"
6#include "core/hle/kernel/k_page_group.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12void KPageGroup::Finalize() {
13 KBlockInfo* cur = m_first_block;
14 while (cur != nullptr) {
15 KBlockInfo* next = cur->GetNext();
16 m_manager->Free(cur);
17 cur = next;
18 }
19
20 m_first_block = nullptr;
21 m_last_block = nullptr;
22}
23
24void KPageGroup::CloseAndReset() {
25 auto& mm = m_kernel.MemoryManager();
26
27 KBlockInfo* cur = m_first_block;
28 while (cur != nullptr) {
29 KBlockInfo* next = cur->GetNext();
30 mm.Close(cur->GetAddress(), cur->GetNumPages());
31 m_manager->Free(cur);
32 cur = next;
33 }
34
35 m_first_block = nullptr;
36 m_last_block = nullptr;
37}
38
39size_t KPageGroup::GetNumPages() const {
40 size_t num_pages = 0;
41
42 for (const auto& it : *this) {
43 num_pages += it.GetNumPages();
44 }
45
46 return num_pages;
47}
48
49Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) {
50 // Succeed immediately if we're adding no pages.
51 R_SUCCEED_IF(num_pages == 0);
52
53 // Check for overflow.
54 ASSERT(addr < addr + num_pages * PageSize);
55
56 // Try to just append to the last block.
57 if (m_last_block != nullptr) {
58 R_SUCCEED_IF(m_last_block->TryConcatenate(addr, num_pages));
59 }
60
61 // Allocate a new block.
62 KBlockInfo* new_block = m_manager->Allocate();
63 R_UNLESS(new_block != nullptr, ResultOutOfResource);
64
65 // Initialize the block.
66 new_block->Initialize(addr, num_pages);
67
68 // Add the block to our list.
69 if (m_last_block != nullptr) {
70 m_last_block->SetNext(new_block);
71 } else {
72 m_first_block = new_block;
73 }
74 m_last_block = new_block;
75
76 R_SUCCEED();
77}
78
79void KPageGroup::Open() const {
80 auto& mm = m_kernel.MemoryManager();
81
82 for (const auto& it : *this) {
83 mm.Open(it.GetAddress(), it.GetNumPages());
84 }
85}
86
87void KPageGroup::OpenFirst() const {
88 auto& mm = m_kernel.MemoryManager();
89
90 for (const auto& it : *this) {
91 mm.OpenFirst(it.GetAddress(), it.GetNumPages());
92 }
93}
94
95void KPageGroup::Close() const {
96 auto& mm = m_kernel.MemoryManager();
97
98 for (const auto& it : *this) {
99 mm.Close(it.GetAddress(), it.GetNumPages());
100 }
101}
102
103bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const {
104 auto lit = this->begin();
105 auto rit = rhs.begin();
106 auto lend = this->end();
107 auto rend = rhs.end();
108
109 while (lit != lend && rit != rend) {
110 if (*lit != *rit) {
111 return false;
112 }
113
114 ++lit;
115 ++rit;
116 }
117
118 return lit == lend && rit == rend;
119}
120
121} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h
index 316f172f2..c07f17663 100644
--- a/src/core/hle/kernel/k_page_group.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -1,4 +1,4 @@
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#pragma once 4#pragma once
@@ -13,24 +13,23 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16class KBlockInfoManager;
17class KernelCore;
16class KPageGroup; 18class KPageGroup;
17 19
18class KBlockInfo { 20class KBlockInfo {
19private:
20 friend class KPageGroup;
21
22public: 21public:
23 constexpr KBlockInfo() = default; 22 constexpr explicit KBlockInfo() : m_next(nullptr) {}
24 23
25 constexpr void Initialize(PAddr addr, size_t np) { 24 constexpr void Initialize(KPhysicalAddress addr, size_t np) {
26 ASSERT(Common::IsAligned(addr, PageSize)); 25 ASSERT(Common::IsAligned(addr, PageSize));
27 ASSERT(static_cast<u32>(np) == np); 26 ASSERT(static_cast<u32>(np) == np);
28 27
29 m_page_index = static_cast<u32>(addr) / PageSize; 28 m_page_index = static_cast<u32>(addr / PageSize);
30 m_num_pages = static_cast<u32>(np); 29 m_num_pages = static_cast<u32>(np);
31 } 30 }
32 31
33 constexpr PAddr GetAddress() const { 32 constexpr KPhysicalAddress GetAddress() const {
34 return m_page_index * PageSize; 33 return m_page_index * PageSize;
35 } 34 }
36 constexpr size_t GetNumPages() const { 35 constexpr size_t GetNumPages() const {
@@ -39,10 +38,10 @@ public:
39 constexpr size_t GetSize() const { 38 constexpr size_t GetSize() const {
40 return this->GetNumPages() * PageSize; 39 return this->GetNumPages() * PageSize;
41 } 40 }
42 constexpr PAddr GetEndAddress() const { 41 constexpr KPhysicalAddress GetEndAddress() const {
43 return (m_page_index + m_num_pages) * PageSize; 42 return (m_page_index + m_num_pages) * PageSize;
44 } 43 }
45 constexpr PAddr GetLastAddress() const { 44 constexpr KPhysicalAddress GetLastAddress() const {
46 return this->GetEndAddress() - 1; 45 return this->GetEndAddress() - 1;
47 } 46 }
48 47
@@ -62,8 +61,8 @@ public:
62 return !(*this == rhs); 61 return !(*this == rhs);
63 } 62 }
64 63
65 constexpr bool IsStrictlyBefore(PAddr addr) const { 64 constexpr bool IsStrictlyBefore(KPhysicalAddress addr) const {
66 const PAddr end = this->GetEndAddress(); 65 const KPhysicalAddress end = this->GetEndAddress();
67 66
68 if (m_page_index != 0 && end == 0) { 67 if (m_page_index != 0 && end == 0) {
69 return false; 68 return false;
@@ -72,11 +71,11 @@ public:
72 return end < addr; 71 return end < addr;
73 } 72 }
74 73
75 constexpr bool operator<(PAddr addr) const { 74 constexpr bool operator<(KPhysicalAddress addr) const {
76 return this->IsStrictlyBefore(addr); 75 return this->IsStrictlyBefore(addr);
77 } 76 }
78 77
79 constexpr bool TryConcatenate(PAddr addr, size_t np) { 78 constexpr bool TryConcatenate(KPhysicalAddress addr, size_t np) {
80 if (addr != 0 && addr == this->GetEndAddress()) { 79 if (addr != 0 && addr == this->GetEndAddress()) {
81 m_num_pages += static_cast<u32>(np); 80 m_num_pages += static_cast<u32>(np);
82 return true; 81 return true;
@@ -90,96 +89,118 @@ private:
90 } 89 }
91 90
92private: 91private:
92 friend class KPageGroup;
93
93 KBlockInfo* m_next{}; 94 KBlockInfo* m_next{};
94 u32 m_page_index{}; 95 u32 m_page_index{};
95 u32 m_num_pages{}; 96 u32 m_num_pages{};
96}; 97};
97static_assert(sizeof(KBlockInfo) <= 0x10); 98static_assert(sizeof(KBlockInfo) <= 0x10);
98 99
99class KPageGroup final { 100class KPageGroup {
100public: 101public:
101 class Node final { 102 class Iterator {
102 public: 103 public:
103 constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {} 104 using iterator_category = std::forward_iterator_tag;
105 using value_type = const KBlockInfo;
106 using difference_type = std::ptrdiff_t;
107 using pointer = value_type*;
108 using reference = value_type&;
109
110 constexpr explicit Iterator(pointer n) : m_node(n) {}
111
112 constexpr bool operator==(const Iterator& rhs) const {
113 return m_node == rhs.m_node;
114 }
115 constexpr bool operator!=(const Iterator& rhs) const {
116 return !(*this == rhs);
117 }
104 118
105 constexpr u64 GetAddress() const { 119 constexpr pointer operator->() const {
106 return addr; 120 return m_node;
121 }
122 constexpr reference operator*() const {
123 return *m_node;
107 } 124 }
108 125
109 constexpr std::size_t GetNumPages() const { 126 constexpr Iterator& operator++() {
110 return num_pages; 127 m_node = m_node->GetNext();
128 return *this;
111 } 129 }
112 130
113 constexpr std::size_t GetSize() const { 131 constexpr Iterator operator++(int) {
114 return GetNumPages() * PageSize; 132 const Iterator it{*this};
133 ++(*this);
134 return it;
115 } 135 }
116 136
117 private: 137 private:
118 u64 addr{}; 138 pointer m_node{};
119 std::size_t num_pages{};
120 }; 139 };
121 140
122public: 141 explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m)
123 KPageGroup() = default; 142 : m_kernel{kernel}, m_manager{m} {}
124 KPageGroup(u64 address, u64 num_pages) { 143 ~KPageGroup() {
125 ASSERT(AddBlock(address, num_pages).IsSuccess()); 144 this->Finalize();
126 } 145 }
127 146
128 constexpr std::list<Node>& Nodes() { 147 void CloseAndReset();
129 return nodes; 148 void Finalize();
130 }
131 149
132 constexpr const std::list<Node>& Nodes() const { 150 Iterator begin() const {
133 return nodes; 151 return Iterator{m_first_block};
152 }
153 Iterator end() const {
154 return Iterator{nullptr};
155 }
156 bool empty() const {
157 return m_first_block == nullptr;
134 } 158 }
135 159
136 std::size_t GetNumPages() const { 160 Result AddBlock(KPhysicalAddress addr, size_t num_pages);
137 std::size_t num_pages = 0; 161 void Open() const;
138 for (const Node& node : nodes) { 162 void OpenFirst() const;
139 num_pages += node.GetNumPages(); 163 void Close() const;
140 } 164
141 return num_pages; 165 size_t GetNumPages() const;
142 } 166
143 167 bool IsEquivalentTo(const KPageGroup& rhs) const;
144 bool IsEqual(KPageGroup& other) const { 168
145 auto this_node = nodes.begin(); 169 bool operator==(const KPageGroup& rhs) const {
146 auto other_node = other.nodes.begin(); 170 return this->IsEquivalentTo(rhs);
147 while (this_node != nodes.end() && other_node != other.nodes.end()) { 171 }
148 if (this_node->GetAddress() != other_node->GetAddress() ||
149 this_node->GetNumPages() != other_node->GetNumPages()) {
150 return false;
151 }
152 this_node = std::next(this_node);
153 other_node = std::next(other_node);
154 }
155 172
156 return this_node == nodes.end() && other_node == other.nodes.end(); 173 bool operator!=(const KPageGroup& rhs) const {
174 return !(*this == rhs);
157 } 175 }
158 176
159 Result AddBlock(u64 address, u64 num_pages) { 177private:
160 if (!num_pages) { 178 KernelCore& m_kernel;
161 return ResultSuccess; 179 KBlockInfo* m_first_block{};
180 KBlockInfo* m_last_block{};
181 KBlockInfoManager* m_manager{};
182};
183
184class KScopedPageGroup {
185public:
186 explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
187 if (m_pg) {
188 m_pg->Open();
162 } 189 }
163 if (!nodes.empty()) { 190 }
164 const auto node = nodes.back(); 191 explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
165 if (node.GetAddress() + node.GetNumPages() * PageSize == address) { 192 ~KScopedPageGroup() {
166 address = node.GetAddress(); 193 if (m_pg) {
167 num_pages += node.GetNumPages(); 194 m_pg->Close();
168 nodes.pop_back();
169 }
170 } 195 }
171 nodes.push_back({address, num_pages});
172 return ResultSuccess;
173 } 196 }
174 197
175 bool Empty() const { 198 void CancelClose() {
176 return nodes.empty(); 199 m_pg = nullptr;
177 } 200 }
178 201
179 void Finalize() {}
180
181private: 202private:
182 std::list<Node> nodes; 203 const KPageGroup* m_pg{};
183}; 204};
184 205
185} // namespace Kernel 206} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 612fc76fa..2e13d5d0d 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -100,7 +100,7 @@ constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType a
100 100
101KPageTable::KPageTable(Core::System& system_) 101KPageTable::KPageTable(Core::System& system_)
102 : m_general_lock{system_.Kernel()}, 102 : m_general_lock{system_.Kernel()},
103 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {} 103 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
104 104
105KPageTable::~KPageTable() = default; 105KPageTable::~KPageTable() = default;
106 106
@@ -373,7 +373,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta
373 m_memory_block_slab_manager); 373 m_memory_block_slab_manager);
374 374
375 // Allocate and open. 375 // Allocate and open.
376 KPageGroup pg; 376 KPageGroup pg{m_kernel, m_block_info_manager};
377 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( 377 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
378 &pg, num_pages, 378 &pg, num_pages,
379 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); 379 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
@@ -432,9 +432,12 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
432 const size_t num_pages = size / PageSize; 432 const size_t num_pages = size / PageSize;
433 433
434 // Create page groups for the memory being mapped. 434 // Create page groups for the memory being mapped.
435 KPageGroup pg; 435 KPageGroup pg{m_kernel, m_block_info_manager};
436 AddRegionToPages(src_address, num_pages, pg); 436 AddRegionToPages(src_address, num_pages, pg);
437 437
438 // We're going to perform an update, so create a helper.
439 KScopedPageTableUpdater updater(this);
440
438 // Reprotect the source as kernel-read/not mapped. 441 // Reprotect the source as kernel-read/not mapped.
439 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | 442 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
440 KMemoryPermission::NotMapped); 443 KMemoryPermission::NotMapped);
@@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
447 }); 450 });
448 451
449 // Map the alias pages. 452 // Map the alias pages.
450 R_TRY(MapPages(dst_address, pg, new_perm)); 453 const KPageProperties dst_properties = {new_perm, false, false,
454 DisableMergeAttribute::DisableHead};
455 R_TRY(
456 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
451 457
452 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on 458 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
453 // failure. 459 // failure.
@@ -593,7 +599,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
593 const size_t size = num_pages * PageSize; 599 const size_t size = num_pages * PageSize;
594 600
595 // We're making a new group, not adding to an existing one. 601 // We're making a new group, not adding to an existing one.
596 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); 602 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
597 603
598 // Begin traversal. 604 // Begin traversal.
599 Common::PageTable::TraversalContext context; 605 Common::PageTable::TraversalContext context;
@@ -640,11 +646,10 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
640 R_SUCCEED(); 646 R_SUCCEED();
641} 647}
642 648
643bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { 649bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) {
644 ASSERT(this->IsLockedByCurrentThread()); 650 ASSERT(this->IsLockedByCurrentThread());
645 651
646 const size_t size = num_pages * PageSize; 652 const size_t size = num_pages * PageSize;
647 const auto& pg = pg_ll.Nodes();
648 const auto& memory_layout = m_system.Kernel().MemoryLayout(); 653 const auto& memory_layout = m_system.Kernel().MemoryLayout();
649 654
650 // Empty groups are necessarily invalid. 655 // Empty groups are necessarily invalid.
@@ -942,9 +947,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
942 947
943 ON_RESULT_FAILURE { 948 ON_RESULT_FAILURE {
944 if (cur_mapped_addr != dst_addr) { 949 if (cur_mapped_addr != dst_addr) {
945 // HACK: Manually close the pages.
946 HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize);
947
948 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, 950 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
949 KMemoryPermission::None, OperationType::Unmap) 951 KMemoryPermission::None, OperationType::Unmap)
950 .IsSuccess()); 952 .IsSuccess());
@@ -1020,9 +1022,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1020 // Map the page. 1022 // Map the page.
1021 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); 1023 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
1022 1024
1023 // HACK: Manually open the pages.
1024 HACK_OpenPages(start_partial_page, 1);
1025
1026 // Update tracking extents. 1025 // Update tracking extents.
1027 cur_mapped_addr += PageSize; 1026 cur_mapped_addr += PageSize;
1028 cur_block_addr += PageSize; 1027 cur_block_addr += PageSize;
@@ -1051,9 +1050,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1051 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, 1050 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
1052 cur_block_addr)); 1051 cur_block_addr));
1053 1052
1054 // HACK: Manually open the pages.
1055 HACK_OpenPages(cur_block_addr, cur_block_size / PageSize);
1056
1057 // Update tracking extents. 1053 // Update tracking extents.
1058 cur_mapped_addr += cur_block_size; 1054 cur_mapped_addr += cur_block_size;
1059 cur_block_addr = next_entry.phys_addr; 1055 cur_block_addr = next_entry.phys_addr;
@@ -1073,9 +1069,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1073 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, 1069 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
1074 cur_block_addr)); 1070 cur_block_addr));
1075 1071
1076 // HACK: Manually open the pages.
1077 HACK_OpenPages(cur_block_addr, last_block_size / PageSize);
1078
1079 // Update tracking extents. 1072 // Update tracking extents.
1080 cur_mapped_addr += last_block_size; 1073 cur_mapped_addr += last_block_size;
1081 cur_block_addr += last_block_size; 1074 cur_block_addr += last_block_size;
@@ -1107,9 +1100,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1107 1100
1108 // Map the page. 1101 // Map the page.
1109 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); 1102 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
1110
1111 // HACK: Manually open the pages.
1112 HACK_OpenPages(end_partial_page, 1);
1113 } 1103 }
1114 1104
1115 // Update memory blocks to reflect our changes 1105 // Update memory blocks to reflect our changes
@@ -1211,9 +1201,6 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
1211 const size_t aligned_size = aligned_end - aligned_start; 1201 const size_t aligned_size = aligned_end - aligned_start;
1212 const size_t aligned_num_pages = aligned_size / PageSize; 1202 const size_t aligned_num_pages = aligned_size / PageSize;
1213 1203
1214 // HACK: Manually close the pages.
1215 HACK_ClosePages(aligned_start, aligned_num_pages);
1216
1217 // Unmap the pages. 1204 // Unmap the pages.
1218 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); 1205 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
1219 1206
@@ -1501,17 +1488,6 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi
1501 } 1488 }
1502} 1489}
1503 1490
1504void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) {
1505 m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages);
1506}
1507
1508void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) {
1509 for (size_t index = 0; index < num_pages; ++index) {
1510 const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize));
1511 m_system.Kernel().MemoryManager().Close(paddr, 1);
1512 }
1513}
1514
1515Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { 1491Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1516 // Lock the physical memory lock. 1492 // Lock the physical memory lock.
1517 KScopedLightLock phys_lk(m_map_physical_memory_lock); 1493 KScopedLightLock phys_lk(m_map_physical_memory_lock);
@@ -1572,7 +1548,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1572 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1548 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1573 1549
1574 // Allocate pages for the new memory. 1550 // Allocate pages for the new memory.
1575 KPageGroup pg; 1551 KPageGroup pg{m_kernel, m_block_info_manager};
1576 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( 1552 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
1577 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); 1553 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
1578 1554
@@ -1650,7 +1626,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1650 KScopedPageTableUpdater updater(this); 1626 KScopedPageTableUpdater updater(this);
1651 1627
1652 // Prepare to iterate over the memory. 1628 // Prepare to iterate over the memory.
1653 auto pg_it = pg.Nodes().begin(); 1629 auto pg_it = pg.begin();
1654 PAddr pg_phys_addr = pg_it->GetAddress(); 1630 PAddr pg_phys_addr = pg_it->GetAddress();
1655 size_t pg_pages = pg_it->GetNumPages(); 1631 size_t pg_pages = pg_it->GetNumPages();
1656 1632
@@ -1680,9 +1656,6 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1680 last_unmap_address + 1 - cur_address) / 1656 last_unmap_address + 1 - cur_address) /
1681 PageSize; 1657 PageSize;
1682 1658
1683 // HACK: Manually close the pages.
1684 HACK_ClosePages(cur_address, cur_pages);
1685
1686 // Unmap. 1659 // Unmap.
1687 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, 1660 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
1688 OperationType::Unmap) 1661 OperationType::Unmap)
@@ -1703,7 +1676,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1703 // Release any remaining unmapped memory. 1676 // Release any remaining unmapped memory.
1704 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); 1677 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
1705 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); 1678 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
1706 for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) { 1679 for (++pg_it; pg_it != pg.end(); ++pg_it) {
1707 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), 1680 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
1708 pg_it->GetNumPages()); 1681 pg_it->GetNumPages());
1709 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), 1682 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
@@ -1731,7 +1704,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1731 // Check if we're at the end of the physical block. 1704 // Check if we're at the end of the physical block.
1732 if (pg_pages == 0) { 1705 if (pg_pages == 0) {
1733 // Ensure there are more pages to map. 1706 // Ensure there are more pages to map.
1734 ASSERT(pg_it != pg.Nodes().end()); 1707 ASSERT(pg_it != pg.end());
1735 1708
1736 // Advance our physical block. 1709 // Advance our physical block.
1737 ++pg_it; 1710 ++pg_it;
@@ -1742,10 +1715,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1742 // Map whatever we can. 1715 // Map whatever we can.
1743 const size_t cur_pages = std::min(pg_pages, map_pages); 1716 const size_t cur_pages = std::min(pg_pages, map_pages);
1744 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, 1717 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
1745 OperationType::Map, pg_phys_addr)); 1718 OperationType::MapFirst, pg_phys_addr));
1746
1747 // HACK: Manually open the pages.
1748 HACK_OpenPages(pg_phys_addr, cur_pages);
1749 1719
1750 // Advance. 1720 // Advance.
1751 cur_address += cur_pages * PageSize; 1721 cur_address += cur_pages * PageSize;
@@ -1888,9 +1858,6 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1888 last_address + 1 - cur_address) / 1858 last_address + 1 - cur_address) /
1889 PageSize; 1859 PageSize;
1890 1860
1891 // HACK: Manually close the pages.
1892 HACK_ClosePages(cur_address, cur_pages);
1893
1894 // Unmap. 1861 // Unmap.
1895 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) 1862 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
1896 .IsSuccess()); 1863 .IsSuccess());
@@ -1920,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1920 R_SUCCEED(); 1887 R_SUCCEED();
1921} 1888}
1922 1889
1923Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1890Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1891 size_t size) {
1924 // Lock the table. 1892 // Lock the table.
1925 KScopedLightLock lk(m_general_lock); 1893 KScopedLightLock lk(m_general_lock);
1926 1894
@@ -1941,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
1941 KMemoryAttribute::None)); 1909 KMemoryAttribute::None));
1942 1910
1943 // Create an update allocator for the source. 1911 // Create an update allocator for the source.
1944 Result src_allocator_result{ResultSuccess}; 1912 Result src_allocator_result;
1945 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 1913 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1946 m_memory_block_slab_manager, 1914 m_memory_block_slab_manager,
1947 num_src_allocator_blocks); 1915 num_src_allocator_blocks);
1948 R_TRY(src_allocator_result); 1916 R_TRY(src_allocator_result);
1949 1917
1950 // Create an update allocator for the destination. 1918 // Create an update allocator for the destination.
1951 Result dst_allocator_result{ResultSuccess}; 1919 Result dst_allocator_result;
1952 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 1920 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1953 m_memory_block_slab_manager, 1921 m_memory_block_slab_manager,
1954 num_dst_allocator_blocks); 1922 num_dst_allocator_blocks);
1955 R_TRY(dst_allocator_result); 1923 R_TRY(dst_allocator_result);
1956 1924
1957 // Map the memory. 1925 // Map the memory.
1958 KPageGroup page_linked_list;
1959 const size_t num_pages{size / PageSize};
1960 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1961 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1962 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1963
1964 AddRegionToPages(src_address, num_pages, page_linked_list);
1965 { 1926 {
1927 // Determine the number of pages being operated on.
1928 const size_t num_pages = size / PageSize;
1929
1930 // Create page groups for the memory being unmapped.
1931 KPageGroup pg{m_kernel, m_block_info_manager};
1932
1933 // Create the page group representing the source.
1934 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1935
1936 // We're going to perform an update, so create a helper.
1937 KScopedPageTableUpdater updater(this);
1938
1966 // Reprotect the source as kernel-read/not mapped. 1939 // Reprotect the source as kernel-read/not mapped.
1967 auto block_guard = detail::ScopeExit([&] { 1940 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1968 Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 1941 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1969 OperationType::ChangePermissions); 1942 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1970 }); 1943 const KPageProperties src_properties = {new_src_perm, false, false,
1971 R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); 1944 DisableMergeAttribute::DisableHeadBodyTail};
1972 R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); 1945 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1946 OperationType::ChangePermissions));
1973 1947
1974 block_guard.Cancel(); 1948 // Ensure that we unprotect the source pages on failure.
1975 } 1949 ON_RESULT_FAILURE {
1950 const KPageProperties unprotect_properties = {
1951 KMemoryPermission::UserReadWrite, false, false,
1952 DisableMergeAttribute::EnableHeadBodyTail};
1953 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
1954 OperationType::ChangePermissions) == ResultSuccess);
1955 };
1976 1956
1977 // Apply the memory block updates. 1957 // Map the alias pages.
1978 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 1958 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1979 new_src_perm, new_src_attr, 1959 DisableMergeAttribute::DisableHead};
1980 KMemoryBlockDisableMergeAttribute::Locked, 1960 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1981 KMemoryBlockDisableMergeAttribute::None); 1961 false));
1982 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 1962
1983 KMemoryState::Stack, KMemoryPermission::UserReadWrite, 1963 // Apply the memory block updates.
1984 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, 1964 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1985 KMemoryBlockDisableMergeAttribute::None); 1965 src_state, new_src_perm, new_src_attr,
1966 KMemoryBlockDisableMergeAttribute::Locked,
1967 KMemoryBlockDisableMergeAttribute::None);
1968 m_memory_block_manager.Update(
1969 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1970 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1971 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1972 }
1986 1973
1987 R_SUCCEED(); 1974 R_SUCCEED();
1988} 1975}
1989 1976
1990Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1977Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1978 size_t size) {
1991 // Lock the table. 1979 // Lock the table.
1992 KScopedLightLock lk(m_general_lock); 1980 KScopedLightLock lk(m_general_lock);
1993 1981
@@ -2009,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
2009 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); 1997 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
2010 1998
2011 // Create an update allocator for the source. 1999 // Create an update allocator for the source.
2012 Result src_allocator_result{ResultSuccess}; 2000 Result src_allocator_result;
2013 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 2001 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
2014 m_memory_block_slab_manager, 2002 m_memory_block_slab_manager,
2015 num_src_allocator_blocks); 2003 num_src_allocator_blocks);
2016 R_TRY(src_allocator_result); 2004 R_TRY(src_allocator_result);
2017 2005
2018 // Create an update allocator for the destination. 2006 // Create an update allocator for the destination.
2019 Result dst_allocator_result{ResultSuccess}; 2007 Result dst_allocator_result;
2020 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 2008 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
2021 m_memory_block_slab_manager, 2009 m_memory_block_slab_manager,
2022 num_dst_allocator_blocks); 2010 num_dst_allocator_blocks);
2023 R_TRY(dst_allocator_result); 2011 R_TRY(dst_allocator_result);
2024 2012
2025 KPageGroup src_pages; 2013 // Unmap the memory.
2026 KPageGroup dst_pages; 2014 {
2027 const size_t num_pages{size / PageSize}; 2015 // Determine the number of pages being operated on.
2016 const size_t num_pages = size / PageSize;
2028 2017
2029 AddRegionToPages(src_address, num_pages, src_pages); 2018 // Create page groups for the memory being unmapped.
2030 AddRegionToPages(dst_address, num_pages, dst_pages); 2019 KPageGroup pg{m_kernel, m_block_info_manager};
2031 2020
2032 R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion); 2021 // Create the page group representing the destination.
2022 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
2033 2023
2034 { 2024 // Ensure the page group is the valid for the source.
2035 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); 2025 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
2036 2026
2037 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2027 // We're going to perform an update, so create a helper.
2038 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 2028 KScopedPageTableUpdater updater(this);
2039 OperationType::ChangePermissions));
2040 2029
2041 block_guard.Cancel(); 2030 // Unmap the aliased copy of the pages.
2042 } 2031 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2032 DisableMergeAttribute::None};
2033 R_TRY(
2034 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2035
2036 // Ensure that we re-map the aliased pages on failure.
2037 ON_RESULT_FAILURE {
2038 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2039 };
2043 2040
2044 // Apply the memory block updates. 2041 // Try to set the permissions for the source pages back to what they should be.
2045 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 2042 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2046 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, 2043 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2047 KMemoryBlockDisableMergeAttribute::None, 2044 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2048 KMemoryBlockDisableMergeAttribute::Locked); 2045 OperationType::ChangePermissions));
2049 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 2046
2050 KMemoryState::None, KMemoryPermission::None, 2047 // Apply the memory block updates.
2051 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 2048 m_memory_block_manager.Update(
2052 KMemoryBlockDisableMergeAttribute::Normal); 2049 std::addressof(src_allocator), src_address, num_pages, src_state,
2050 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2051 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2052 m_memory_block_manager.Update(
2053 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2054 KMemoryPermission::None, KMemoryAttribute::None,
2055 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2056 }
2053 2057
2054 R_SUCCEED(); 2058 R_SUCCEED();
2055} 2059}
2056 2060
2057Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, 2061Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2058 KMemoryPermission perm) { 2062 size_t num_pages, KMemoryPermission perm) {
2059 ASSERT(this->IsLockedByCurrentThread()); 2063 ASSERT(this->IsLockedByCurrentThread());
2060 2064
2061 VAddr cur_addr{addr}; 2065 // Create a page group to hold the pages we allocate.
2066 KPageGroup pg{m_kernel, m_block_info_manager};
2062 2067
2063 for (const auto& node : page_linked_list.Nodes()) { 2068 // Allocate the pages.
2064 if (const auto result{ 2069 R_TRY(
2065 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 2070 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2066 result.IsError()) {
2067 const size_t num_pages{(addr - cur_addr) / PageSize};
2068 2071
2069 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) 2072 // Ensure that the page group is closed when we're done working with it.
2070 .IsSuccess()); 2073 SCOPE_EXIT({ pg.Close(); });
2074
2075 // Clear all pages.
2076 for (const auto& it : pg) {
2077 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2078 it.GetSize());
2079 }
2080
2081 // Map the pages.
2082 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2083}
2084
2085Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2086 const KPageGroup& pg, const KPageProperties properties,
2087 bool reuse_ll) {
2088 ASSERT(this->IsLockedByCurrentThread());
2089
2090 // Note the current address, so that we can iterate.
2091 const KProcessAddress start_address = address;
2092 KProcessAddress cur_address = address;
2071 2093
2072 R_RETURN(result); 2094 // Ensure that we clean up on failure.
2095 ON_RESULT_FAILURE {
2096 ASSERT(!reuse_ll);
2097 if (cur_address != start_address) {
2098 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2099 DisableMergeAttribute::None};
2100 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2101 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2073 } 2102 }
2103 };
2074 2104
2075 cur_addr += node.GetNumPages() * PageSize; 2105 // Iterate, mapping all pages in the group.
2106 for (const auto& block : pg) {
2107 // Map and advance.
2108 const KPageProperties cur_properties =
2109 (cur_address == start_address)
2110 ? properties
2111 : KPageProperties{properties.perm, properties.io, properties.uncached,
2112 DisableMergeAttribute::None};
2113 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2114 block.GetAddress());
2115 cur_address += block.GetSize();
2076 } 2116 }
2077 2117
2118 // We succeeded!
2078 R_SUCCEED(); 2119 R_SUCCEED();
2079} 2120}
2080 2121
2081Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, 2122void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2082 KMemoryPermission perm) { 2123 const KPageGroup& pg) {
2083 // Check that the map is in range. 2124 ASSERT(this->IsLockedByCurrentThread());
2084 const size_t num_pages{page_linked_list.GetNumPages()};
2085 const size_t size{num_pages * PageSize};
2086 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2087 2125
2088 // Lock the table. 2126 // Note the current address, so that we can iterate.
2089 KScopedLightLock lk(m_general_lock); 2127 const KProcessAddress start_address = address;
2128 const KProcessAddress last_address = start_address + size - 1;
2129 const KProcessAddress end_address = last_address + 1;
2090 2130
2091 // Check the memory state. 2131 // Iterate over the memory.
2092 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 2132 auto pg_it = pg.begin();
2093 KMemoryPermission::None, KMemoryPermission::None, 2133 ASSERT(pg_it != pg.end());
2094 KMemoryAttribute::None, KMemoryAttribute::None));
2095 2134
2096 // Create an update allocator. 2135 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2097 Result allocator_result{ResultSuccess}; 2136 size_t pg_pages = pg_it->GetNumPages();
2098 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2099 m_memory_block_slab_manager);
2100 2137
2101 // Map the pages. 2138 auto it = m_memory_block_manager.FindIterator(start_address);
2102 R_TRY(MapPages(address, page_linked_list, perm)); 2139 while (true) {
2140 // Check that the iterator is valid.
2141 ASSERT(it != m_memory_block_manager.end());
2103 2142
2104 // Update the blocks. 2143 // Get the memory info.
2105 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, 2144 const KMemoryInfo info = it->GetMemoryInfo();
2106 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2107 KMemoryBlockDisableMergeAttribute::None);
2108 2145
2109 R_SUCCEED(); 2146 // Determine the range to map.
2147 KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address);
2148 const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address);
2149 ASSERT(map_end_address != map_address);
2150
2151 // Determine if we should disable head merge.
2152 const bool disable_head_merge =
2153 info.GetAddress() >= start_address &&
2154 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2155 const KPageProperties map_properties = {
2156 info.GetPermission(), false, false,
2157 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2158
2159 // While we have pages to map, map them.
2160 size_t map_pages = (map_end_address - map_address) / PageSize;
2161 while (map_pages > 0) {
2162 // Check if we're at the end of the physical block.
2163 if (pg_pages == 0) {
2164 // Ensure there are more pages to map.
2165 ASSERT(pg_it != pg.end());
2166
2167 // Advance our physical block.
2168 ++pg_it;
2169 pg_phys_addr = pg_it->GetAddress();
2170 pg_pages = pg_it->GetNumPages();
2171 }
2172
2173 // Map whatever we can.
2174 const size_t cur_pages = std::min(pg_pages, map_pages);
2175 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2176 pg_phys_addr) == ResultSuccess);
2177
2178 // Advance.
2179 map_address += cur_pages * PageSize;
2180 map_pages -= cur_pages;
2181
2182 pg_phys_addr += cur_pages * PageSize;
2183 pg_pages -= cur_pages;
2184 }
2185
2186 // Check if we're done.
2187 if (last_address <= info.GetLastAddress()) {
2188 break;
2189 }
2190
2191 // Advance.
2192 ++it;
2193 }
2194
2195 // Check that we re-mapped precisely the page group.
2196 ASSERT((++pg_it) == pg.end());
2110} 2197}
2111 2198
2112Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 2199Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2113 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 2200 KPhysicalAddress phys_addr, bool is_pa_valid,
2201 KProcessAddress region_start, size_t region_num_pages,
2114 KMemoryState state, KMemoryPermission perm) { 2202 KMemoryState state, KMemoryPermission perm) {
2115 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); 2203 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2116 2204
@@ -2123,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2123 KScopedLightLock lk(m_general_lock); 2211 KScopedLightLock lk(m_general_lock);
2124 2212
2125 // Find a random address to map at. 2213 // Find a random address to map at.
2126 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, 2214 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2127 this->GetNumGuardPages()); 2215 0, this->GetNumGuardPages());
2128 R_UNLESS(addr != 0, ResultOutOfMemory); 2216 R_UNLESS(addr != 0, ResultOutOfMemory);
2129 ASSERT(Common::IsAligned(addr, alignment)); 2217 ASSERT(Common::IsAligned(addr, alignment));
2130 ASSERT(this->CanContain(addr, num_pages * PageSize, state)); 2218 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2131 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, 2219 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2132 KMemoryPermission::None, KMemoryPermission::None, 2220 KMemoryPermission::None, KMemoryPermission::None,
2133 KMemoryAttribute::None, KMemoryAttribute::None) 2221 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2134 .IsSuccess());
2135 2222
2136 // Create an update allocator. 2223 // Create an update allocator.
2137 Result allocator_result{ResultSuccess}; 2224 Result allocator_result;
2138 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2225 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2139 m_memory_block_slab_manager); 2226 m_memory_block_slab_manager);
2227 R_TRY(allocator_result);
2228
2229 // We're going to perform an update, so create a helper.
2230 KScopedPageTableUpdater updater(this);
2140 2231
2141 // Perform mapping operation. 2232 // Perform mapping operation.
2142 if (is_pa_valid) { 2233 if (is_pa_valid) {
2143 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); 2234 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2235 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2144 } else { 2236 } else {
2145 UNIMPLEMENTED(); 2237 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2146 } 2238 }
2147 2239
2148 // Update the blocks. 2240 // Update the blocks.
@@ -2155,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2155 R_SUCCEED(); 2247 R_SUCCEED();
2156} 2248}
2157 2249
2158Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { 2250Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2159 ASSERT(this->IsLockedByCurrentThread()); 2251 KMemoryPermission perm) {
2252 // Check that the map is in range.
2253 const size_t size = num_pages * PageSize;
2254 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2160 2255
2161 VAddr cur_addr{addr}; 2256 // Lock the table.
2257 KScopedLightLock lk(m_general_lock);
2162 2258
2163 for (const auto& node : page_linked_list.Nodes()) { 2259 // Check the memory state.
2164 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 2260 size_t num_allocator_blocks;
2165 OperationType::Unmap)}; 2261 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2166 result.IsError()) { 2262 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2167 R_RETURN(result); 2263 KMemoryPermission::None, KMemoryAttribute::None,
2168 } 2264 KMemoryAttribute::None));
2169 2265
2170 cur_addr += node.GetNumPages() * PageSize; 2266 // Create an update allocator.
2171 } 2267 Result allocator_result;
2268 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2269 m_memory_block_slab_manager, num_allocator_blocks);
2270 R_TRY(allocator_result);
2271
2272 // We're going to perform an update, so create a helper.
2273 KScopedPageTableUpdater updater(this);
2274
2275 // Map the pages.
2276 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2277
2278 // Update the blocks.
2279 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2280 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2281 KMemoryBlockDisableMergeAttribute::None);
2172 2282
2173 R_SUCCEED(); 2283 R_SUCCEED();
2174} 2284}
2175 2285
2176Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { 2286Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2177 // Check that the unmap is in range. 2287 // Check that the unmap is in range.
2178 const size_t num_pages{page_linked_list.GetNumPages()}; 2288 const size_t size = num_pages * PageSize;
2179 const size_t size{num_pages * PageSize};
2180 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2289 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2181 2290
2182 // Lock the table. 2291 // Lock the table.
@@ -2190,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2190 KMemoryAttribute::None)); 2299 KMemoryAttribute::None));
2191 2300
2192 // Create an update allocator. 2301 // Create an update allocator.
2193 Result allocator_result{ResultSuccess}; 2302 Result allocator_result;
2194 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2303 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2195 m_memory_block_slab_manager, num_allocator_blocks); 2304 m_memory_block_slab_manager, num_allocator_blocks);
2196 R_TRY(allocator_result); 2305 R_TRY(allocator_result);
2197 2306
2307 // We're going to perform an update, so create a helper.
2308 KScopedPageTableUpdater updater(this);
2309
2198 // Perform the unmap. 2310 // Perform the unmap.
2199 R_TRY(UnmapPages(address, page_linked_list)); 2311 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2312 DisableMergeAttribute::None};
2313 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2200 2314
2201 // Update the blocks. 2315 // Update the blocks.
2202 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2316 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2207,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2207 R_SUCCEED(); 2321 R_SUCCEED();
2208} 2322}
2209 2323
2210Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { 2324Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2211 // Check that the unmap is in range. 2325 KProcessAddress region_start, size_t region_num_pages,
2326 KMemoryState state, KMemoryPermission perm) {
2327 ASSERT(!this->IsLockedByCurrentThread());
2328
2329 // Ensure this is a valid map request.
2330 const size_t num_pages = pg.GetNumPages();
2331 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2332 ResultInvalidCurrentMemory);
2333 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2334
2335 // Lock the table.
2336 KScopedLightLock lk(m_general_lock);
2337
2338 // Find a random address to map at.
2339 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2340 0, this->GetNumGuardPages());
2341 R_UNLESS(addr != 0, ResultOutOfMemory);
2342 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2343 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2344 KMemoryPermission::None, KMemoryPermission::None,
2345 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2346
2347 // Create an update allocator.
2348 Result allocator_result;
2349 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2350 m_memory_block_slab_manager);
2351 R_TRY(allocator_result);
2352
2353 // We're going to perform an update, so create a helper.
2354 KScopedPageTableUpdater updater(this);
2355
2356 // Perform mapping operation.
2357 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2358 DisableMergeAttribute::DisableHead};
2359 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2360
2361 // Update the blocks.
2362 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2363 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2364 KMemoryBlockDisableMergeAttribute::None);
2365
2366 // We successfully mapped the pages.
2367 *out_addr = addr;
2368 R_SUCCEED();
2369}
2370
2371Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2372 KMemoryPermission perm) {
2373 ASSERT(!this->IsLockedByCurrentThread());
2374
2375 // Ensure this is a valid map request.
2376 const size_t num_pages = pg.GetNumPages();
2212 const size_t size = num_pages * PageSize; 2377 const size_t size = num_pages * PageSize;
2213 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2378 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2214 2379
2215 // Lock the table. 2380 // Lock the table.
2216 KScopedLightLock lk(m_general_lock); 2381 KScopedLightLock lk(m_general_lock);
2217 2382
2218 // Check the memory state. 2383 // Check if state allows us to map.
2219 size_t num_allocator_blocks{}; 2384 size_t num_allocator_blocks;
2385 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2386 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2387 KMemoryPermission::None, KMemoryAttribute::None,
2388 KMemoryAttribute::None));
2389
2390 // Create an update allocator.
2391 Result allocator_result;
2392 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2393 m_memory_block_slab_manager, num_allocator_blocks);
2394 R_TRY(allocator_result);
2395
2396 // We're going to perform an update, so create a helper.
2397 KScopedPageTableUpdater updater(this);
2398
2399 // Perform mapping operation.
2400 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2401 DisableMergeAttribute::DisableHead};
2402 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2403
2404 // Update the blocks.
2405 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2406 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2407 KMemoryBlockDisableMergeAttribute::None);
2408
2409 // We successfully mapped the pages.
2410 R_SUCCEED();
2411}
2412
2413Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2414 KMemoryState state) {
2415 ASSERT(!this->IsLockedByCurrentThread());
2416
2417 // Ensure this is a valid unmap request.
2418 const size_t num_pages = pg.GetNumPages();
2419 const size_t size = num_pages * PageSize;
2420 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2421
2422 // Lock the table.
2423 KScopedLightLock lk(m_general_lock);
2424
2425 // Check if state allows us to unmap.
2426 size_t num_allocator_blocks;
2220 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, 2427 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2221 KMemoryState::All, state, KMemoryPermission::None, 2428 KMemoryState::All, state, KMemoryPermission::None,
2222 KMemoryPermission::None, KMemoryAttribute::All, 2429 KMemoryPermission::None, KMemoryAttribute::All,
2223 KMemoryAttribute::None)); 2430 KMemoryAttribute::None));
2224 2431
2432 // Check that the page group is valid.
2433 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2434
2225 // Create an update allocator. 2435 // Create an update allocator.
2226 Result allocator_result{ResultSuccess}; 2436 Result allocator_result;
2227 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2437 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2228 m_memory_block_slab_manager, num_allocator_blocks); 2438 m_memory_block_slab_manager, num_allocator_blocks);
2229 R_TRY(allocator_result); 2439 R_TRY(allocator_result);
2230 2440
2231 // Perform the unmap. 2441 // We're going to perform an update, so create a helper.
2232 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2442 KScopedPageTableUpdater updater(this);
2443
2444 // Perform unmapping operation.
2445 const KPageProperties properties = {KMemoryPermission::None, false, false,
2446 DisableMergeAttribute::None};
2447 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2233 2448
2234 // Update the blocks. 2449 // Update the blocks.
2235 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2450 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2527,13 +2742,13 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2527 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 2742 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2528 2743
2529 // Allocate pages for the heap extension. 2744 // Allocate pages for the heap extension.
2530 KPageGroup pg; 2745 KPageGroup pg{m_kernel, m_block_info_manager};
2531 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( 2746 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2532 &pg, allocation_size / PageSize, 2747 &pg, allocation_size / PageSize,
2533 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); 2748 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2534 2749
2535 // Clear all the newly allocated pages. 2750 // Clear all the newly allocated pages.
2536 for (const auto& it : pg.Nodes()) { 2751 for (const auto& it : pg) {
2537 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, 2752 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2538 it.GetSize()); 2753 it.GetSize());
2539 } 2754 }
@@ -2589,42 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2589 } 2804 }
2590} 2805}
2591 2806
2592ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
2593 bool is_map_only, VAddr region_start,
2594 size_t region_num_pages, KMemoryState state,
2595 KMemoryPermission perm, PAddr map_addr) {
2596 KScopedLightLock lk(m_general_lock);
2597
2598 R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
2599 ResultInvalidCurrentMemory);
2600 R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
2601 const VAddr addr{
2602 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
2603 R_UNLESS(addr, ResultOutOfMemory);
2604
2605 // Create an update allocator.
2606 Result allocator_result{ResultSuccess};
2607 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2608 m_memory_block_slab_manager);
2609
2610 if (is_map_only) {
2611 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
2612 } else {
2613 KPageGroup page_group;
2614 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
2615 &page_group, needed_num_pages,
2616 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
2617 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
2618 }
2619
2620 // Update the blocks.
2621 m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
2622 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2623 KMemoryBlockDisableMergeAttribute::None);
2624
2625 return addr;
2626}
2627
2628Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 2807Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
2629 KMemoryPermission perm, bool is_aligned, 2808 KMemoryPermission perm, bool is_aligned,
2630 bool check_heap) { 2809 bool check_heap) {
@@ -2795,19 +2974,28 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
2795 ASSERT(num_pages > 0); 2974 ASSERT(num_pages > 0);
2796 ASSERT(num_pages == page_group.GetNumPages()); 2975 ASSERT(num_pages == page_group.GetNumPages());
2797 2976
2798 for (const auto& node : page_group.Nodes()) { 2977 switch (operation) {
2799 const size_t size{node.GetNumPages() * PageSize}; 2978 case OperationType::MapGroup: {
2979 // We want to maintain a new reference to every page in the group.
2980 KScopedPageGroup spg(page_group);
2981
2982 for (const auto& node : page_group) {
2983 const size_t size{node.GetNumPages() * PageSize};
2800 2984
2801 switch (operation) { 2985 // Map the pages.
2802 case OperationType::MapGroup:
2803 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); 2986 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
2804 break; 2987
2805 default: 2988 addr += size;
2806 ASSERT(false);
2807 break;
2808 } 2989 }
2809 2990
2810 addr += size; 2991 // We succeeded! We want to persist the reference to the pages.
2992 spg.CancelClose();
2993
2994 break;
2995 }
2996 default:
2997 ASSERT(false);
2998 break;
2811 } 2999 }
2812 3000
2813 R_SUCCEED(); 3001 R_SUCCEED();
@@ -2822,13 +3010,29 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2822 ASSERT(ContainsPages(addr, num_pages)); 3010 ASSERT(ContainsPages(addr, num_pages));
2823 3011
2824 switch (operation) { 3012 switch (operation) {
2825 case OperationType::Unmap: 3013 case OperationType::Unmap: {
3014 // Ensure that any pages we track close on exit.
3015 KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
3016 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
3017
3018 this->AddRegionToPages(addr, num_pages, pages_to_close);
2826 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); 3019 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
2827 break; 3020 break;
3021 }
3022 case OperationType::MapFirst:
2828 case OperationType::Map: { 3023 case OperationType::Map: {
2829 ASSERT(map_addr); 3024 ASSERT(map_addr);
2830 ASSERT(Common::IsAligned(map_addr, PageSize)); 3025 ASSERT(Common::IsAligned(map_addr, PageSize));
2831 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); 3026 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
3027
3028 // Open references to pages, if we should.
3029 if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
3030 if (operation == OperationType::MapFirst) {
3031 m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
3032 } else {
3033 m_kernel.MemoryManager().Open(map_addr, num_pages);
3034 }
3035 }
2832 break; 3036 break;
2833 } 3037 }
2834 case OperationType::Separate: { 3038 case OperationType::Separate: {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index f1ca785d7..367dab613 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -24,12 +24,36 @@ class System;
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27enum class DisableMergeAttribute : u8 {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
27class KBlockInfoManager; 48class KBlockInfoManager;
28class KMemoryBlockManager; 49class KMemoryBlockManager;
29class KResourceLimit; 50class KResourceLimit;
30class KSystemResource; 51class KSystemResource;
31 52
32class KPageTable final { 53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
33public: 57public:
34 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; 58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
35 59
@@ -57,27 +81,12 @@ public:
57 Result UnmapPhysicalMemory(VAddr addr, size_t size); 81 Result UnmapPhysicalMemory(VAddr addr, size_t size);
58 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 82 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
59 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 83 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
60 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
61 KMemoryPermission perm);
62 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
63 KMemoryState state, KMemoryPermission perm) {
64 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
65 this->GetRegionAddress(state),
66 this->GetRegionSize(state) / PageSize, state, perm));
67 }
68 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
69 Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
70 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); 84 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
71 KMemoryInfo QueryInfo(VAddr addr); 85 KMemoryInfo QueryInfo(VAddr addr);
72 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); 86 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
73 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); 87 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
74 Result SetMaxHeapSize(size_t size); 88 Result SetMaxHeapSize(size_t size);
75 Result SetHeapSize(VAddr* out, size_t size); 89 Result SetHeapSize(VAddr* out, size_t size);
76 ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
77 VAddr region_start, size_t region_num_pages,
78 KMemoryState state, KMemoryPermission perm,
79 PAddr map_addr = 0);
80
81 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 90 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
82 KMemoryPermission perm, bool is_aligned, bool check_heap); 91 KMemoryPermission perm, bool is_aligned, bool check_heap);
83 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); 92 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
@@ -107,8 +116,46 @@ public:
107 return *m_page_table_impl; 116 return *m_page_table_impl;
108 } 117 }
109 118
119 KBlockInfoManager* GetBlockInfoManager() {
120 return m_block_info_manager;
121 }
122
110 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 123 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
111 124
125 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
126 KPhysicalAddress phys_addr, KProcessAddress region_start,
127 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
128 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
129 region_num_pages, state, perm));
130 }
131
132 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
133 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
134 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
135 this->GetRegionAddress(state),
136 this->GetRegionSize(state) / PageSize, state, perm));
137 }
138
139 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
140 KMemoryPermission perm) {
141 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
142 this->GetRegionAddress(state),
143 this->GetRegionSize(state) / PageSize, state, perm));
144 }
145
146 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
147 KMemoryPermission perm);
148 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
149
150 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
151 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
152 KMemoryPermission perm);
153 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
154 KMemoryPermission perm);
155 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
156 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
157 const KPageGroup& pg);
158
112protected: 159protected:
113 struct PageLinkedList { 160 struct PageLinkedList {
114 private: 161 private:
@@ -162,11 +209,9 @@ private:
162 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 209 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
163 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; 210 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
164 211
165 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); 212 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
166 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 213 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
167 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 214 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
168 KMemoryState state, KMemoryPermission perm);
169 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
170 bool IsRegionContiguous(VAddr addr, u64 size) const; 215 bool IsRegionContiguous(VAddr addr, u64 size) const;
171 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); 216 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
172 KMemoryInfo QueryInfoImpl(VAddr addr); 217 KMemoryInfo QueryInfoImpl(VAddr addr);
@@ -261,9 +306,10 @@ private:
261 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, 306 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
262 size_t size, KMemoryPermission prot_perm); 307 size_t size, KMemoryPermission prot_perm);
263 308
264 // HACK: These will be removed once we automatically manage page reference counts. 309 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
265 void HACK_OpenPages(PAddr phys_addr, size_t num_pages); 310 size_t num_pages, KMemoryPermission perm);
266 void HACK_ClosePages(VAddr virt_addr, size_t num_pages); 311 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
312 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
267 313
268 mutable KLightLock m_general_lock; 314 mutable KLightLock m_general_lock;
269 mutable KLightLock m_map_physical_memory_lock; 315 mutable KLightLock m_map_physical_memory_lock;
@@ -488,6 +534,7 @@ private:
488 std::unique_ptr<Common::PageTable> m_page_table_impl; 534 std::unique_ptr<Common::PageTable> m_page_table_impl;
489 535
490 Core::System& m_system; 536 Core::System& m_system;
537 KernelCore& m_kernel;
491}; 538};
492 539
493} // namespace Kernel 540} // namespace Kernel
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index cb2512b0b..645c5b531 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -17,35 +17,41 @@ namespace Kernel {
17class KThread; 17class KThread;
18 18
19template <typename T> 19template <typename T>
20concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) { 20concept KPriorityQueueAffinityMask = !
21 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>; 21std::is_reference_v<T>&& requires(T& t) {
22 {t.SetAffinityMask(0)}; 22 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
23 { t.SetAffinityMask(0) };
23 24
24 { t.GetAffinity(0) } -> std::same_as<bool>; 25 { t.GetAffinity(0) } -> std::same_as<bool>;
25 {t.SetAffinity(0, false)}; 26 { t.SetAffinity(0, false) };
26 {t.SetAll()}; 27 { t.SetAll() };
27}; 28 };
28 29
29template <typename T> 30template <typename T>
30concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) { 31concept KPriorityQueueMember = !
31 {typename T::QueueEntry()}; 32std::is_reference_v<T>&& requires(T& t) {
32 {(typename T::QueueEntry()).Initialize()}; 33 { typename T::QueueEntry() };
33 {(typename T::QueueEntry()).SetPrev(std::addressof(t))}; 34 { (typename T::QueueEntry()).Initialize() };
34 {(typename T::QueueEntry()).SetNext(std::addressof(t))}; 35 { (typename T::QueueEntry()).SetPrev(std::addressof(t)) };
35 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; 36 { (typename T::QueueEntry()).SetNext(std::addressof(t)) };
36 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; 37 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
37 { t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>; 38 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
38 39 {
39 {t.GetAffinityMask()}; 40 t.GetPriorityQueueEntry(0)
40 { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask; 41 } -> std::same_as<typename T::QueueEntry&>;
41 42
42 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>; 43 { t.GetAffinityMask() };
43 { t.GetPriority() } -> Common::ConvertibleTo<s32>; 44 {
44 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>; 45 std::remove_cvref_t<decltype(t.GetAffinityMask())>()
45}; 46 } -> KPriorityQueueAffinityMask;
47
48 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
49 { t.GetPriority() } -> Common::ConvertibleTo<s32>;
50 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
51 };
46 52
47template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority> 53template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
48requires KPriorityQueueMember<Member> 54 requires KPriorityQueueMember<Member>
49class KPriorityQueue { 55class KPriorityQueue {
50public: 56public:
51 using AffinityMaskType = std::remove_cv_t< 57 using AffinityMaskType = std::remove_cv_t<
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index a1abf5d68..e201bb0cd 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
417} 417}
418 418
419void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 419void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
420 AllocateMainThreadStack(stack_size); 420 ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);
421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); 421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
422 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
423 422
424 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 423 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
425 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 424 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {
675} 674}
676 675
677Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { 676Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
678 ASSERT(stack_size); 677 // Ensure that we haven't already allocated stack.
679 678 ASSERT(main_thread_stack_size == 0);
680 // The kernel always ensures that the given stack size is page aligned. 679
681 main_thread_stack_size = Common::AlignUp(stack_size, PageSize); 680 // Ensure that we're allocating a valid stack.
682 681 stack_size = Common::AlignUp(stack_size, PageSize);
683 const VAddr start{page_table.GetStackRegionStart()}; 682 // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory);
684 const std::size_t size{page_table.GetStackRegionEnd() - start}; 683 R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory);
685 684
686 CASCADE_RESULT(main_thread_stack_top, 685 // Place a tentative reservation of memory for our new stack.
687 page_table.AllocateAndMapMemory( 686 KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax,
688 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, 687 stack_size);
689 KMemoryState::Stack, KMemoryPermission::UserReadWrite)); 688 R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached);
689
690 // Allocate and map our stack.
691 if (stack_size) {
692 KProcessAddress stack_bottom;
693 R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize,
694 KMemoryState::Stack, KMemoryPermission::UserReadWrite));
695
696 main_thread_stack_top = stack_bottom + stack_size;
697 main_thread_stack_size = stack_size;
698 }
690 699
691 main_thread_stack_top += main_thread_stack_size; 700 // We succeeded! Commit our memory reservation.
701 mem_reservation.Commit();
692 702
693 R_SUCCEED(); 703 R_SUCCEED();
694} 704}
diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h
index 857e21156..59b3e32ae 100644
--- a/src/core/hle/kernel/k_scoped_lock.h
+++ b/src/core/hle/kernel/k_scoped_lock.h
@@ -9,13 +9,14 @@
9namespace Kernel { 9namespace Kernel {
10 10
11template <typename T> 11template <typename T>
12concept KLockable = !std::is_reference_v<T> && requires(T & t) { 12concept KLockable = !
13 { t.Lock() } -> std::same_as<void>; 13std::is_reference_v<T>&& requires(T& t) {
14 { t.Unlock() } -> std::same_as<void>; 14 { t.Lock() } -> std::same_as<void>;
15}; 15 { t.Unlock() } -> std::same_as<void>;
16 };
16 17
17template <typename T> 18template <typename T>
18requires KLockable<T> 19 requires KLockable<T>
19class [[nodiscard]] KScopedLock { 20class [[nodiscard]] KScopedLock {
20public: 21public:
21 explicit KScopedLock(T* l) : lock_ptr(l) { 22 explicit KScopedLock(T* l) : lock_ptr(l) {
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 0aa68103c..df505edfe 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -13,10 +13,7 @@
13namespace Kernel { 13namespace Kernel {
14 14
15KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} 15KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
16 16KSharedMemory::~KSharedMemory() = default;
17KSharedMemory::~KSharedMemory() {
18 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
19}
20 17
21Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, 18Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
22 Svc::MemoryPermission owner_permission_, 19 Svc::MemoryPermission owner_permission_,
@@ -49,7 +46,8 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
49 R_UNLESS(physical_address != 0, ResultOutOfMemory); 46 R_UNLESS(physical_address != 0, ResultOutOfMemory);
50 47
51 //! Insert the result into our page group. 48 //! Insert the result into our page group.
52 page_group.emplace(physical_address, num_pages); 49 page_group.emplace(kernel, &kernel.GetSystemSystemResource().GetBlockInfoManager());
50 page_group->AddBlock(physical_address, num_pages);
53 51
54 // Commit our reservation. 52 // Commit our reservation.
55 memory_reservation.Commit(); 53 memory_reservation.Commit();
@@ -62,7 +60,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
62 is_initialized = true; 60 is_initialized = true;
63 61
64 // Clear all pages in the memory. 62 // Clear all pages in the memory.
65 for (const auto& block : page_group->Nodes()) { 63 for (const auto& block : *page_group) {
66 std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize()); 64 std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize());
67 } 65 }
68 66
@@ -71,13 +69,8 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
71 69
72void KSharedMemory::Finalize() { 70void KSharedMemory::Finalize() {
73 // Close and finalize the page group. 71 // Close and finalize the page group.
74 // page_group->Close(); 72 page_group->Close();
75 // page_group->Finalize(); 73 page_group->Finalize();
76
77 //! HACK: Manually close.
78 for (const auto& block : page_group->Nodes()) {
79 kernel.MemoryManager().Close(block.GetAddress(), block.GetNumPages());
80 }
81 74
82 // Release the memory reservation. 75 // Release the memory reservation.
83 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); 76 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
@@ -101,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m
101 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); 94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
102 } 95 }
103 96
104 return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, 97 return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared,
105 ConvertToKMemoryPermission(map_perm)); 98 ConvertToKMemoryPermission(map_perm));
106} 99}
107 100
108Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { 101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
109 // Validate the size. 102 // Validate the size.
110 R_UNLESS(size == unmap_size, ResultInvalidSize); 103 R_UNLESS(size == unmap_size, ResultInvalidSize);
111 104
112 return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); 105 return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);
113} 106}
114 107
115} // namespace Kernel 108} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 21207fe99..84ff3c64b 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -330,7 +330,7 @@ void KThread::Finalize() {
330 KThread* const waiter = std::addressof(*it); 330 KThread* const waiter = std::addressof(*it);
331 331
332 // The thread shouldn't be a kernel waiter. 332 // The thread shouldn't be a kernel waiter.
333 ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); 333 ASSERT(!waiter->GetAddressKeyIsKernel());
334 334
335 // Clear the lock owner. 335 // Clear the lock owner.
336 waiter->SetLockOwner(nullptr); 336 waiter->SetLockOwner(nullptr);
@@ -763,19 +763,6 @@ void KThread::Continue() {
763 KScheduler::OnThreadStateChanged(kernel, this, old_state); 763 KScheduler::OnThreadStateChanged(kernel, this, old_state);
764} 764}
765 765
766void KThread::WaitUntilSuspended() {
767 // Make sure we have a suspend requested.
768 ASSERT(IsSuspendRequested());
769
770 // Loop until the thread is not executing on any core.
771 for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
772 KThread* core_thread{};
773 do {
774 core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
775 } while (core_thread == this);
776 }
777}
778
779Result KThread::SetActivity(Svc::ThreadActivity activity) { 766Result KThread::SetActivity(Svc::ThreadActivity activity) {
780 // Lock ourselves. 767 // Lock ourselves.
781 KScopedLightLock lk(activity_pause_lock); 768 KScopedLightLock lk(activity_pause_lock);
@@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
897 } 884 }
898 885
899 // Keep track of how many kernel waiters we have. 886 // Keep track of how many kernel waiters we have.
900 if (IsKernelAddressKey(thread->GetAddressKey())) { 887 if (thread->GetAddressKeyIsKernel()) {
901 ASSERT((num_kernel_waiters++) >= 0); 888 ASSERT((num_kernel_waiters++) >= 0);
902 KScheduler::SetSchedulerUpdateNeeded(kernel); 889 KScheduler::SetSchedulerUpdateNeeded(kernel);
903 } 890 }
@@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
911 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 898 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
912 899
913 // Keep track of how many kernel waiters we have. 900 // Keep track of how many kernel waiters we have.
914 if (IsKernelAddressKey(thread->GetAddressKey())) { 901 if (thread->GetAddressKeyIsKernel()) {
915 ASSERT((num_kernel_waiters--) > 0); 902 ASSERT((num_kernel_waiters--) > 0);
916 KScheduler::SetSchedulerUpdateNeeded(kernel); 903 KScheduler::SetSchedulerUpdateNeeded(kernel);
917 } 904 }
@@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
987 KThread* thread = std::addressof(*it); 974 KThread* thread = std::addressof(*it);
988 975
989 // Keep track of how many kernel waiters we have. 976 // Keep track of how many kernel waiters we have.
990 if (IsKernelAddressKey(thread->GetAddressKey())) { 977 if (thread->GetAddressKeyIsKernel()) {
991 ASSERT((num_kernel_waiters--) > 0); 978 ASSERT((num_kernel_waiters--) > 0);
992 KScheduler::SetSchedulerUpdateNeeded(kernel); 979 KScheduler::SetSchedulerUpdateNeeded(kernel);
993 } 980 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 7cd94a340..8b8dc51be 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -214,8 +214,6 @@ public:
214 214
215 void Continue(); 215 void Continue();
216 216
217 void WaitUntilSuspended();
218
219 constexpr void SetSyncedIndex(s32 index) { 217 constexpr void SetSyncedIndex(s32 index) {
220 synced_index = index; 218 synced_index = index;
221 } 219 }
@@ -607,13 +605,30 @@ public:
607 return address_key_value; 605 return address_key_value;
608 } 606 }
609 607
610 void SetAddressKey(VAddr key) { 608 [[nodiscard]] bool GetAddressKeyIsKernel() const {
609 return address_key_is_kernel;
610 }
611
612 //! NB: intentional deviation from official kernel.
613 //
614 // Separate SetAddressKey into user and kernel versions
615 // to cope with arbitrary host pointers making their way
616 // into things.
617
618 void SetUserAddressKey(VAddr key) {
611 address_key = key; 619 address_key = key;
620 address_key_is_kernel = false;
612 } 621 }
613 622
614 void SetAddressKey(VAddr key, u32 val) { 623 void SetUserAddressKey(VAddr key, u32 val) {
615 address_key = key; 624 address_key = key;
616 address_key_value = val; 625 address_key_value = val;
626 address_key_is_kernel = false;
627 }
628
629 void SetKernelAddressKey(VAddr key) {
630 address_key = key;
631 address_key_is_kernel = true;
617 } 632 }
618 633
619 void ClearWaitQueue() { 634 void ClearWaitQueue() {
@@ -662,7 +677,7 @@ private:
662 union SyncObjectBuffer { 677 union SyncObjectBuffer {
663 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; 678 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
664 std::array<Handle, 679 std::array<Handle,
665 Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> 680 Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))>
666 handles; 681 handles;
667 constexpr SyncObjectBuffer() {} 682 constexpr SyncObjectBuffer() {}
668 }; 683 };
@@ -683,10 +698,8 @@ private:
683 }; 698 };
684 699
685 template <typename T> 700 template <typename T>
686 requires( 701 requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
687 std::same_as<T, KThread> || 702 static constexpr int Compare(const T& lhs, const KThread& rhs) {
688 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
689 const KThread& rhs) {
690 const u64 l_key = lhs.GetConditionVariableKey(); 703 const u64 l_key = lhs.GetConditionVariableKey();
691 const u64 r_key = rhs.GetConditionVariableKey(); 704 const u64 r_key = rhs.GetConditionVariableKey();
692 705
@@ -772,6 +785,7 @@ private:
772 bool debug_attached{}; 785 bool debug_attached{};
773 s8 priority_inheritance_count{}; 786 s8 priority_inheritance_count{};
774 bool resource_limit_release_hint{}; 787 bool resource_limit_release_hint{};
788 bool address_key_is_kernel{};
775 StackParameters stack_parameters{}; 789 StackParameters stack_parameters{};
776 Common::SpinLock context_guard{}; 790 Common::SpinLock context_guard{};
777 791
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index fe0cff084..71254eb55 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -70,10 +70,8 @@ public:
70 } 70 }
71 71
72 template <typename T> 72 template <typename T>
73 requires(std::same_as<T, KThreadLocalPage> || 73 requires(std::same_as<T, KThreadLocalPage> || std::same_as<T, RedBlackKeyType>)
74 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, 74 static constexpr int Compare(const T& lhs, const KThreadLocalPage& rhs) {
75 const KThreadLocalPage&
76 rhs) {
77 const VAddr lval = GetRedBlackKey(lhs); 75 const VAddr lval = GetRedBlackKey(lhs);
78 const VAddr rval = GetRedBlackKey(rhs); 76 const VAddr rval = GetRedBlackKey(rhs);
79 77
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fb25f221..d9eafe261 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1198,28 +1198,35 @@ void KernelCore::Suspend(bool suspended) {
1198 const bool should_suspend{exception_exited || suspended}; 1198 const bool should_suspend{exception_exited || suspended};
1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1200 1200
1201 std::vector<KScopedAutoObject<KThread>> process_threads; 1201 //! This refers to the application process, not the current process.
1202 { 1202 KScopedAutoObject<KProcess> process = CurrentProcess();
1203 KScopedSchedulerLock sl{*this}; 1203 if (process.IsNull()) {
1204 return;
1205 }
1204 1206
1205 if (auto* process = CurrentProcess(); process != nullptr) { 1207 // Set the new activity.
1206 process->SetActivity(activity); 1208 process->SetActivity(activity);
1207 1209
1208 if (!should_suspend) { 1210 // Wait for process execution to stop.
1209 // Runnable now; no need to wait. 1211 bool must_wait{should_suspend};
1210 return; 1212
1211 } 1213 // KernelCore::Suspend must be called from locked context, or we
1214 // could race another call to SetActivity, interfering with waiting.
1215 while (must_wait) {
1216 KScopedSchedulerLock sl{*this};
1217
1218 // Assume that all threads have finished running.
1219 must_wait = false;
1212 1220
1213 for (auto* thread : process->GetThreadList()) { 1221 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1214 process_threads.emplace_back(thread); 1222 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
1223 process.GetPointerUnsafe()) {
1224 // A thread has not finished running yet.
1225 // Continue waiting.
1226 must_wait = true;
1215 } 1227 }
1216 } 1228 }
1217 } 1229 }
1218
1219 // Wait for execution to stop.
1220 for (auto& thread : process_threads) {
1221 thread->WaitUntilSuspended();
1222 }
1223} 1230}
1224 1231
1225void KernelCore::ShutdownCores() { 1232void KernelCore::ShutdownCores() {
diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h
index 3975507bd..92b8b37ac 100644
--- a/src/core/hle/kernel/memory_types.h
+++ b/src/core/hle/kernel/memory_types.h
@@ -14,4 +14,7 @@ constexpr std::size_t PageSize{1 << PageBits};
14 14
15using Page = std::array<u8, PageSize>; 15using Page = std::array<u8, PageSize>;
16 16
17using KPhysicalAddress = PAddr;
18using KProcessAddress = VAddr;
19
17} // namespace Kernel 20} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 788ee2160..67fa5d71c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1485,15 +1485,15 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p
1485 ResultInvalidMemoryRegion); 1485 ResultInvalidMemoryRegion);
1486 1486
1487 // Create a new page group. 1487 // Create a new page group.
1488 KPageGroup pg; 1488 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
1489 R_TRY(src_pt.MakeAndOpenPageGroup( 1489 R_TRY(src_pt.MakeAndOpenPageGroup(
1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, 1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, 1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1492 KMemoryAttribute::All, KMemoryAttribute::None)); 1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493 1493
1494 // Map the group. 1494 // Map the group.
1495 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1495 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
1496 KMemoryPermission::UserReadWrite)); 1496 KMemoryPermission::UserReadWrite));
1497 1497
1498 return ResultSuccess; 1498 return ResultSuccess;
1499} 1499}
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 713f8e0c6..84edc8839 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -272,6 +272,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
272 } 272 }
273 break; 273 break;
274 case Core::HID::NpadStyleIndex::JoyconLeft: 274 case Core::HID::NpadStyleIndex::JoyconLeft:
275 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
276 shared_memory->fullkey_color.fullkey = body_colors.left;
275 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 277 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
276 shared_memory->joycon_color.left = body_colors.left; 278 shared_memory->joycon_color.left = body_colors.left;
277 shared_memory->battery_level_dual = battery_level.left.battery_level; 279 shared_memory->battery_level_dual = battery_level.left.battery_level;
@@ -285,6 +287,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
285 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); 287 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
286 break; 288 break;
287 case Core::HID::NpadStyleIndex::JoyconRight: 289 case Core::HID::NpadStyleIndex::JoyconRight:
290 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
291 shared_memory->fullkey_color.fullkey = body_colors.right;
288 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 292 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
289 shared_memory->joycon_color.right = body_colors.right; 293 shared_memory->joycon_color.right = body_colors.right;
290 shared_memory->battery_level_right = battery_level.right.battery_level; 294 shared_memory->battery_level_right = battery_level.right.battery_level;
@@ -332,6 +336,20 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
332 336
333 controller.is_connected = true; 337 controller.is_connected = true;
334 controller.device->Connect(); 338 controller.device->Connect();
339 controller.device->SetLedPattern();
340 if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
341 if (controller.is_dual_left_connected) {
342 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex,
343 Common::Input::PollingMode::Active);
344 }
345 if (controller.is_dual_right_connected) {
346 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
347 Common::Input::PollingMode::Active);
348 }
349 } else {
350 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
351 Common::Input::PollingMode::Active);
352 }
335 SignalStyleSetChangedEvent(npad_id); 353 SignalStyleSetChangedEvent(npad_id);
336 WriteEmptyEntry(controller.shared_memory); 354 WriteEmptyEntry(controller.shared_memory);
337} 355}
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index e5e50845f..17252a84a 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -297,13 +297,13 @@ void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
297 297
298 const auto parameters{rp.PopRaw<Parameters>()}; 298 const auto parameters{rp.PopRaw<Parameters>()};
299 299
300 LOG_INFO(Service_HID, 300 LOG_DEBUG(Service_HID,
301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " 301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", 302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
303 parameters.enable, parameters.bus_handle.abstracted_pad_id, 303 parameters.enable, parameters.bus_handle.abstracted_pad_id,
304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, 304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, 305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
306 parameters.applet_resource_user_id); 306 parameters.applet_resource_user_id);
307 307
308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); 308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
309 309
@@ -326,11 +326,11 @@ void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx}; 326 IPC::RequestParser rp{ctx};
327 const auto bus_handle_{rp.PopRaw<BusHandle>()}; 327 const auto bus_handle_{rp.PopRaw<BusHandle>()};
328 328
329 LOG_INFO(Service_HID, 329 LOG_DEBUG(Service_HID,
330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " 330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
331 "is_valid={}", 331 "is_valid={}",
332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, 332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
333 bus_handle_.player_number, bus_handle_.is_valid); 333 bus_handle_.player_number, bus_handle_.is_valid);
334 334
335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_); 335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
336 336
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
index 7ab3903dc..35847cbdd 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.cpp
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hid/emulated_devices.h" 4#include "core/hid/emulated_controller.h"
5#include "core/hid/hid_core.h" 5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h" 6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h" 7#include "core/hle/kernel/k_readable_event.h"
@@ -12,16 +12,20 @@ namespace Service::HID {
12RingController::RingController(Core::HID::HIDCore& hid_core_, 12RingController::RingController(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_) 13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) { 14 : HidbusBase(service_context_) {
15 input = hid_core_.GetEmulatedDevices(); 15 input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
16} 16}
17 17
18RingController::~RingController() = default; 18RingController::~RingController() = default;
19 19
20void RingController::OnInit() { 20void RingController::OnInit() {
21 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
22 Common::Input::PollingMode::Ring);
21 return; 23 return;
22} 24}
23 25
24void RingController::OnRelease() { 26void RingController::OnRelease() {
27 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
28 Common::Input::PollingMode::Active);
25 return; 29 return;
26}; 30};
27 31
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
index 8e195ca79..c2fb386b1 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.h
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -10,7 +10,7 @@
10#include "core/hle/service/hid/hidbus/hidbus_base.h" 10#include "core/hle/service/hid/hidbus/hidbus_base.h"
11 11
12namespace Core::HID { 12namespace Core::HID {
13class EmulatedDevices; 13class EmulatedController;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
@@ -249,6 +249,6 @@ private:
249 .zero = {.value = idle_value, .crc = 225}, 249 .zero = {.value = idle_value, .crc = 225},
250 }; 250 };
251 251
252 Core::HID::EmulatedDevices* input; 252 Core::HID::EmulatedController* input;
253}; 253};
254} // namespace Service::HID 254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 6a3453457..52f402c56 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -108,6 +108,8 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
108 auto result = IsIrCameraHandleValid(parameters.camera_handle); 108 auto result = IsIrCameraHandleValid(parameters.camera_handle);
109 if (result.IsSuccess()) { 109 if (result.IsSuccess()) {
110 // TODO: Stop Image processor 110 // TODO: Stop Image processor
111 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
112 Common::Input::PollingMode::Active);
111 result = ResultSuccess; 113 result = ResultSuccess;
112 } 114 }
113 115
@@ -139,6 +141,8 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
139 MakeProcessor<MomentProcessor>(parameters.camera_handle, device); 141 MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
140 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); 142 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
141 image_transfer_processor.SetConfig(parameters.processor_config); 143 image_transfer_processor.SetConfig(parameters.processor_config);
144 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
145 Common::Input::PollingMode::IR);
142 } 146 }
143 147
144 IPC::ResponseBuilder rb{ctx, 2}; 148 IPC::ResponseBuilder rb{ctx, 2};
@@ -170,6 +174,8 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
170 auto& image_transfer_processor = 174 auto& image_transfer_processor =
171 GetProcessor<ClusteringProcessor>(parameters.camera_handle); 175 GetProcessor<ClusteringProcessor>(parameters.camera_handle);
172 image_transfer_processor.SetConfig(parameters.processor_config); 176 image_transfer_processor.SetConfig(parameters.processor_config);
177 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
178 Common::Input::PollingMode::IR);
173 } 179 }
174 180
175 IPC::ResponseBuilder rb{ctx, 2}; 181 IPC::ResponseBuilder rb{ctx, 2};
@@ -219,6 +225,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
219 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 225 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
220 image_transfer_processor.SetConfig(parameters.processor_config); 226 image_transfer_processor.SetConfig(parameters.processor_config);
221 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 227 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
228 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
229 Common::Input::PollingMode::IR);
222 } 230 }
223 231
224 IPC::ResponseBuilder rb{ctx, 2}; 232 IPC::ResponseBuilder rb{ctx, 2};
@@ -294,6 +302,8 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
294 auto& image_transfer_processor = 302 auto& image_transfer_processor =
295 GetProcessor<TeraPluginProcessor>(parameters.camera_handle); 303 GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
296 image_transfer_processor.SetConfig(parameters.processor_config); 304 image_transfer_processor.SetConfig(parameters.processor_config);
305 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
306 Common::Input::PollingMode::IR);
297 } 307 }
298 308
299 IPC::ResponseBuilder rb{ctx, 2}; 309 IPC::ResponseBuilder rb{ctx, 2};
@@ -343,6 +353,8 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
343 MakeProcessor<PointingProcessor>(camera_handle, device); 353 MakeProcessor<PointingProcessor>(camera_handle, device);
344 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); 354 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
345 image_transfer_processor.SetConfig(processor_config); 355 image_transfer_processor.SetConfig(processor_config);
356 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
357 Common::Input::PollingMode::IR);
346 } 358 }
347 359
348 IPC::ResponseBuilder rb{ctx, 2}; 360 IPC::ResponseBuilder rb{ctx, 2};
@@ -453,6 +465,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
453 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 465 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
454 image_transfer_processor.SetConfig(parameters.processor_config); 466 image_transfer_processor.SetConfig(parameters.processor_config);
455 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 467 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
468 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
469 Common::Input::PollingMode::IR);
456 } 470 }
457 471
458 IPC::ResponseBuilder rb{ctx, 2}; 472 IPC::ResponseBuilder rb{ctx, 2};
@@ -479,6 +493,8 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
479 MakeProcessor<IrLedProcessor>(camera_handle, device); 493 MakeProcessor<IrLedProcessor>(camera_handle, device);
480 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); 494 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
481 image_transfer_processor.SetConfig(processor_config); 495 image_transfer_processor.SetConfig(processor_config);
496 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
497 Common::Input::PollingMode::IR);
482 } 498 }
483 499
484 IPC::ResponseBuilder rb{ctx, 2}; 500 IPC::ResponseBuilder rb{ctx, 2};
@@ -504,6 +520,8 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
504 auto result = IsIrCameraHandleValid(parameters.camera_handle); 520 auto result = IsIrCameraHandleValid(parameters.camera_handle);
505 if (result.IsSuccess()) { 521 if (result.IsSuccess()) {
506 // TODO: Stop image processor async 522 // TODO: Stop image processor async
523 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
524 Common::Input::PollingMode::Active);
507 result = ResultSuccess; 525 result = ResultSuccess;
508 } 526 }
509 527
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 78578f723..9a3234e8c 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -130,7 +130,9 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
130 return WrongDeviceState; 130 return WrongDeviceState;
131 } 131 }
132 132
133 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 133 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
134 Common::Input::PollingMode::NFC) !=
135 Common::Input::DriverResult::Success) {
134 LOG_ERROR(Service_NFC, "Nfc not supported"); 136 LOG_ERROR(Service_NFC, "Nfc not supported");
135 return NfcDisabled; 137 return NfcDisabled;
136 } 138 }
@@ -141,7 +143,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
141} 143}
142 144
143Result NfcDevice::StopDetection() { 145Result NfcDevice::StopDetection() {
144 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 146 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
147 Common::Input::PollingMode::Active);
145 148
146 if (device_state == NFP::DeviceState::Initialized) { 149 if (device_state == NFP::DeviceState::Initialized) {
147 return ResultSuccess; 150 return ResultSuccess;
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index c860fd1a1..e67a76f55 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -152,7 +152,9 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
152 return WrongDeviceState; 152 return WrongDeviceState;
153 } 153 }
154 154
155 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 155 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
156 Common::Input::PollingMode::NFC) !=
157 Common::Input::DriverResult::Success) {
156 LOG_ERROR(Service_NFP, "Nfc not supported"); 158 LOG_ERROR(Service_NFP, "Nfc not supported");
157 return NfcDisabled; 159 return NfcDisabled;
158 } 160 }
@@ -163,7 +165,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
163} 165}
164 166
165Result NfpDevice::StopDetection() { 167Result NfpDevice::StopDetection() {
166 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 168 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
169 Common::Input::PollingMode::Active);
167 170
168 if (device_state == DeviceState::Initialized) { 171 if (device_state == DeviceState::Initialized) {
169 return ResultSuccess; 172 return ResultSuccess;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 4fa9f51a6..5d32adf64 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -22,15 +22,19 @@ namespace {
22 22
23namespace Service::NIFM { 23namespace Service::NIFM {
24 24
25// This is nn::nifm::RequestState
25enum class RequestState : u32 { 26enum class RequestState : u32 {
26 NotSubmitted = 1, 27 NotSubmitted = 1,
27 Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW. 28 Invalid = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
28 Pending = 2, 29 OnHold = 2,
29 Connected = 3, 30 Accepted = 3,
31 Blocking = 4,
30}; 32};
31 33
32enum class InternetConnectionType : u8 { 34// This is nn::nifm::NetworkInterfaceType
33 WiFi = 1, 35enum class NetworkInterfaceType : u32 {
36 Invalid = 0,
37 WiFi_Ieee80211 = 1,
34 Ethernet = 2, 38 Ethernet = 2,
35}; 39};
36 40
@@ -42,14 +46,23 @@ enum class InternetConnectionStatus : u8 {
42 Connected, 46 Connected,
43}; 47};
44 48
49// This is nn::nifm::NetworkProfileType
50enum class NetworkProfileType : u32 {
51 User,
52 SsidList,
53 Temporary,
54};
55
56// This is nn::nifm::IpAddressSetting
45struct IpAddressSetting { 57struct IpAddressSetting {
46 bool is_automatic{}; 58 bool is_automatic{};
47 Network::IPv4Address current_address{}; 59 Network::IPv4Address ip_address{};
48 Network::IPv4Address subnet_mask{}; 60 Network::IPv4Address subnet_mask{};
49 Network::IPv4Address gateway{}; 61 Network::IPv4Address default_gateway{};
50}; 62};
51static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size."); 63static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size.");
52 64
65// This is nn::nifm::DnsSetting
53struct DnsSetting { 66struct DnsSetting {
54 bool is_automatic{}; 67 bool is_automatic{};
55 Network::IPv4Address primary_dns{}; 68 Network::IPv4Address primary_dns{};
@@ -57,18 +70,26 @@ struct DnsSetting {
57}; 70};
58static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size."); 71static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size.");
59 72
73// This is nn::nifm::AuthenticationSetting
74struct AuthenticationSetting {
75 bool is_enabled{};
76 std::array<char, 0x20> user{};
77 std::array<char, 0x20> password{};
78};
79static_assert(sizeof(AuthenticationSetting) == 0x41, "AuthenticationSetting has incorrect size.");
80
81// This is nn::nifm::ProxySetting
60struct ProxySetting { 82struct ProxySetting {
61 bool enabled{}; 83 bool is_enabled{};
62 INSERT_PADDING_BYTES(1); 84 INSERT_PADDING_BYTES(1);
63 u16 port{}; 85 u16 port{};
64 std::array<char, 0x64> proxy_server{}; 86 std::array<char, 0x64> proxy_server{};
65 bool automatic_auth_enabled{}; 87 AuthenticationSetting authentication{};
66 std::array<char, 0x20> user{};
67 std::array<char, 0x20> password{};
68 INSERT_PADDING_BYTES(1); 88 INSERT_PADDING_BYTES(1);
69}; 89};
70static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size."); 90static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size.");
71 91
92// This is nn::nifm::IpSettingData
72struct IpSettingData { 93struct IpSettingData {
73 IpAddressSetting ip_address_setting{}; 94 IpAddressSetting ip_address_setting{};
74 DnsSetting dns_setting{}; 95 DnsSetting dns_setting{};
@@ -101,6 +122,7 @@ static_assert(sizeof(NifmWirelessSettingData) == 0x70,
101 "NifmWirelessSettingData has incorrect size."); 122 "NifmWirelessSettingData has incorrect size.");
102 123
103#pragma pack(push, 1) 124#pragma pack(push, 1)
125// This is nn::nifm::detail::sf::NetworkProfileData
104struct SfNetworkProfileData { 126struct SfNetworkProfileData {
105 IpSettingData ip_setting_data{}; 127 IpSettingData ip_setting_data{};
106 u128 uuid{}; 128 u128 uuid{};
@@ -114,13 +136,14 @@ struct SfNetworkProfileData {
114}; 136};
115static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size."); 137static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
116 138
139// This is nn::nifm::NetworkProfileData
117struct NifmNetworkProfileData { 140struct NifmNetworkProfileData {
118 u128 uuid{}; 141 u128 uuid{};
119 std::array<char, 0x40> network_name{}; 142 std::array<char, 0x40> network_name{};
120 u32 unknown_1{}; 143 NetworkProfileType network_profile_type{};
121 u32 unknown_2{}; 144 NetworkInterfaceType network_interface_type{};
122 u8 unknown_3{}; 145 bool is_auto_connect{};
123 u8 unknown_4{}; 146 bool is_large_capacity{};
124 INSERT_PADDING_BYTES(2); 147 INSERT_PADDING_BYTES(2);
125 NifmWirelessSettingData wireless_setting_data{}; 148 NifmWirelessSettingData wireless_setting_data{};
126 IpSettingData ip_setting_data{}; 149 IpSettingData ip_setting_data{};
@@ -184,6 +207,7 @@ public:
184 207
185 event1 = CreateKEvent(service_context, "IRequest:Event1"); 208 event1 = CreateKEvent(service_context, "IRequest:Event1");
186 event2 = CreateKEvent(service_context, "IRequest:Event2"); 209 event2 = CreateKEvent(service_context, "IRequest:Event2");
210 state = RequestState::NotSubmitted;
187 } 211 }
188 212
189 ~IRequest() override { 213 ~IRequest() override {
@@ -196,7 +220,7 @@ private:
196 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 220 LOG_WARNING(Service_NIFM, "(STUBBED) called");
197 221
198 if (state == RequestState::NotSubmitted) { 222 if (state == RequestState::NotSubmitted) {
199 UpdateState(RequestState::Pending); 223 UpdateState(RequestState::OnHold);
200 } 224 }
201 225
202 IPC::ResponseBuilder rb{ctx, 2}; 226 IPC::ResponseBuilder rb{ctx, 2};
@@ -219,14 +243,14 @@ private:
219 switch (state) { 243 switch (state) {
220 case RequestState::NotSubmitted: 244 case RequestState::NotSubmitted:
221 return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled; 245 return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled;
222 case RequestState::Pending: 246 case RequestState::OnHold:
223 if (has_connection) { 247 if (has_connection) {
224 UpdateState(RequestState::Connected); 248 UpdateState(RequestState::Accepted);
225 } else { 249 } else {
226 UpdateState(RequestState::Error); 250 UpdateState(RequestState::Invalid);
227 } 251 }
228 return ResultPendingConnection; 252 return ResultPendingConnection;
229 case RequestState::Connected: 253 case RequestState::Accepted:
230 default: 254 default:
231 return ResultSuccess; 255 return ResultSuccess;
232 } 256 }
@@ -338,9 +362,9 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
338 .ip_setting_data{ 362 .ip_setting_data{
339 .ip_address_setting{ 363 .ip_address_setting{
340 .is_automatic{true}, 364 .is_automatic{true},
341 .current_address{Network::TranslateIPv4(net_iface->ip_address)}, 365 .ip_address{Network::TranslateIPv4(net_iface->ip_address)},
342 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, 366 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
343 .gateway{Network::TranslateIPv4(net_iface->gateway)}, 367 .default_gateway{Network::TranslateIPv4(net_iface->gateway)},
344 }, 368 },
345 .dns_setting{ 369 .dns_setting{
346 .is_automatic{true}, 370 .is_automatic{true},
@@ -348,12 +372,14 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
348 .secondary_dns{1, 0, 0, 1}, 372 .secondary_dns{1, 0, 0, 1},
349 }, 373 },
350 .proxy_setting{ 374 .proxy_setting{
351 .enabled{false}, 375 .is_enabled{false},
352 .port{}, 376 .port{},
353 .proxy_server{}, 377 .proxy_server{},
354 .automatic_auth_enabled{}, 378 .authentication{
355 .user{}, 379 .is_enabled{},
356 .password{}, 380 .user{},
381 .password{},
382 },
357 }, 383 },
358 .mtu{1500}, 384 .mtu{1500},
359 }, 385 },
@@ -370,7 +396,7 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
370 // When we're connected to a room, spoof the hosts IP address 396 // When we're connected to a room, spoof the hosts IP address
371 if (auto room_member = network.GetRoomMember().lock()) { 397 if (auto room_member = network.GetRoomMember().lock()) {
372 if (room_member->IsConnected()) { 398 if (room_member->IsConnected()) {
373 network_profile_data.ip_setting_data.ip_address_setting.current_address = 399 network_profile_data.ip_setting_data.ip_address_setting.ip_address =
374 room_member->GetFakeIpAddress(); 400 room_member->GetFakeIpAddress();
375 } 401 }
376 } 402 }
@@ -444,9 +470,9 @@ void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
444 return IpConfigInfo{ 470 return IpConfigInfo{
445 .ip_address_setting{ 471 .ip_address_setting{
446 .is_automatic{true}, 472 .is_automatic{true},
447 .current_address{Network::TranslateIPv4(net_iface->ip_address)}, 473 .ip_address{Network::TranslateIPv4(net_iface->ip_address)},
448 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, 474 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
449 .gateway{Network::TranslateIPv4(net_iface->gateway)}, 475 .default_gateway{Network::TranslateIPv4(net_iface->gateway)},
450 }, 476 },
451 .dns_setting{ 477 .dns_setting{
452 .is_automatic{true}, 478 .is_automatic{true},
@@ -459,7 +485,7 @@ void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
459 // When we're connected to a room, spoof the hosts IP address 485 // When we're connected to a room, spoof the hosts IP address
460 if (auto room_member = network.GetRoomMember().lock()) { 486 if (auto room_member = network.GetRoomMember().lock()) {
461 if (room_member->IsConnected()) { 487 if (room_member->IsConnected()) {
462 ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); 488 ip_config_info.ip_address_setting.ip_address = room_member->GetFakeIpAddress();
463 } 489 }
464 } 490 }
465 491
@@ -480,7 +506,7 @@ void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx
480 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 506 LOG_WARNING(Service_NIFM, "(STUBBED) called");
481 507
482 struct Output { 508 struct Output {
483 InternetConnectionType type{InternetConnectionType::WiFi}; 509 u8 type{static_cast<u8>(NetworkInterfaceType::WiFi_Ieee80211)};
484 u8 wifi_strength{3}; 510 u8 wifi_strength{3};
485 InternetConnectionStatus state{InternetConnectionStatus::Connected}; 511 InternetConnectionStatus state{InternetConnectionStatus::Connected};
486 }; 512 };
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d1cbadde4..f4416f5b2 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -312,8 +312,6 @@ void NVFlinger::Compose() {
312} 312}
313 313
314s64 NVFlinger::GetNextTicks() const { 314s64 NVFlinger::GetNextTicks() const {
315 static constexpr s64 max_hertz = 120LL;
316
317 const auto& settings = Settings::values; 315 const auto& settings = Settings::values;
318 auto speed_scale = 1.f; 316 auto speed_scale = 1.f;
319 if (settings.use_multi_core.GetValue()) { 317 if (settings.use_multi_core.GetValue()) {
@@ -327,9 +325,11 @@ s64 NVFlinger::GetNextTicks() const {
327 } 325 }
328 } 326 }
329 327
330 const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz); 328 // As an extension, treat nonpositive swap interval as framerate multiplier.
329 const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
330 : 60.f / static_cast<f32>(swap_interval);
331 331
332 return static_cast<s64>(speed_scale * static_cast<float>(next_ticks)); 332 return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
333} 333}
334 334
335} // namespace Service::NVFlinger 335} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 9b22397db..3828cf272 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -133,7 +133,7 @@ private:
133 /// layers. 133 /// layers.
134 u32 next_buffer_queue_id = 1; 134 u32 next_buffer_queue_id = 1;
135 135
136 u32 swap_interval = 1; 136 s32 swap_interval = 1;
137 137
138 /// Event that handles screen composition. 138 /// Event that handles screen composition.
139 std::shared_ptr<Core::Timing::EventType> multi_composition_event; 139 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 452212964..7494fb62d 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -117,6 +117,8 @@ Errno TranslateNativeError(int e) {
117 return Errno::NETUNREACH; 117 return Errno::NETUNREACH;
118 case WSAEMSGSIZE: 118 case WSAEMSGSIZE:
119 return Errno::MSGSIZE; 119 return Errno::MSGSIZE;
120 case WSAETIMEDOUT:
121 return Errno::TIMEDOUT;
120 default: 122 default:
121 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 123 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
122 return Errno::OTHER; 124 return Errno::OTHER;
@@ -211,6 +213,8 @@ Errno TranslateNativeError(int e) {
211 return Errno::NETUNREACH; 213 return Errno::NETUNREACH;
212 case EMSGSIZE: 214 case EMSGSIZE:
213 return Errno::MSGSIZE; 215 return Errno::MSGSIZE;
216 case ETIMEDOUT:
217 return Errno::TIMEDOUT;
214 default: 218 default:
215 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 219 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
216 return Errno::OTHER; 220 return Errno::OTHER;
@@ -226,7 +230,7 @@ Errno GetAndLogLastError() {
226 int e = errno; 230 int e = errno;
227#endif 231#endif
228 const Errno err = TranslateNativeError(e); 232 const Errno err = TranslateNativeError(e);
229 if (err == Errno::AGAIN) { 233 if (err == Errno::AGAIN || err == Errno::TIMEDOUT) {
230 return err; 234 return err;
231 } 235 }
232 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); 236 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 26be74df4..af9660b55 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -383,6 +383,10 @@ struct Memory::Impl {
383 return; 383 return;
384 } 384 }
385 385
386 if (Settings::IsFastmemEnabled()) {
387 system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
388 }
389
386 // Iterate over a contiguous CPU address space, marking/unmarking the region. 390 // Iterate over a contiguous CPU address space, marking/unmarking the region.
387 // The region is at a granularity of CPU pages. 391 // The region is at a granularity of CPU pages.
388 392
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
index 5bbe1d4b5..136109a0c 100644
--- a/src/dedicated_room/CMakeLists.txt
+++ b/src/dedicated_room/CMakeLists.txt
@@ -1,8 +1,6 @@
1# SPDX-FileCopyrightText: 2017 Citra Emulator Project 1# SPDX-FileCopyrightText: 2017 Citra Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
5
6add_executable(yuzu-room 4add_executable(yuzu-room
7 precompiled_headers.h 5 precompiled_headers.h
8 yuzu_room.cpp 6 yuzu_room.cpp
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index f24c89b04..e3b627e4f 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -4,14 +4,10 @@
4add_library(input_common STATIC 4add_library(input_common STATIC
5 drivers/camera.cpp 5 drivers/camera.cpp
6 drivers/camera.h 6 drivers/camera.h
7 drivers/gc_adapter.cpp
8 drivers/gc_adapter.h
9 drivers/keyboard.cpp 7 drivers/keyboard.cpp
10 drivers/keyboard.h 8 drivers/keyboard.h
11 drivers/mouse.cpp 9 drivers/mouse.cpp
12 drivers/mouse.h 10 drivers/mouse.h
13 drivers/sdl_driver.cpp
14 drivers/sdl_driver.h
15 drivers/tas_input.cpp 11 drivers/tas_input.cpp
16 drivers/tas_input.h 12 drivers/tas_input.h
17 drivers/touch_screen.cpp 13 drivers/touch_screen.cpp
@@ -55,15 +51,45 @@ endif()
55 51
56if (ENABLE_SDL2) 52if (ENABLE_SDL2)
57 target_sources(input_common PRIVATE 53 target_sources(input_common PRIVATE
54 drivers/joycon.cpp
55 drivers/joycon.h
58 drivers/sdl_driver.cpp 56 drivers/sdl_driver.cpp
59 drivers/sdl_driver.h 57 drivers/sdl_driver.h
58 helpers/joycon_driver.cpp
59 helpers/joycon_driver.h
60 helpers/joycon_protocol/calibration.cpp
61 helpers/joycon_protocol/calibration.h
62 helpers/joycon_protocol/common_protocol.cpp
63 helpers/joycon_protocol/common_protocol.h
64 helpers/joycon_protocol/generic_functions.cpp
65 helpers/joycon_protocol/generic_functions.h
66 helpers/joycon_protocol/joycon_types.h
67 helpers/joycon_protocol/irs.cpp
68 helpers/joycon_protocol/irs.h
69 helpers/joycon_protocol/nfc.cpp
70 helpers/joycon_protocol/nfc.h
71 helpers/joycon_protocol/poller.cpp
72 helpers/joycon_protocol/poller.h
73 helpers/joycon_protocol/ringcon.cpp
74 helpers/joycon_protocol/ringcon.h
75 helpers/joycon_protocol/rumble.cpp
76 helpers/joycon_protocol/rumble.h
60 ) 77 )
61 target_link_libraries(input_common PRIVATE SDL2::SDL2) 78 target_link_libraries(input_common PRIVATE SDL2::SDL2)
62 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 79 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
63endif() 80endif()
64 81
82if (ENABLE_LIBUSB)
83 target_sources(input_common PRIVATE
84 drivers/gc_adapter.cpp
85 drivers/gc_adapter.h
86 )
87 target_link_libraries(input_common PRIVATE libusb::usb)
88 target_compile_definitions(input_common PRIVATE HAVE_LIBUSB)
89endif()
90
65create_target_directory_groups(input_common) 91create_target_directory_groups(input_common)
66target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb) 92target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
67 93
68if (YUZU_USE_PRECOMPILED_HEADERS) 94if (YUZU_USE_PRECOMPILED_HEADERS)
69 target_precompile_headers(input_common PRIVATE precompiled_headers.h) 95 target_precompile_headers(input_common PRIVATE precompiled_headers.h)
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
index fad9177dc..04970f635 100644
--- a/src/input_common/drivers/camera.cpp
+++ b/src/input_common/drivers/camera.cpp
@@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const {
72 } 72 }
73} 73}
74 74
75Common::Input::CameraError Camera::SetCameraFormat( 75Common::Input::DriverResult Camera::SetCameraFormat(
76 [[maybe_unused]] const PadIdentifier& identifier_, 76 [[maybe_unused]] const PadIdentifier& identifier_,
77 const Common::Input::CameraFormat camera_format) { 77 const Common::Input::CameraFormat camera_format) {
78 status.format = camera_format; 78 status.format = camera_format;
79 return Common::Input::CameraError::None; 79 return Common::Input::DriverResult::Success;
80} 80}
81 81
82} // namespace InputCommon 82} // namespace InputCommon
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index ead3e0fde..24b27e325 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -22,8 +22,8 @@ public:
22 std::size_t getImageWidth() const; 22 std::size_t getImageWidth() const;
23 std::size_t getImageHeight() const; 23 std::size_t getImageHeight() const;
24 24
25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
26 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
27 27
28private: 28private:
29 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 826fa2109..d09ff178b 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetVibration( 327Common::Input::DriverResult GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
333 pads[identifier.port].rumble_amplitude = processed_amplitude; 333 pads[identifier.port].rumble_amplitude = processed_amplitude;
334 334
335 if (!rumble_enabled) { 335 if (!rumble_enabled) {
336 return Common::Input::VibrationError::Disabled; 336 return Common::Input::DriverResult::Disabled;
337 } 337 }
338 return Common::Input::VibrationError::None; 338 return Common::Input::DriverResult::Success;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { 341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index b5270fd0b..3c2eb376d 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,7 +25,7 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetVibration( 28 Common::Input::DriverResult SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
new file mode 100644
index 000000000..4fcfb4510
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,677 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fmt/format.h>
5
6#include "common/param_package.h"
7#include "common/polyfill_ranges.h"
8#include "common/polyfill_thread.h"
9#include "common/settings.h"
10#include "common/thread.h"
11#include "input_common/drivers/joycon.h"
12#include "input_common/helpers/joycon_driver.h"
13#include "input_common/helpers/joycon_protocol/joycon_types.h"
14
15namespace InputCommon {
16
17Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
18 // Avoid conflicting with SDL driver
19 if (!Settings::values.enable_joycon_driver) {
20 return;
21 }
22 LOG_INFO(Input, "Joycon driver Initialization started");
23 const int init_res = SDL_hid_init();
24 if (init_res == 0) {
25 Setup();
26 } else {
27 LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
28 }
29}
30
31Joycons::~Joycons() {
32 Reset();
33}
34
35void Joycons::Reset() {
36 scan_thread = {};
37 for (const auto& device : left_joycons) {
38 if (!device) {
39 continue;
40 }
41 device->Stop();
42 }
43 for (const auto& device : right_joycons) {
44 if (!device) {
45 continue;
46 }
47 device->Stop();
48 }
49 SDL_hid_exit();
50}
51
52void Joycons::Setup() {
53 u32 port = 0;
54 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
55 for (auto& device : left_joycons) {
56 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
57 device = std::make_shared<Joycon::JoyconDriver>(port++);
58 }
59 port = 0;
60 for (auto& device : right_joycons) {
61 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
62 device = std::make_shared<Joycon::JoyconDriver>(port++);
63 }
64
65 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
66}
67
68void Joycons::ScanThread(std::stop_token stop_token) {
69 constexpr u16 nintendo_vendor_id = 0x057e;
70 Common::SetCurrentThreadName("JoyconScanThread");
71
72 do {
73 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
74 SDL_hid_device_info* cur_dev = devs;
75
76 while (cur_dev) {
77 if (IsDeviceNew(cur_dev)) {
78 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
79 cur_dev->product_id);
80 RegisterNewDevice(cur_dev);
81 }
82 cur_dev = cur_dev->next;
83 }
84
85 SDL_hid_free_enumeration(devs);
86 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
87}
88
89bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
90 Joycon::ControllerType type{};
91 Joycon::SerialNumber serial_number{};
92
93 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
94 if (result != Joycon::DriverResult::Success) {
95 return false;
96 }
97
98 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
99 if (result2 != Joycon::DriverResult::Success) {
100 return false;
101 }
102
103 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
104 if (!device) {
105 return false;
106 }
107 if (!device->IsConnected()) {
108 return false;
109 }
110 if (device->GetHandleSerialNumber() != serial_number) {
111 return false;
112 }
113 return true;
114 };
115
116 // Check if device already exist
117 switch (type) {
118 case Joycon::ControllerType::Left:
119 for (const auto& device : left_joycons) {
120 if (is_handle_identical(device)) {
121 return false;
122 }
123 }
124 break;
125 case Joycon::ControllerType::Right:
126 for (const auto& device : right_joycons) {
127 if (is_handle_identical(device)) {
128 return false;
129 }
130 }
131 break;
132 default:
133 return false;
134 }
135
136 return true;
137}
138
139void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
140 Joycon::ControllerType type{};
141 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
142 auto handle = GetNextFreeHandle(type);
143 if (handle == nullptr) {
144 LOG_WARNING(Input, "No free handles available");
145 return;
146 }
147 if (result == Joycon::DriverResult::Success) {
148 result = handle->RequestDeviceAccess(device_info);
149 }
150 if (result == Joycon::DriverResult::Success) {
151 LOG_WARNING(Input, "Initialize device");
152
153 const std::size_t port = handle->GetDevicePort();
154 const Joycon::JoyconCallbacks callbacks{
155 .on_battery_data = {[this, port, type](Joycon::Battery value) {
156 OnBatteryUpdate(port, type, value);
157 }},
158 .on_color_data = {[this, port, type](Joycon::Color value) {
159 OnColorUpdate(port, type, value);
160 }},
161 .on_button_data = {[this, port, type](int id, bool value) {
162 OnButtonUpdate(port, type, id, value);
163 }},
164 .on_stick_data = {[this, port, type](int id, f32 value) {
165 OnStickUpdate(port, type, id, value);
166 }},
167 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
168 OnMotionUpdate(port, type, id, value);
169 }},
170 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
171 .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
172 OnAmiiboUpdate(port, amiibo_data);
173 }},
174 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
175 Joycon::IrsResolution format) {
176 OnCameraUpdate(port, camera_data, format);
177 }},
178 };
179
180 handle->InitializeDevice();
181 handle->SetCallbacks(callbacks);
182 }
183}
184
185std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
186 Joycon::ControllerType type) const {
187 if (type == Joycon::ControllerType::Left) {
188 const auto unconnected_device =
189 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
190 if (unconnected_device != left_joycons.end()) {
191 return *unconnected_device;
192 }
193 }
194 if (type == Joycon::ControllerType::Right) {
195 const auto unconnected_device = std::ranges::find_if(
196 right_joycons, [](auto& device) { return !device->IsConnected(); });
197
198 if (unconnected_device != right_joycons.end()) {
199 return *unconnected_device;
200 }
201 }
202 return nullptr;
203}
204
205bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
206 const auto handle = GetHandle(identifier);
207 if (handle == nullptr) {
208 return false;
209 }
210 return handle->IsVibrationEnabled();
211}
212
213Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
214 const Common::Input::VibrationStatus& vibration) {
215 const Joycon::VibrationValue native_vibration{
216 .low_amplitude = vibration.low_amplitude,
217 .low_frequency = vibration.low_frequency,
218 .high_amplitude = vibration.high_amplitude,
219 .high_frequency = vibration.high_frequency,
220 };
221 auto handle = GetHandle(identifier);
222 if (handle == nullptr) {
223 return Common::Input::DriverResult::InvalidHandle;
224 }
225
226 handle->SetVibration(native_vibration);
227 return Common::Input::DriverResult::Success;
228}
229
230Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
231 const Common::Input::LedStatus& led_status) {
232 auto handle = GetHandle(identifier);
233 if (handle == nullptr) {
234 return Common::Input::DriverResult::InvalidHandle;
235 }
236 int led_config = led_status.led_1 ? 1 : 0;
237 led_config += led_status.led_2 ? 2 : 0;
238 led_config += led_status.led_3 ? 4 : 0;
239 led_config += led_status.led_4 ? 8 : 0;
240
241 return static_cast<Common::Input::DriverResult>(
242 handle->SetLedConfig(static_cast<u8>(led_config)));
243}
244
245Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
246 Common::Input::CameraFormat camera_format) {
247 auto handle = GetHandle(identifier);
248 if (handle == nullptr) {
249 return Common::Input::DriverResult::InvalidHandle;
250 }
251 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
252 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
253};
254
255Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
256 return Common::Input::NfcState::Success;
257};
258
259Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
260 const std::vector<u8>& data) {
261 return Common::Input::NfcState::NotSupported;
262};
263
264Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
265 const Common::Input::PollingMode polling_mode) {
266 auto handle = GetHandle(identifier);
267 if (handle == nullptr) {
268 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
269 return Common::Input::DriverResult::InvalidHandle;
270 }
271
272 switch (polling_mode) {
273 case Common::Input::PollingMode::Active:
274 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
275 case Common::Input::PollingMode::Pasive:
276 return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
277 case Common::Input::PollingMode::IR:
278 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
279 case Common::Input::PollingMode::NFC:
280 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
281 case Common::Input::PollingMode::Ring:
282 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
283 default:
284 return Common::Input::DriverResult::NotSupported;
285 }
286}
287
288void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
289 Joycon::Battery value) {
290 const auto identifier = GetIdentifier(port, type);
291 if (value.charging != 0) {
292 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
293 return;
294 }
295
296 Common::Input::BatteryLevel battery{};
297 switch (value.status) {
298 case 0:
299 battery = Common::Input::BatteryLevel::Empty;
300 break;
301 case 1:
302 battery = Common::Input::BatteryLevel::Critical;
303 break;
304 case 2:
305 battery = Common::Input::BatteryLevel::Low;
306 break;
307 case 3:
308 battery = Common::Input::BatteryLevel::Medium;
309 break;
310 case 4:
311 default:
312 battery = Common::Input::BatteryLevel::Full;
313 break;
314 }
315 SetBattery(identifier, battery);
316}
317
318void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
319 const Joycon::Color& value) {
320 const auto identifier = GetIdentifier(port, type);
321 Common::Input::BodyColorStatus color{
322 .body = value.body,
323 .buttons = value.buttons,
324 .left_grip = value.left_grip,
325 .right_grip = value.right_grip,
326 };
327 SetColor(identifier, color);
328}
329
330void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
331 const auto identifier = GetIdentifier(port, type);
332 SetButton(identifier, id, value);
333}
334
335void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
336 const auto identifier = GetIdentifier(port, type);
337 SetAxis(identifier, id, value);
338}
339
340void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
341 const Joycon::MotionData& value) {
342 const auto identifier = GetIdentifier(port, type);
343 BasicMotion motion_data{
344 .gyro_x = value.gyro_x,
345 .gyro_y = value.gyro_y,
346 .gyro_z = value.gyro_z,
347 .accel_x = value.accel_x,
348 .accel_y = value.accel_y,
349 .accel_z = value.accel_z,
350 .delta_timestamp = 15000,
351 };
352 SetMotion(identifier, id, motion_data);
353}
354
355void Joycons::OnRingConUpdate(f32 ring_data) {
356 // To simplify ring detection it will always be mapped to an empty identifier for all
357 // controllers
358 constexpr PadIdentifier identifier = {
359 .guid = Common::UUID{},
360 .port = 0,
361 .pad = 0,
362 };
363 SetAxis(identifier, 100, ring_data);
364}
365
366void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
367 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
368 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
369 : Common::Input::NfcState::NewAmiibo;
370 SetNfc(identifier, {nfc_state, amiibo_data});
371}
372
373void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
374 Joycon::IrsResolution format) {
375 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
376 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
377}
378
379std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
380 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
381 if (!device) {
382 return false;
383 }
384 if (!device->IsConnected()) {
385 return false;
386 }
387 if (device->GetDevicePort() == identifier.port) {
388 return true;
389 }
390 return false;
391 };
392 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
393
394 if (type == Joycon::ControllerType::Left) {
395 const auto matching_device = std::ranges::find_if(
396 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
397
398 if (matching_device != left_joycons.end()) {
399 return *matching_device;
400 }
401 }
402
403 if (type == Joycon::ControllerType::Right) {
404 const auto matching_device = std::ranges::find_if(
405 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
406
407 if (matching_device != right_joycons.end()) {
408 return *matching_device;
409 }
410 }
411
412 return nullptr;
413}
414
415PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
416 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
417 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
418 return {
419 .guid = Common::UUID{guid},
420 .port = port,
421 .pad = static_cast<std::size_t>(type),
422 };
423}
424
425Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
426 const auto identifier = GetIdentifier(port, type);
427 return {
428 {"engine", GetEngineName()},
429 {"guid", identifier.guid.RawString()},
430 {"port", std::to_string(identifier.port)},
431 {"pad", std::to_string(identifier.pad)},
432 };
433}
434
435std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
436 std::vector<Common::ParamPackage> devices{};
437
438 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
439 if (!device) {
440 return;
441 }
442 if (!device->IsConnected()) {
443 return;
444 }
445 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
446 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
447 device->GetDevicePort() + 1);
448 param.Set("display", std::move(name));
449 devices.emplace_back(param);
450 };
451
452 for (const auto& controller : left_joycons) {
453 add_entry(controller);
454 }
455 for (const auto& controller : right_joycons) {
456 add_entry(controller);
457 }
458
459 // List dual joycon pairs
460 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
461 if (!left_joycons[i] || !right_joycons[i]) {
462 continue;
463 }
464 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
465 continue;
466 }
467 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
468 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
469 const auto type = Joycon::ControllerType::Dual;
470 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
471
472 main_param.Set("display", std::move(name));
473 main_param.Set("guid2", second_param.Get("guid", ""));
474 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
475 devices.emplace_back(main_param);
476 }
477
478 return devices;
479}
480
481ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
482 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
483 18>
484 switch_to_joycon_button = {
485 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
486 {Settings::NativeButton::B, Joycon::PadButton::B, true},
487 {Settings::NativeButton::X, Joycon::PadButton::X, true},
488 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
489 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
490 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
491 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
492 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
493 {Settings::NativeButton::L, Joycon::PadButton::L, false},
494 {Settings::NativeButton::R, Joycon::PadButton::R, true},
495 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
496 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
497 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
498 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
499 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
500 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
501 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
502 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
503 };
504
505 if (!params.Has("port")) {
506 return {};
507 }
508
509 ButtonMapping mapping{};
510 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
511 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
512 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
513 if (pad == Joycon::ControllerType::Dual) {
514 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
515 }
516
517 Common::ParamPackage button_params = GetParamPackage(port, pad);
518 button_params.Set("button", static_cast<int>(joycon_button));
519 mapping.insert_or_assign(switch_button, std::move(button_params));
520 }
521
522 // Map SL and SR buttons for left joycons
523 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
524 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
525 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
526
527 Common::ParamPackage sl_button_params = button_params;
528 Common::ParamPackage sr_button_params = button_params;
529 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
530 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
531 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
532 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
533 }
534
535 // Map SL and SR buttons for right joycons
536 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
537 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
538 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
539
540 Common::ParamPackage sl_button_params = button_params;
541 Common::ParamPackage sr_button_params = button_params;
542 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
543 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
544 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
545 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
546 }
547
548 return mapping;
549}
550
551AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
552 if (!params.Has("port")) {
553 return {};
554 }
555
556 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
557 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
558 auto pad_right = pad_left;
559 if (pad_left == Joycon::ControllerType::Dual) {
560 pad_left = Joycon::ControllerType::Left;
561 pad_right = Joycon::ControllerType::Right;
562 }
563
564 AnalogMapping mapping = {};
565 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
566 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
567 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
568 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
569 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
570 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
571 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
572 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
573 return mapping;
574}
575
576MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
577 if (!params.Has("port")) {
578 return {};
579 }
580
581 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
582 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
583 auto pad_right = pad_left;
584 if (pad_left == Joycon::ControllerType::Dual) {
585 pad_left = Joycon::ControllerType::Left;
586 pad_right = Joycon::ControllerType::Right;
587 }
588
589 MotionMapping mapping = {};
590 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
591 left_motion_params.Set("motion", 0);
592 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
593 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
594 right_Motion_params.Set("motion", 1);
595 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
596 return mapping;
597}
598
599Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
600 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
601 switch (button) {
602 case Joycon::PadButton::Left:
603 return Common::Input::ButtonNames::ButtonLeft;
604 case Joycon::PadButton::Right:
605 return Common::Input::ButtonNames::ButtonRight;
606 case Joycon::PadButton::Down:
607 return Common::Input::ButtonNames::ButtonDown;
608 case Joycon::PadButton::Up:
609 return Common::Input::ButtonNames::ButtonUp;
610 case Joycon::PadButton::LeftSL:
611 case Joycon::PadButton::RightSL:
612 return Common::Input::ButtonNames::TriggerSL;
613 case Joycon::PadButton::LeftSR:
614 case Joycon::PadButton::RightSR:
615 return Common::Input::ButtonNames::TriggerSR;
616 case Joycon::PadButton::L:
617 return Common::Input::ButtonNames::TriggerL;
618 case Joycon::PadButton::R:
619 return Common::Input::ButtonNames::TriggerR;
620 case Joycon::PadButton::ZL:
621 return Common::Input::ButtonNames::TriggerZL;
622 case Joycon::PadButton::ZR:
623 return Common::Input::ButtonNames::TriggerZR;
624 case Joycon::PadButton::A:
625 return Common::Input::ButtonNames::ButtonA;
626 case Joycon::PadButton::B:
627 return Common::Input::ButtonNames::ButtonB;
628 case Joycon::PadButton::X:
629 return Common::Input::ButtonNames::ButtonX;
630 case Joycon::PadButton::Y:
631 return Common::Input::ButtonNames::ButtonY;
632 case Joycon::PadButton::Plus:
633 return Common::Input::ButtonNames::ButtonPlus;
634 case Joycon::PadButton::Minus:
635 return Common::Input::ButtonNames::ButtonMinus;
636 case Joycon::PadButton::Home:
637 return Common::Input::ButtonNames::ButtonHome;
638 case Joycon::PadButton::Capture:
639 return Common::Input::ButtonNames::ButtonCapture;
640 case Joycon::PadButton::StickL:
641 return Common::Input::ButtonNames::ButtonStickL;
642 case Joycon::PadButton::StickR:
643 return Common::Input::ButtonNames::ButtonStickR;
644 default:
645 return Common::Input::ButtonNames::Undefined;
646 }
647}
648
649Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
650 if (params.Has("button")) {
651 return GetUIButtonName(params);
652 }
653 if (params.Has("axis")) {
654 return Common::Input::ButtonNames::Value;
655 }
656 if (params.Has("motion")) {
657 return Common::Input::ButtonNames::Engine;
658 }
659
660 return Common::Input::ButtonNames::Invalid;
661}
662
663std::string Joycons::JoyconName(Joycon::ControllerType type) const {
664 switch (type) {
665 case Joycon::ControllerType::Left:
666 return "Left Joycon";
667 case Joycon::ControllerType::Right:
668 return "Right Joycon";
669 case Joycon::ControllerType::Pro:
670 return "Pro Controller";
671 case Joycon::ControllerType::Dual:
672 return "Dual Joycon";
673 default:
674 return "Unknown Switch Controller";
675 }
676}
677} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..2149ab7fd
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include <thread>
9#include <SDL_hidapi.h>
10
11#include "input_common/input_engine.h"
12
13namespace InputCommon::Joycon {
14using SerialNumber = std::array<u8, 15>;
15struct Battery;
16struct Color;
17struct MotionData;
18enum class ControllerType : u8;
19enum class DriverResult;
20enum class IrsResolution;
21class JoyconDriver;
22} // namespace InputCommon::Joycon
23
24namespace InputCommon {
25
26class Joycons final : public InputCommon::InputEngine {
27public:
28 explicit Joycons(const std::string& input_engine_);
29
30 ~Joycons();
31
32 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
33 Common::Input::DriverResult SetVibration(
34 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
35
36 Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
37 const Common::Input::LedStatus& led_status) override;
38
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
44 const std::vector<u8>& data) override;
45
46 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
48
49 /// Used for automapping features
50 std::vector<Common::ParamPackage> GetInputDevices() const override;
51 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
52 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
53 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
54 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
55
56private:
57 static constexpr std::size_t MaxSupportedControllers = 8;
58
59 /// For shutting down, clear all data, join all threads, release usb devices
60 void Reset();
61
62 /// Registers controllers, clears all data and starts the scan thread
63 void Setup();
64
65 /// Actively searchs for new devices
66 void ScanThread(std::stop_token stop_token);
67
68 /// Returns true if device is valid and not registered
69 bool IsDeviceNew(SDL_hid_device_info* device_info) const;
70
71 /// Tries to connect to the new device
72 void RegisterNewDevice(SDL_hid_device_info* device_info);
73
74 /// Returns the next free handle
75 std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
76
77 void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
78 void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
79 void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
80 void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
81 void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
82 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
85 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
86 Joycon::IrsResolution format);
87
88 /// Returns a JoyconHandle corresponding to a PadIdentifier
89 std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
90
91 /// Returns a PadIdentifier corresponding to the port number and joycon type
92 PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
93
94 /// Returns a ParamPackage corresponding to the port number and joycon type
95 Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
96
97 std::string JoyconName(std::size_t port) const;
98
99 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
100
101 /// Returns the name of the device in text format
102 std::string JoyconName(Joycon::ControllerType type) const;
103
104 std::jthread scan_thread;
105
106 // Joycon types are split by type to ease supporting dualjoycon configurations
107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
109};
110
111} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 4818bb744..d975eb815 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,25 +40,26 @@ public:
40 } 40 }
41 41
42 void EnableMotion() { 42 void EnableMotion() {
43 if (sdl_controller) { 43 if (!sdl_controller) {
44 SDL_GameController* controller = sdl_controller.get(); 44 return;
45 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; 45 }
46 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; 46 SDL_GameController* controller = sdl_controller.get();
47 if (has_accel) { 47 if (HasMotion()) {
48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); 48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
49 } 49 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
50 if (has_gyro) { 50 }
51 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); 51 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
52 } 52 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
53 if (has_accel) {
54 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
55 }
56 if (has_gyro) {
57 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
53 } 58 }
54 } 59 }
55 60
56 bool HasGyro() const { 61 bool HasMotion() const {
57 return has_gyro; 62 return has_gyro || has_accel;
58 }
59
60 bool HasAccel() const {
61 return has_accel;
62 } 63 }
63 64
64 bool UpdateMotion(SDL_ControllerSensorEvent event) { 65 bool UpdateMotion(SDL_ControllerSensorEvent event) {
@@ -85,6 +86,20 @@ public:
85 if (time_difference == 0) { 86 if (time_difference == 0) {
86 return false; 87 return false;
87 } 88 }
89
90 // Motion data is invalid
91 if (motion.accel_x == 0 && motion.gyro_x == 0 && motion.accel_y == 0 &&
92 motion.gyro_y == 0 && motion.accel_z == 0 && motion.gyro_z == 0) {
93 if (motion_error_count++ < 200) {
94 return false;
95 }
96 // Try restarting the sensor
97 motion_error_count = 0;
98 EnableMotion();
99 return false;
100 }
101
102 motion_error_count = 0;
88 motion.delta_timestamp = time_difference * 1000; 103 motion.delta_timestamp = time_difference * 1000;
89 return true; 104 return true;
90 } 105 }
@@ -250,6 +265,7 @@ private:
250 mutable std::mutex mutex; 265 mutable std::mutex mutex;
251 266
252 u64 last_motion_update{}; 267 u64 last_motion_update{};
268 std::size_t motion_error_count{};
253 bool has_gyro{false}; 269 bool has_gyro{false};
254 bool has_accel{false}; 270 bool has_accel{false};
255 bool has_vibration{false}; 271 bool has_vibration{false};
@@ -318,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) {
318 334
319 const auto guid = GetGUID(sdl_joystick); 335 const auto guid = GetGUID(sdl_joystick);
320 336
337 if (Settings::values.enable_joycon_driver) {
338 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
339 (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
340 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
341 SDL_JoystickClose(sdl_joystick);
342 return;
343 }
344 }
345
321 std::scoped_lock lock{joystick_map_mutex}; 346 std::scoped_lock lock{joystick_map_mutex};
322 if (joystick_map.find(guid) == joystick_map.end()) { 347 if (joystick_map.find(guid) == joystick_map.end()) {
323 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 348 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -440,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
440 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
441 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
442 467
443 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and 468 // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled
444 // not a generic one 469 if (Settings::values.enable_joycon_driver) {
445 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 470 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
471 } else {
472 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
473 }
474 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
446 475
447 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 476 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
448 // driver on Linux. 477 // driver on Linux.
@@ -532,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
532 return devices; 561 return devices;
533} 562}
534 563
535Common::Input::VibrationError SDLDriver::SetVibration( 564Common::Input::DriverResult SDLDriver::SetVibration(
536 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 565 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
537 const auto joystick = 566 const auto joystick =
538 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 567 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -566,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration(
566 .vibration = new_vibration, 595 .vibration = new_vibration,
567 }); 596 });
568 597
569 return Common::Input::VibrationError::None; 598 return Common::Input::DriverResult::Success;
570} 599}
571 600
572bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 601bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
@@ -942,18 +971,18 @@ MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& p
942 MotionMapping mapping = {}; 971 MotionMapping mapping = {};
943 joystick->EnableMotion(); 972 joystick->EnableMotion();
944 973
945 if (joystick->HasGyro() || joystick->HasAccel()) { 974 if (joystick->HasMotion()) {
946 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, 975 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
947 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 976 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
948 } 977 }
949 if (params.Has("guid2")) { 978 if (params.Has("guid2")) {
950 joystick2->EnableMotion(); 979 joystick2->EnableMotion();
951 if (joystick2->HasGyro() || joystick2->HasAccel()) { 980 if (joystick2->HasMotion()) {
952 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 981 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
953 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); 982 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
954 } 983 }
955 } else { 984 } else {
956 if (joystick->HasGyro() || joystick->HasAccel()) { 985 if (joystick->HasMotion()) {
957 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 986 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
958 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 987 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
959 } 988 }
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 366bcc496..ffde169b3 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -63,7 +63,7 @@ public:
63 63
64 bool IsStickInverted(const Common::ParamPackage& params) override; 64 bool IsStickInverted(const Common::ParamPackage& params) override;
65 65
66 Common::Input::VibrationError SetVibration( 66 Common::Input::DriverResult SetVibration(
67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
68 68
69 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 69 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index f3ade90da..f3cb14c56 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -156,10 +156,12 @@ void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
156 }; 156 };
157} 157}
158 158
159std::tuple<TasState, size_t, size_t> Tas::GetStatus() const { 159std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> Tas::GetStatus() const {
160 TasState state; 160 TasState state;
161 std::array<size_t, PLAYER_NUMBER> lengths{0};
161 if (is_recording) { 162 if (is_recording) {
162 return {TasState::Recording, 0, record_commands.size()}; 163 lengths[0] = record_commands.size();
164 return {TasState::Recording, record_commands.size(), lengths};
163 } 165 }
164 166
165 if (is_running) { 167 if (is_running) {
@@ -168,7 +170,11 @@ std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
168 state = TasState::Stopped; 170 state = TasState::Stopped;
169 } 171 }
170 172
171 return {state, current_command, script_length}; 173 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
174 lengths[i] = commands[i].size();
175 }
176
177 return {state, current_command, lengths};
172} 178}
173 179
174void Tas::UpdateThread() { 180void Tas::UpdateThread() {
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
index 38a27a230..5be66d142 100644
--- a/src/input_common/drivers/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -124,7 +124,7 @@ public:
124 * Current playback progress ; 124 * Current playback progress ;
125 * Total length of script file currently loaded or being recorded 125 * Total length of script file currently loaded or being recorded
126 */ 126 */
127 std::tuple<TasState, size_t, size_t> GetStatus() const; 127 std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> GetStatus() const;
128 128
129private: 129private:
130 enum class TasAxis : u8; 130 enum class TasAxis : u8;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 63ffaca67..4a0268a4d 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -22,22 +22,23 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(
22 22
23VirtualAmiibo::~VirtualAmiibo() = default; 23VirtualAmiibo::~VirtualAmiibo() = default;
24 24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode( 25Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_, 26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) { 27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_; 28 polling_mode = polling_mode_;
29 29
30 if (polling_mode == Common::Input::PollingMode::NFC) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC:
31 if (state == State::Initialized) { 32 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo; 33 state = State::WaitingForAmiibo;
33 } 34 }
34 } else { 35 return Common::Input::DriverResult::Success;
36 default:
35 if (state == State::AmiiboIsOpen) { 37 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo(); 38 CloseAmiibo();
37 } 39 }
40 return Common::Input::DriverResult::NotSupported;
38 } 41 }
39
40 return Common::Input::PollingError::None;
41} 42}
42 43
43Common::Input::NfcState VirtualAmiibo::SupportsNfc( 44Common::Input::NfcState VirtualAmiibo::SupportsNfc(
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 0f9dad333..13cacfc0a 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -36,7 +36,7 @@ public:
36 ~VirtualAmiibo() override; 36 ~VirtualAmiibo() override;
37 37
38 // Sets polling mode to a controller 38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode( 39 Common::Input::DriverResult SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41 41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
new file mode 100644
index 000000000..8f94c9f45
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,575 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "common/swap.h"
6#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h"
8#include "input_common/helpers/joycon_protocol/calibration.h"
9#include "input_common/helpers/joycon_protocol/generic_functions.h"
10#include "input_common/helpers/joycon_protocol/irs.h"
11#include "input_common/helpers/joycon_protocol/nfc.h"
12#include "input_common/helpers/joycon_protocol/poller.h"
13#include "input_common/helpers/joycon_protocol/ringcon.h"
14#include "input_common/helpers/joycon_protocol/rumble.h"
15
16namespace InputCommon::Joycon {
17JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
18 hidapi_handle = std::make_shared<JoyconHandle>();
19}
20
21JoyconDriver::~JoyconDriver() {
22 Stop();
23}
24
25void JoyconDriver::Stop() {
26 is_connected = false;
27 input_thread = {};
28}
29
30DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
31 std::scoped_lock lock{mutex};
32
33 handle_device_type = ControllerType::None;
34 GetDeviceType(device_info, handle_device_type);
35 if (handle_device_type == ControllerType::None) {
36 return DriverResult::UnsupportedControllerType;
37 }
38
39 hidapi_handle->handle =
40 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
41 std::memcpy(&handle_serial_number, device_info->serial_number, 15);
42 if (!hidapi_handle->handle) {
43 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
44 device_info->vendor_id, device_info->product_id);
45 return DriverResult::HandleInUse;
46 }
47 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
48 return DriverResult::Success;
49}
50
51DriverResult JoyconDriver::InitializeDevice() {
52 if (!hidapi_handle->handle) {
53 return DriverResult::InvalidHandle;
54 }
55 std::scoped_lock lock{mutex};
56 disable_input_thread = true;
57
58 // Reset Counters
59 error_counter = 0;
60 hidapi_handle->packet_counter = 0;
61
62 // Reset external device status
63 starlink_connected = false;
64 ring_connected = false;
65 amiibo_detected = false;
66
67 // Set HW default configuration
68 vibration_enabled = true;
69 motion_enabled = true;
70 hidbus_enabled = false;
71 nfc_enabled = false;
72 passive_enabled = false;
73 irs_enabled = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
77 accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
78
79 // Initialize HW Protocols
80 calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);
81 generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle);
82 irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);
83 nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);
84 ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86
87 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version);
89 generic_protocol->SetLowPowerMode(false);
90 generic_protocol->GetColor(color);
91 if (handle_device_type == ControllerType::Pro) {
92 // Some 3rd party controllers aren't pro controllers
93 generic_protocol->GetControllerType(device_type);
94 } else {
95 device_type = handle_device_type;
96 }
97 generic_protocol->GetSerialNumber(serial_number);
98 supported_features = GetSupportedFeatures();
99
100 // Get Calibration data
101 calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration);
102 calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration);
103 calibration_protocol->GetImuCalibration(motion_calibration);
104
105 // Set led status
106 generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port));
107
108 // Apply HW configuration
109 SetPollingMode();
110
111 // Initialize joycon poller
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration);
114
115 // Start pooling for data
116 is_connected = true;
117 if (!input_thread_running) {
118 input_thread =
119 std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
120 }
121
122 disable_input_thread = false;
123 return DriverResult::Success;
124}
125
126void JoyconDriver::InputThread(std::stop_token stop_token) {
127 LOG_INFO(Input, "Joycon Adapter input thread started");
128 Common::SetCurrentThreadName("JoyconInput");
129 input_thread_running = true;
130
131 // Max update rate is 5ms, ensure we are always able to read a bit faster
132 constexpr int ThreadDelay = 2;
133 std::vector<u8> buffer(MaxBufferSize);
134
135 while (!stop_token.stop_requested()) {
136 int status = 0;
137
138 if (!IsInputThreadValid()) {
139 input_thread.request_stop();
140 continue;
141 }
142
143 // By disabling the input thread we can ensure custom commands will succeed as no package is
144 // skipped
145 if (!disable_input_thread) {
146 status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
147 ThreadDelay);
148 } else {
149 std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
150 }
151
152 if (IsPayloadCorrect(status, buffer)) {
153 OnNewData(buffer);
154 }
155
156 std::this_thread::yield();
157 }
158
159 is_connected = false;
160 input_thread_running = false;
161 LOG_INFO(Input, "Joycon Adapter input thread stopped");
162}
163
164void JoyconDriver::OnNewData(std::span<u8> buffer) {
165 const auto report_mode = static_cast<ReportMode>(buffer[0]);
166
167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
168 // experience
169 switch (report_mode) {
170 case ReportMode::STANDARD_FULL_60HZ:
171 case ReportMode::NFC_IR_MODE_60HZ:
172 case ReportMode::SIMPLE_HID_MODE: {
173 const auto now = std::chrono::steady_clock::now();
174 const auto new_delta_time = static_cast<u64>(
175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
176 delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10;
177 last_update = now;
178 joycon_poller->UpdateColor(color);
179 break;
180 }
181 default:
182 break;
183 }
184
185 const MotionStatus motion_status{
186 .is_enabled = motion_enabled,
187 .delta_time = delta_time,
188 .gyro_sensitivity = gyro_sensitivity,
189 .accelerometer_sensitivity = accelerometer_sensitivity,
190 };
191
192 // TODO: Remove this when calibration is properly loaded and not calculated
193 if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
194 InputReportActive data{};
195 memcpy(&data, buffer.data(), sizeof(InputReportActive));
196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
197 }
198
199 const RingStatus ring_status{
200 .is_enabled = ring_connected,
201 .default_value = ring_calibration.default_value,
202 .max_value = ring_calibration.max_value,
203 .min_value = ring_calibration.min_value,
204 };
205
206 if (irs_protocol->IsEnabled()) {
207 irs_protocol->RequestImage(buffer);
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 }
210
211 if (nfc_protocol->IsEnabled()) {
212 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({});
215 amiibo_detected = false;
216 return;
217 }
218 }
219
220 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C);
222 const auto result = nfc_protocol->ScanAmiibo(data);
223 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data);
225 amiibo_detected = true;
226 }
227 }
228 }
229
230 switch (report_mode) {
231 case ReportMode::STANDARD_FULL_60HZ:
232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
233 break;
234 case ReportMode::NFC_IR_MODE_60HZ:
235 joycon_poller->ReadNfcIRMode(buffer, motion_status);
236 break;
237 case ReportMode::SIMPLE_HID_MODE:
238 joycon_poller->ReadPassiveMode(buffer);
239 break;
240 case ReportMode::SUBCMD_REPLY:
241 LOG_DEBUG(Input, "Unhandled command reply");
242 break;
243 default:
244 LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
245 break;
246 }
247}
248
249DriverResult JoyconDriver::SetPollingMode() {
250 disable_input_thread = true;
251
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
253
254 if (motion_enabled && supported_features.motion) {
255 generic_protocol->EnableImu(true);
256 generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance,
257 accelerometer_sensitivity, accelerometer_performance);
258 } else {
259 generic_protocol->EnableImu(false);
260 }
261
262 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs();
264 }
265
266 if (nfc_protocol->IsEnabled()) {
267 amiibo_detected = false;
268 nfc_protocol->DisableNfc();
269 }
270
271 if (ring_protocol->IsEnabled()) {
272 ring_connected = false;
273 ring_protocol->DisableRingCon();
274 }
275
276 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result;
281 }
282 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS");
284 }
285
286 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result;
294 }
295 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC");
297 }
298
299 if (hidbus_enabled && supported_features.hidbus) {
300 auto result = ring_protocol->EnableRingCon();
301 if (result == DriverResult::Success) {
302 result = ring_protocol->StartRingconPolling();
303 }
304 if (result == DriverResult::Success) {
305 ring_connected = true;
306 disable_input_thread = false;
307 return result;
308 }
309 ring_connected = false;
310 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon");
312 }
313
314 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result;
319 }
320 LOG_ERROR(Input, "Error enabling passive mode");
321 }
322
323 // Default Mode
324 const auto result = generic_protocol->EnableActiveMode();
325 if (result != DriverResult::Success) {
326 LOG_ERROR(Input, "Error enabling active mode");
327 }
328 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed();
330
331 disable_input_thread = false;
332 return result;
333}
334
335JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
336 SupportedFeatures features{
337 .passive = true,
338 .motion = true,
339 .vibration = true,
340 };
341
342 if (device_type == ControllerType::Right) {
343 features.nfc = true;
344 features.irs = true;
345 features.hidbus = true;
346 }
347
348 if (device_type == ControllerType::Pro) {
349 features.nfc = true;
350 }
351 return features;
352}
353
354bool JoyconDriver::IsInputThreadValid() const {
355 if (!is_connected.load()) {
356 return false;
357 }
358 if (hidapi_handle->handle == nullptr) {
359 return false;
360 }
361 // Controller is not responding. Terminate connection
362 if (error_counter > MaxErrorCount) {
363 return false;
364 }
365 return true;
366}
367
368bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
369 if (status <= -1) {
370 error_counter++;
371 return false;
372 }
373 // There's no new data
374 if (status == 0) {
375 return false;
376 }
377 // No reply ever starts with zero
378 if (buffer[0] == 0x00) {
379 error_counter++;
380 return false;
381 }
382 error_counter = 0;
383 return true;
384}
385
386DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
387 std::scoped_lock lock{mutex};
388 if (disable_input_thread) {
389 return DriverResult::HandleInUse;
390 }
391 return rumble_protocol->SendVibration(vibration);
392}
393
394DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
395 std::scoped_lock lock{mutex};
396 if (disable_input_thread) {
397 return DriverResult::HandleInUse;
398 }
399 return generic_protocol->SetLedPattern(led_pattern);
400}
401
402DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
403 std::scoped_lock lock{mutex};
404 if (disable_input_thread) {
405 return DriverResult::HandleInUse;
406 }
407 disable_input_thread = true;
408 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
409 disable_input_thread = false;
410 return result;
411}
412
413DriverResult JoyconDriver::SetPasiveMode() {
414 std::scoped_lock lock{mutex};
415 motion_enabled = false;
416 hidbus_enabled = false;
417 nfc_enabled = false;
418 passive_enabled = true;
419 irs_enabled = false;
420 return SetPollingMode();
421}
422
423DriverResult JoyconDriver::SetActiveMode() {
424 if (is_ring_disabled_by_irs) {
425 is_ring_disabled_by_irs = false;
426 SetActiveMode();
427 return SetRingConMode();
428 }
429
430 std::scoped_lock lock{mutex};
431 motion_enabled = true;
432 hidbus_enabled = false;
433 nfc_enabled = false;
434 passive_enabled = false;
435 irs_enabled = false;
436 return SetPollingMode();
437}
438
439DriverResult JoyconDriver::SetIrMode() {
440 std::scoped_lock lock{mutex};
441
442 if (!supported_features.irs) {
443 return DriverResult::NotSupported;
444 }
445
446 if (ring_connected) {
447 is_ring_disabled_by_irs = true;
448 }
449
450 motion_enabled = false;
451 hidbus_enabled = false;
452 nfc_enabled = false;
453 passive_enabled = false;
454 irs_enabled = true;
455 return SetPollingMode();
456}
457
458DriverResult JoyconDriver::SetNfcMode() {
459 std::scoped_lock lock{mutex};
460
461 if (!supported_features.nfc) {
462 return DriverResult::NotSupported;
463 }
464
465 motion_enabled = true;
466 hidbus_enabled = false;
467 nfc_enabled = true;
468 passive_enabled = false;
469 irs_enabled = false;
470 return SetPollingMode();
471}
472
473DriverResult JoyconDriver::SetRingConMode() {
474 std::scoped_lock lock{mutex};
475
476 if (!supported_features.hidbus) {
477 return DriverResult::NotSupported;
478 }
479
480 motion_enabled = true;
481 hidbus_enabled = true;
482 nfc_enabled = false;
483 passive_enabled = false;
484 irs_enabled = false;
485
486 const auto result = SetPollingMode();
487
488 if (!ring_connected) {
489 return DriverResult::NoDeviceDetected;
490 }
491
492 return result;
493}
494
495bool JoyconDriver::IsConnected() const {
496 std::scoped_lock lock{mutex};
497 return is_connected.load();
498}
499
500bool JoyconDriver::IsVibrationEnabled() const {
501 std::scoped_lock lock{mutex};
502 return vibration_enabled;
503}
504
505FirmwareVersion JoyconDriver::GetDeviceVersion() const {
506 std::scoped_lock lock{mutex};
507 return version;
508}
509
510Color JoyconDriver::GetDeviceColor() const {
511 std::scoped_lock lock{mutex};
512 return color;
513}
514
515std::size_t JoyconDriver::GetDevicePort() const {
516 std::scoped_lock lock{mutex};
517 return port;
518}
519
520ControllerType JoyconDriver::GetDeviceType() const {
521 std::scoped_lock lock{mutex};
522 return device_type;
523}
524
525ControllerType JoyconDriver::GetHandleDeviceType() const {
526 std::scoped_lock lock{mutex};
527 return handle_device_type;
528}
529
530SerialNumber JoyconDriver::GetSerialNumber() const {
531 std::scoped_lock lock{mutex};
532 return serial_number;
533}
534
535SerialNumber JoyconDriver::GetHandleSerialNumber() const {
536 std::scoped_lock lock{mutex};
537 return handle_serial_number;
538}
539
540void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
541 joycon_poller->SetCallbacks(callbacks);
542}
543
544DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
545 ControllerType& controller_type) {
546 static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{
547 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
548 {0x2007, ControllerType::Right},
549 };
550 constexpr u16 nintendo_vendor_id = 0x057e;
551
552 controller_type = ControllerType::None;
553 if (device_info->vendor_id != nintendo_vendor_id) {
554 return DriverResult::UnsupportedControllerType;
555 }
556
557 for (const auto& [product_id, type] : supported_devices) {
558 if (device_info->product_id == static_cast<u16>(product_id)) {
559 controller_type = type;
560 return Joycon::DriverResult::Success;
561 }
562 }
563 return Joycon::DriverResult::UnsupportedControllerType;
564}
565
566DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
567 SerialNumber& serial_number) {
568 if (device_info->serial_number == nullptr) {
569 return DriverResult::Unknown;
570 }
571 std::memcpy(&serial_number, device_info->serial_number, 15);
572 return Joycon::DriverResult::Success;
573}
574
575} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
new file mode 100644
index 000000000..c1e189fa5
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.h
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <functional>
8#include <mutex>
9#include <span>
10#include <thread>
11
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15class CalibrationProtocol;
16class GenericProtocol;
17class IrsProtocol;
18class NfcProtocol;
19class JoyconPoller;
20class RingConProtocol;
21class RumbleProtocol;
22
23class JoyconDriver final {
24public:
25 explicit JoyconDriver(std::size_t port_);
26
27 ~JoyconDriver();
28
29 DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
30 DriverResult InitializeDevice();
31 void Stop();
32
33 bool IsConnected() const;
34 bool IsVibrationEnabled() const;
35
36 FirmwareVersion GetDeviceVersion() const;
37 Color GetDeviceColor() const;
38 std::size_t GetDevicePort() const;
39 ControllerType GetDeviceType() const;
40 ControllerType GetHandleDeviceType() const;
41 SerialNumber GetSerialNumber() const;
42 SerialNumber GetHandleSerialNumber() const;
43
44 DriverResult SetVibration(const VibrationValue& vibration);
45 DriverResult SetLedConfig(u8 led_pattern);
46 DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
47 DriverResult SetPasiveMode();
48 DriverResult SetActiveMode();
49 DriverResult SetIrMode();
50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode();
52
53 void SetCallbacks(const JoyconCallbacks& callbacks);
54
55 // Returns device type from hidapi handle
56 static DriverResult GetDeviceType(SDL_hid_device_info* device_info,
57 ControllerType& controller_type);
58
59 // Returns serial number from hidapi handle
60 static DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
61 SerialNumber& serial_number);
62
63private:
64 struct SupportedFeatures {
65 bool passive{};
66 bool hidbus{};
67 bool irs{};
68 bool motion{};
69 bool nfc{};
70 bool vibration{};
71 };
72
73 /// Main thread, actively request new data from the handle
74 void InputThread(std::stop_token stop_token);
75
76 /// Called everytime a valid package arrives
77 void OnNewData(std::span<u8> buffer);
78
79 /// Updates device configuration to enable or disable features
80 DriverResult SetPollingMode();
81
82 /// Returns true if input thread is valid and doesn't need to be stopped
83 bool IsInputThreadValid() const;
84
85 /// Returns true if the data should be interpreted. Otherwise the error counter is incremented
86 bool IsPayloadCorrect(int status, std::span<const u8> buffer);
87
88 /// Returns a list of supported features that can be enabled on this device
89 SupportedFeatures GetSupportedFeatures();
90
91 // Protocol Features
92 std::unique_ptr<CalibrationProtocol> calibration_protocol;
93 std::unique_ptr<GenericProtocol> generic_protocol;
94 std::unique_ptr<IrsProtocol> irs_protocol;
95 std::unique_ptr<NfcProtocol> nfc_protocol;
96 std::unique_ptr<JoyconPoller> joycon_poller;
97 std::unique_ptr<RingConProtocol> ring_protocol;
98 std::unique_ptr<RumbleProtocol> rumble_protocol;
99
100 // Connection status
101 std::atomic<bool> is_connected{};
102 u64 delta_time;
103 std::size_t error_counter{};
104 std::shared_ptr<JoyconHandle> hidapi_handle;
105 std::chrono::time_point<std::chrono::steady_clock> last_update;
106
107 // External device status
108 bool starlink_connected{};
109 bool ring_connected{};
110 bool amiibo_detected{};
111 bool is_ring_disabled_by_irs{};
112
113 // Harware configuration
114 u8 leds{};
115 ReportMode mode{};
116 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
117 bool hidbus_enabled{}; // External device support
118 bool irs_enabled{}; // Infrared camera input
119 bool motion_enabled{}; // Enables motion input
120 bool nfc_enabled{}; // Enables Amiibo detection
121 bool vibration_enabled{}; // Allows vibrations
122
123 // Calibration data
124 GyroSensitivity gyro_sensitivity{};
125 GyroPerformance gyro_performance{};
126 AccelerometerSensitivity accelerometer_sensitivity{};
127 AccelerometerPerformance accelerometer_performance{};
128 JoyStickCalibration left_stick_calibration{};
129 JoyStickCalibration right_stick_calibration{};
130 MotionCalibration motion_calibration{};
131 RingCalibration ring_calibration{};
132
133 // Fixed joycon info
134 FirmwareVersion version{};
135 Color color{};
136 std::size_t port{};
137 ControllerType device_type{}; // Device type reported by controller
138 ControllerType handle_device_type{}; // Device type reported by hidapi
139 SerialNumber serial_number{}; // Serial number reported by controller
140 SerialNumber handle_serial_number{}; // Serial number type reported by hidapi
141 SupportedFeatures supported_features{};
142
143 // Thread related
144 mutable std::mutex mutex;
145 std::jthread input_thread;
146 bool input_thread_running{};
147 bool disable_input_thread{};
148};
149
150} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
new file mode 100644
index 000000000..d8f040f75
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -0,0 +1,218 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "input_common/helpers/joycon_protocol/calibration.h"
7#include "input_common/helpers/joycon_protocol/joycon_types.h"
8
9namespace InputCommon::Joycon {
10
11CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
12 : JoyconCommonProtocol(std::move(handle)) {}
13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false;
19 calibration = {};
20
21 if (result == DriverResult::Success) {
22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
23 }
24
25 // Read User defined calibration
26 if (result == DriverResult::Success && has_user_calibration) {
27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
28 }
29
30 // Read Factory calibration
31 if (result == DriverResult::Success && !has_user_calibration) {
32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 }
34
35 if (result == DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
39 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
40 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
41 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
42 }
43
44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration);
46
47 return result;
48}
49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this);
52 DriverResult result{DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false;
55 calibration = {};
56
57 if (result == DriverResult::Success) {
58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
59 }
60
61 // Read User defined calibration
62 if (result == DriverResult::Success && has_user_calibration) {
63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
64 }
65
66 // Read Factory calibration
67 if (result == DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
69 }
70
71 if (result == DriverResult::Success) {
72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
75 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
76 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
77 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
78 }
79
80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration);
82
83 return result;
84}
85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this);
88 DriverResult result{DriverResult::Success};
89 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false;
91 calibration = {};
92
93 if (result == DriverResult::Success) {
94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
95 }
96
97 // Read User defined calibration
98 if (result == DriverResult::Success && has_user_calibration) {
99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 }
101
102 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
105 }
106
107 if (result == DriverResult::Success) {
108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
111
112 calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
113 calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
114 calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
115
116 calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
117 calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
118 calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
119
120 calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
121 calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
122 calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
123 }
124
125 ValidateCalibration(calibration);
126
127 return result;
128}
129
130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
131 s16 current_value) {
132 constexpr s16 DefaultRingRange{800};
133
134 // TODO: Get default calibration form ring itself
135 if (ring_data_max == 0 && ring_data_min == 0) {
136 ring_data_max = current_value + DefaultRingRange;
137 ring_data_min = current_value - DefaultRingRange;
138 ring_data_default = current_value;
139 }
140 ring_data_max = std::max(ring_data_max, current_value);
141 ring_data_min = std::min(ring_data_min, current_value);
142 calibration = {
143 .default_value = ring_data_default,
144 .max_value = ring_data_max,
145 .min_value = ring_data_min,
146 };
147 return DriverResult::Success;
148}
149
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false;
155 if (result == DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 }
159 return result;
160}
161
162u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
163 return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
164}
165
166u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
167 return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
168}
169
170void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
171 constexpr u16 DefaultStickCenter{0x800};
172 constexpr u16 DefaultStickRange{0x6cc};
173
174 calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
175 calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
176 calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
177
178 calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
179 calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
180 calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
181}
182
183void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
184 constexpr s16 DefaultAccelerometerScale{0x4000};
185 constexpr s16 DefaultGyroScale{0x3be7};
186 constexpr s16 DefaultOffset{0};
187
188 for (auto& sensor : calibration.accelerometer) {
189 sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
190 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
191 }
192 for (auto& sensor : calibration.gyro) {
193 sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
194 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
195 }
196}
197
198u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
199 if (value == 0) {
200 return default_value;
201 }
202 if (value == 0xFFF) {
203 return default_value;
204 }
205 return value;
206}
207
208s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
209 if (value == 0) {
210 return default_value;
211 }
212 if (value == 0xFFF) {
213 return default_value;
214 }
215 return value;
216}
217
218} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
new file mode 100644
index 000000000..c6fd0f729
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14
15namespace InputCommon::Joycon {
16enum class DriverResult;
17struct JoyStickCalibration;
18struct IMUCalibration;
19struct JoyconHandle;
20} // namespace InputCommon::Joycon
21
22namespace InputCommon::Joycon {
23
24/// Driver functions related to retrieving calibration data from the device
25class CalibrationProtocol final : private JoyconCommonProtocol {
26public:
27 explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle);
28
29 /**
30 * Sends a request to obtain the left stick calibration from memory
31 * @param is_factory_calibration if true factory values will be returned
32 * @returns JoyStickCalibration of the left joystick
33 */
34 DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
35
36 /**
37 * Sends a request to obtain the right stick calibration from memory
38 * @param is_factory_calibration if true factory values will be returned
39 * @returns JoyStickCalibration of the right joystick
40 */
41 DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
42
43 /**
44 * Sends a request to obtain the motion calibration from memory
45 * @returns ImuCalibration of the motion sensor
46 */
47 DriverResult GetImuCalibration(MotionCalibration& calibration);
48
49 /**
50 * Calculates on run time the proper calibration of the ring controller
51 * @returns RingCalibration of the ring sensor
52 */
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54
55private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58
59 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
61
62 /// Converts a raw calibration block to an u16 value containing the y axis value
63 u16 GetYAxisCalibrationValue(std::span<u8> block) const;
64
65 /// Ensures that all joystick calibration values are set
66 void ValidateCalibration(JoyStickCalibration& calibration);
67
68 /// Ensures that all motion calibration values are set
69 void ValidateCalibration(MotionCalibration& calibration);
70
71 /// Returns the default value if the value is either zero or 0xFFF
72 u16 ValidateValue(u16 value, u16 default_value) const;
73
74 /// Returns the default value if the value is either zero or 0xFFF
75 s16 ValidateValue(s16 value, s16 default_value) const;
76
77 s16 ring_data_max = 0;
78 s16 ring_data_default = 0;
79 s16 ring_data_min = 0;
80};
81
82} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
new file mode 100644
index 000000000..2b42a4555
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -0,0 +1,316 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/common_protocol.h"
6
7namespace InputCommon::Joycon {
8JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_)
9 : hidapi_handle{std::move(hidapi_handle_)} {}
10
11u8 JoyconCommonProtocol::GetCounter() {
12 hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F;
13 return hidapi_handle->packet_counter;
14}
15
16void JoyconCommonProtocol::SetBlocking() {
17 SDL_hid_set_nonblocking(hidapi_handle->handle, 0);
18}
19
20void JoyconCommonProtocol::SetNonBlocking() {
21 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
22}
23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26
27 if (result == DriverResult::Success) {
28 // Fallback to 3rd party pro controllers
29 if (controller_type == ControllerType::None) {
30 controller_type = ControllerType::Pro;
31 }
32 }
33
34 return result;
35}
36
37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
38 ControllerType controller_type{ControllerType::None};
39 const auto result = GetDeviceType(controller_type);
40
41 if (result != DriverResult::Success || controller_type == ControllerType::None) {
42 return DriverResult::UnsupportedControllerType;
43 }
44
45 hidapi_handle->handle =
46 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
47
48 if (!hidapi_handle->handle) {
49 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
50 device_info->vendor_id, device_info->product_id);
51 return DriverResult::HandleInUse;
52 }
53
54 SetNonBlocking();
55 return DriverResult::Success;
56}
57
58DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
59 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
61}
62
63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
65
66 if (result == -1) {
67 return DriverResult::ErrorWritingData;
68 }
69
70 return DriverResult::Success;
71}
72
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) {
75 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 15;
77 int tries = 0;
78
79 do {
80 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
81 sizeof(SubCommandResponse), timeout_mili);
82
83 if (result < 1) {
84 LOG_ERROR(Input, "No response from joycon");
85 }
86 if (tries++ > MaxTries) {
87 return DriverResult::Timeout;
88 }
89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
90 output.sub_command != sc);
91
92 return DriverResult::Success;
93}
94
95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
96 SubCommandResponse& output) {
97 SubCommandPacket packet{
98 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
99 .packet_counter = GetCounter(),
100 .sub_command = sc,
101 .command_data = {},
102 };
103
104 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters;
106 }
107
108 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109
110 auto result = SendData(packet);
111
112 if (result != DriverResult::Success) {
113 return result;
114 }
115
116 result = GetSubCommandResponse(sc, output);
117
118 return DriverResult::Success;
119}
120
121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
122 SubCommandResponse output{};
123 return SendSubCommand(sc, buffer, output);
124}
125
126DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
127 SubCommandPacket packet{
128 .output_report = OutputReport::MCU_DATA,
129 .packet_counter = GetCounter(),
130 .sub_command = sc,
131 .command_data = {},
132 };
133
134 if (buffer.size() > packet.command_data.size()) {
135 return DriverResult::InvalidParameters;
136 }
137
138 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
139
140 return SendData(packet);
141}
142
143DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
144 VibrationPacket packet{
145 .output_report = OutputReport::RUMBLE_ONLY,
146 .packet_counter = GetCounter(),
147 .vibration_data = {},
148 };
149
150 if (buffer.size() > packet.vibration_data.size()) {
151 return DriverResult::InvalidParameters;
152 }
153
154 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
155
156 return SendData(packet);
157}
158
159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5;
161 constexpr std::size_t MaxTries = 10;
162 std::size_t tries = 0;
163 SubCommandResponse response{};
164 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
165 const ReadSpiPacket packet_data{
166 .spi_address = addr,
167 .size = static_cast<u8>(output.size()),
168 };
169
170 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
171 do {
172 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
173 if (result != DriverResult::Success) {
174 return result;
175 }
176
177 if (tries++ > MaxTries) {
178 return DriverResult::Timeout;
179 }
180 } while (response.spi_address != addr);
181
182 if (response.command_data.size() < packet_data.size + HeaderSize) {
183 return DriverResult::WrongReply;
184 }
185
186 // Remove header from output
187 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
188 return DriverResult::Success;
189}
190
191DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
192 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
193 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
194
195 if (result != DriverResult::Success) {
196 LOG_ERROR(Input, "Failed with error {}", result);
197 }
198
199 return result;
200}
201
202DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
203 LOG_DEBUG(Input, "ConfigureMCU");
204 std::array<u8, sizeof(MCUConfig)> config_buffer;
205 memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
206 config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36);
207
208 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
209
210 if (result != DriverResult::Success) {
211 LOG_ERROR(Input, "Failed with error {}", result);
212 }
213
214 return result;
215}
216
217DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
218 MCUCommandResponse& output) {
219 constexpr int TimeoutMili = 200;
220 constexpr int MaxTries = 9;
221 int tries = 0;
222
223 do {
224 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
225 sizeof(MCUCommandResponse), TimeoutMili);
226
227 if (result < 1) {
228 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
229 }
230 if (tries++ > MaxTries) {
231 return DriverResult::Timeout;
232 }
233 } while (output.input_report.report_mode != report_mode ||
234 output.mcu_report == MCUReport::EmptyAwaitingCmd);
235
236 return DriverResult::Success;
237}
238
239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
240 std::span<const u8> buffer,
241 MCUCommandResponse& output) {
242 SubCommandPacket packet{
243 .output_report = OutputReport::MCU_DATA,
244 .packet_counter = GetCounter(),
245 .sub_command = sc,
246 .command_data = {},
247 };
248
249 if (buffer.size() > packet.command_data.size()) {
250 return DriverResult::InvalidParameters;
251 }
252
253 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
254
255 auto result = SendData(packet);
256
257 if (result != DriverResult::Success) {
258 return result;
259 }
260
261 result = GetMCUDataResponse(report_mode, output);
262
263 return DriverResult::Success;
264}
265
266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
267 MCUCommandResponse output{};
268 constexpr std::size_t MaxTries{8};
269 std::size_t tries{};
270
271 do {
272 const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
273 const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
274
275 if (result != DriverResult::Success) {
276 return result;
277 }
278
279 if (tries++ > MaxTries) {
280 return DriverResult::WrongReply;
281 }
282 } while (output.mcu_report != MCUReport::StateReport ||
283 output.mcu_data[6] != static_cast<u8>(mode));
284
285 return DriverResult::Success;
286}
287
288// crc-8-ccitt / polynomial 0x07 look up table
289constexpr std::array<u8, 256> mcu_crc8_table = {
290 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
291 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
292 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
293 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
294 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
295 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
296 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
297 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
298 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
299 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
300 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
301 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
302 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
303 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
304 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
305 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
306
307u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const {
308 u8 crc8 = 0x0;
309
310 for (int i = 0; i < size; ++i) {
311 crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])];
312 }
313 return crc8;
314}
315
316} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
new file mode 100644
index 000000000..f44f73ba4
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -0,0 +1,201 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <memory>
12#include <span>
13#include <vector>
14
15#include "common/common_types.h"
16#include "input_common/helpers/joycon_protocol/joycon_types.h"
17
18namespace InputCommon::Joycon {
19
20/// Joycon driver functions that handle low level communication
21class JoyconCommonProtocol {
22public:
23 explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_);
24
25 /**
26 * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is
27 * data to read before returning.
28 */
29 void SetBlocking();
30
31 /**
32 * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return
33 * immediately with a value of 0 if there is no data to be read
34 */
35 void SetNonBlocking();
36
37 /**
38 * Sends a request to obtain the joycon type from device
39 * @returns controller type of the joycon
40 */
41 DriverResult GetDeviceType(ControllerType& controller_type);
42
43 /**
44 * Verifies and sets the joycon_handle if device is valid
45 * @param device info from the driver
46 * @returns success if the device is valid
47 */
48 DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
49
50 /**
51 * Sends a request to set the polling mode of the joycon
52 * @param report_mode polling mode to be set
53 */
54 DriverResult SetReportMode(Joycon::ReportMode report_mode);
55
56 /**
57 * Sends data to the joycon device
58 * @param buffer data to be send
59 */
60 DriverResult SendRawData(std::span<const u8> buffer);
61
62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer);
68 }
69
70 /**
71 * Waits for incoming data of the joycon device that matchs the subcommand
72 * @param sub_command type of data to be returned
73 * @returns a buffer containing the response
74 */
75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
76
77 /**
78 * Sends a sub command to the device and waits for it's reply
79 * @param sc sub command to be send
80 * @param buffer data to be send
81 * @returns output buffer containing the response
82 */
83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output);
85
86 /**
87 * Sends a sub command to the device and waits for it's reply and ignores the output
88 * @param sc sub command to be send
89 * @param buffer data to be send
90 */
91 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
92
93 /**
94 * Sends a mcu command to the device
95 * @param sc sub command to be send
96 * @param buffer data to be send
97 */
98 DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
99
100 /**
101 * Sends vibration data to the joycon
102 * @param buffer data to be send
103 */
104 DriverResult SendVibrationReport(std::span<const u8> buffer);
105
106 /**
107 * Reads the SPI memory stored on the joycon
108 * @param Initial address location
109 * @returns output buffer containing the response
110 */
111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112
113 /**
114 * Reads the SPI memory stored on the joycon
115 * @param Initial address location
116 * @returns output object containing the response
117 */
118 template <typename Output>
119 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer;
122 output = {};
123
124 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) {
126 return result;
127 }
128
129 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success;
131 }
132
133 /**
134 * Enables MCU chip on the joycon
135 * @param enable if true the chip will be enabled
136 */
137 DriverResult EnableMCU(bool enable);
138
139 /**
140 * Configures the MCU to the correspoinding mode
141 * @param MCUConfig configuration
142 */
143 DriverResult ConfigureMCU(const MCUConfig& config);
144
145 /**
146 * Waits until there's MCU data available. On timeout returns error
147 * @param report mode of the expected reply
148 * @returns a buffer containing the response
149 */
150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
151
152 /**
153 * Sends data to the MCU chip and waits for it's reply
154 * @param report mode of the expected reply
155 * @param sub command to be send
156 * @param buffer data to be send
157 * @returns output buffer containing the response
158 */
159 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
160 MCUCommandResponse& output);
161
162 /**
163 * Wait's until the MCU chip is on the specified mode
164 * @param report mode of the expected reply
165 * @param MCUMode configuration
166 */
167 DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
168
169 /**
170 * Calculates the checksum from the MCU data
171 * @param buffer containing the data to be send
172 * @param size of the buffer in bytes
173 * @returns byte with the correct checksum
174 */
175 u8 CalculateMCU_CRC8(u8* buffer, u8 size) const;
176
177private:
178 /**
179 * Increments and returns the packet counter of the handle
180 * @param joycon_handle device to send the data
181 * @returns packet counter value
182 */
183 u8 GetCounter();
184
185 std::shared_ptr<JoyconHandle> hidapi_handle;
186};
187
188class ScopedSetBlocking {
189public:
190 explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} {
191 m_self->SetBlocking();
192 }
193
194 ~ScopedSetBlocking() {
195 m_self->SetNonBlocking();
196 }
197
198private:
199 JoyconCommonProtocol* m_self{};
200};
201} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
new file mode 100644
index 000000000..548a4b9e3
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/generic_functions.h"
6
7namespace InputCommon::Joycon {
8
9GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult GenericProtocol::EnablePassiveMode() {
13 ScopedSetBlocking sb(this);
14 return SetReportMode(ReportMode::SIMPLE_HID_MODE);
15}
16
17DriverResult GenericProtocol::EnableActiveMode() {
18 ScopedSetBlocking sb(this);
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20}
21
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26}
27
28DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31}
32
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this);
35 SubCommandResponse output{};
36
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38
39 device_info = {};
40 if (result == DriverResult::Success) {
41 device_info = output.device_info;
42 }
43
44 return result;
45}
46
47DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
48 return GetDeviceType(controller_type);
49}
50
51DriverResult GenericProtocol::EnableImu(bool enable) {
52 ScopedSetBlocking sb(this);
53 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
54 return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
55}
56
57DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
58 AccelerometerSensitivity asen,
59 AccelerometerPerformance afrec) {
60 ScopedSetBlocking sb(this);
61 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
62 static_cast<u8>(gfrec), static_cast<u8>(afrec)};
63 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
64}
65
66DriverResult GenericProtocol::GetBattery(u32& battery_level) {
67 // This function is meant to request the high resolution battery status
68 battery_level = 0;
69 return DriverResult::NotSupported;
70}
71
72DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this);
74 std::array<u8, 12> buffer{};
75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76
77 color = {};
78 if (result == DriverResult::Success) {
79 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]);
80 color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
81 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
82 color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
83 }
84
85 return result;
86}
87
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this);
90 std::array<u8, 16> buffer{};
91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92
93 serial_number = {};
94 if (result == DriverResult::Success) {
95 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
96 }
97
98 return result;
99}
100
101DriverResult GenericProtocol::GetTemperature(u32& temperature) {
102 // Not all devices have temperature sensor
103 temperature = 25;
104 return DriverResult::NotSupported;
105}
106
107DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
108 DeviceInfo device_info{};
109
110 const auto result = GetDeviceInfo(device_info);
111 version = device_info.firmware;
112
113 return result;
114}
115
116DriverResult GenericProtocol::SetHomeLight() {
117 ScopedSetBlocking sb(this);
118 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
119 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
120}
121
122DriverResult GenericProtocol::SetLedBusy() {
123 return DriverResult::NotSupported;
124}
125
126DriverResult GenericProtocol::SetLedPattern(u8 leds) {
127 ScopedSetBlocking sb(this);
128 const std::array<u8, 1> buffer{leds};
129 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
130}
131
132DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
133 return SetLedPattern(static_cast<u8>(leds << 4));
134}
135
136} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
new file mode 100644
index 000000000..424831e81
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -0,0 +1,114 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include "input_common/helpers/joycon_protocol/common_protocol.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15
16/// Joycon driver functions that easily implemented
17class GenericProtocol final : private JoyconCommonProtocol {
18public:
19 explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle);
20
21 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
22 /// data instead of analog. Motion will be disabled
23 DriverResult EnablePassiveMode();
24
25 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode();
27
28 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable);
30
31 /// Unknown function used by the switch
32 DriverResult TriggersElapsed();
33
34 /**
35 * Sends a request to obtain the joycon firmware and mac from handle
36 * @returns controller device info
37 */
38 DriverResult GetDeviceInfo(DeviceInfo& controller_type);
39
40 /**
41 * Sends a request to obtain the joycon type from handle
42 * @returns controller type of the joycon
43 */
44 DriverResult GetControllerType(ControllerType& controller_type);
45
46 /**
47 * Enables motion input
48 * @param enable if true motion data will be enabled
49 */
50 DriverResult EnableImu(bool enable);
51
52 /**
53 * Configures the motion sensor with the specified parameters
54 * @param gsen gyroscope sensor sensitvity in degrees per second
55 * @param gfrec gyroscope sensor frequency in hertz
56 * @param asen accelerometer sensitivity in G force
57 * @param afrec accelerometer frequency in hertz
58 */
59 DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
60 AccelerometerSensitivity asen, AccelerometerPerformance afrec);
61
62 /**
63 * Request battery level from the device
64 * @returns battery level
65 */
66 DriverResult GetBattery(u32& battery_level);
67
68 /**
69 * Request joycon colors from the device
70 * @returns colors of the body and buttons
71 */
72 DriverResult GetColor(Color& color);
73
74 /**
75 * Request joycon serial number from the device
76 * @returns 16 byte serial number
77 */
78 DriverResult GetSerialNumber(SerialNumber& serial_number);
79
80 /**
81 * Request joycon serial number from the device
82 * @returns 16 byte serial number
83 */
84 DriverResult GetTemperature(u32& temperature);
85
86 /**
87 * Request joycon serial number from the device
88 * @returns 16 byte serial number
89 */
90 DriverResult GetVersionNumber(FirmwareVersion& version);
91
92 /**
93 * Sets home led behaviour
94 */
95 DriverResult SetHomeLight();
96
97 /**
98 * Sets home led into a slow breathing state
99 */
100 DriverResult SetLedBusy();
101
102 /**
103 * Sets the 4 player leds on the joycon on a solid state
104 * @params bit flag containing the led state
105 */
106 DriverResult SetLedPattern(u8 leds);
107
108 /**
109 * Sets the 4 player leds on the joycon on a blinking state
110 * @returns bit flag containing the led state
111 */
112 DriverResult SetLedBlinkPattern(u8 leds);
113};
114} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
new file mode 100644
index 000000000..731fd5981
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h"
7
8namespace InputCommon::Joycon {
9
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::IR,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37 if (result == DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 }
40 if (result == DriverResult::Success) {
41 result = ConfigureIrs();
42 }
43 if (result == DriverResult::Success) {
44 result = WriteRegistersStep1();
45 }
46 if (result == DriverResult::Success) {
47 result = WriteRegistersStep2();
48 }
49
50 is_enabled = true;
51
52 return result;
53}
54
55DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59
60 if (result == DriverResult::Success) {
61 result = EnableMCU(false);
62 }
63
64 is_enabled = false;
65
66 return result;
67}
68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode;
71 switch (format) {
72 case IrsResolution::Size320x240:
73 resolution_code = IrsResolutionCode::Size320x240;
74 fragments = IrsFragments::Size320x240;
75 resolution = IrsResolution::Size320x240;
76 break;
77 case IrsResolution::Size160x120:
78 resolution_code = IrsResolutionCode::Size160x120;
79 fragments = IrsFragments::Size160x120;
80 resolution = IrsResolution::Size160x120;
81 break;
82 case IrsResolution::Size80x60:
83 resolution_code = IrsResolutionCode::Size80x60;
84 fragments = IrsFragments::Size80x60;
85 resolution = IrsResolution::Size80x60;
86 break;
87 case IrsResolution::Size20x15:
88 resolution_code = IrsResolutionCode::Size20x15;
89 fragments = IrsFragments::Size20x15;
90 resolution = IrsResolution::Size20x15;
91 break;
92 case IrsResolution::Size40x30:
93 default:
94 resolution_code = IrsResolutionCode::Size40x30;
95 fragments = IrsFragments::Size40x30;
96 resolution = IrsResolution::Size40x30;
97 break;
98 }
99
100 // Restart feature
101 if (is_enabled) {
102 DisableIrs();
103 return EnableIrs();
104 }
105
106 return DriverResult::Success;
107}
108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112
113 if (buffer[0] == 0x31 && buffer[49] == 0x03) {
114 u8 new_packet_fragment = buffer[52];
115 if (new_packet_fragment == next_packet_fragment) {
116 packet_fragment = next_packet_fragment;
117 memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300);
118
119 return RequestFrame(packet_fragment);
120 }
121
122 if (new_packet_fragment == packet_fragment) {
123 return RequestFrame(packet_fragment);
124 }
125
126 return ResendFrame(next_packet_fragment);
127 }
128
129 return RequestFrame(packet_fragment);
130}
131
132DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28;
135 SubCommandResponse output{};
136 std::size_t tries = 0;
137
138 const IrsConfigure irs_configuration{
139 .command = MCUCommand::ConfigureIR,
140 .sub_command = MCUSubCommand::SetDeviceMode,
141 .irs_mode = IrsMode::ImageTransfer,
142 .number_of_fragments = fragments,
143 .mcu_major_version = 0x0500,
144 .mcu_minor_version = 0x1800,
145 .crc = {},
146 };
147 buf_image.resize((static_cast<u8>(fragments) + 1) * 300);
148
149 std::array<u8, sizeof(IrsConfigure)> request_data{};
150 memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure));
151 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154
155 if (result != DriverResult::Success) {
156 return result;
157 }
158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply;
160 }
161 } while (output.command_data[0] != 0x0b);
162
163 return DriverResult::Success;
164}
165
166DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28;
170 SubCommandResponse output{};
171 std::size_t tries = 0;
172
173 const IrsWriteRegisters irs_registers{
174 .command = MCUCommand::ConfigureIR,
175 .sub_command = MCUSubCommand::WriteDeviceRegisters,
176 .number_of_registers = 0x9,
177 .registers =
178 {
179 IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)},
180 {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)},
181 {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)},
182 {IrRegistersAddress::ExposureTime, 0x00},
183 {IrRegistersAddress::Leds, static_cast<u8>(leds)},
184 {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)},
185 {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)},
186 {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)},
187 {IrRegistersAddress::WhitePixelThreshold, 0xc8},
188 },
189 .crc = {},
190 };
191
192 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
193 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
194 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
195
196 std::array<u8, 38> mcu_request{0x02};
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF;
199
200 if (result != DriverResult::Success) {
201 return result;
202 }
203
204 do {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206
207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 }
211 if (result == DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 }
214
215 if (result != DriverResult::Success) {
216 return result;
217 }
218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply;
220 }
221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23);
223
224 return DriverResult::Success;
225}
226
227DriverResult IrsProtocol::WriteRegistersStep2() {
228 LOG_DEBUG(Input, "WriteRegistersStep2");
229 constexpr std::size_t max_tries = 28;
230 SubCommandResponse output{};
231 std::size_t tries = 0;
232
233 const IrsWriteRegisters irs_registers{
234 .command = MCUCommand::ConfigureIR,
235 .sub_command = MCUSubCommand::WriteDeviceRegisters,
236 .number_of_registers = 0x8,
237 .registers =
238 {
239 IrsRegister{IrRegistersAddress::LedIntensitiyMSB,
240 static_cast<u8>(led_intensity >> 8)},
241 {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)},
242 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
243 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
244 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
245 {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)},
246 {IrRegistersAddress::UpdateTime, 0x2d},
247 {IrRegistersAddress::FinalizeConfig, 0x01},
248 },
249 .crc = {},
250 };
251
252 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
253 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
254 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
255 do {
256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
257
258 if (result != DriverResult::Success) {
259 return result;
260 }
261 if (tries++ >= max_tries) {
262 return DriverResult::WrongReply;
263 }
264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
265
266 return DriverResult::Success;
267}
268
269DriverResult IrsProtocol::RequestFrame(u8 frame) {
270 std::array<u8, 38> mcu_request{};
271 mcu_request[3] = frame;
272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
273 mcu_request[37] = 0xFF;
274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
275}
276
277DriverResult IrsProtocol::ResendFrame(u8 frame) {
278 std::array<u8, 38> mcu_request{};
279 mcu_request[1] = 0x1;
280 mcu_request[2] = frame;
281 mcu_request[3] = 0x0;
282 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
283 mcu_request[37] = 0xFF;
284 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
285}
286
287std::vector<u8> IrsProtocol::GetImage() const {
288 return buf_image;
289}
290
291IrsResolution IrsProtocol::GetIrsFormat() const {
292 return resolution;
293}
294
295bool IrsProtocol::IsEnabled() const {
296 return is_enabled;
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h
new file mode 100644
index 000000000..76dfa02ea
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class IrsProtocol final : private JoyconCommonProtocol {
19public:
20 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableIrs();
23
24 DriverResult DisableIrs();
25
26 DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
27
28 DriverResult RequestImage(std::span<u8> buffer);
29
30 std::vector<u8> GetImage() const;
31
32 IrsResolution GetIrsFormat() const;
33
34 bool IsEnabled() const;
35
36private:
37 DriverResult ConfigureIrs();
38
39 DriverResult WriteRegistersStep1();
40 DriverResult WriteRegistersStep2();
41
42 DriverResult RequestFrame(u8 frame);
43 DriverResult ResendFrame(u8 frame);
44
45 IrsMode irs_mode{IrsMode::ImageTransfer};
46 IrsResolution resolution{IrsResolution::Size40x30};
47 IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30};
48 IrsFragments fragments{IrsFragments::Size40x30};
49 IrLeds leds{IrLeds::BrightAndDim};
50 IrExLedFilter led_filter{IrExLedFilter::Enabled};
51 IrImageFlip image_flip{IrImageFlip::Normal};
52 u8 digital_gain{0x01};
53 u16 exposure{0x2490};
54 u16 led_intensity{0x0f10};
55 u32 denoise{0x012344};
56
57 u8 packet_fragment{};
58 std::vector<u8> buf_image; // 8bpp greyscale image.
59
60 bool is_enabled{};
61};
62
63} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
new file mode 100644
index 000000000..b91934990
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -0,0 +1,697 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <array>
12#include <functional>
13#include <SDL_hidapi.h>
14
15#include "common/bit_field.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18
19namespace InputCommon::Joycon {
20constexpr u32 MaxErrorCount = 50;
21constexpr u32 MaxBufferSize = 368;
22constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
23
24using MacAddress = std::array<u8, 6>;
25using SerialNumber = std::array<u8, 15>;
26
27enum class ControllerType : u8 {
28 None = 0x00,
29 Left = 0x01,
30 Right = 0x02,
31 Pro = 0x03,
32 Dual = 0x05, // TODO: Verify this id
33 LarkHvc1 = 0x07,
34 LarkHvc2 = 0x08,
35 LarkNesLeft = 0x09,
36 LarkNesRight = 0x0A,
37 Lucia = 0x0B,
38 Lagon = 0x0C,
39 Lager = 0x0D,
40};
41
42enum class PadAxes {
43 LeftStickX,
44 LeftStickY,
45 RightStickX,
46 RightStickY,
47 Undefined,
48};
49
50enum class PadMotion {
51 LeftMotion,
52 RightMotion,
53 Undefined,
54};
55
56enum class PadButton : u32 {
57 Down = 0x000001,
58 Up = 0x000002,
59 Right = 0x000004,
60 Left = 0x000008,
61 LeftSR = 0x000010,
62 LeftSL = 0x000020,
63 L = 0x000040,
64 ZL = 0x000080,
65 Y = 0x000100,
66 X = 0x000200,
67 B = 0x000400,
68 A = 0x000800,
69 RightSR = 0x001000,
70 RightSL = 0x002000,
71 R = 0x004000,
72 ZR = 0x008000,
73 Minus = 0x010000,
74 Plus = 0x020000,
75 StickR = 0x040000,
76 StickL = 0x080000,
77 Home = 0x100000,
78 Capture = 0x200000,
79};
80
81enum class PasivePadButton : u32 {
82 Down_A = 0x0001,
83 Right_X = 0x0002,
84 Left_B = 0x0004,
85 Up_Y = 0x0008,
86 SL = 0x0010,
87 SR = 0x0020,
88 Minus = 0x0100,
89 Plus = 0x0200,
90 StickL = 0x0400,
91 StickR = 0x0800,
92 Home = 0x1000,
93 Capture = 0x2000,
94 L_R = 0x4000,
95 ZL_ZR = 0x8000,
96};
97
98enum class OutputReport : u8 {
99 RUMBLE_AND_SUBCMD = 0x01,
100 FW_UPDATE_PKT = 0x03,
101 RUMBLE_ONLY = 0x10,
102 MCU_DATA = 0x11,
103 USB_CMD = 0x80,
104};
105
106enum class FeatureReport : u8 {
107 Last_SUBCMD = 0x02,
108 OTA_GW_UPGRADE = 0x70,
109 SETUP_MEM_READ = 0x71,
110 MEM_READ = 0x72,
111 ERASE_MEM_SECTOR = 0x73,
112 MEM_WRITE = 0x74,
113 LAUNCH = 0x75,
114};
115
116enum class SubCommand : u8 {
117 STATE = 0x00,
118 MANUAL_BT_PAIRING = 0x01,
119 REQ_DEV_INFO = 0x02,
120 SET_REPORT_MODE = 0x03,
121 TRIGGERS_ELAPSED = 0x04,
122 GET_PAGE_LIST_STATE = 0x05,
123 SET_HCI_STATE = 0x06,
124 RESET_PAIRING_INFO = 0x07,
125 LOW_POWER_MODE = 0x08,
126 SPI_FLASH_READ = 0x10,
127 SPI_FLASH_WRITE = 0x11,
128 SPI_SECTOR_ERASE = 0x12,
129 RESET_MCU = 0x20,
130 SET_MCU_CONFIG = 0x21,
131 SET_MCU_STATE = 0x22,
132 SET_PLAYER_LIGHTS = 0x30,
133 GET_PLAYER_LIGHTS = 0x31,
134 SET_HOME_LIGHT = 0x38,
135 ENABLE_IMU = 0x40,
136 SET_IMU_SENSITIVITY = 0x41,
137 WRITE_IMU_REG = 0x42,
138 READ_IMU_REG = 0x43,
139 ENABLE_VIBRATION = 0x48,
140 GET_REGULATED_VOLTAGE = 0x50,
141 SET_EXTERNAL_CONFIG = 0x58,
142 GET_EXTERNAL_DEVICE_INFO = 0x59,
143 ENABLE_EXTERNAL_POLLING = 0x5A,
144 DISABLE_EXTERNAL_POLLING = 0x5B,
145 SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
146};
147
148enum class UsbSubCommand : u8 {
149 CONN_STATUS = 0x01,
150 HADSHAKE = 0x02,
151 BAUDRATE_3M = 0x03,
152 NO_TIMEOUT = 0x04,
153 EN_TIMEOUT = 0x05,
154 RESET = 0x06,
155 PRE_HANDSHAKE = 0x91,
156 SEND_UART = 0x92,
157};
158
159enum class CalibrationMagic : u8 {
160 USR_MAGIC_0 = 0xB2,
161 USR_MAGIC_1 = 0xA1,
162};
163
164enum class SpiAddress : u16 {
165 MAGIC = 0x0000,
166 MAC_ADDRESS = 0x0015,
167 PAIRING_INFO = 0x2000,
168 SHIPMENT = 0x5000,
169 SERIAL_NUMBER = 0x6000,
170 DEVICE_TYPE = 0x6012,
171 FORMAT_VERSION = 0x601B,
172 FACT_IMU_DATA = 0x6020,
173 FACT_LEFT_DATA = 0x603d,
174 FACT_RIGHT_DATA = 0x6046,
175 COLOR_DATA = 0x6050,
176 DESIGN_VARIATION = 0x605C,
177 SENSOR_DATA = 0x6080,
178 USER_LEFT_MAGIC = 0x8010,
179 USER_LEFT_DATA = 0x8012,
180 USER_RIGHT_MAGIC = 0x801B,
181 USER_RIGHT_DATA = 0x801D,
182 USER_IMU_MAGIC = 0x8026,
183 USER_IMU_DATA = 0x8028,
184};
185
186enum class ReportMode : u8 {
187 ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
188 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
189 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
190 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
191 SUBCMD_REPLY = 0x21,
192 MCU_UPDATE_STATE = 0x23,
193 STANDARD_FULL_60HZ = 0x30,
194 NFC_IR_MODE_60HZ = 0x31,
195 SIMPLE_HID_MODE = 0x3F,
196 INPUT_USB_RESPONSE = 0x81,
197};
198
199enum class GyroSensitivity : u8 {
200 DPS250,
201 DPS500,
202 DPS1000,
203 DPS2000, // Default
204};
205
206enum class AccelerometerSensitivity : u8 {
207 G8, // Default
208 G4,
209 G2,
210 G16,
211};
212
213enum class GyroPerformance : u8 {
214 HZ833,
215 HZ208, // Default
216};
217
218enum class AccelerometerPerformance : u8 {
219 HZ200,
220 HZ100, // Default
221};
222
223enum class MCUCommand : u8 {
224 ConfigureMCU = 0x21,
225 ConfigureIR = 0x23,
226};
227
228enum class MCUSubCommand : u8 {
229 SetMCUMode = 0x0,
230 SetDeviceMode = 0x1,
231 ReadDeviceMode = 0x02,
232 WriteDeviceRegisters = 0x4,
233};
234
235enum class MCUMode : u8 {
236 Suspend = 0,
237 Standby = 1,
238 Ringcon = 3,
239 NFC = 4,
240 IR = 5,
241 MaybeFWUpdate = 6,
242};
243
244enum class MCURequest : u8 {
245 GetMCUStatus = 1,
246 GetNFCData = 2,
247 GetIRData = 3,
248};
249
250enum class MCUReport : u8 {
251 Empty = 0x00,
252 StateReport = 0x01,
253 IRData = 0x03,
254 BusyInitializing = 0x0b,
255 IRStatus = 0x13,
256 IRRegisters = 0x1b,
257 NFCState = 0x2a,
258 NFCReadData = 0x3a,
259 EmptyAwaitingCmd = 0xff,
260};
261
262enum class MCUPacketFlag : u8 {
263 MorePacketsRemaining = 0x00,
264 LastCommandPacket = 0x08,
265};
266
267enum class NFCReadCommand : u8 {
268 CancelAll = 0x00,
269 StartPolling = 0x01,
270 StopPolling = 0x02,
271 StartWaitingRecieve = 0x04,
272 Ntag = 0x06,
273 Mifare = 0x0F,
274};
275
276enum class NFCTagType : u8 {
277 AllTags = 0x00,
278 Ntag215 = 0x01,
279};
280
281enum class NFCPages {
282 Block0 = 0,
283 Block45 = 45,
284 Block135 = 135,
285 Block231 = 231,
286};
287
288enum class NFCStatus : u8 {
289 LastPackage = 0x04,
290 TagLost = 0x07,
291};
292
293enum class IrsMode : u8 {
294 None = 0x02,
295 Moment = 0x03,
296 Dpd = 0x04,
297 Clustering = 0x06,
298 ImageTransfer = 0x07,
299 Silhouette = 0x08,
300 TeraImage = 0x09,
301 SilhouetteTeraImage = 0x0A,
302};
303
304enum class IrsResolution {
305 Size320x240,
306 Size160x120,
307 Size80x60,
308 Size40x30,
309 Size20x15,
310 None,
311};
312
313enum class IrsResolutionCode : u8 {
314 Size320x240 = 0x00, // Full pixel array
315 Size160x120 = 0x50, // Sensor Binning [2 X 2]
316 Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2]
317 Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4]
318 Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4]
319};
320
321// Size of image divided by 300
322enum class IrsFragments : u8 {
323 Size20x15 = 0x00,
324 Size40x30 = 0x03,
325 Size80x60 = 0x0f,
326 Size160x120 = 0x3f,
327 Size320x240 = 0xFF,
328};
329
330enum class IrLeds : u8 {
331 BrightAndDim = 0x00,
332 Bright = 0x20,
333 Dim = 0x10,
334 None = 0x30,
335};
336
337enum class IrExLedFilter : u8 {
338 Disabled = 0x00,
339 Enabled = 0x03,
340};
341
342enum class IrImageFlip : u8 {
343 Normal = 0x00,
344 Inverted = 0x02,
345};
346
347enum class IrRegistersAddress : u16 {
348 UpdateTime = 0x0400,
349 FinalizeConfig = 0x0700,
350 LedFilter = 0x0e00,
351 Leds = 0x1000,
352 LedIntensitiyMSB = 0x1100,
353 LedIntensitiyLSB = 0x1200,
354 ImageFlip = 0x2d00,
355 Resolution = 0x2e00,
356 DigitalGainLSB = 0x2e01,
357 DigitalGainMSB = 0x2f01,
358 ExposureLSB = 0x3001,
359 ExposureMSB = 0x3101,
360 ExposureTime = 0x3201,
361 WhitePixelThreshold = 0x4301,
362 DenoiseSmoothing = 0x6701,
363 DenoiseEdge = 0x6801,
364 DenoiseColor = 0x6901,
365};
366
367enum class ExternalDeviceId : u16 {
368 RingController = 0x2000,
369 Starlink = 0x2800,
370};
371
372enum class DriverResult {
373 Success,
374 WrongReply,
375 Timeout,
376 InvalidParameters,
377 UnsupportedControllerType,
378 HandleInUse,
379 ErrorReadingData,
380 ErrorWritingData,
381 NoDeviceDetected,
382 InvalidHandle,
383 NotSupported,
384 Disabled,
385 Unknown,
386};
387
388struct MotionSensorCalibration {
389 s16 offset;
390 s16 scale;
391};
392
393struct MotionCalibration {
394 std::array<MotionSensorCalibration, 3> accelerometer;
395 std::array<MotionSensorCalibration, 3> gyro;
396};
397
398// Basic motion data containing data from the sensors and a timestamp in microseconds
399struct MotionData {
400 float gyro_x{};
401 float gyro_y{};
402 float gyro_z{};
403 float accel_x{};
404 float accel_y{};
405 float accel_z{};
406 u64 delta_timestamp{};
407};
408
409// Output from SPI read command containing user calibration magic
410struct MagicSpiCalibration {
411 CalibrationMagic first;
412 CalibrationMagic second;
413};
414static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
415
416// Output from SPI read command containing left joystick calibration
417struct JoystickLeftSpiCalibration {
418 std::array<u8, 3> max;
419 std::array<u8, 3> center;
420 std::array<u8, 3> min;
421};
422static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
423 "JoystickLeftSpiCalibration is an invalid size");
424
425// Output from SPI read command containing right joystick calibration
426struct JoystickRightSpiCalibration {
427 std::array<u8, 3> center;
428 std::array<u8, 3> min;
429 std::array<u8, 3> max;
430};
431static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
432 "JoystickRightSpiCalibration is an invalid size");
433
434struct JoyStickAxisCalibration {
435 u16 max;
436 u16 min;
437 u16 center;
438};
439
440struct JoyStickCalibration {
441 JoyStickAxisCalibration x;
442 JoyStickAxisCalibration y;
443};
444
445struct ImuSpiCalibration {
446 std::array<s16, 3> accelerometer_offset;
447 std::array<s16, 3> accelerometer_scale;
448 std::array<s16, 3> gyroscope_offset;
449 std::array<s16, 3> gyroscope_scale;
450};
451static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
452
453struct RingCalibration {
454 s16 default_value;
455 s16 max_value;
456 s16 min_value;
457};
458
459struct Color {
460 u32 body;
461 u32 buttons;
462 u32 left_grip;
463 u32 right_grip;
464};
465
466struct Battery {
467 union {
468 u8 raw{};
469
470 BitField<0, 4, u8> unknown;
471 BitField<4, 1, u8> charging;
472 BitField<5, 3, u8> status;
473 };
474};
475
476struct VibrationValue {
477 f32 low_amplitude;
478 f32 low_frequency;
479 f32 high_amplitude;
480 f32 high_frequency;
481};
482
483struct JoyconHandle {
484 SDL_hid_device* handle = nullptr;
485 u8 packet_counter{};
486};
487
488struct MCUConfig {
489 MCUCommand command;
490 MCUSubCommand sub_command;
491 MCUMode mode;
492 INSERT_PADDING_BYTES(0x22);
493 u8 crc;
494};
495static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
496
497#pragma pack(push, 1)
498struct InputReportPassive {
499 ReportMode report_mode;
500 u16 button_input;
501 u8 stick_state;
502 std::array<u8, 10> unknown_data;
503};
504static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
505
506struct InputReportActive {
507 ReportMode report_mode;
508 u8 packet_id;
509 Battery battery_status;
510 std::array<u8, 3> button_input;
511 std::array<u8, 3> left_stick_state;
512 std::array<u8, 3> right_stick_state;
513 u8 vibration_code;
514 std::array<s16, 6 * 2> motion_input;
515 INSERT_PADDING_BYTES(0x2);
516 s16 ring_input;
517};
518static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
519
520struct InputReportNfcIr {
521 ReportMode report_mode;
522 u8 packet_id;
523 Battery battery_status;
524 std::array<u8, 3> button_input;
525 std::array<u8, 3> left_stick_state;
526 std::array<u8, 3> right_stick_state;
527 u8 vibration_code;
528 std::array<s16, 6 * 2> motion_input;
529 INSERT_PADDING_BYTES(0x4);
530};
531static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
532#pragma pack(pop)
533
534struct NFCReadBlock {
535 u8 start;
536 u8 end;
537};
538static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size");
539
540struct NFCReadBlockCommand {
541 u8 block_count{};
542 std::array<NFCReadBlock, 4> blocks{};
543};
544static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size");
545
546struct NFCReadCommandData {
547 u8 unknown;
548 u8 uuid_length;
549 u8 unknown_2;
550 std::array<u8, 6> uid;
551 NFCTagType tag_type;
552 NFCReadBlockCommand read_block;
553};
554static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
555
556struct NFCPollingCommandData {
557 u8 enable_mifare;
558 u8 unknown_1;
559 u8 unknown_2;
560 u8 unknown_3;
561 u8 unknown_4;
562};
563static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
564
565struct NFCRequestState {
566 MCUSubCommand sub_command;
567 NFCReadCommand command_argument;
568 u8 packet_id;
569 INSERT_PADDING_BYTES(0x1);
570 MCUPacketFlag packet_flag;
571 u8 data_length;
572 union {
573 std::array<u8, 0x1F> raw_data;
574 NFCReadCommandData nfc_read;
575 NFCPollingCommandData nfc_polling;
576 };
577 u8 crc;
578};
579static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
580
581struct IrsConfigure {
582 MCUCommand command;
583 MCUSubCommand sub_command;
584 IrsMode irs_mode;
585 IrsFragments number_of_fragments;
586 u16 mcu_major_version;
587 u16 mcu_minor_version;
588 INSERT_PADDING_BYTES(0x1D);
589 u8 crc;
590};
591static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size");
592
593#pragma pack(push, 1)
594struct IrsRegister {
595 IrRegistersAddress address;
596 u8 value;
597};
598static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size");
599
600struct IrsWriteRegisters {
601 MCUCommand command;
602 MCUSubCommand sub_command;
603 u8 number_of_registers;
604 std::array<IrsRegister, 9> registers;
605 INSERT_PADDING_BYTES(0x7);
606 u8 crc;
607};
608static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size");
609#pragma pack(pop)
610
611struct FirmwareVersion {
612 u8 major;
613 u8 minor;
614};
615static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
616
617struct DeviceInfo {
618 FirmwareVersion firmware;
619 std::array<u8, 2> unknown_1;
620 MacAddress mac_address;
621 std::array<u8, 2> unknown_2;
622};
623static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size");
624
625struct MotionStatus {
626 bool is_enabled;
627 u64 delta_time;
628 GyroSensitivity gyro_sensitivity;
629 AccelerometerSensitivity accelerometer_sensitivity;
630};
631
632struct RingStatus {
633 bool is_enabled;
634 s16 default_value;
635 s16 max_value;
636 s16 min_value;
637};
638
639struct VibrationPacket {
640 OutputReport output_report;
641 u8 packet_counter;
642 std::array<u8, 0x8> vibration_data;
643};
644static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size");
645
646struct SubCommandPacket {
647 OutputReport output_report;
648 u8 packet_counter;
649 INSERT_PADDING_BYTES(0x8); // This contains vibration data
650 SubCommand sub_command;
651 std::array<u8, 0x26> command_data;
652};
653static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
654
655#pragma pack(push, 1)
656struct ReadSpiPacket {
657 SpiAddress spi_address;
658 INSERT_PADDING_BYTES(0x2);
659 u8 size;
660};
661static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size");
662
663struct SubCommandResponse {
664 InputReportPassive input_report;
665 SubCommand sub_command;
666 union {
667 std::array<u8, 0x30> command_data;
668 SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand
669 ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand
670 DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand
671 };
672 u8 crc; // This is never used
673};
674static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size");
675#pragma pack(pop)
676
677struct MCUCommandResponse {
678 InputReportNfcIr input_report;
679 INSERT_PADDING_BYTES(0x8);
680 MCUReport mcu_report;
681 std::array<u8, 0x13D> mcu_data;
682 u8 crc;
683};
684static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
685
686struct JoyconCallbacks {
687 std::function<void(Battery)> on_battery_data;
688 std::function<void(Color)> on_color_data;
689 std::function<void(int, bool)> on_button_data;
690 std::function<void(int, f32)> on_stick_data;
691 std::function<void(int, const MotionData&)> on_motion_data;
692 std::function<void(f32)> on_ring_data;
693 std::function<void(const std::vector<u8>&)> on_amiibo_data;
694 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
695};
696
697} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
new file mode 100644
index 000000000..eeba82986
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -0,0 +1,406 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h"
7
8namespace InputCommon::Joycon {
9
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::NFC,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37
38 return result;
39}
40
41DriverResult NfcProtocol::DisableNfc() {
42 LOG_DEBUG(Input, "Disable NFC");
43 ScopedSetBlocking sb(this);
44 DriverResult result{DriverResult::Success};
45
46 if (result == DriverResult::Success) {
47 result = EnableMCU(false);
48 }
49
50 is_enabled = false;
51
52 return result;
53}
54
55DriverResult NfcProtocol::StartNFCPollingMode() {
56 LOG_DEBUG(Input, "Start NFC pooling Mode");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59 TagFoundData tag_data{};
60
61 if (result == DriverResult::Success) {
62 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
63 }
64 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady();
66 }
67 if (result == DriverResult::Success) {
68 is_enabled = true;
69 }
70
71 return result;
72}
73
74DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
75 LOG_DEBUG(Input, "Start NFC pooling Mode");
76 ScopedSetBlocking sb(this);
77 DriverResult result{DriverResult::Success};
78 TagFoundData tag_data{};
79
80 if (result == DriverResult::Success) {
81 result = StartPolling(tag_data);
82 }
83 if (result == DriverResult::Success) {
84 result = ReadTag(tag_data);
85 }
86 if (result == DriverResult::Success) {
87 result = WaitUntilNfcIsReady();
88 }
89 if (result == DriverResult::Success) {
90 result = StartPolling(tag_data);
91 }
92 if (result == DriverResult::Success) {
93 result = GetAmiiboData(data);
94 }
95
96 return result;
97}
98
99bool NfcProtocol::HasAmiibo() {
100 ScopedSetBlocking sb(this);
101 DriverResult result{DriverResult::Success};
102 TagFoundData tag_data{};
103
104 if (result == DriverResult::Success) {
105 result = StartPolling(tag_data);
106 }
107
108 return result == DriverResult::Success;
109}
110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10;
113 MCUCommandResponse output{};
114 std::size_t tries = 0;
115
116 do {
117 auto result = SendStartWaitingRecieveRequest(output);
118
119 if (result != DriverResult::Success) {
120 return result;
121 }
122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout;
124 }
125 } while (output.mcu_report != MCUReport::NFCState ||
126 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
127 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
128
129 return DriverResult::Success;
130}
131
132DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
133 LOG_DEBUG(Input, "Start Polling for tag");
134 constexpr std::size_t timeout_limit = 7;
135 MCUCommandResponse output{};
136 std::size_t tries = 0;
137
138 do {
139 const auto result = SendStartPollingRequest(output);
140 if (result != DriverResult::Success) {
141 return result;
142 }
143 if (tries++ > timeout_limit) {
144 return DriverResult::Timeout;
145 }
146 } while (output.mcu_report != MCUReport::NFCState ||
147 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
148 output.mcu_data[6] != 0x09);
149
150 data.type = output.mcu_data[12];
151 data.uuid.resize(output.mcu_data[14]);
152 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
153
154 return DriverResult::Success;
155}
156
157DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
158 constexpr std::size_t timeout_limit = 10;
159 MCUCommandResponse output{};
160 std::size_t tries = 0;
161
162 std::string uuid_string;
163 for (auto& content : data.uuid) {
164 uuid_string += fmt::format(" {:02x}", content);
165 }
166
167 LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
168
169 tries = 0;
170 NFCPages ntag_pages = NFCPages::Block0;
171 // Read Tag data
172 while (true) {
173 auto result = SendReadAmiiboRequest(output, ntag_pages);
174 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
175
176 if (result != DriverResult::Success) {
177 return result;
178 }
179
180 if ((output.mcu_report == MCUReport::NFCReadData ||
181 output.mcu_report == MCUReport::NFCState) &&
182 nfc_status == NFCStatus::TagLost) {
183 return DriverResult::ErrorReadingData;
184 }
185
186 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
187 output.mcu_data[2] == 0x01) {
188 if (data.type != 2) {
189 continue;
190 }
191 switch (output.mcu_data[24]) {
192 case 0:
193 ntag_pages = NFCPages::Block135;
194 break;
195 case 3:
196 ntag_pages = NFCPages::Block45;
197 break;
198 case 4:
199 ntag_pages = NFCPages::Block231;
200 break;
201 default:
202 return DriverResult::ErrorReadingData;
203 }
204 continue;
205 }
206
207 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
208 // finished
209 SendStopPollingRequest(output);
210 return DriverResult::Success;
211 }
212
213 // Ignore other state reports
214 if (output.mcu_report == MCUReport::NFCState) {
215 continue;
216 }
217
218 if (tries++ > timeout_limit) {
219 return DriverResult::Timeout;
220 }
221 }
222
223 return DriverResult::Success;
224}
225
226DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
227 constexpr std::size_t timeout_limit = 10;
228 MCUCommandResponse output{};
229 std::size_t tries = 0;
230
231 NFCPages ntag_pages = NFCPages::Block135;
232 std::size_t ntag_buffer_pos = 0;
233 // Read Tag data
234 while (true) {
235 auto result = SendReadAmiiboRequest(output, ntag_pages);
236 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
237
238 if (result != DriverResult::Success) {
239 return result;
240 }
241
242 if ((output.mcu_report == MCUReport::NFCReadData ||
243 output.mcu_report == MCUReport::NFCState) &&
244 nfc_status == NFCStatus::TagLost) {
245 return DriverResult::ErrorReadingData;
246 }
247
248 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
249 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
250 if (output.mcu_data[2] == 0x01) {
251 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
252 payload_size - 60);
253 ntag_buffer_pos += payload_size - 60;
254 } else {
255 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
256 payload_size);
257 }
258 continue;
259 }
260
261 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
262 LOG_INFO(Input, "Finished reading amiibo");
263 return DriverResult::Success;
264 }
265
266 // Ignore other state reports
267 if (output.mcu_report == MCUReport::NFCState) {
268 continue;
269 }
270
271 if (tries++ > timeout_limit) {
272 return DriverResult::Timeout;
273 }
274 }
275
276 return DriverResult::Success;
277}
278
279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
280 NFCRequestState request{
281 .sub_command = MCUSubCommand::ReadDeviceMode,
282 .command_argument = NFCReadCommand::StartPolling,
283 .packet_id = 0x0,
284 .packet_flag = MCUPacketFlag::LastCommandPacket,
285 .data_length = sizeof(NFCPollingCommandData),
286 .nfc_polling =
287 {
288 .enable_mifare = 0x01,
289 .unknown_1 = 0x00,
290 .unknown_2 = 0x00,
291 .unknown_3 = 0x2c,
292 .unknown_4 = 0x01,
293 },
294 .crc = {},
295 };
296
297 std::array<u8, sizeof(NFCRequestState)> request_data{};
298 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
299 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
300 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
301}
302
303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
304 NFCRequestState request{
305 .sub_command = MCUSubCommand::ReadDeviceMode,
306 .command_argument = NFCReadCommand::StopPolling,
307 .packet_id = 0x0,
308 .packet_flag = MCUPacketFlag::LastCommandPacket,
309 .data_length = 0,
310 .raw_data = {},
311 .crc = {},
312 };
313
314 std::array<u8, sizeof(NFCRequestState)> request_data{};
315 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
316 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
317 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
318}
319
320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
321 NFCRequestState request{
322 .sub_command = MCUSubCommand::ReadDeviceMode,
323 .command_argument = NFCReadCommand::StartWaitingRecieve,
324 .packet_id = 0x0,
325 .packet_flag = MCUPacketFlag::LastCommandPacket,
326 .data_length = 0,
327 .raw_data = {},
328 .crc = {},
329 };
330
331 std::vector<u8> request_data(sizeof(NFCRequestState));
332 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
333 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
334 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
335}
336
337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
338 NFCRequestState request{
339 .sub_command = MCUSubCommand::ReadDeviceMode,
340 .command_argument = NFCReadCommand::Ntag,
341 .packet_id = 0x0,
342 .packet_flag = MCUPacketFlag::LastCommandPacket,
343 .data_length = sizeof(NFCReadCommandData),
344 .nfc_read =
345 {
346 .unknown = 0xd0,
347 .uuid_length = 0x07,
348 .unknown_2 = 0x00,
349 .uid = {},
350 .tag_type = NFCTagType::AllTags,
351 .read_block = GetReadBlockCommand(ntag_pages),
352 },
353 .crc = {},
354 };
355
356 std::array<u8, sizeof(NFCRequestState)> request_data{};
357 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
358 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
359 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
360}
361
362NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
363 switch (pages) {
364 case NFCPages::Block0:
365 return {
366 .block_count = 1,
367 };
368 case NFCPages::Block45:
369 return {
370 .block_count = 1,
371 .blocks =
372 {
373 NFCReadBlock{0x00, 0x2C},
374 },
375 };
376 case NFCPages::Block135:
377 return {
378 .block_count = 3,
379 .blocks =
380 {
381 NFCReadBlock{0x00, 0x3b},
382 {0x3c, 0x77},
383 {0x78, 0x86},
384 },
385 };
386 case NFCPages::Block231:
387 return {
388 .block_count = 4,
389 .blocks =
390 {
391 NFCReadBlock{0x00, 0x3b},
392 {0x3c, 0x77},
393 {0x78, 0x83},
394 {0xb4, 0xe6},
395 },
396 };
397 default:
398 return {};
399 };
400}
401
402bool NfcProtocol::IsEnabled() const {
403 return is_enabled;
404}
405
406} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
new file mode 100644
index 000000000..11e263e07
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class NfcProtocol final : private JoyconCommonProtocol {
19public:
20 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableNfc();
23
24 DriverResult DisableNfc();
25
26 DriverResult StartNFCPollingMode();
27
28 DriverResult ScanAmiibo(std::vector<u8>& data);
29
30 bool HasAmiibo();
31
32 bool IsEnabled() const;
33
34private:
35 struct TagFoundData {
36 u8 type;
37 std::vector<u8> uuid;
38 };
39
40 DriverResult WaitUntilNfcIsReady();
41
42 DriverResult StartPolling(TagFoundData& data);
43
44 DriverResult ReadTag(const TagFoundData& data);
45
46 DriverResult GetAmiiboData(std::vector<u8>& data);
47
48 DriverResult SendStartPollingRequest(MCUCommandResponse& output);
49
50 DriverResult SendStopPollingRequest(MCUCommandResponse& output);
51
52 DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
53
54 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
55
56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
57
58 bool is_enabled{};
59};
60
61} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
new file mode 100644
index 000000000..9bb15e935
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -0,0 +1,337 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/poller.h"
6
7namespace InputCommon::Joycon {
8
9JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
10 JoyStickCalibration right_stick_calibration_,
11 MotionCalibration motion_calibration_)
12 : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
13 right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
14
15void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
16 callbacks = std::move(callbacks_);
17}
18
19void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
20 const RingStatus& ring_status) {
21 InputReportActive data{};
22 memcpy(&data, buffer.data(), sizeof(InputReportActive));
23
24 switch (device_type) {
25 case Joycon::ControllerType::Left:
26 UpdateActiveLeftPadInput(data, motion_status);
27 break;
28 case Joycon::ControllerType::Right:
29 UpdateActiveRightPadInput(data, motion_status);
30 break;
31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status);
33 break;
34 default:
35 break;
36 }
37
38 if (ring_status.is_enabled) {
39 UpdateRing(data.ring_input, ring_status);
40 }
41
42 callbacks.on_battery_data(data.battery_status);
43}
44
45void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
46 InputReportPassive data{};
47 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
48
49 switch (device_type) {
50 case Joycon::ControllerType::Left:
51 UpdatePasiveLeftPadInput(data);
52 break;
53 case Joycon::ControllerType::Right:
54 UpdatePasiveRightPadInput(data);
55 break;
56 case Joycon::ControllerType::Pro:
57 UpdatePasiveProPadInput(data);
58 break;
59 default:
60 break;
61 }
62}
63
64void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
65 // This mode is compatible with the active mode
66 ReadActiveMode(buffer, motion_status, {});
67}
68
69void JoyconPoller::UpdateColor(const Color& color) {
70 callbacks.on_color_data(color);
71}
72
73void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
74 callbacks.on_amiibo_data(amiibo_data);
75}
76
77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
78 callbacks.on_camera_data(camera_data, format);
79}
80
81void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {
82 float normalized_value = static_cast<float>(value - ring_status.default_value);
83 if (normalized_value > 0) {
84 normalized_value = normalized_value /
85 static_cast<float>(ring_status.max_value - ring_status.default_value);
86 }
87 if (normalized_value < 0) {
88 normalized_value = normalized_value /
89 static_cast<float>(ring_status.default_value - ring_status.min_value);
90 }
91 callbacks.on_ring_data(normalized_value);
92}
93
94void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
95 const MotionStatus& motion_status) {
96 static constexpr std::array<Joycon::PadButton, 11> left_buttons{
97 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
98 Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
99 Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
100 Joycon::PadButton::Capture, Joycon::PadButton::StickL,
101 };
102
103 const u32 raw_button =
104 static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
105 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
106 const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
107 const int button = static_cast<int>(left_buttons[i]);
108 callbacks.on_button_data(button, button_status);
109 }
110
111 const u16 raw_left_axis_x =
112 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
113 const u16 raw_left_axis_y =
114 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
115 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
116 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
117 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
118 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
119
120 if (motion_status.is_enabled) {
121 auto left_motion = GetMotionInput(input, motion_status);
122 // Rotate motion axis to the correct direction
123 left_motion.accel_y = -left_motion.accel_y;
124 left_motion.accel_z = -left_motion.accel_z;
125 left_motion.gyro_x = -left_motion.gyro_x;
126 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
127 }
128}
129
130void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
131 const MotionStatus& motion_status) {
132 static constexpr std::array<Joycon::PadButton, 11> right_buttons{
133 Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
134 Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
135 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
136 Joycon::PadButton::Home, Joycon::PadButton::StickR,
137 };
138
139 const u32 raw_button =
140 static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
141 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
142 const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
143 const int button = static_cast<int>(right_buttons[i]);
144 callbacks.on_button_data(button, button_status);
145 }
146
147 const u16 raw_right_axis_x =
148 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
149 const u16 raw_right_axis_y =
150 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
151 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
152 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
153 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
154 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
155
156 if (motion_status.is_enabled) {
157 auto right_motion = GetMotionInput(input, motion_status);
158 // Rotate motion axis to the correct direction
159 right_motion.accel_x = -right_motion.accel_x;
160 right_motion.accel_y = -right_motion.accel_y;
161 right_motion.gyro_z = -right_motion.gyro_z;
162 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
163 }
164}
165
166void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
167 const MotionStatus& motion_status) {
168 static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
169 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
170 Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
171 Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
172 Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
173 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
174 Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
175 };
176
177 const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
178 (input.button_input[1] << 16));
179 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
180 const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
181 const int button = static_cast<int>(pro_buttons[i]);
182 callbacks.on_button_data(button, button_status);
183 }
184
185 const u16 raw_left_axis_x =
186 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
187 const u16 raw_left_axis_y =
188 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
189 const u16 raw_right_axis_x =
190 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
191 const u16 raw_right_axis_y =
192 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
193
194 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
195 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
196 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
197 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
198 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
199 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
200 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
201 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
202
203 if (motion_status.is_enabled) {
204 auto pro_motion = GetMotionInput(input, motion_status);
205 pro_motion.gyro_x = -pro_motion.gyro_x;
206 pro_motion.accel_y = -pro_motion.accel_y;
207 pro_motion.accel_z = -pro_motion.accel_z;
208 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
209 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
210 }
211}
212
213void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
214 static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
215 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
216 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
217 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
218 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
219 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
220 Joycon::PasivePadButton::StickL,
221 };
222
223 for (auto left_button : left_buttons) {
224 const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0;
225 const int button = static_cast<int>(left_button);
226 callbacks.on_button_data(button, button_status);
227 }
228}
229
230void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
231 static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
232 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
233 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
234 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
235 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
236 Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
237 Joycon::PasivePadButton::StickR,
238 };
239
240 for (auto right_button : right_buttons) {
241 const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0;
242 const int button = static_cast<int>(right_button);
243 callbacks.on_button_data(button, button_status);
244 }
245}
246
247void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
248 static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
249 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
250 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
251 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
252 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
253 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
254 Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
255 Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
256 };
257
258 for (auto pro_button : pro_buttons) {
259 const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0;
260 const int button = static_cast<int>(pro_button);
261 callbacks.on_button_data(button, button_status);
262 }
263}
264
265f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
266 const f32 value = static_cast<f32>(raw_value - calibration.center);
267 if (value > 0.0f) {
268 return value / calibration.max;
269 }
270 return value / calibration.min;
271}
272
273f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
274 AccelerometerSensitivity sensitivity) const {
275 const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
276 switch (sensitivity) {
277 case Joycon::AccelerometerSensitivity::G2:
278 return value / 4.0f;
279 case Joycon::AccelerometerSensitivity::G4:
280 return value / 2.0f;
281 case Joycon::AccelerometerSensitivity::G8:
282 return value;
283 case Joycon::AccelerometerSensitivity::G16:
284 return value * 2.0f;
285 }
286 return value;
287}
288
289f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
290 GyroSensitivity sensitivity) const {
291 const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
292 switch (sensitivity) {
293 case Joycon::GyroSensitivity::DPS250:
294 return value / 8.0f;
295 case Joycon::GyroSensitivity::DPS500:
296 return value / 4.0f;
297 case Joycon::GyroSensitivity::DPS1000:
298 return value / 2.0f;
299 case Joycon::GyroSensitivity::DPS2000:
300 return value;
301 }
302 return value;
303}
304
305s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
306 const InputReportActive& input) const {
307 return input.motion_input[(sensor * 3) + axis];
308}
309
310MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
311 const MotionStatus& motion_status) const {
312 MotionData motion{};
313 const auto& accel_cal = motion_calibration.accelerometer;
314 const auto& gyro_cal = motion_calibration.gyro;
315 const s16 raw_accel_x = input.motion_input[1];
316 const s16 raw_accel_y = input.motion_input[0];
317 const s16 raw_accel_z = input.motion_input[2];
318 const s16 raw_gyro_x = input.motion_input[4];
319 const s16 raw_gyro_y = input.motion_input[3];
320 const s16 raw_gyro_z = input.motion_input[5];
321
322 motion.delta_timestamp = motion_status.delta_time;
323 motion.accel_x =
324 GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
325 motion.accel_y =
326 GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
327 motion.accel_z =
328 GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
329 motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
330 motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
331 motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
332
333 // TODO(German77): Return all three samples data
334 return motion;
335}
336
337} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
new file mode 100644
index 000000000..354d41dad
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <functional>
12#include <span>
13
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18// Handles input packages and triggers the corresponding input events
19class JoyconPoller {
20public:
21 JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
22 JoyStickCalibration right_stick_calibration_,
23 MotionCalibration motion_calibration_);
24
25 void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_);
26
27 /// Handles data from passive packages
28 void ReadPassiveMode(std::span<u8> buffer);
29
30 /// Handles data from active packages
31 void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
32 const RingStatus& ring_status);
33
34 /// Handles data from nfc or ir packages
35 void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status);
36
37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);
41
42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input,
44 const MotionStatus& motion_status);
45 void UpdateActiveRightPadInput(const InputReportActive& input,
46 const MotionStatus& motion_status);
47 void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status);
48
49 void UpdatePasiveLeftPadInput(const InputReportPassive& buffer);
50 void UpdatePasiveRightPadInput(const InputReportPassive& buffer);
51 void UpdatePasiveProPadInput(const InputReportPassive& buffer);
52
53 /// Returns a calibrated joystick axis from raw axis data
54 f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const;
55
56 /// Returns a calibrated accelerometer axis from raw motion data
57 f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
58 AccelerometerSensitivity sensitivity) const;
59
60 /// Returns a calibrated gyro axis from raw motion data
61 f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal,
62 GyroSensitivity sensitivity) const;
63
64 /// Returns a raw motion value from a buffer
65 s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const;
66
67 /// Returns motion data from a buffer
68 MotionData GetMotionInput(const InputReportActive& input,
69 const MotionStatus& motion_status) const;
70
71 ControllerType device_type{};
72
73 // Device calibration
74 JoyStickCalibration left_stick_calibration{};
75 JoyStickCalibration right_stick_calibration{};
76 MotionCalibration motion_calibration{};
77
78 Joycon::JoyconCallbacks callbacks{};
79};
80
81} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
new file mode 100644
index 000000000..190cef812
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -0,0 +1,115 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/ringcon.h"
6
7namespace InputCommon::Joycon {
8
9RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult RingConProtocol::EnableRingCon() {
13 LOG_DEBUG(Input, "Enable Ringcon");
14 ScopedSetBlocking sb(this);
15 DriverResult result{DriverResult::Success};
16
17 if (result == DriverResult::Success) {
18 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
19 }
20 if (result == DriverResult::Success) {
21 result = EnableMCU(true);
22 }
23 if (result == DriverResult::Success) {
24 const MCUConfig config{
25 .command = MCUCommand::ConfigureMCU,
26 .sub_command = MCUSubCommand::SetDeviceMode,
27 .mode = MCUMode::Standby,
28 .crc = {},
29 };
30 result = ConfigureMCU(config);
31 }
32
33 return result;
34}
35
36DriverResult RingConProtocol::DisableRingCon() {
37 LOG_DEBUG(Input, "Disable RingCon");
38 ScopedSetBlocking sb(this);
39 DriverResult result{DriverResult::Success};
40
41 if (result == DriverResult::Success) {
42 result = EnableMCU(false);
43 }
44
45 is_enabled = false;
46
47 return result;
48}
49
50DriverResult RingConProtocol::StartRingconPolling() {
51 LOG_DEBUG(Input, "Enable Ringcon");
52 ScopedSetBlocking sb(this);
53 DriverResult result{DriverResult::Success};
54 bool is_connected = false;
55
56 if (result == DriverResult::Success) {
57 result = IsRingConnected(is_connected);
58 }
59 if (result == DriverResult::Success && is_connected) {
60 LOG_INFO(Input, "Ringcon detected");
61 result = ConfigureRing();
62 }
63 if (result == DriverResult::Success) {
64 is_enabled = true;
65 }
66
67 return result;
68}
69
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28;
73 SubCommandResponse output{};
74 std::size_t tries = 0;
75 is_connected = false;
76
77 do {
78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
79
80 if (result != DriverResult::Success) {
81 return result;
82 }
83
84 if (tries++ >= max_tries) {
85 return DriverResult::NoDeviceDetected;
86 }
87 } while (output.external_device_id != ExternalDeviceId::RingController);
88
89 is_connected = true;
90 return DriverResult::Success;
91}
92
93DriverResult RingConProtocol::ConfigureRing() {
94 LOG_DEBUG(Input, "ConfigureRing");
95
96 static constexpr std::array<u8, 37> ring_config{
97 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36,
98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
100
101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
102
103 if (result != DriverResult::Success) {
104 return result;
105 }
106
107 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
108 return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data);
109}
110
111bool RingConProtocol::IsEnabled() const {
112 return is_enabled;
113}
114
115} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h
new file mode 100644
index 000000000..6e858f3fc
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RingConProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRingCon();
23
24 DriverResult DisableRingCon();
25
26 DriverResult StartRingconPolling();
27
28 bool IsEnabled() const;
29
30private:
31 DriverResult IsRingConnected(bool& is_connected);
32
33 DriverResult ConfigureRing();
34
35 bool is_enabled{};
36};
37
38} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
new file mode 100644
index 000000000..63b60c946
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <cmath>
6
7#include "common/logging/log.h"
8#include "input_common/helpers/joycon_protocol/rumble.h"
9
10namespace InputCommon::Joycon {
11
12RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
13 : JoyconCommonProtocol(std::move(handle)) {}
14
15DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
16 LOG_DEBUG(Input, "Enable Rumble");
17 ScopedSetBlocking sb(this);
18 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
19 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
20}
21
22DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
23 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
24
25 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
26 return SendVibrationReport(DefaultVibrationBuffer);
27 }
28
29 // Protect joycons from damage from strong vibrations
30 const f32 clamp_amplitude =
31 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude);
32
33 const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency);
34 const u8 encoded_high_amplitude =
35 EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude);
36 const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency);
37 const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude);
38
39 buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF);
40 buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01));
41 buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80));
42 buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF);
43
44 // Duplicate rumble for now
45 buffer[4] = buffer[0];
46 buffer[5] = buffer[1];
47 buffer[6] = buffer[2];
48 buffer[7] = buffer[3];
49
50 return SendVibrationReport(buffer);
51}
52
53u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const {
54 const u8 new_frequency =
55 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
56 return static_cast<u16>((new_frequency - 0x60) * 4);
57}
58
59u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const {
60 const u8 new_frequency =
61 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
62 return static_cast<u8>(new_frequency - 0x40);
63}
64
65u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
66 // More information about these values can be found here:
67 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
68
69 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
70 std::pair<f32, int>{0.0f, 0x0},
71 {0.01f, 0x2},
72 {0.012f, 0x4},
73 {0.014f, 0x6},
74 {0.017f, 0x8},
75 {0.02f, 0x0a},
76 {0.024f, 0x0c},
77 {0.028f, 0x0e},
78 {0.033f, 0x10},
79 {0.04f, 0x12},
80 {0.047f, 0x14},
81 {0.056f, 0x16},
82 {0.067f, 0x18},
83 {0.08f, 0x1a},
84 {0.095f, 0x1c},
85 {0.112f, 0x1e},
86 {0.117f, 0x20},
87 {0.123f, 0x22},
88 {0.128f, 0x24},
89 {0.134f, 0x26},
90 {0.14f, 0x28},
91 {0.146f, 0x2a},
92 {0.152f, 0x2c},
93 {0.159f, 0x2e},
94 {0.166f, 0x30},
95 {0.173f, 0x32},
96 {0.181f, 0x34},
97 {0.189f, 0x36},
98 {0.198f, 0x38},
99 {0.206f, 0x3a},
100 {0.215f, 0x3c},
101 {0.225f, 0x3e},
102 {0.23f, 0x40},
103 {0.235f, 0x42},
104 {0.24f, 0x44},
105 {0.245f, 0x46},
106 {0.251f, 0x48},
107 {0.256f, 0x4a},
108 {0.262f, 0x4c},
109 {0.268f, 0x4e},
110 {0.273f, 0x50},
111 {0.279f, 0x52},
112 {0.286f, 0x54},
113 {0.292f, 0x56},
114 {0.298f, 0x58},
115 {0.305f, 0x5a},
116 {0.311f, 0x5c},
117 {0.318f, 0x5e},
118 {0.325f, 0x60},
119 {0.332f, 0x62},
120 {0.34f, 0x64},
121 {0.347f, 0x66},
122 {0.355f, 0x68},
123 {0.362f, 0x6a},
124 {0.37f, 0x6c},
125 {0.378f, 0x6e},
126 {0.387f, 0x70},
127 {0.395f, 0x72},
128 {0.404f, 0x74},
129 {0.413f, 0x76},
130 {0.422f, 0x78},
131 {0.431f, 0x7a},
132 {0.44f, 0x7c},
133 {0.45f, 0x7e},
134 {0.46f, 0x80},
135 {0.47f, 0x82},
136 {0.48f, 0x84},
137 {0.491f, 0x86},
138 {0.501f, 0x88},
139 {0.512f, 0x8a},
140 {0.524f, 0x8c},
141 {0.535f, 0x8e},
142 {0.547f, 0x90},
143 {0.559f, 0x92},
144 {0.571f, 0x94},
145 {0.584f, 0x96},
146 {0.596f, 0x98},
147 {0.609f, 0x9a},
148 {0.623f, 0x9c},
149 {0.636f, 0x9e},
150 {0.65f, 0xa0},
151 {0.665f, 0xa2},
152 {0.679f, 0xa4},
153 {0.694f, 0xa6},
154 {0.709f, 0xa8},
155 {0.725f, 0xaa},
156 {0.741f, 0xac},
157 {0.757f, 0xae},
158 {0.773f, 0xb0},
159 {0.79f, 0xb2},
160 {0.808f, 0xb4},
161 {0.825f, 0xb6},
162 {0.843f, 0xb8},
163 {0.862f, 0xba},
164 {0.881f, 0xbc},
165 {0.9f, 0xbe},
166 {0.92f, 0xc0},
167 {0.94f, 0xc2},
168 {0.96f, 0xc4},
169 {0.981f, 0xc6},
170 {1.003f, 0xc8},
171 };
172
173 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
174 if (amplitude <= amplitude_value) {
175 return static_cast<u8>(code);
176 }
177 }
178
179 return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
180}
181
182u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
183 // More information about these values can be found here:
184 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
185
186 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
187 std::pair<f32, int>{0.0f, 0x0040},
188 {0.01f, 0x8040},
189 {0.012f, 0x0041},
190 {0.014f, 0x8041},
191 {0.017f, 0x0042},
192 {0.02f, 0x8042},
193 {0.024f, 0x0043},
194 {0.028f, 0x8043},
195 {0.033f, 0x0044},
196 {0.04f, 0x8044},
197 {0.047f, 0x0045},
198 {0.056f, 0x8045},
199 {0.067f, 0x0046},
200 {0.08f, 0x8046},
201 {0.095f, 0x0047},
202 {0.112f, 0x8047},
203 {0.117f, 0x0048},
204 {0.123f, 0x8048},
205 {0.128f, 0x0049},
206 {0.134f, 0x8049},
207 {0.14f, 0x004a},
208 {0.146f, 0x804a},
209 {0.152f, 0x004b},
210 {0.159f, 0x804b},
211 {0.166f, 0x004c},
212 {0.173f, 0x804c},
213 {0.181f, 0x004d},
214 {0.189f, 0x804d},
215 {0.198f, 0x004e},
216 {0.206f, 0x804e},
217 {0.215f, 0x004f},
218 {0.225f, 0x804f},
219 {0.23f, 0x0050},
220 {0.235f, 0x8050},
221 {0.24f, 0x0051},
222 {0.245f, 0x8051},
223 {0.251f, 0x0052},
224 {0.256f, 0x8052},
225 {0.262f, 0x0053},
226 {0.268f, 0x8053},
227 {0.273f, 0x0054},
228 {0.279f, 0x8054},
229 {0.286f, 0x0055},
230 {0.292f, 0x8055},
231 {0.298f, 0x0056},
232 {0.305f, 0x8056},
233 {0.311f, 0x0057},
234 {0.318f, 0x8057},
235 {0.325f, 0x0058},
236 {0.332f, 0x8058},
237 {0.34f, 0x0059},
238 {0.347f, 0x8059},
239 {0.355f, 0x005a},
240 {0.362f, 0x805a},
241 {0.37f, 0x005b},
242 {0.378f, 0x805b},
243 {0.387f, 0x005c},
244 {0.395f, 0x805c},
245 {0.404f, 0x005d},
246 {0.413f, 0x805d},
247 {0.422f, 0x005e},
248 {0.431f, 0x805e},
249 {0.44f, 0x005f},
250 {0.45f, 0x805f},
251 {0.46f, 0x0060},
252 {0.47f, 0x8060},
253 {0.48f, 0x0061},
254 {0.491f, 0x8061},
255 {0.501f, 0x0062},
256 {0.512f, 0x8062},
257 {0.524f, 0x0063},
258 {0.535f, 0x8063},
259 {0.547f, 0x0064},
260 {0.559f, 0x8064},
261 {0.571f, 0x0065},
262 {0.584f, 0x8065},
263 {0.596f, 0x0066},
264 {0.609f, 0x8066},
265 {0.623f, 0x0067},
266 {0.636f, 0x8067},
267 {0.65f, 0x0068},
268 {0.665f, 0x8068},
269 {0.679f, 0x0069},
270 {0.694f, 0x8069},
271 {0.709f, 0x006a},
272 {0.725f, 0x806a},
273 {0.741f, 0x006b},
274 {0.757f, 0x806b},
275 {0.773f, 0x006c},
276 {0.79f, 0x806c},
277 {0.808f, 0x006d},
278 {0.825f, 0x806d},
279 {0.843f, 0x006e},
280 {0.862f, 0x806e},
281 {0.881f, 0x006f},
282 {0.9f, 0x806f},
283 {0.92f, 0x0070},
284 {0.94f, 0x8070},
285 {0.96f, 0x0071},
286 {0.981f, 0x8071},
287 {1.003f, 0x0072},
288 };
289
290 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
291 if (amplitude <= amplitude_value) {
292 return static_cast<u16>(code);
293 }
294 }
295
296 return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h
new file mode 100644
index 000000000..6c12b7925
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RumbleProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRumble(bool is_enabled);
23
24 DriverResult SendVibration(const VibrationValue& vibration);
25
26private:
27 u16 EncodeHighFrequency(f32 frequency) const;
28 u8 EncodeLowFrequency(f32 frequency) const;
29 u8 EncodeHighAmplitude(f32 amplitude) const;
30 u16 EncodeLowAmplitude(f32 amplitude) const;
31};
32
33} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 82aa6ac2f..096c23b07 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -11,13 +11,18 @@ namespace InputCommon {
11 11
12class Stick final : public Common::Input::InputDevice { 12class Stick final : public Common::Input::InputDevice {
13public: 13public:
14 // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18
14 using Button = std::unique_ptr<Common::Input::InputDevice>; 19 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 20
16 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, 21 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_,
17 float modifier_scale_, float modifier_angle_) 22 float modifier_scale_, float modifier_angle_)
18 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), 23 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
19 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), 24 right(std::move(right_)), modifier(std::move(modifier_)), updater(std::move(updater_)),
20 modifier_angle(modifier_angle_) { 25 modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) {
21 up->SetCallback({ 26 up->SetCallback({
22 .on_change = 27 .on_change =
23 [this](const Common::Input::CallbackStatus& callback_) { 28 [this](const Common::Input::CallbackStatus& callback_) {
@@ -48,6 +53,9 @@ public:
48 UpdateModButtonStatus(callback_); 53 UpdateModButtonStatus(callback_);
49 }, 54 },
50 }); 55 });
56 updater->SetCallback({
57 .on_change = [this](const Common::Input::CallbackStatus& callback_) { SoftUpdate(); },
58 });
51 last_x_axis_value = 0.0f; 59 last_x_axis_value = 0.0f;
52 last_y_axis_value = 0.0f; 60 last_y_axis_value = 0.0f;
53 } 61 }
@@ -193,7 +201,7 @@ public:
193 } 201 }
194 202
195 void UpdateStatus() { 203 void UpdateStatus() {
196 const float coef = modifier_status.value ? modifier_scale : 1.0f; 204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
197 205
198 bool r = right_status; 206 bool r = right_status;
199 bool l = left_status; 207 bool l = left_status;
@@ -248,7 +256,7 @@ public:
248 modifier->ForceUpdate(); 256 modifier->ForceUpdate();
249 } 257 }
250 258
251 void SoftUpdate() override { 259 void SoftUpdate() {
252 Common::Input::CallbackStatus status{ 260 Common::Input::CallbackStatus status{
253 .type = Common::Input::InputType::Stick, 261 .type = Common::Input::InputType::Stick,
254 .stick_status = GetStatus(), 262 .stick_status = GetStatus(),
@@ -287,7 +295,7 @@ public:
287 if (down_status) { 295 if (down_status) {
288 --y; 296 --y;
289 } 297 }
290 const float coef = modifier_status.value ? modifier_scale : 1.0f; 298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
291 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); 299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
292 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); 300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
293 return status; 301 return status;
@@ -308,6 +316,7 @@ private:
308 Button left; 316 Button left;
309 Button right; 317 Button right;
310 Button modifier; 318 Button modifier;
319 Button updater;
311 float modifier_scale{}; 320 float modifier_scale{};
312 float modifier_angle{}; 321 float modifier_angle{};
313 float angle{}; 322 float angle{};
@@ -331,11 +340,12 @@ std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
331 auto left = Common::Input::CreateInputDeviceFromString(params.Get("left", null_engine)); 340 auto left = Common::Input::CreateInputDeviceFromString(params.Get("left", null_engine));
332 auto right = Common::Input::CreateInputDeviceFromString(params.Get("right", null_engine)); 341 auto right = Common::Input::CreateInputDeviceFromString(params.Get("right", null_engine));
333 auto modifier = Common::Input::CreateInputDeviceFromString(params.Get("modifier", null_engine)); 342 auto modifier = Common::Input::CreateInputDeviceFromString(params.Get("modifier", null_engine));
343 auto updater = Common::Input::CreateInputDeviceFromString("engine:updater,button:0");
334 auto modifier_scale = params.Get("modifier_scale", 0.5f); 344 auto modifier_scale = params.Get("modifier_scale", 0.5f);
335 auto modifier_angle = params.Get("modifier_angle", 5.5f); 345 auto modifier_angle = params.Get("modifier_angle", 5.5f);
336 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left), 346 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
337 std::move(right), std::move(modifier), modifier_scale, 347 std::move(right), std::move(modifier), std::move(updater),
338 modifier_angle); 348 modifier_scale, modifier_angle);
339} 349}
340 350
341} // namespace InputCommon 351} // namespace InputCommon
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 61cfd0911..91aa96aa7 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -79,6 +79,17 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
79 TriggerOnBatteryChange(identifier, value); 79 TriggerOnBatteryChange(identifier, value);
80} 80}
81 81
82void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) {
83 {
84 std::scoped_lock lock{mutex};
85 ControllerData& controller = controller_list.at(identifier);
86 if (!configuring) {
87 controller.color = value;
88 }
89 }
90 TriggerOnColorChange(identifier, value);
91}
92
82void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { 93void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
83 { 94 {
84 std::scoped_lock lock{mutex}; 95 std::scoped_lock lock{mutex};
@@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
176 return controller.battery; 187 return controller.battery;
177} 188}
178 189
190Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const {
191 std::scoped_lock lock{mutex};
192 const auto controller_iter = controller_list.find(identifier);
193 if (controller_iter == controller_list.cend()) {
194 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
195 identifier.pad, identifier.port);
196 return {};
197 }
198 const ControllerData& controller = controller_iter->second;
199 return controller.color;
200}
201
179BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { 202BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
180 std::scoped_lock lock{mutex}; 203 std::scoped_lock lock{mutex};
181 const auto controller_iter = controller_list.find(identifier); 204 const auto controller_iter = controller_list.find(identifier);
@@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
328 } 351 }
329} 352}
330 353
354void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier,
355 [[maybe_unused]] Common::Input::BodyColorStatus value) {
356 std::scoped_lock lock{mutex_callback};
357 for (const auto& poller_pair : callback_list) {
358 const InputIdentifier& poller = poller_pair.second;
359 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) {
360 continue;
361 }
362 if (poller.callback.on_change) {
363 poller.callback.on_change();
364 }
365 }
366}
367
331void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 368void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
332 const BasicMotion& value) { 369 const BasicMotion& value) {
333 std::scoped_lock lock{mutex_callback}; 370 std::scoped_lock lock{mutex_callback};
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 6cbcf5207..50b5a3dc8 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -40,6 +40,7 @@ enum class EngineInputType {
40 Battery, 40 Battery,
41 Button, 41 Button,
42 Camera, 42 Camera,
43 Color,
43 HatButton, 44 HatButton,
44 Motion, 45 Motion,
45 Nfc, 46 Nfc,
@@ -104,14 +105,17 @@ public:
104 void EndConfiguration(); 105 void EndConfiguration();
105 106
106 // Sets a led pattern for a controller 107 // Sets a led pattern for a controller
107 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, 108 virtual Common::Input::DriverResult SetLeds(
108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 109 [[maybe_unused]] const PadIdentifier& identifier,
110 [[maybe_unused]] const Common::Input::LedStatus& led_status) {
111 return Common::Input::DriverResult::NotSupported;
112 }
109 113
110 // Sets rumble to a controller 114 // Sets rumble to a controller
111 virtual Common::Input::VibrationError SetVibration( 115 virtual Common::Input::DriverResult SetVibration(
112 [[maybe_unused]] const PadIdentifier& identifier, 116 [[maybe_unused]] const PadIdentifier& identifier,
113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 117 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
114 return Common::Input::VibrationError::NotSupported; 118 return Common::Input::DriverResult::NotSupported;
115 } 119 }
116 120
117 // Returns true if device supports vibrations 121 // Returns true if device supports vibrations
@@ -120,17 +124,17 @@ public:
120 } 124 }
121 125
122 // Sets polling mode to a controller 126 // Sets polling mode to a controller
123 virtual Common::Input::PollingError SetPollingMode( 127 virtual Common::Input::DriverResult SetPollingMode(
124 [[maybe_unused]] const PadIdentifier& identifier, 128 [[maybe_unused]] const PadIdentifier& identifier,
125 [[maybe_unused]] const Common::Input::PollingMode polling_mode) { 129 [[maybe_unused]] const Common::Input::PollingMode polling_mode) {
126 return Common::Input::PollingError::NotSupported; 130 return Common::Input::DriverResult::NotSupported;
127 } 131 }
128 132
129 // Sets camera format to a controller 133 // Sets camera format to a controller
130 virtual Common::Input::CameraError SetCameraFormat( 134 virtual Common::Input::DriverResult SetCameraFormat(
131 [[maybe_unused]] const PadIdentifier& identifier, 135 [[maybe_unused]] const PadIdentifier& identifier,
132 [[maybe_unused]] Common::Input::CameraFormat camera_format) { 136 [[maybe_unused]] Common::Input::CameraFormat camera_format) {
133 return Common::Input::CameraError::NotSupported; 137 return Common::Input::DriverResult::NotSupported;
134 } 138 }
135 139
136 // Returns success if nfc is supported 140 // Returns success if nfc is supported
@@ -199,6 +203,7 @@ public:
199 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; 203 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
200 f32 GetAxis(const PadIdentifier& identifier, int axis) const; 204 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
201 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 205 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
206 Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const;
202 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 207 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
203 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; 208 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
204 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; 209 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
@@ -212,6 +217,7 @@ protected:
212 void SetHatButton(const PadIdentifier& identifier, int button, u8 value); 217 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
213 void SetAxis(const PadIdentifier& identifier, int axis, f32 value); 218 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
214 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 219 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
220 void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value);
215 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 221 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
216 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); 222 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
217 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); 223 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
@@ -227,6 +233,7 @@ private:
227 std::unordered_map<int, float> axes; 233 std::unordered_map<int, float> axes;
228 std::unordered_map<int, BasicMotion> motions; 234 std::unordered_map<int, BasicMotion> motions;
229 Common::Input::BatteryLevel battery{}; 235 Common::Input::BatteryLevel battery{};
236 Common::Input::BodyColorStatus color{};
230 Common::Input::CameraStatus camera{}; 237 Common::Input::CameraStatus camera{};
231 Common::Input::NfcStatus nfc{}; 238 Common::Input::NfcStatus nfc{};
232 }; 239 };
@@ -235,6 +242,8 @@ private:
235 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); 242 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
236 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); 243 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
237 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 244 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
245 void TriggerOnColorChange(const PadIdentifier& identifier,
246 Common::Input::BodyColorStatus value);
238 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 247 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
239 const BasicMotion& value); 248 const BasicMotion& value);
240 void TriggerOnCameraChange(const PadIdentifier& identifier, 249 void TriggerOnCameraChange(const PadIdentifier& identifier,
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index edd5287c1..d6e49d2c5 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -76,7 +76,7 @@ void MappingFactory::RegisterButton(const MappingData& data) {
76 break; 76 break;
77 case EngineInputType::Analog: 77 case EngineInputType::Analog:
78 // Ignore mouse axis when mapping buttons 78 // Ignore mouse axis when mapping buttons
79 if (data.engine == "mouse") { 79 if (data.engine == "mouse" && data.index != 4) {
80 return; 80 return;
81 } 81 }
82 new_input.Set("axis", data.index); 82 new_input.Set("axis", data.index);
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index fb8be42e2..15cbf7e5f 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -498,6 +498,58 @@ private:
498 InputEngine* input_engine; 498 InputEngine* input_engine;
499}; 499};
500 500
501class InputFromColor final : public Common::Input::InputDevice {
502public:
503 explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_)
504 : identifier(identifier_), input_engine(input_engine_) {
505 UpdateCallback engine_callback{[this]() { OnChange(); }};
506 const InputIdentifier input_identifier{
507 .identifier = identifier,
508 .type = EngineInputType::Color,
509 .index = 0,
510 .callback = engine_callback,
511 };
512 last_color_value = {};
513 callback_key = input_engine->SetCallback(input_identifier);
514 }
515
516 ~InputFromColor() override {
517 input_engine->DeleteCallback(callback_key);
518 }
519
520 Common::Input::BodyColorStatus GetStatus() const {
521 return input_engine->GetColor(identifier);
522 }
523
524 void ForceUpdate() override {
525 const Common::Input::CallbackStatus status{
526 .type = Common::Input::InputType::Color,
527 .color_status = GetStatus(),
528 };
529
530 last_color_value = status.color_status;
531 TriggerOnChange(status);
532 }
533
534 void OnChange() {
535 const Common::Input::CallbackStatus status{
536 .type = Common::Input::InputType::Color,
537 .color_status = GetStatus(),
538 };
539
540 if (status.color_status.body != last_color_value.body) {
541 last_color_value = status.color_status;
542 TriggerOnChange(status);
543 }
544 }
545
546private:
547 const PadIdentifier identifier;
548 int callback_key;
549 Common::Input::BodyColorStatus last_color_value;
550 InputEngine* input_engine;
551};
552
501class InputFromMotion final : public Common::Input::InputDevice { 553class InputFromMotion final : public Common::Input::InputDevice {
502public: 554public:
503 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, 555 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_,
@@ -754,11 +806,11 @@ public:
754 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) 806 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
755 : identifier(identifier_), input_engine(input_engine_) {} 807 : identifier(identifier_), input_engine(input_engine_) {}
756 808
757 void SetLED(const Common::Input::LedStatus& led_status) override { 809 Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override {
758 input_engine->SetLeds(identifier, led_status); 810 return input_engine->SetLeds(identifier, led_status);
759 } 811 }
760 812
761 Common::Input::VibrationError SetVibration( 813 Common::Input::DriverResult SetVibration(
762 const Common::Input::VibrationStatus& vibration_status) override { 814 const Common::Input::VibrationStatus& vibration_status) override {
763 return input_engine->SetVibration(identifier, vibration_status); 815 return input_engine->SetVibration(identifier, vibration_status);
764 } 816 }
@@ -767,11 +819,12 @@ public:
767 return input_engine->IsVibrationEnabled(identifier); 819 return input_engine->IsVibrationEnabled(identifier);
768 } 820 }
769 821
770 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 822 Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override {
771 return input_engine->SetPollingMode(identifier, polling_mode); 823 return input_engine->SetPollingMode(identifier, polling_mode);
772 } 824 }
773 825
774 Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { 826 Common::Input::DriverResult SetCameraFormat(
827 Common::Input::CameraFormat camera_format) override {
775 return input_engine->SetCameraFormat(identifier, camera_format); 828 return input_engine->SetCameraFormat(identifier, camera_format);
776 } 829 }
777 830
@@ -966,6 +1019,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
966 return std::make_unique<InputFromBattery>(identifier, input_engine.get()); 1019 return std::make_unique<InputFromBattery>(identifier, input_engine.get());
967} 1020}
968 1021
1022std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice(
1023 const Common::ParamPackage& params) {
1024 const PadIdentifier identifier = {
1025 .guid = Common::UUID{params.Get("guid", "")},
1026 .port = static_cast<std::size_t>(params.Get("port", 0)),
1027 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
1028 };
1029
1030 input_engine->PreSetController(identifier);
1031 return std::make_unique<InputFromColor>(identifier, input_engine.get());
1032}
1033
969std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( 1034std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
970 Common::ParamPackage params) { 1035 Common::ParamPackage params) {
971 const PadIdentifier identifier = { 1036 const PadIdentifier identifier = {
@@ -1053,6 +1118,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
1053 if (params.Has("battery")) { 1118 if (params.Has("battery")) {
1054 return CreateBatteryDevice(params); 1119 return CreateBatteryDevice(params);
1055 } 1120 }
1121 if (params.Has("color")) {
1122 return CreateColorDevice(params);
1123 }
1056 if (params.Has("camera")) { 1124 if (params.Has("camera")) {
1057 return CreateCameraDevice(params); 1125 return CreateCameraDevice(params);
1058 } 1126 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index d7db13ce4..e097e254c 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -191,6 +191,17 @@ private:
191 const Common::ParamPackage& params); 191 const Common::ParamPackage& params);
192 192
193 /** 193 /**
194 * Creates a color device from the parameters given.
195 * @param params contains parameters for creating the device:
196 * - "guid": text string for identifying controllers
197 * - "port": port of the connected device
198 * - "pad": slot of the connected controller
199 * @returns a unique input device with the parameters specified
200 */
201 std::unique_ptr<Common::Input::InputDevice> CreateColorDevice(
202 const Common::ParamPackage& params);
203
204 /**
194 * Creates a motion device from the parameters given. 205 * Creates a motion device from the parameters given.
195 * @param params contains parameters for creating the device: 206 * @param params contains parameters for creating the device:
196 * - "axis_x": the controller horizontal axis id to bind with the input 207 * - "axis_x": the controller horizontal axis id to bind with the input
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 86deb4c7c..c77fc04ee 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -5,7 +5,6 @@
5#include "common/input.h" 5#include "common/input.h"
6#include "common/param_package.h" 6#include "common/param_package.h"
7#include "input_common/drivers/camera.h" 7#include "input_common/drivers/camera.h"
8#include "input_common/drivers/gc_adapter.h"
9#include "input_common/drivers/keyboard.h" 8#include "input_common/drivers/keyboard.h"
10#include "input_common/drivers/mouse.h" 9#include "input_common/drivers/mouse.h"
11#include "input_common/drivers/tas_input.h" 10#include "input_common/drivers/tas_input.h"
@@ -19,12 +18,39 @@
19#include "input_common/input_mapping.h" 18#include "input_common/input_mapping.h"
20#include "input_common/input_poller.h" 19#include "input_common/input_poller.h"
21#include "input_common/main.h" 20#include "input_common/main.h"
21
22#ifdef HAVE_LIBUSB
23#include "input_common/drivers/gc_adapter.h"
24#endif
22#ifdef HAVE_SDL2 25#ifdef HAVE_SDL2
26#include "input_common/drivers/joycon.h"
23#include "input_common/drivers/sdl_driver.h" 27#include "input_common/drivers/sdl_driver.h"
24#endif 28#endif
25 29
26namespace InputCommon { 30namespace InputCommon {
27 31
32/// Dummy engine to get periodic updates
33class UpdateEngine final : public InputEngine {
34public:
35 explicit UpdateEngine(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
36 PreSetController(identifier);
37 }
38
39 void PumpEvents() {
40 SetButton(identifier, 0, last_state);
41 last_state = !last_state;
42 }
43
44private:
45 static constexpr PadIdentifier identifier = {
46 .guid = Common::UUID{},
47 .port = 0,
48 .pad = 0,
49 };
50
51 bool last_state{};
52};
53
28struct InputSubsystem::Impl { 54struct InputSubsystem::Impl {
29 template <typename Engine> 55 template <typename Engine>
30 void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) { 56 void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
@@ -42,10 +68,13 @@ struct InputSubsystem::Impl {
42 void Initialize() { 68 void Initialize() {
43 mapping_factory = std::make_shared<MappingFactory>(); 69 mapping_factory = std::make_shared<MappingFactory>();
44 70
71 RegisterEngine("updater", update_engine);
45 RegisterEngine("keyboard", keyboard); 72 RegisterEngine("keyboard", keyboard);
46 RegisterEngine("mouse", mouse); 73 RegisterEngine("mouse", mouse);
47 RegisterEngine("touch", touch_screen); 74 RegisterEngine("touch", touch_screen);
75#ifdef HAVE_LIBUSB
48 RegisterEngine("gcpad", gcadapter); 76 RegisterEngine("gcpad", gcadapter);
77#endif
49 RegisterEngine("cemuhookudp", udp_client); 78 RegisterEngine("cemuhookudp", udp_client);
50 RegisterEngine("tas", tas_input); 79 RegisterEngine("tas", tas_input);
51 RegisterEngine("camera", camera); 80 RegisterEngine("camera", camera);
@@ -53,6 +82,7 @@ struct InputSubsystem::Impl {
53 RegisterEngine("virtual_gamepad", virtual_gamepad); 82 RegisterEngine("virtual_gamepad", virtual_gamepad);
54#ifdef HAVE_SDL2 83#ifdef HAVE_SDL2
55 RegisterEngine("sdl", sdl); 84 RegisterEngine("sdl", sdl);
85 RegisterEngine("joycon", joycon);
56#endif 86#endif
57 87
58 Common::Input::RegisterInputFactory("touch_from_button", 88 Common::Input::RegisterInputFactory("touch_from_button",
@@ -69,10 +99,13 @@ struct InputSubsystem::Impl {
69 } 99 }
70 100
71 void Shutdown() { 101 void Shutdown() {
102 UnregisterEngine(update_engine);
72 UnregisterEngine(keyboard); 103 UnregisterEngine(keyboard);
73 UnregisterEngine(mouse); 104 UnregisterEngine(mouse);
74 UnregisterEngine(touch_screen); 105 UnregisterEngine(touch_screen);
106#ifdef HAVE_LIBUSB
75 UnregisterEngine(gcadapter); 107 UnregisterEngine(gcadapter);
108#endif
76 UnregisterEngine(udp_client); 109 UnregisterEngine(udp_client);
77 UnregisterEngine(tas_input); 110 UnregisterEngine(tas_input);
78 UnregisterEngine(camera); 111 UnregisterEngine(camera);
@@ -80,6 +113,7 @@ struct InputSubsystem::Impl {
80 UnregisterEngine(virtual_gamepad); 113 UnregisterEngine(virtual_gamepad);
81#ifdef HAVE_SDL2 114#ifdef HAVE_SDL2
82 UnregisterEngine(sdl); 115 UnregisterEngine(sdl);
116 UnregisterEngine(joycon);
83#endif 117#endif
84 118
85 Common::Input::UnregisterInputFactory("touch_from_button"); 119 Common::Input::UnregisterInputFactory("touch_from_button");
@@ -95,11 +129,15 @@ struct InputSubsystem::Impl {
95 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); 129 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
96 auto mouse_devices = mouse->GetInputDevices(); 130 auto mouse_devices = mouse->GetInputDevices();
97 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); 131 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
132#ifdef HAVE_LIBUSB
98 auto gcadapter_devices = gcadapter->GetInputDevices(); 133 auto gcadapter_devices = gcadapter->GetInputDevices();
99 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); 134 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
135#endif
100 auto udp_devices = udp_client->GetInputDevices(); 136 auto udp_devices = udp_client->GetInputDevices();
101 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); 137 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
102#ifdef HAVE_SDL2 138#ifdef HAVE_SDL2
139 auto joycon_devices = joycon->GetInputDevices();
140 devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
103 auto sdl_devices = sdl->GetInputDevices(); 141 auto sdl_devices = sdl->GetInputDevices();
104 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); 142 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
105#endif 143#endif
@@ -119,9 +157,11 @@ struct InputSubsystem::Impl {
119 if (engine == mouse->GetEngineName()) { 157 if (engine == mouse->GetEngineName()) {
120 return mouse; 158 return mouse;
121 } 159 }
160#ifdef HAVE_LIBUSB
122 if (engine == gcadapter->GetEngineName()) { 161 if (engine == gcadapter->GetEngineName()) {
123 return gcadapter; 162 return gcadapter;
124 } 163 }
164#endif
125 if (engine == udp_client->GetEngineName()) { 165 if (engine == udp_client->GetEngineName()) {
126 return udp_client; 166 return udp_client;
127 } 167 }
@@ -129,6 +169,9 @@ struct InputSubsystem::Impl {
129 if (engine == sdl->GetEngineName()) { 169 if (engine == sdl->GetEngineName()) {
130 return sdl; 170 return sdl;
131 } 171 }
172 if (engine == joycon->GetEngineName()) {
173 return joycon;
174 }
132#endif 175#endif
133 return nullptr; 176 return nullptr;
134 } 177 }
@@ -194,9 +237,11 @@ struct InputSubsystem::Impl {
194 if (engine == mouse->GetEngineName()) { 237 if (engine == mouse->GetEngineName()) {
195 return true; 238 return true;
196 } 239 }
240#ifdef HAVE_LIBUSB
197 if (engine == gcadapter->GetEngineName()) { 241 if (engine == gcadapter->GetEngineName()) {
198 return true; 242 return true;
199 } 243 }
244#endif
200 if (engine == udp_client->GetEngineName()) { 245 if (engine == udp_client->GetEngineName()) {
201 return true; 246 return true;
202 } 247 }
@@ -210,6 +255,9 @@ struct InputSubsystem::Impl {
210 if (engine == sdl->GetEngineName()) { 255 if (engine == sdl->GetEngineName()) {
211 return true; 256 return true;
212 } 257 }
258 if (engine == joycon->GetEngineName()) {
259 return true;
260 }
213#endif 261#endif
214 return false; 262 return false;
215 } 263 }
@@ -217,24 +265,31 @@ struct InputSubsystem::Impl {
217 void BeginConfiguration() { 265 void BeginConfiguration() {
218 keyboard->BeginConfiguration(); 266 keyboard->BeginConfiguration();
219 mouse->BeginConfiguration(); 267 mouse->BeginConfiguration();
268#ifdef HAVE_LIBUSB
220 gcadapter->BeginConfiguration(); 269 gcadapter->BeginConfiguration();
270#endif
221 udp_client->BeginConfiguration(); 271 udp_client->BeginConfiguration();
222#ifdef HAVE_SDL2 272#ifdef HAVE_SDL2
223 sdl->BeginConfiguration(); 273 sdl->BeginConfiguration();
274 joycon->BeginConfiguration();
224#endif 275#endif
225 } 276 }
226 277
227 void EndConfiguration() { 278 void EndConfiguration() {
228 keyboard->EndConfiguration(); 279 keyboard->EndConfiguration();
229 mouse->EndConfiguration(); 280 mouse->EndConfiguration();
281#ifdef HAVE_LIBUSB
230 gcadapter->EndConfiguration(); 282 gcadapter->EndConfiguration();
283#endif
231 udp_client->EndConfiguration(); 284 udp_client->EndConfiguration();
232#ifdef HAVE_SDL2 285#ifdef HAVE_SDL2
233 sdl->EndConfiguration(); 286 sdl->EndConfiguration();
287 joycon->EndConfiguration();
234#endif 288#endif
235 } 289 }
236 290
237 void PumpEvents() const { 291 void PumpEvents() const {
292 update_engine->PumpEvents();
238#ifdef HAVE_SDL2 293#ifdef HAVE_SDL2
239 sdl->PumpEvents(); 294 sdl->PumpEvents();
240#endif 295#endif
@@ -246,9 +301,9 @@ struct InputSubsystem::Impl {
246 301
247 std::shared_ptr<MappingFactory> mapping_factory; 302 std::shared_ptr<MappingFactory> mapping_factory;
248 303
304 std::shared_ptr<UpdateEngine> update_engine;
249 std::shared_ptr<Keyboard> keyboard; 305 std::shared_ptr<Keyboard> keyboard;
250 std::shared_ptr<Mouse> mouse; 306 std::shared_ptr<Mouse> mouse;
251 std::shared_ptr<GCAdapter> gcadapter;
252 std::shared_ptr<TouchScreen> touch_screen; 307 std::shared_ptr<TouchScreen> touch_screen;
253 std::shared_ptr<TasInput::Tas> tas_input; 308 std::shared_ptr<TasInput::Tas> tas_input;
254 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 309 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
@@ -256,8 +311,13 @@ struct InputSubsystem::Impl {
256 std::shared_ptr<VirtualAmiibo> virtual_amiibo; 311 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
257 std::shared_ptr<VirtualGamepad> virtual_gamepad; 312 std::shared_ptr<VirtualGamepad> virtual_gamepad;
258 313
314#ifdef HAVE_LIBUSB
315 std::shared_ptr<GCAdapter> gcadapter;
316#endif
317
259#ifdef HAVE_SDL2 318#ifdef HAVE_SDL2
260 std::shared_ptr<SDLDriver> sdl; 319 std::shared_ptr<SDLDriver> sdl;
320 std::shared_ptr<Joycons> joycon;
261#endif 321#endif
262}; 322};
263 323
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 0cb1e193e..fd4a61a4d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -279,6 +279,8 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
279 header += "OPTION NV_internal;" 279 header += "OPTION NV_internal;"
280 "OPTION NV_shader_storage_buffer;" 280 "OPTION NV_shader_storage_buffer;"
281 "OPTION NV_gpu_program_fp64;"; 281 "OPTION NV_gpu_program_fp64;";
282 // TODO: Enable only when MS is used
283 header += "OPTION NV_texture_multisample;";
282 if (info.uses_int64_bit_atomics) { 284 if (info.uses_int64_bit_atomics) {
283 header += "OPTION NV_shader_atomic_int64;"; 285 header += "OPTION NV_shader_atomic_int64;";
284 } 286 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 5bfdecc09..2fc2a0ac6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -43,10 +43,6 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
43 Alias(inst, value); 43 Alias(inst, value);
44} 44}
45 45
46void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value);
48}
49
50void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { 46void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
51 Alias(inst, value); 47 Alias(inst, value);
52} 48}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index f0bd84ab2..c7d7d5fef 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
@@ -137,6 +137,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
137 case IR::Attribute::VertexId: 137 case IR::Attribute::VertexId:
138 ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name); 138 ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
139 break; 139 break;
140 case IR::Attribute::BaseInstance:
141 ctx.Add("MOV.F {}.x,{}.baseInstance;", inst, ctx.attrib_name);
142 break;
143 case IR::Attribute::BaseVertex:
144 ctx.Add("MOV.F {}.x,{}.baseVertex;", inst, ctx.attrib_name);
145 break;
146 case IR::Attribute::DrawID:
147 ctx.Add("MOV.F {}.x,{}.draw.id;", inst, ctx.attrib_name);
148 break;
140 case IR::Attribute::FrontFace: 149 case IR::Attribute::FrontFace:
141 ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name); 150 ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
142 break; 151 break;
@@ -156,6 +165,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, S
156 case IR::Attribute::VertexId: 165 case IR::Attribute::VertexId:
157 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name); 166 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
158 break; 167 break;
168 case IR::Attribute::BaseInstance:
169 ctx.Add("MOV.S {}.x,{}.baseInstance;", inst, ctx.attrib_name);
170 break;
171 case IR::Attribute::BaseVertex:
172 ctx.Add("MOV.S {}.x,{}.baseVertex;", inst, ctx.attrib_name);
173 break;
174 case IR::Attribute::DrawID:
175 ctx.Add("MOV.S {}.x,{}.draw.id;", inst, ctx.attrib_name);
176 break;
159 default: 177 default:
160 throw NotImplementedException("Get U32 attribute {}", attr); 178 throw NotImplementedException("Get U32 attribute {}", attr);
161 } 179 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index e67e80fac..b7bc11416 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -59,7 +59,7 @@ std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
59 } 59 }
60} 60}
61 61
62std::string_view TextureType(IR::TextureInstInfo info) { 62std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
63 if (info.is_depth) { 63 if (info.is_depth) {
64 switch (info.type) { 64 switch (info.type) {
65 case TextureType::Color1D: 65 case TextureType::Color1D:
@@ -88,9 +88,9 @@ std::string_view TextureType(IR::TextureInstInfo info) {
88 return "ARRAY1D"; 88 return "ARRAY1D";
89 case TextureType::Color2D: 89 case TextureType::Color2D:
90 case TextureType::Color2DRect: 90 case TextureType::Color2DRect:
91 return "2D"; 91 return is_ms ? "2DMS" : "2D";
92 case TextureType::ColorArray2D: 92 case TextureType::ColorArray2D:
93 return "ARRAY2D"; 93 return is_ms ? "ARRAY2DMS" : "ARRAY2D";
94 case TextureType::Color3D: 94 case TextureType::Color3D:
95 return "3D"; 95 return "3D";
96 case TextureType::ColorCube: 96 case TextureType::ColorCube:
@@ -510,15 +510,16 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) { 510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
511 const auto info{inst.Flags<IR::TextureInstInfo>()}; 511 const auto info{inst.Flags<IR::TextureInstInfo>()};
512 const auto sparse_inst{PrepareSparse(inst)}; 512 const auto sparse_inst{PrepareSparse(inst)};
513 const bool is_multisample{ms.type != Type::Void};
513 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""}; 514 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
514 const std::string_view type{TextureType(info)}; 515 const std::string_view type{TextureType(info, is_multisample)};
515 const std::string texture{Texture(ctx, info, index)}; 516 const std::string texture{Texture(ctx, info, index)};
516 const std::string offset_vec{Offset(ctx, offset)}; 517 const std::string offset_vec{Offset(ctx, offset)};
517 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)}; 518 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
518 const Register ret{ctx.reg_alloc.Define(inst)}; 519 const Register ret{ctx.reg_alloc.Define(inst)};
519 if (info.type == TextureType::Buffer) { 520 if (info.type == TextureType::Buffer) {
520 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec); 521 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
521 } else if (ms.type != Type::Void) { 522 } else if (is_multisample) {
522 ctx.Add("MOV.S {}.w,{};" 523 ctx.Add("MOV.S {}.w,{};"
523 "TXFMS.F{} {},{},{},{}{};", 524 "TXFMS.F{} {},{},{},{}{};",
524 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec); 525 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
@@ -531,7 +532,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531} 532}
532 533
533void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 534void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
534 ScalarS32 lod) { 535 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
535 const auto info{inst.Flags<IR::TextureInstInfo>()}; 536 const auto info{inst.Flags<IR::TextureInstInfo>()};
536 const std::string texture{Texture(ctx, info, index)}; 537 const std::string texture{Texture(ctx, info, index)};
537 const std::string_view type{TextureType(info)}; 538 const std::string_view type{TextureType(info)};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index eaaf9ba39..1a1ea61d5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -197,7 +197,6 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist
197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
201void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 200void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
202void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 201void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
203void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 202void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
@@ -582,7 +581,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
582void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 581void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
583 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); 582 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
584void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 583void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
585 ScalarS32 lod); 584 ScalarS32 lod, const IR::Value& skip_mips);
586void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); 585void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
587void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 586void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
588 const IR::Value& coord, const IR::Value& derivatives, 587 const IR::Value& coord, const IR::Value& derivatives,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index e8a4390f6..d91e04446 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -219,7 +219,7 @@ std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR
219 EmitContext ctx{program, bindings, profile, runtime_info}; 219 EmitContext ctx{program, bindings, profile, runtime_info};
220 Precolor(program); 220 Precolor(program);
221 EmitCode(ctx, program); 221 EmitCode(ctx, program);
222 const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))}; 222 const std::string version{fmt::format("#version 460{}\n", GlslVersionSpecifier(ctx))};
223 ctx.header.insert(0, version); 223 ctx.header.insert(0, version);
224 if (program.shared_memory_size > 0) { 224 if (program.shared_memory_size > 0) {
225 const auto requested_size{program.shared_memory_size}; 225 const auto requested_size{program.shared_memory_size};
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 8e5e6cf1f..1be4a0f59 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -48,10 +48,6 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value)
48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); 48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
49} 49}
50 50
51void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
52 ctx.AddF32("{}=ftoi({});", inst, value);
53}
54
55void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { 51void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
56 NotImplemented(); 52 NotImplemented();
57} 53}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 39579cf5d..2e369ed72 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -234,6 +234,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
234 case IR::Attribute::FrontFace: 234 case IR::Attribute::FrontFace:
235 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst); 235 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
236 break; 236 break;
237 case IR::Attribute::BaseInstance:
238 ctx.AddF32("{}=itof(gl_BaseInstance);", inst);
239 break;
240 case IR::Attribute::BaseVertex:
241 ctx.AddF32("{}=itof(gl_BaseVertex);", inst);
242 break;
243 case IR::Attribute::DrawID:
244 ctx.AddF32("{}=itof(gl_DrawID);", inst);
245 break;
237 default: 246 default:
238 throw NotImplementedException("Get attribute {}", attr); 247 throw NotImplementedException("Get attribute {}", attr);
239 } 248 }
@@ -250,6 +259,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, s
250 case IR::Attribute::VertexId: 259 case IR::Attribute::VertexId:
251 ctx.AddU32("{}=uint(gl_VertexID);", inst); 260 ctx.AddU32("{}=uint(gl_VertexID);", inst);
252 break; 261 break;
262 case IR::Attribute::BaseInstance:
263 ctx.AddU32("{}=uint(gl_BaseInstance);", inst);
264 break;
265 case IR::Attribute::BaseVertex:
266 ctx.AddU32("{}=uint(gl_BaseVertex);", inst);
267 break;
268 case IR::Attribute::DrawID:
269 ctx.AddU32("{}=uint(gl_DrawID);", inst);
270 break;
253 default: 271 default:
254 throw NotImplementedException("Get U32 attribute {}", attr); 272 throw NotImplementedException("Get U32 attribute {}", attr);
255 } 273 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index cecdbb9d6..4be2c25ec 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -414,7 +414,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
414 414
415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
416 std::string_view coords, std::string_view offset, std::string_view lod, 416 std::string_view coords, std::string_view offset, std::string_view lod,
417 [[maybe_unused]] std::string_view ms) { 417 std::string_view ms) {
418 const auto info{inst.Flags<IR::TextureInstInfo>()}; 418 const auto info{inst.Flags<IR::TextureInstInfo>()};
419 if (info.has_bias) { 419 if (info.has_bias) {
420 throw NotImplementedException("EmitImageFetch Bias texture samples"); 420 throw NotImplementedException("EmitImageFetch Bias texture samples");
@@ -431,19 +431,24 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
431 ctx.AddU1("{}=true;", *sparse_inst); 431 ctx.AddU1("{}=true;", *sparse_inst);
432 } 432 }
433 if (!sparse_inst || !supports_sparse) { 433 if (!sparse_inst || !supports_sparse) {
434 if (!offset.empty()) { 434 const auto int_coords{CoordsCastToInt(coords, info)};
435 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, 435 if (!ms.empty()) {
436 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info)); 436 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
437 } else if (!offset.empty()) {
438 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
439 CoordsCastToInt(offset, info));
437 } else { 440 } else {
438 if (info.type == TextureType::Buffer) { 441 if (info.type == TextureType::Buffer) {
439 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords); 442 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
440 } else { 443 } else {
441 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, 444 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, lod);
442 CoordsCastToInt(coords, info), lod);
443 } 445 }
444 } 446 }
445 return; 447 return;
446 } 448 }
449 if (!ms.empty()) {
450 throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
451 }
447 if (!offset.empty()) { 452 if (!offset.empty()) {
448 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", 453 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
449 *sparse_inst, texture, CastToIntVec(coords, info), lod, 454 *sparse_inst, texture, CastToIntVec(coords, info), lod,
@@ -455,27 +460,27 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
455} 460}
456 461
457void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 462void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
458 std::string_view lod) { 463 std::string_view lod, const IR::Value& skip_mips_val) {
459 const auto info{inst.Flags<IR::TextureInstInfo>()}; 464 const auto info{inst.Flags<IR::TextureInstInfo>()};
460 const auto texture{Texture(ctx, info, index)}; 465 const auto texture{Texture(ctx, info, index)};
466 const bool skip_mips{skip_mips_val.U1()};
467 const auto mips{
468 [&] { return skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture); }};
461 switch (info.type) { 469 switch (info.type) {
462 case TextureType::Color1D: 470 case TextureType::Color1D:
463 return ctx.AddU32x4( 471 return ctx.AddU32x4("{}=uvec4(uint(textureSize({},int({}))),0u,0u,{});", inst, texture, lod,
464 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst, 472 mips());
465 texture, lod, texture);
466 case TextureType::ColorArray1D: 473 case TextureType::ColorArray1D:
467 case TextureType::Color2D: 474 case TextureType::Color2D:
468 case TextureType::ColorCube: 475 case TextureType::ColorCube:
469 case TextureType::Color2DRect: 476 case TextureType::Color2DRect:
470 return ctx.AddU32x4( 477 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({},int({}))),0u,{});", inst, texture, lod,
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, 478 mips());
472 texture, lod, texture);
473 case TextureType::ColorArray2D: 479 case TextureType::ColorArray2D:
474 case TextureType::Color3D: 480 case TextureType::Color3D:
475 case TextureType::ColorArrayCube: 481 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4( 482 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({},int({}))),{});", inst, texture, lod,
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture, 483 mips());
478 lod, texture);
479 case TextureType::Buffer: 484 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); 485 throw NotImplementedException("EmitImageQueryDimensions Texture buffers");
481 } 486 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 4151c89de..8d0a65047 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -231,7 +231,6 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); 231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
235void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); 234void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
236void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 235void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
237void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 236void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
@@ -655,7 +654,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
655 std::string_view coords, std::string_view offset, std::string_view lod, 654 std::string_view coords, std::string_view offset, std::string_view lod,
656 std::string_view ms); 655 std::string_view ms);
657void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 656void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
658 std::string_view lod); 657 std::string_view lod, const IR::Value& skip_mips);
659void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 658void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
660 std::string_view coords); 659 std::string_view coords);
661void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 660void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 5d01ec0cd..1b006e811 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -61,24 +61,28 @@ std::string OutputDecorator(Stage stage, u32 size) {
61 } 61 }
62} 62}
63 63
64std::string_view SamplerType(TextureType type, bool is_depth) { 64std::string_view DepthSamplerType(TextureType type) {
65 if (is_depth) { 65 switch (type) {
66 switch (type) { 66 case TextureType::Color1D:
67 case TextureType::Color1D: 67 return "sampler1DShadow";
68 return "sampler1DShadow"; 68 case TextureType::ColorArray1D:
69 case TextureType::ColorArray1D: 69 return "sampler1DArrayShadow";
70 return "sampler1DArrayShadow"; 70 case TextureType::Color2D:
71 case TextureType::Color2D: 71 return "sampler2DShadow";
72 return "sampler2DShadow"; 72 case TextureType::ColorArray2D:
73 case TextureType::ColorArray2D: 73 return "sampler2DArrayShadow";
74 return "sampler2DArrayShadow"; 74 case TextureType::ColorCube:
75 case TextureType::ColorCube: 75 return "samplerCubeShadow";
76 return "samplerCubeShadow"; 76 case TextureType::ColorArrayCube:
77 case TextureType::ColorArrayCube: 77 return "samplerCubeArrayShadow";
78 return "samplerCubeArrayShadow"; 78 default:
79 default: 79 throw NotImplementedException("Texture type: {}", type);
80 throw NotImplementedException("Texture type: {}", type); 80 }
81 } 81}
82
83std::string_view ColorSamplerType(TextureType type, bool is_multisample = false) {
84 if (is_multisample) {
85 ASSERT(type == TextureType::Color2D || type == TextureType::ColorArray2D);
82 } 86 }
83 switch (type) { 87 switch (type) {
84 case TextureType::Color1D: 88 case TextureType::Color1D:
@@ -87,9 +91,9 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
87 return "sampler1DArray"; 91 return "sampler1DArray";
88 case TextureType::Color2D: 92 case TextureType::Color2D:
89 case TextureType::Color2DRect: 93 case TextureType::Color2DRect:
90 return "sampler2D"; 94 return is_multisample ? "sampler2DMS" : "sampler2D";
91 case TextureType::ColorArray2D: 95 case TextureType::ColorArray2D:
92 return "sampler2DArray"; 96 return is_multisample ? "sampler2DMSArray" : "sampler2DArray";
93 case TextureType::Color3D: 97 case TextureType::Color3D:
94 return "sampler3D"; 98 return "sampler3D";
95 case TextureType::ColorCube: 99 case TextureType::ColorCube:
@@ -677,7 +681,7 @@ void EmitContext::SetupTextures(Bindings& bindings) {
677 texture_buffers.reserve(info.texture_buffer_descriptors.size()); 681 texture_buffers.reserve(info.texture_buffer_descriptors.size());
678 for (const auto& desc : info.texture_buffer_descriptors) { 682 for (const auto& desc : info.texture_buffer_descriptors) {
679 texture_buffers.push_back({bindings.texture, desc.count}); 683 texture_buffers.push_back({bindings.texture, desc.count});
680 const auto sampler_type{SamplerType(TextureType::Buffer, false)}; 684 const auto sampler_type{ColorSamplerType(TextureType::Buffer)};
681 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 685 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
682 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 686 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
683 sampler_type, bindings.texture, array_decorator); 687 sampler_type, bindings.texture, array_decorator);
@@ -686,7 +690,8 @@ void EmitContext::SetupTextures(Bindings& bindings) {
686 textures.reserve(info.texture_descriptors.size()); 690 textures.reserve(info.texture_descriptors.size());
687 for (const auto& desc : info.texture_descriptors) { 691 for (const auto& desc : info.texture_descriptors) {
688 textures.push_back({bindings.texture, desc.count}); 692 textures.push_back({bindings.texture, desc.count});
689 const auto sampler_type{SamplerType(desc.type, desc.is_depth)}; 693 const auto sampler_type{desc.is_depth ? DepthSamplerType(desc.type)
694 : ColorSamplerType(desc.type, desc.is_multisample)};
690 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 695 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
691 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 696 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
692 sampler_type, bindings.texture, array_decorator); 697 sampler_type, bindings.texture, array_decorator);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index 50daacd95..c4ca28d11 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -18,10 +18,6 @@ void EmitBitCastU64F64(EmitContext&) {
18 throw NotImplementedException("SPIR-V Instruction"); 18 throw NotImplementedException("SPIR-V Instruction");
19} 19}
20 20
21void EmitBitCastS32F32(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction");
23}
24
25void EmitBitCastF16U16(EmitContext&) { 21void EmitBitCastF16U16(EmitContext&) {
26 throw NotImplementedException("SPIR-V Instruction"); 22 throw NotImplementedException("SPIR-V Instruction");
27} 23}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 73b67f0af..0cd87a48f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -321,8 +321,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
321 case IR::Attribute::PositionY: 321 case IR::Attribute::PositionY:
322 case IR::Attribute::PositionZ: 322 case IR::Attribute::PositionZ:
323 case IR::Attribute::PositionW: 323 case IR::Attribute::PositionW:
324 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, 324 return ctx.OpLoad(
325 ctx.Const(element))); 325 ctx.F32[1],
326 ctx.need_input_position_indirect
327 ? AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.u32_zero_value,
328 ctx.Const(element))
329 : AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.Const(element)));
326 case IR::Attribute::InstanceId: 330 case IR::Attribute::InstanceId:
327 if (ctx.profile.support_vertex_instance_id) { 331 if (ctx.profile.support_vertex_instance_id) {
328 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); 332 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
@@ -339,6 +343,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
339 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; 343 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
340 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); 344 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
341 } 345 }
346 case IR::Attribute::BaseInstance:
347 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
348 case IR::Attribute::BaseVertex:
349 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_vertex));
350 case IR::Attribute::DrawID:
351 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.draw_index));
342 case IR::Attribute::FrontFace: 352 case IR::Attribute::FrontFace:
343 return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face), 353 return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
344 ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())), 354 ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
@@ -380,6 +390,12 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
380 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; 390 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
381 return ctx.OpISub(ctx.U32[1], index, base); 391 return ctx.OpISub(ctx.U32[1], index, base);
382 } 392 }
393 case IR::Attribute::BaseInstance:
394 return ctx.OpLoad(ctx.U32[1], ctx.base_instance);
395 case IR::Attribute::BaseVertex:
396 return ctx.OpLoad(ctx.U32[1], ctx.base_vertex);
397 case IR::Attribute::DrawID:
398 return ctx.OpLoad(ctx.U32[1], ctx.draw_index);
383 default: 399 default:
384 throw NotImplementedException("Read U32 attribute {}", attr); 400 throw NotImplementedException("Read U32 attribute {}", attr);
385 } 401 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index fb5799c42..3b969d915 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -436,16 +436,22 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
436 if (info.type == TextureType::Buffer) { 436 if (info.type == TextureType::Buffer) {
437 lod = Id{}; 437 lod = Id{};
438 } 438 }
439 if (Sirit::ValidId(ms)) {
440 // This image is multisampled, lod must be implicit
441 lod = Id{};
442 }
439 const ImageOperands operands(offset, lod, ms); 443 const ImageOperands operands(offset, lod, ms);
440 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 444 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
441 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
442} 446}
443 447
444Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { 448Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
449 const IR::Value& skip_mips_val) {
445 const auto info{inst->Flags<IR::TextureInstInfo>()}; 450 const auto info{inst->Flags<IR::TextureInstInfo>()};
446 const Id image{TextureImage(ctx, info, index)}; 451 const Id image{TextureImage(ctx, info, index)};
447 const Id zero{ctx.u32_zero_value}; 452 const Id zero{ctx.u32_zero_value};
448 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 453 const bool skip_mips{skip_mips_val.U1()};
454 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
449 switch (info.type) { 455 switch (info.type) {
450 case TextureType::Color1D: 456 case TextureType::Color1D:
451 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), 457 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index e31cdc5e8..a440b557d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -179,7 +179,6 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
179void EmitBitCastU16F16(EmitContext& ctx); 179void EmitBitCastU16F16(EmitContext& ctx);
180Id EmitBitCastU32F32(EmitContext& ctx, Id value); 180Id EmitBitCastU32F32(EmitContext& ctx, Id value);
181void EmitBitCastU64F64(EmitContext& ctx); 181void EmitBitCastU64F64(EmitContext& ctx);
182void EmitBitCastS32F32(EmitContext& ctx);
183void EmitBitCastF16U16(EmitContext&); 182void EmitBitCastF16U16(EmitContext&);
184Id EmitBitCastF32U32(EmitContext& ctx, Id value); 183Id EmitBitCastF32U32(EmitContext& ctx, Id value);
185void EmitBitCastF64U64(EmitContext& ctx); 184void EmitBitCastF64U64(EmitContext& ctx);
@@ -540,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
540 const IR::Value& offset, const IR::Value& offset2, Id dref); 539 const IR::Value& offset, const IR::Value& offset2, Id dref);
541Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
542 Id lod, Id ms); 541 Id lod, Id ms);
543Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); 542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivates, Id offset, Id lod_clamp);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 2c90f2368..c5db19d09 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -58,11 +58,10 @@ Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); 58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
59} 59}
60 60
61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id AddPartitionBase(EmitContext& ctx, Id thread_id) {
62 const Id thirty_two{ctx.Const(32u)}; 62 const Id partition_idx{ctx.OpShiftRightLogical(ctx.U32[1], GetThreadId(ctx), ctx.Const(5u))};
63 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)}; 63 const Id partition_base{ctx.OpShiftLeftLogical(ctx.U32[1], partition_idx, ctx.Const(5u))};
64 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)}; 64 return ctx.OpIAdd(ctx.U32[1], thread_id, partition_base);
65 return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
66} 65}
67} // Anonymous namespace 66} // Anonymous namespace
68 67
@@ -145,64 +144,63 @@ Id EmitSubgroupGeMask(EmitContext& ctx) {
145Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 144Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
146 Id segmentation_mask) { 145 Id segmentation_mask) {
147 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; 146 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
148 const Id thread_id{GetThreadId(ctx)}; 147 const Id thread_id{EmitLaneId(ctx)};
149 if (ctx.profile.warp_size_potentially_larger_than_guest) {
150 const Id thirty_two{ctx.Const(32u)};
151 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
152 const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
153 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
154 index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
155 clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
156 }
157 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; 148 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
158 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; 149 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
159 150
160 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)}; 151 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
161 const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)}; 152 Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
162 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 153 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
163 154
155 if (ctx.profile.warp_size_potentially_larger_than_guest) {
156 src_thread_id = AddPartitionBase(ctx, src_thread_id);
157 }
158
164 SetInBoundsFlag(inst, in_range); 159 SetInBoundsFlag(inst, in_range);
165 return SelectValue(ctx, in_range, value, src_thread_id); 160 return SelectValue(ctx, in_range, value, src_thread_id);
166} 161}
167 162
168Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 163Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
169 Id segmentation_mask) { 164 Id segmentation_mask) {
170 const Id thread_id{GetThreadId(ctx)}; 165 const Id thread_id{EmitLaneId(ctx)};
171 if (ctx.profile.warp_size_potentially_larger_than_guest) {
172 clamp = GetUpperClamp(ctx, thread_id, clamp);
173 }
174 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 166 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
175 const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; 167 Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
176 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 168 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
177 169
170 if (ctx.profile.warp_size_potentially_larger_than_guest) {
171 src_thread_id = AddPartitionBase(ctx, src_thread_id);
172 }
173
178 SetInBoundsFlag(inst, in_range); 174 SetInBoundsFlag(inst, in_range);
179 return SelectValue(ctx, in_range, value, src_thread_id); 175 return SelectValue(ctx, in_range, value, src_thread_id);
180} 176}
181 177
182Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 178Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
183 Id segmentation_mask) { 179 Id segmentation_mask) {
184 const Id thread_id{GetThreadId(ctx)}; 180 const Id thread_id{EmitLaneId(ctx)};
185 if (ctx.profile.warp_size_potentially_larger_than_guest) {
186 clamp = GetUpperClamp(ctx, thread_id, clamp);
187 }
188 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 181 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
189 const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; 182 Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
190 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 183 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
191 184
185 if (ctx.profile.warp_size_potentially_larger_than_guest) {
186 src_thread_id = AddPartitionBase(ctx, src_thread_id);
187 }
188
192 SetInBoundsFlag(inst, in_range); 189 SetInBoundsFlag(inst, in_range);
193 return SelectValue(ctx, in_range, value, src_thread_id); 190 return SelectValue(ctx, in_range, value, src_thread_id);
194} 191}
195 192
196Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 193Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
197 Id segmentation_mask) { 194 Id segmentation_mask) {
198 const Id thread_id{GetThreadId(ctx)}; 195 const Id thread_id{EmitLaneId(ctx)};
199 if (ctx.profile.warp_size_potentially_larger_than_guest) {
200 clamp = GetUpperClamp(ctx, thread_id, clamp);
201 }
202 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 196 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
203 const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; 197 Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
204 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 198 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
205 199
200 if (ctx.profile.warp_size_potentially_larger_than_guest) {
201 src_thread_id = AddPartitionBase(ctx, src_thread_id);
202 }
203
206 SetInBoundsFlag(inst, in_range); 204 SetInBoundsFlag(inst, in_range);
207 return SelectValue(ctx, in_range, value, src_thread_id); 205 return SelectValue(ctx, in_range, value, src_thread_id);
208} 206}
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 41dc6d031..3b97721e1 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
35 const spv::ImageFormat format{spv::ImageFormat::Unknown}; 35 const spv::ImageFormat format{spv::ImageFormat::Unknown};
36 const Id type{ctx.F32[1]}; 36 const Id type{ctx.F32[1]};
37 const bool depth{desc.is_depth}; 37 const bool depth{desc.is_depth};
38 const bool ms{desc.is_multisample};
38 switch (desc.type) { 39 switch (desc.type) {
39 case TextureType::Color1D: 40 case TextureType::Color1D:
40 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); 41 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
42 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); 43 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
43 case TextureType::Color2D: 44 case TextureType::Color2D:
44 case TextureType::Color2DRect: 45 case TextureType::Color2DRect:
45 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); 46 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
46 case TextureType::ColorArray2D: 47 case TextureType::ColorArray2D:
47 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); 48 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
48 case TextureType::Color3D: 49 case TextureType::Color3D:
49 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); 50 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
50 case TextureType::ColorCube: 51 case TextureType::ColorCube:
@@ -544,7 +545,7 @@ void EmitContext::DefineCommonTypes(const Info& info) {
544 U16 = Name(TypeInt(16, false), "u16"); 545 U16 = Name(TypeInt(16, false), "u16");
545 S16 = Name(TypeInt(16, true), "s16"); 546 S16 = Name(TypeInt(16, true), "s16");
546 } 547 }
547 if (info.uses_int64) { 548 if (info.uses_int64 && profile.support_int64) {
548 AddCapability(spv::Capability::Int64); 549 AddCapability(spv::Capability::Int64);
549 U64 = Name(TypeInt(64, false), "u64"); 550 U64 = Name(TypeInt(64, false), "u64");
550 } 551 }
@@ -721,9 +722,21 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
721 size_t label_index{0}; 722 size_t label_index{0};
722 if (info.loads.AnyComponent(IR::Attribute::PositionX)) { 723 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
723 AddLabel(labels[label_index]); 724 AddLabel(labels[label_index]);
724 const Id pointer{is_array 725 const Id pointer{[&]() {
725 ? OpAccessChain(input_f32, input_position, vertex, masked_index) 726 if (need_input_position_indirect) {
726 : OpAccessChain(input_f32, input_position, masked_index)}; 727 if (is_array)
728 return OpAccessChain(input_f32, input_position, vertex, u32_zero_value,
729 masked_index);
730 else
731 return OpAccessChain(input_f32, input_position, u32_zero_value,
732 masked_index);
733 } else {
734 if (is_array)
735 return OpAccessChain(input_f32, input_position, vertex, masked_index);
736 else
737 return OpAccessChain(input_f32, input_position, masked_index);
738 }
739 }()};
727 const Id result{OpLoad(F32[1], pointer)}; 740 const Id result{OpLoad(F32[1], pointer)};
728 OpReturnValue(result); 741 OpReturnValue(result);
729 ++label_index; 742 ++label_index;
@@ -1367,30 +1380,56 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1367 Decorate(layer, spv::Decoration::Flat); 1380 Decorate(layer, spv::Decoration::Flat);
1368 } 1381 }
1369 if (loads.AnyComponent(IR::Attribute::PositionX)) { 1382 if (loads.AnyComponent(IR::Attribute::PositionX)) {
1370 const bool is_fragment{stage != Stage::Fragment}; 1383 const bool is_fragment{stage == Stage::Fragment};
1371 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 1384 if (!is_fragment && profile.has_broken_spirv_position_input) {
1372 input_position = DefineInput(*this, F32[4], true, built_in); 1385 need_input_position_indirect = true;
1373 if (profile.support_geometry_shader_passthrough) { 1386
1374 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) { 1387 const Id input_position_struct = TypeStruct(F32[4]);
1375 Decorate(input_position, spv::Decoration::PassthroughNV); 1388 input_position = DefineInput(*this, input_position_struct, true);
1389
1390 MemberDecorate(input_position_struct, 0, spv::Decoration::BuiltIn,
1391 static_cast<unsigned>(spv::BuiltIn::Position));
1392 Decorate(input_position_struct, spv::Decoration::Block);
1393 } else {
1394 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::FragCoord
1395 : spv::BuiltIn::Position};
1396 input_position = DefineInput(*this, F32[4], true, built_in);
1397
1398 if (profile.support_geometry_shader_passthrough) {
1399 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
1400 Decorate(input_position, spv::Decoration::PassthroughNV);
1401 }
1376 } 1402 }
1377 } 1403 }
1378 } 1404 }
1379 if (loads[IR::Attribute::InstanceId]) { 1405 if (loads[IR::Attribute::InstanceId]) {
1380 if (profile.support_vertex_instance_id) { 1406 if (profile.support_vertex_instance_id) {
1381 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); 1407 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
1408 if (loads[IR::Attribute::BaseInstance]) {
1409 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1410 }
1382 } else { 1411 } else {
1383 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex); 1412 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
1384 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance); 1413 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1385 } 1414 }
1415 } else if (loads[IR::Attribute::BaseInstance]) {
1416 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1386 } 1417 }
1387 if (loads[IR::Attribute::VertexId]) { 1418 if (loads[IR::Attribute::VertexId]) {
1388 if (profile.support_vertex_instance_id) { 1419 if (profile.support_vertex_instance_id) {
1389 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId); 1420 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
1421 if (loads[IR::Attribute::BaseVertex]) {
1422 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1423 }
1390 } else { 1424 } else {
1391 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex); 1425 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
1392 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); 1426 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1393 } 1427 }
1428 } else if (loads[IR::Attribute::BaseVertex]) {
1429 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1430 }
1431 if (loads[IR::Attribute::DrawID]) {
1432 draw_index = DefineInput(*this, U32[1], true, spv::BuiltIn::DrawIndex);
1394 } 1433 }
1395 if (loads[IR::Attribute::FrontFace]) { 1434 if (loads[IR::Attribute::FrontFace]) {
1396 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing); 1435 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index dde45b4bc..dbc5c55b9 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -218,6 +218,7 @@ public:
218 Id base_instance{}; 218 Id base_instance{};
219 Id vertex_id{}; 219 Id vertex_id{};
220 Id vertex_index{}; 220 Id vertex_index{};
221 Id draw_index{};
221 Id base_vertex{}; 222 Id base_vertex{};
222 Id front_face{}; 223 Id front_face{};
223 Id point_coord{}; 224 Id point_coord{};
@@ -279,6 +280,7 @@ public:
279 Id write_global_func_u32x2{}; 280 Id write_global_func_u32x2{};
280 Id write_global_func_u32x4{}; 281 Id write_global_func_u32x4{};
281 282
283 bool need_input_position_indirect{};
282 Id input_position{}; 284 Id input_position{};
283 std::array<Id, 32> input_generics{}; 285 std::array<Id, 32> input_generics{};
284 286
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 402f2664f..26e8307c1 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -34,6 +34,11 @@ public:
34 34
35 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0; 35 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
36 36
37 [[nodiscard]] virtual bool HasHLEMacroState() const = 0;
38
39 [[nodiscard]] virtual std::optional<ReplaceConstant> GetReplaceConstBuffer(u32 bank,
40 u32 offset) = 0;
41
37 virtual void Dump(u64 hash) = 0; 42 virtual void Dump(u64 hash) = 0;
38 43
39 [[nodiscard]] const ProgramHeader& SPH() const noexcept { 44 [[nodiscard]] const ProgramHeader& SPH() const noexcept {
@@ -52,11 +57,16 @@ public:
52 return start_address; 57 return start_address;
53 } 58 }
54 59
60 [[nodiscard]] bool IsPropietaryDriver() const noexcept {
61 return is_propietary_driver;
62 }
63
55protected: 64protected:
56 ProgramHeader sph{}; 65 ProgramHeader sph{};
57 std::array<u32, 8> gp_passthrough_mask{}; 66 std::array<u32, 8> gp_passthrough_mask{};
58 Stage stage{}; 67 Stage stage{};
59 u32 start_address{}; 68 u32 start_address{};
69 bool is_propietary_driver{};
60}; 70};
61 71
62} // namespace Shader 72} // namespace Shader
diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp
index 7d3d882e4..1bf9db935 100644
--- a/src/shader_recompiler/frontend/ir/attribute.cpp
+++ b/src/shader_recompiler/frontend/ir/attribute.cpp
@@ -446,6 +446,12 @@ std::string NameOf(Attribute attribute) {
446 return "ViewportMask"; 446 return "ViewportMask";
447 case Attribute::FrontFace: 447 case Attribute::FrontFace:
448 return "FrontFace"; 448 return "FrontFace";
449 case Attribute::BaseInstance:
450 return "BaseInstance";
451 case Attribute::BaseVertex:
452 return "BaseVertex";
453 case Attribute::DrawID:
454 return "DrawID";
449 } 455 }
450 return fmt::format("<reserved attribute {}>", static_cast<int>(attribute)); 456 return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
451} 457}
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h
index 6ee3947b1..5f039b6f6 100644
--- a/src/shader_recompiler/frontend/ir/attribute.h
+++ b/src/shader_recompiler/frontend/ir/attribute.h
@@ -219,6 +219,11 @@ enum class Attribute : u64 {
219 FixedFncTexture9Q = 231, 219 FixedFncTexture9Q = 231,
220 ViewportMask = 232, 220 ViewportMask = 232,
221 FrontFace = 255, 221 FrontFace = 255,
222
223 // Implementation attributes
224 BaseInstance = 256,
225 BaseVertex = 257,
226 DrawID = 258,
222}; 227};
223 228
224constexpr size_t NUM_GENERICS = 32; 229constexpr size_t NUM_GENERICS = 32;
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 0cdac0eff..b7caa4246 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -294,6 +294,14 @@ F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) {
294 return Inst<F32>(Opcode::GetAttribute, attribute, vertex); 294 return Inst<F32>(Opcode::GetAttribute, attribute, vertex);
295} 295}
296 296
297U32 IREmitter::GetAttributeU32(IR::Attribute attribute) {
298 return GetAttributeU32(attribute, Imm32(0));
299}
300
301U32 IREmitter::GetAttributeU32(IR::Attribute attribute, const U32& vertex) {
302 return Inst<U32>(Opcode::GetAttributeU32, attribute, vertex);
303}
304
297void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { 305void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) {
298 Inst(Opcode::SetAttribute, attribute, value, vertex); 306 Inst(Opcode::SetAttribute, attribute, value, vertex);
299} 307}
@@ -696,11 +704,6 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) {
696} 704}
697 705
698template <> 706template <>
699IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
700 return Inst<IR::S32>(Opcode::BitCastS32F32, value);
701}
702
703template <>
704IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { 707IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
705 return Inst<IR::F32>(Opcode::BitCastF32U32, value); 708 return Inst<IR::F32>(Opcode::BitCastF32U32, value);
706} 709}
@@ -1843,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu
1843 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); 1846 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
1844} 1847}
1845 1848
1846Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { 1849Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1850 const IR::U1& skip_mips) {
1847 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions 1851 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions
1848 : Opcode::BindlessImageQueryDimensions}; 1852 : Opcode::BindlessImageQueryDimensions};
1849 return Inst(op, handle, lod); 1853 return Inst(op, handle, lod, skip_mips);
1850} 1854}
1851 1855
1852Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, 1856Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1853 TextureInstInfo info) { 1857 const IR::U1& skip_mips, TextureInstInfo info) {
1854 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); 1858 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
1855} 1859}
1856 1860
1857Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { 1861Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 2df992feb..f3c81dbe1 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -74,6 +74,8 @@ public:
74 74
75 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute); 75 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
76 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex); 76 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
77 [[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute);
78 [[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute, const U32& vertex);
77 void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex); 79 void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
78 80
79 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); 81 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
@@ -318,9 +320,10 @@ public:
318 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, 320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
319 const F32& dref, const F32& lod, 321 const F32& dref, const F32& lod,
320 const Value& offset, TextureInstInfo info); 322 const Value& offset, TextureInstInfo info);
321 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
322 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, 323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
323 TextureInstInfo info); 324 const IR::U1& skip_mips);
325 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
326 const IR::U1& skip_mips, TextureInstInfo info);
324 327
325 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, 328 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
326 TextureInstInfo info); 329 TextureInstInfo info);
@@ -406,7 +409,8 @@ private:
406 } 409 }
407 410
408 template <typename T> 411 template <typename T>
409 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags { 412 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>)
413 struct Flags {
410 Flags() = default; 414 Flags() = default;
411 Flags(T proxy_) : proxy{proxy_} {} 415 Flags(T proxy_) : proxy{proxy_} {}
412 416
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index d155afd0f..e300714f3 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -38,7 +38,6 @@ constexpr Type U8{Type::U8};
38constexpr Type U16{Type::U16}; 38constexpr Type U16{Type::U16};
39constexpr Type U32{Type::U32}; 39constexpr Type U32{Type::U32};
40constexpr Type U64{Type::U64}; 40constexpr Type U64{Type::U64};
41constexpr Type S32{Type::S32};
42constexpr Type F16{Type::F16}; 41constexpr Type F16{Type::F16};
43constexpr Type F32{Type::F32}; 42constexpr Type F32{Type::F32};
44constexpr Type F64{Type::F64}; 43constexpr Type F64{Type::F64};
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 1fe3749cc..4447d67b0 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -175,7 +175,6 @@ OPCODE(SelectF64, F64, U1,
175OPCODE(BitCastU16F16, U16, F16, ) 175OPCODE(BitCastU16F16, U16, F16, )
176OPCODE(BitCastU32F32, U32, F32, ) 176OPCODE(BitCastU32F32, U32, F32, )
177OPCODE(BitCastU64F64, U64, F64, ) 177OPCODE(BitCastU64F64, U64, F64, )
178OPCODE(BitCastS32F32, S32, F32, )
179OPCODE(BitCastF16U16, F16, U16, ) 178OPCODE(BitCastF16U16, F16, U16, )
180OPCODE(BitCastF32U32, F32, U32, ) 179OPCODE(BitCastF32U32, F32, U32, )
181OPCODE(BitCastF64U64, F64, U64, ) 180OPCODE(BitCastF64U64, F64, U64, )
@@ -483,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32,
483OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 482OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
484OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 483OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
485OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 484OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
486OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) 485OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, )
487OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) 486OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
488OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 487OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
489OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) 488OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
@@ -496,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32,
496OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 495OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
497OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 496OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
498OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 497OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
499OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) 498OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, )
500OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) 499OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
501OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 500OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
502OPCODE(BoundImageRead, U32x4, U32, Opaque, ) 501OPCODE(BoundImageRead, U32x4, U32, Opaque, )
@@ -509,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq
509OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) 508OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
510OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) 509OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
511OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) 510OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
512OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) 511OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
513OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) 512OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
514OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) 513OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
515OPCODE(ImageRead, U32x4, Opaque, Opaque, ) 514OPCODE(ImageRead, U32x4, Opaque, Opaque, )
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
index 5a7c706ad..04c8c4ddb 100644
--- a/src/shader_recompiler/frontend/ir/type.h
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -24,22 +24,21 @@ enum class Type {
24 U16 = 1 << 7, 24 U16 = 1 << 7,
25 U32 = 1 << 8, 25 U32 = 1 << 8,
26 U64 = 1 << 9, 26 U64 = 1 << 9,
27 S32 = 1 << 10, 27 F16 = 1 << 10,
28 F16 = 1 << 11, 28 F32 = 1 << 11,
29 F32 = 1 << 12, 29 F64 = 1 << 12,
30 F64 = 1 << 13, 30 U32x2 = 1 << 13,
31 U32x2 = 1 << 14, 31 U32x3 = 1 << 14,
32 U32x3 = 1 << 15, 32 U32x4 = 1 << 15,
33 U32x4 = 1 << 16, 33 F16x2 = 1 << 16,
34 F16x2 = 1 << 17, 34 F16x3 = 1 << 17,
35 F16x3 = 1 << 18, 35 F16x4 = 1 << 18,
36 F16x4 = 1 << 19, 36 F32x2 = 1 << 19,
37 F32x2 = 1 << 20, 37 F32x3 = 1 << 20,
38 F32x3 = 1 << 21, 38 F32x4 = 1 << 21,
39 F32x4 = 1 << 22, 39 F64x2 = 1 << 22,
40 F64x2 = 1 << 23, 40 F64x3 = 1 << 23,
41 F64x3 = 1 << 24, 41 F64x4 = 1 << 24,
42 F64x4 = 1 << 25,
43}; 42};
44DECLARE_ENUM_FLAG_OPERATORS(Type) 43DECLARE_ENUM_FLAG_OPERATORS(Type)
45 44
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 30ba12316..346169328 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -23,8 +23,6 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
23 23
24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} 24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
25 25
26Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
27
28Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} 26Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
29 27
30Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} 28Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -71,7 +69,6 @@ bool Value::operator==(const Value& other) const {
71 return imm_u16 == other.imm_u16; 69 return imm_u16 == other.imm_u16;
72 case Type::U32: 70 case Type::U32:
73 case Type::F32: 71 case Type::F32:
74 case Type::S32:
75 return imm_u32 == other.imm_u32; 72 return imm_u32 == other.imm_u32;
76 case Type::U64: 73 case Type::U64:
77 case Type::F64: 74 case Type::F64:
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 8b34356fd..22e89dd1b 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -101,9 +101,8 @@ public:
101 TypedValue() = default; 101 TypedValue() = default;
102 102
103 template <IR::Type other_type> 103 template <IR::Type other_type>
104 requires((other_type & type_) != IR::Type::Void) explicit(false) 104 requires((other_type & type_) != IR::Type::Void)
105 TypedValue(const TypedValue<other_type>& value) 105 explicit(false) TypedValue(const TypedValue<other_type>& value) : Value(value) {}
106 : Value(value) {}
107 106
108 explicit TypedValue(const Value& value) : Value(value) { 107 explicit TypedValue(const Value& value) : Value(value) {
109 if ((value.Type() & type_) == IR::Type::Void) { 108 if ((value.Type() & type_) == IR::Type::Void) {
@@ -194,16 +193,16 @@ public:
194 void ReplaceOpcode(IR::Opcode opcode); 193 void ReplaceOpcode(IR::Opcode opcode);
195 194
196 template <typename FlagsType> 195 template <typename FlagsType>
197 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) 196 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
198 [[nodiscard]] FlagsType Flags() const noexcept { 197 [[nodiscard]] FlagsType Flags() const noexcept {
199 FlagsType ret; 198 FlagsType ret;
200 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret)); 199 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret));
201 return ret; 200 return ret;
202 } 201 }
203 202
204 template <typename FlagsType> 203 template <typename FlagsType>
205 requires(sizeof(FlagsType) <= sizeof(u32) && 204 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
206 std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept { 205 void SetFlags(FlagsType value) noexcept {
207 std::memcpy(&flags, &value, sizeof(value)); 206 std::memcpy(&flags, &value, sizeof(value));
208 } 207 }
209 208
@@ -268,7 +267,6 @@ using U8 = TypedValue<Type::U8>;
268using U16 = TypedValue<Type::U16>; 267using U16 = TypedValue<Type::U16>;
269using U32 = TypedValue<Type::U32>; 268using U32 = TypedValue<Type::U32>;
270using U64 = TypedValue<Type::U64>; 269using U64 = TypedValue<Type::U64>;
271using S32 = TypedValue<Type::S32>;
272using F16 = TypedValue<Type::F16>; 270using F16 = TypedValue<Type::F16>;
273using F32 = TypedValue<Type::F32>; 271using F32 = TypedValue<Type::F32>;
274using F64 = TypedValue<Type::F64>; 272using F64 = TypedValue<Type::F64>;
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
index f8cfd4ab6..39af62559 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
@@ -15,11 +15,13 @@ enum class Mode : u64 {
15 SamplePos = 5, 15 SamplePos = 5,
16}; 16};
17 17
18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) { 18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) {
19 switch (mode) { 19 switch (mode) {
20 case Mode::Dimension: { 20 case Mode::Dimension: {
21 const bool needs_num_mips{((mask >> 3) & 1) != 0};
22 const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
21 const IR::U32 lod{v.X(src_reg)}; 23 const IR::U32 lod{v.X(src_reg)};
22 return v.ir.ImageQueryDimension(handle, lod); 24 return v.ir.ImageQueryDimension(handle, lod, skip_mips);
23 } 25 }
24 case Mode::TextureType: 26 case Mode::TextureType:
25 case Mode::SamplePos: 27 case Mode::SamplePos:
@@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
46 handle = v.X(src_reg); 48 handle = v.X(src_reg);
47 ++src_reg; 49 ++src_reg;
48 } 50 }
49 const IR::Value query{Query(v, handle, txq.mode, src_reg)}; 51 const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)};
50 IR::Reg dest_reg{txq.dest_reg}; 52 IR::Reg dest_reg{txq.dest_reg};
51 for (int element = 0; element < 4; ++element) { 53 for (int element = 0; element < 4; ++element) {
52 if (((txq.mask >> element) & 1) == 0) { 54 if (((txq.mask >> element) & 1) == 0) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 3adbd2b16..a42453e90 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -171,6 +171,70 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
171 } 171 }
172 return mapping; 172 return mapping;
173} 173}
174
175void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
176 const Shader::VaryingState& passthrough_mask,
177 bool passthrough_position,
178 std::optional<IR::Attribute> passthrough_layer_attr) {
179 for (u32 i = 0; i < program.output_vertices; i++) {
180 // Assign generics from input
181 for (u32 j = 0; j < 32; j++) {
182 if (!passthrough_mask.Generic(j)) {
183 continue;
184 }
185
186 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
187 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
188 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
189 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
190 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
191 }
192
193 if (passthrough_position) {
194 // Assign position from input
195 const IR::Attribute attr = IR::Attribute::PositionX;
196 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
197 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
198 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
199 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
200 }
201
202 if (passthrough_layer_attr) {
203 // Assign layer
204 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(*passthrough_layer_attr),
205 ir.Imm32(0));
206 }
207
208 // Emit vertex
209 ir.EmitVertex(ir.Imm32(0));
210 }
211 ir.EndPrimitive(ir.Imm32(0));
212}
213
214u32 GetOutputTopologyVertices(OutputTopology output_topology) {
215 switch (output_topology) {
216 case OutputTopology::PointList:
217 return 1;
218 case OutputTopology::LineStrip:
219 return 2;
220 default:
221 return 3;
222 }
223}
224
225void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
226 for (IR::Block* const block : program.blocks) {
227 for (IR::Inst& inst : block->Instructions()) {
228 if (inst.GetOpcode() == IR::Opcode::Epilogue) {
229 IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
230 EmitGeometryPassthrough(
231 ir, program, program.info.passthrough,
232 program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
233 }
234 }
235 }
236}
237
174} // Anonymous namespace 238} // Anonymous namespace
175 239
176IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 240IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
@@ -195,9 +259,14 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
195 program.is_geometry_passthrough = sph.common0.geometry_passthrough != 0; 259 program.is_geometry_passthrough = sph.common0.geometry_passthrough != 0;
196 if (program.is_geometry_passthrough) { 260 if (program.is_geometry_passthrough) {
197 const auto& mask{env.GpPassthroughMask()}; 261 const auto& mask{env.GpPassthroughMask()};
198 for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) { 262 for (size_t i = 0; i < mask.size() * 32; ++i) {
199 program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0; 263 program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
200 } 264 }
265
266 if (!host_info.support_geometry_shader_passthrough) {
267 program.output_vertices = GetOutputTopologyVertices(program.output_topology);
268 LowerGeometryPassthrough(program, host_info);
269 }
201 } 270 }
202 break; 271 break;
203 } 272 }
@@ -219,11 +288,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
219 } 288 }
220 Optimization::SsaRewritePass(program); 289 Optimization::SsaRewritePass(program);
221 290
222 Optimization::ConstantPropagationPass(program); 291 Optimization::ConstantPropagationPass(env, program);
223 292
224 Optimization::PositionPass(env, program); 293 Optimization::PositionPass(env, program);
225 294
226 Optimization::GlobalMemoryToStorageBufferPass(program); 295 Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
227 Optimization::TexturePass(env, program, host_info); 296 Optimization::TexturePass(env, program, host_info);
228 297
229 if (Settings::values.resolution_info.active) { 298 if (Settings::values.resolution_info.active) {
@@ -342,17 +411,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
342 IR::Program program; 411 IR::Program program;
343 program.stage = Stage::Geometry; 412 program.stage = Stage::Geometry;
344 program.output_topology = output_topology; 413 program.output_topology = output_topology;
345 switch (output_topology) { 414 program.output_vertices = GetOutputTopologyVertices(output_topology);
346 case OutputTopology::PointList:
347 program.output_vertices = 1;
348 break;
349 case OutputTopology::LineStrip:
350 program.output_vertices = 2;
351 break;
352 default:
353 program.output_vertices = 3;
354 break;
355 }
356 415
357 program.is_geometry_passthrough = false; 416 program.is_geometry_passthrough = false;
358 program.info.loads.mask = source_program.info.stores.mask; 417 program.info.loads.mask = source_program.info.stores.mask;
@@ -366,35 +425,8 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
366 node.data.block = current_block; 425 node.data.block = current_block;
367 426
368 IR::IREmitter ir{*current_block}; 427 IR::IREmitter ir{*current_block};
369 for (u32 i = 0; i < program.output_vertices; i++) { 428 EmitGeometryPassthrough(ir, program, program.info.stores, true,
370 // Assign generics from input 429 source_program.info.emulated_layer);
371 for (u32 j = 0; j < 32; j++) {
372 if (!program.info.stores.Generic(j)) {
373 continue;
374 }
375
376 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
377 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
378 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
379 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
380 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
381 }
382
383 // Assign position from input
384 const IR::Attribute attr = IR::Attribute::PositionX;
385 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
386 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
387 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
388 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
389
390 // Assign layer
391 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
392 ir.Imm32(0));
393
394 // Emit vertex
395 ir.EmitVertex(ir.Imm32(0));
396 }
397 ir.EndPrimitive(ir.Imm32(0));
398 430
399 IR::Block* return_block{block_pool.Create(inst_pool)}; 431 IR::Block* return_block{block_pool.Create(inst_pool)};
400 IR::IREmitter{*return_block}.Epilogue(); 432 IR::IREmitter{*return_block}.Epilogue();
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index d5d279554..55fc48768 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -15,6 +15,9 @@ struct HostTranslateInfo {
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS 17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
18 u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
19 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
20 ///< passthrough shaders
18}; 21};
19 22
20} // namespace Shader 23} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index 826f9a54a..4d81e9336 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -7,6 +7,7 @@
7#include <type_traits> 7#include <type_traits>
8 8
9#include "common/bit_cast.h" 9#include "common/bit_cast.h"
10#include "shader_recompiler/environment.h"
10#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
@@ -515,6 +516,9 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
515 case IR::Attribute::PrimitiveId: 516 case IR::Attribute::PrimitiveId:
516 case IR::Attribute::InstanceId: 517 case IR::Attribute::InstanceId:
517 case IR::Attribute::VertexId: 518 case IR::Attribute::VertexId:
519 case IR::Attribute::BaseVertex:
520 case IR::Attribute::BaseInstance:
521 case IR::Attribute::DrawID:
518 break; 522 break;
519 default: 523 default:
520 return; 524 return;
@@ -644,7 +648,63 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
644 } 648 }
645} 649}
646 650
647void ConstantPropagation(IR::Block& block, IR::Inst& inst) { 651void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
652 const IR::Value bank{inst.Arg(0)};
653 const IR::Value offset{inst.Arg(1)};
654 if (!bank.IsImmediate() || !offset.IsImmediate()) {
655 return;
656 }
657 const auto bank_value = bank.U32();
658 const auto offset_value = offset.U32();
659 auto replacement = env.GetReplaceConstBuffer(bank_value, offset_value);
660 if (!replacement) {
661 return;
662 }
663 const auto new_attribute = [replacement]() {
664 switch (*replacement) {
665 case ReplaceConstant::BaseInstance:
666 return IR::Attribute::BaseInstance;
667 case ReplaceConstant::BaseVertex:
668 return IR::Attribute::BaseVertex;
669 case ReplaceConstant::DrawID:
670 return IR::Attribute::DrawID;
671 default:
672 throw NotImplementedException("Not implemented replacement variable {}", *replacement);
673 }
674 }();
675 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
676 if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
677 inst.ReplaceUsesWith(ir.GetAttributeU32(new_attribute));
678 } else {
679 inst.ReplaceUsesWith(ir.GetAttribute(new_attribute));
680 }
681}
682
683void FoldDriverConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst, u32 which_bank,
684 u32 offset_start = 0, u32 offset_end = std::numeric_limits<u16>::max()) {
685 const IR::Value bank{inst.Arg(0)};
686 const IR::Value offset{inst.Arg(1)};
687 if (!bank.IsImmediate() || !offset.IsImmediate()) {
688 return;
689 }
690 const auto bank_value = bank.U32();
691 if (bank_value != which_bank) {
692 return;
693 }
694 const auto offset_value = offset.U32();
695 if (offset_value < offset_start || offset_value >= offset_end) {
696 return;
697 }
698 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
699 if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
700 inst.ReplaceUsesWith(IR::Value{env.ReadCbufValue(bank_value, offset_value)});
701 } else {
702 inst.ReplaceUsesWith(
703 IR::Value{Common::BitCast<f32>(env.ReadCbufValue(bank_value, offset_value))});
704 }
705}
706
707void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
648 switch (inst.GetOpcode()) { 708 switch (inst.GetOpcode()) {
649 case IR::Opcode::GetRegister: 709 case IR::Opcode::GetRegister:
650 return FoldGetRegister(inst); 710 return FoldGetRegister(inst);
@@ -789,18 +849,28 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
789 IR::Opcode::CompositeInsertF16x4); 849 IR::Opcode::CompositeInsertF16x4);
790 case IR::Opcode::FSwizzleAdd: 850 case IR::Opcode::FSwizzleAdd:
791 return FoldFSwizzleAdd(block, inst); 851 return FoldFSwizzleAdd(block, inst);
852 case IR::Opcode::GetCbufF32:
853 case IR::Opcode::GetCbufU32:
854 if (env.HasHLEMacroState()) {
855 FoldConstBuffer(env, block, inst);
856 }
857 if (env.IsPropietaryDriver()) {
858 FoldDriverConstBuffer(env, block, inst, 1);
859 }
860 break;
792 default: 861 default:
793 break; 862 break;
794 } 863 }
795} 864}
865
796} // Anonymous namespace 866} // Anonymous namespace
797 867
798void ConstantPropagationPass(IR::Program& program) { 868void ConstantPropagationPass(Environment& env, IR::Program& program) {
799 const auto end{program.post_order_blocks.rend()}; 869 const auto end{program.post_order_blocks.rend()};
800 for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) { 870 for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
801 IR::Block* const block{*it}; 871 IR::Block* const block{*it};
802 for (IR::Inst& inst : block->Instructions()) { 872 for (IR::Inst& inst : block->Instructions()) {
803 ConstantPropagation(*block, inst); 873 ConstantPropagation(env, *block, inst);
804 } 874 }
805 } 875 }
806} 876}
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index 336338e62..9101722ba 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -11,6 +11,7 @@
11#include "shader_recompiler/frontend/ir/breadth_first_search.h" 11#include "shader_recompiler/frontend/ir/breadth_first_search.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
13#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
14#include "shader_recompiler/host_translate_info.h"
14#include "shader_recompiler/ir_opt/passes.h" 15#include "shader_recompiler/ir_opt/passes.h"
15 16
16namespace Shader::Optimization { 17namespace Shader::Optimization {
@@ -402,7 +403,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info)
402} 403}
403 404
404/// Returns the offset in indices (not bytes) for an equivalent storage instruction 405/// Returns the offset in indices (not bytes) for an equivalent storage instruction
405IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { 406IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) {
406 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 407 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
407 IR::U32 offset; 408 IR::U32 offset;
408 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { 409 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) {
@@ -415,7 +416,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer
415 } 416 }
416 // Subtract the least significant 32 bits from the guest offset. The result is the storage 417 // Subtract the least significant 32 bits from the guest offset. The result is the storage
417 // buffer offset in bytes. 418 // buffer offset in bytes.
418 const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; 419 IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
420
421 // Align the offset base to match the host alignment requirements
422 low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U)));
419 return ir.ISub(offset, low_cbuf); 423 return ir.ISub(offset, low_cbuf);
420} 424}
421 425
@@ -510,7 +514,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
510} 514}
511} // Anonymous namespace 515} // Anonymous namespace
512 516
513void GlobalMemoryToStorageBufferPass(IR::Program& program) { 517void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
514 StorageInfo info; 518 StorageInfo info;
515 for (IR::Block* const block : program.post_order_blocks) { 519 for (IR::Block* const block : program.post_order_blocks) {
516 for (IR::Inst& inst : block->Instructions()) { 520 for (IR::Inst& inst : block->Instructions()) {
@@ -534,7 +538,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) {
534 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; 538 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}};
535 IR::Block* const block{storage_inst.block}; 539 IR::Block* const block{storage_inst.block};
536 IR::Inst* const inst{storage_inst.inst}; 540 IR::Inst* const inst{storage_inst.inst};
537 const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; 541 const IR::U32 offset{
542 StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
538 Replace(*block, *inst, index, offset); 543 Replace(*block, *inst, index, offset);
539 } 544 }
540} 545}
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 11bfe801a..4ffad1172 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -13,9 +13,9 @@ struct HostTranslateInfo;
13namespace Shader::Optimization { 13namespace Shader::Optimization {
14 14
15void CollectShaderInfoPass(Environment& env, IR::Program& program); 15void CollectShaderInfoPass(Environment& env, IR::Program& program);
16void ConstantPropagationPass(IR::Program& program); 16void ConstantPropagationPass(Environment& env, IR::Program& program);
17void DeadCodeEliminationPass(IR::Program& program); 17void DeadCodeEliminationPass(IR::Program& program);
18void GlobalMemoryToStorageBufferPass(IR::Program& program); 18void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
19void IdentityRemovalPass(IR::Program& program); 19void IdentityRemovalPass(IR::Program& program);
20void LowerFp16ToFp32(IR::Program& program); 20void LowerFp16ToFp32(IR::Program& program);
21void LowerInt64ToInt32(IR::Program& program); 21void LowerInt64ToInt32(IR::Program& program);
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index f5c86fcb1..d374c976a 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -355,21 +355,21 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
355 }; 355 };
356} 356}
357 357
358TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 358u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; 361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) 362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
363 << cbuf.secondary_shift_left}; 363 << cbuf.secondary_shift_left};
364 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return lhs_raw | rhs_raw;
365}
366
367TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
368 return env.ReadTextureType(GetTextureHandle(env, cbuf));
365} 369}
366 370
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { 371TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373} 373}
374 374
375class Descriptors { 375class Descriptors {
@@ -386,8 +386,10 @@ public:
386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { 386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
387 return desc.cbuf_index == existing.cbuf_index && 387 return desc.cbuf_index == existing.cbuf_index &&
388 desc.cbuf_offset == existing.cbuf_offset && 388 desc.cbuf_offset == existing.cbuf_offset &&
389 desc.shift_left == existing.shift_left &&
389 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 390 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
390 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 391 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
392 desc.secondary_shift_left == existing.secondary_shift_left &&
391 desc.count == existing.count && desc.size_shift == existing.size_shift && 393 desc.count == existing.count && desc.size_shift == existing.size_shift &&
392 desc.has_secondary == existing.has_secondary; 394 desc.has_secondary == existing.has_secondary;
393 }); 395 });
@@ -405,15 +407,20 @@ public:
405 } 407 }
406 408
407 u32 Add(const TextureDescriptor& desc) { 409 u32 Add(const TextureDescriptor& desc) {
408 return Add(texture_descriptors, desc, [&desc](const auto& existing) { 410 const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
409 return desc.type == existing.type && desc.is_depth == existing.is_depth && 411 return desc.type == existing.type && desc.is_depth == existing.is_depth &&
410 desc.has_secondary == existing.has_secondary && 412 desc.has_secondary == existing.has_secondary &&
411 desc.cbuf_index == existing.cbuf_index && 413 desc.cbuf_index == existing.cbuf_index &&
412 desc.cbuf_offset == existing.cbuf_offset && 414 desc.cbuf_offset == existing.cbuf_offset &&
415 desc.shift_left == existing.shift_left &&
413 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 416 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
414 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 417 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
418 desc.secondary_shift_left == existing.secondary_shift_left &&
415 desc.count == existing.count && desc.size_shift == existing.size_shift; 419 desc.count == existing.count && desc.size_shift == existing.size_shift;
416 }); 420 })};
421 // TODO: Read this from TIC
422 texture_descriptors[index].is_multisample |= desc.is_multisample;
423 return index;
417 } 424 }
418 425
419 u32 Add(const ImageDescriptor& desc) { 426 u32 Add(const ImageDescriptor& desc) {
@@ -452,7 +459,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
452 const IR::Value coord(inst.Arg(1)); 459 const IR::Value coord(inst.Arg(1));
453 const IR::Value handle(ir.Imm32(0)); 460 const IR::Value handle(ir.Imm32(0));
454 const IR::U32 lod{ir.Imm32(0)}; 461 const IR::U32 lod{ir.Imm32(0)};
455 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); 462 const IR::U1 skip_mips{ir.Imm1(true)};
463 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info);
456 inst.SetArg( 464 inst.SetArg(
457 1, ir.CompositeConstruct( 465 1, ir.CompositeConstruct(
458 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), 466 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)),
@@ -486,10 +494,10 @@ void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_
486 const IR::F32 w(ir.CompositeExtract(new_inst, 3)); 494 const IR::F32 w(ir.CompositeExtract(new_inst, 3));
487 const IR::F16F32F64 max_value(ir.Imm32(get_max_value())); 495 const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
488 const IR::Value converted = 496 const IR::Value converted =
489 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value), 497 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(x)), max_value),
490 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value), 498 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(y)), max_value),
491 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value), 499 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(z)), max_value),
492 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value)); 500 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(w)), max_value));
493 inst.ReplaceUsesWith(converted); 501 inst.ReplaceUsesWith(converted);
494} 502}
495} // Anonymous namespace 503} // Anonymous namespace
@@ -524,6 +532,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
524 532
525 const auto& cbuf{texture_inst.cbuf}; 533 const auto& cbuf{texture_inst.cbuf};
526 auto flags{inst->Flags<IR::TextureInstInfo>()}; 534 auto flags{inst->Flags<IR::TextureInstInfo>()};
535 bool is_multisample{false};
527 switch (inst->GetOpcode()) { 536 switch (inst->GetOpcode()) {
528 case IR::Opcode::ImageQueryDimensions: 537 case IR::Opcode::ImageQueryDimensions:
529 flags.type.Assign(ReadTextureType(env, cbuf)); 538 flags.type.Assign(ReadTextureType(env, cbuf));
@@ -538,6 +547,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
538 } 547 }
539 break; 548 break;
540 case IR::Opcode::ImageFetch: 549 case IR::Opcode::ImageFetch:
550 if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
551 flags.type == TextureType::ColorArray2D) {
552 is_multisample = !inst->Arg(4).IsEmpty();
553 } else {
554 inst->SetArg(4, IR::U32{});
555 }
541 if (flags.type != TextureType::Color1D) { 556 if (flags.type != TextureType::Color1D) {
542 break; 557 break;
543 } 558 }
@@ -613,6 +628,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
613 index = descriptors.Add(TextureDescriptor{ 628 index = descriptors.Add(TextureDescriptor{
614 .type = flags.type, 629 .type = flags.type,
615 .is_depth = flags.is_depth != 0, 630 .is_depth = flags.is_depth != 0,
631 .is_multisample = is_multisample,
616 .has_secondary = cbuf.has_secondary, 632 .has_secondary = cbuf.has_secondary,
617 .cbuf_index = cbuf.index, 633 .cbuf_index = cbuf.index,
618 .cbuf_offset = cbuf.offset, 634 .cbuf_offset = cbuf.offset,
diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h
index 2b42c4ba2..5d648b159 100644
--- a/src/shader_recompiler/object_pool.h
+++ b/src/shader_recompiler/object_pool.h
@@ -10,7 +10,7 @@
10namespace Shader { 10namespace Shader {
11 11
12template <typename T> 12template <typename T>
13requires std::is_destructible_v<T> 13 requires std::is_destructible_v<T>
14class ObjectPool { 14class ObjectPool {
15public: 15public:
16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} { 16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
@@ -18,7 +18,7 @@ public:
18 } 18 }
19 19
20 template <typename... Args> 20 template <typename... Args>
21 requires std::is_constructible_v<T, Args...> 21 requires std::is_constructible_v<T, Args...>
22 [[nodiscard]] T* Create(Args&&... args) { 22 [[nodiscard]] T* Create(Args&&... args) {
23 return std::construct_at(Memory(), std::forward<Args>(args)...); 23 return std::construct_at(Memory(), std::forward<Args>(args)...);
24 } 24 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index b8841a536..253e0d0bd 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -55,6 +55,8 @@ struct Profile {
55 55
56 /// OpFClamp is broken and OpFMax + OpFMin should be used instead 56 /// OpFClamp is broken and OpFMax + OpFMin should be used instead
57 bool has_broken_spirv_clamp{}; 57 bool has_broken_spirv_clamp{};
58 /// The Position builtin needs to be wrapped in a struct when used as an input
59 bool has_broken_spirv_position_input{};
58 /// Offset image operands with an unsigned type do not work 60 /// Offset image operands with an unsigned type do not work
59 bool has_broken_unsigned_image_offsets{}; 61 bool has_broken_unsigned_image_offsets{};
60 /// Signed instructions with unsigned data types are misinterpreted 62 /// Signed instructions with unsigned data types are misinterpreted
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index d9c6e92db..d308db942 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -16,6 +16,12 @@
16 16
17namespace Shader { 17namespace Shader {
18 18
19enum class ReplaceConstant : u32 {
20 BaseInstance,
21 BaseVertex,
22 DrawID,
23};
24
19enum class TextureType : u32 { 25enum class TextureType : u32 {
20 Color1D, 26 Color1D,
21 ColorArray1D, 27 ColorArray1D,
@@ -59,6 +65,8 @@ enum class Interpolation {
59struct ConstantBufferDescriptor { 65struct ConstantBufferDescriptor {
60 u32 index; 66 u32 index;
61 u32 count; 67 u32 count;
68
69 auto operator<=>(const ConstantBufferDescriptor&) const = default;
62}; 70};
63 71
64struct StorageBufferDescriptor { 72struct StorageBufferDescriptor {
@@ -66,6 +74,8 @@ struct StorageBufferDescriptor {
66 u32 cbuf_offset; 74 u32 cbuf_offset;
67 u32 count; 75 u32 count;
68 bool is_written; 76 bool is_written;
77
78 auto operator<=>(const StorageBufferDescriptor&) const = default;
69}; 79};
70 80
71struct TextureBufferDescriptor { 81struct TextureBufferDescriptor {
@@ -78,6 +88,8 @@ struct TextureBufferDescriptor {
78 u32 secondary_shift_left; 88 u32 secondary_shift_left;
79 u32 count; 89 u32 count;
80 u32 size_shift; 90 u32 size_shift;
91
92 auto operator<=>(const TextureBufferDescriptor&) const = default;
81}; 93};
82using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; 94using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>;
83 95
@@ -89,12 +101,15 @@ struct ImageBufferDescriptor {
89 u32 cbuf_offset; 101 u32 cbuf_offset;
90 u32 count; 102 u32 count;
91 u32 size_shift; 103 u32 size_shift;
104
105 auto operator<=>(const ImageBufferDescriptor&) const = default;
92}; 106};
93using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>; 107using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>;
94 108
95struct TextureDescriptor { 109struct TextureDescriptor {
96 TextureType type; 110 TextureType type;
97 bool is_depth; 111 bool is_depth;
112 bool is_multisample;
98 bool has_secondary; 113 bool has_secondary;
99 u32 cbuf_index; 114 u32 cbuf_index;
100 u32 cbuf_offset; 115 u32 cbuf_offset;
@@ -104,6 +119,8 @@ struct TextureDescriptor {
104 u32 secondary_shift_left; 119 u32 secondary_shift_left;
105 u32 count; 120 u32 count;
106 u32 size_shift; 121 u32 size_shift;
122
123 auto operator<=>(const TextureDescriptor&) const = default;
107}; 124};
108using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; 125using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>;
109 126
@@ -116,6 +133,8 @@ struct ImageDescriptor {
116 u32 cbuf_offset; 133 u32 cbuf_offset;
117 u32 count; 134 u32 count;
118 u32 size_shift; 135 u32 size_shift;
136
137 auto operator<=>(const ImageDescriptor&) const = default;
119}; 138};
120using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 139using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
121 140
diff --git a/src/shader_recompiler/varying_state.h b/src/shader_recompiler/varying_state.h
index 7b28a285f..18a9aaf50 100644
--- a/src/shader_recompiler/varying_state.h
+++ b/src/shader_recompiler/varying_state.h
@@ -11,7 +11,7 @@
11namespace Shader { 11namespace Shader {
12 12
13struct VaryingState { 13struct VaryingState {
14 std::bitset<256> mask{}; 14 std::bitset<512> mask{};
15 15
16 void Set(IR::Attribute attribute, bool state = true) { 16 void Set(IR::Attribute attribute, bool state = true) {
17 mask[static_cast<size_t>(attribute)] = state; 17 mask[static_cast<size_t>(attribute)] = state;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6a4022e45..ae84408bc 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -7,13 +7,13 @@ add_executable(tests
7 common/fibers.cpp 7 common/fibers.cpp
8 common/host_memory.cpp 8 common/host_memory.cpp
9 common/param_package.cpp 9 common/param_package.cpp
10 common/range_map.cpp
10 common/ring_buffer.cpp 11 common/ring_buffer.cpp
11 common/scratch_buffer.cpp 12 common/scratch_buffer.cpp
12 common/unique_function.cpp 13 common/unique_function.cpp
13 core/core_timing.cpp 14 core/core_timing.cpp
14 core/internal_network/network.cpp 15 core/internal_network/network.cpp
15 precompiled_headers.h 16 precompiled_headers.h
16 tests.cpp
17 video_core/buffer_base.cpp 17 video_core/buffer_base.cpp
18 input_common/calibration_configuration_job.cpp 18 input_common/calibration_configuration_job.cpp
19) 19)
@@ -21,7 +21,7 @@ add_executable(tests
21create_target_directory_groups(tests) 21create_target_directory_groups(tests)
22 22
23target_link_libraries(tests PRIVATE common core input_common) 23target_link_libraries(tests PRIVATE common core input_common)
24target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) 24target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
25 25
26add_test(NAME tests COMMAND tests) 26add_test(NAME tests COMMAND tests)
27 27
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp
index 0071ae52e..75e990ecd 100644
--- a/src/tests/common/bit_field.cpp
+++ b/src/tests/common/bit_field.cpp
@@ -4,7 +4,7 @@
4#include <array> 4#include <array>
5#include <cstring> 5#include <cstring>
6#include <type_traits> 6#include <type_traits>
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9 9
10TEST_CASE("BitField", "[common]") { 10TEST_CASE("BitField", "[common]") {
diff --git a/src/tests/common/cityhash.cpp b/src/tests/common/cityhash.cpp
index 05942eadb..2a391dff1 100644
--- a/src/tests/common/cityhash.cpp
+++ b/src/tests/common/cityhash.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/cityhash.h" 6#include "common/cityhash.h"
7 7
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4e29f9199..ecad7583f 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -11,7 +11,7 @@
11#include <unordered_map> 11#include <unordered_map>
12#include <vector> 12#include <vector>
13 13
14#include <catch2/catch.hpp> 14#include <catch2/catch_test_macros.hpp>
15 15
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/fiber.h" 17#include "common/fiber.h"
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index e49d0a09f..1b014b632 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/host_memory.h" 6#include "common/host_memory.h"
7#include "common/literals.h" 7#include "common/literals.h"
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index d036cc83a..41575def4 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5#include <math.h> 5#include <math.h>
6#include "common/logging/backend.h" 6#include "common/logging/backend.h"
7#include "common/param_package.h" 7#include "common/param_package.h"
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp
new file mode 100644
index 000000000..d301ac5f6
--- /dev/null
+++ b/src/tests/common/range_map.cpp
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <stdexcept>
5
6#include <catch2/catch_test_macros.hpp>
7
8#include "common/range_map.h"
9
10enum class MappedEnum : u32 {
11 Invalid = 0,
12 Valid_1 = 1,
13 Valid_2 = 2,
14 Valid_3 = 3,
15};
16
17TEST_CASE("Range Map: Setup", "[video_core]") {
18 Common::RangeMap<u64, MappedEnum> my_map(MappedEnum::Invalid);
19 my_map.Map(3000, 3500, MappedEnum::Valid_1);
20 my_map.Unmap(3200, 3600);
21 my_map.Map(4000, 4500, MappedEnum::Valid_2);
22 my_map.Map(4200, 4400, MappedEnum::Valid_2);
23 my_map.Map(4200, 4400, MappedEnum::Valid_1);
24 REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
25 REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
26 REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
27
28 REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
29 REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
30 REQUIRE(my_map.GetValueAt(3000) == MappedEnum::Valid_1);
31 REQUIRE(my_map.GetValueAt(3200) == MappedEnum::Invalid);
32
33 REQUIRE(my_map.GetValueAt(4199) == MappedEnum::Valid_2);
34 REQUIRE(my_map.GetValueAt(4200) == MappedEnum::Valid_1);
35 REQUIRE(my_map.GetValueAt(4400) == MappedEnum::Valid_2);
36 REQUIRE(my_map.GetValueAt(4500) == MappedEnum::Invalid);
37 REQUIRE(my_map.GetValueAt(4600) == MappedEnum::Invalid);
38
39 my_map.Unmap(0, 6000);
40 for (u64 address = 0; address < 10000; address += 1000) {
41 REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
42 }
43
44 my_map.Map(1000, 3000, MappedEnum::Valid_1);
45 my_map.Map(4000, 5000, MappedEnum::Valid_1);
46 my_map.Map(2500, 4100, MappedEnum::Valid_1);
47 REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
48
49 my_map.Map(1000, 3000, MappedEnum::Valid_1);
50 my_map.Map(4000, 5000, MappedEnum::Valid_2);
51 my_map.Map(2500, 4100, MappedEnum::Valid_3);
52 REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
53 REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
54 REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
55 REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
56 REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
57 REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
58 REQUIRE(my_map.GetValueAt(4100) == MappedEnum::Valid_2);
59 REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
60
61 my_map.Map(2000, 6000, MappedEnum::Valid_3);
62 REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
63 REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
64 REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
65 REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
66 REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
67 REQUIRE(my_map.GetValueAt(2001) == MappedEnum::Valid_3);
68 REQUIRE(my_map.GetValueAt(5999) == MappedEnum::Valid_3);
69 REQUIRE(my_map.GetValueAt(6000) == MappedEnum::Invalid);
70}
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index 4f81b6e5e..7dee988c8 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -7,7 +7,7 @@
7#include <numeric> 7#include <numeric>
8#include <thread> 8#include <thread>
9#include <vector> 9#include <vector>
10#include <catch2/catch.hpp> 10#include <catch2/catch_test_macros.hpp>
11#include "common/ring_buffer.h" 11#include "common/ring_buffer.h"
12 12
13namespace Common { 13namespace Common {
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
index b602c8d0a..132f139fa 100644
--- a/src/tests/common/scratch_buffer.cpp
+++ b/src/tests/common/scratch_buffer.cpp
@@ -3,8 +3,9 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <array> 5#include <array>
6#include <cstring>
6#include <span> 7#include <span>
7#include <catch2/catch.hpp> 8#include <catch2/catch_test_macros.hpp>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/scratch_buffer.h" 10#include "common/scratch_buffer.h"
10 11
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
index 311272506..f7a23e876 100644
--- a/src/tests/common/unique_function.cpp
+++ b/src/tests/common/unique_function.cpp
@@ -3,7 +3,7 @@
3 3
4#include <string> 4#include <string>
5 5
6#include <catch2/catch.hpp> 6#include <catch2/catch_test_macros.hpp>
7 7
8#include "common/unique_function.h" 8#include "common/unique_function.h"
9 9
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 284b2ae66..f08afbf9a 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
diff --git a/src/tests/core/internal_network/network.cpp b/src/tests/core/internal_network/network.cpp
index 164b0ff24..10ddd8b42 100644
--- a/src/tests/core/internal_network/network.cpp
+++ b/src/tests/core/internal_network/network.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "core/internal_network/network.h" 6#include "core/internal_network/network.h"
7#include "core/internal_network/sockets.h" 7#include "core/internal_network/sockets.h"
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp
index e5f698886..516ff1b30 100644
--- a/src/tests/input_common/calibration_configuration_job.cpp
+++ b/src/tests/input_common/calibration_configuration_job.cpp
@@ -6,7 +6,7 @@
6#include <thread> 6#include <thread>
7#include <boost/asio.hpp> 7#include <boost/asio.hpp>
8#include <boost/crc.hpp> 8#include <boost/crc.hpp>
9#include <catch2/catch.hpp> 9#include <catch2/catch_test_macros.hpp>
10 10
11#include "input_common/drivers/udp_client.h" 11#include "input_common/drivers/udp_client.h"
12#include "input_common/helpers/udp_protocol.h" 12#include "input_common/helpers/udp_protocol.h"
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
deleted file mode 100644
index 3f905c05c..000000000
--- a/src/tests/tests.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#define CATCH_CONFIG_MAIN
5#include <catch2/catch.hpp>
6
7// Catch provides the main function since we've given it the
8// CATCH_CONFIG_MAIN preprocessor directive.
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index f7236afab..1275cca24 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -4,7 +4,7 @@
4#include <stdexcept> 4#include <stdexcept>
5#include <unordered_map> 5#include <unordered_map>
6 6
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
@@ -538,7 +538,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
538 int num = 0; 538 int num = 0;
539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); 539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0); 541 REQUIRE(num == 1);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); 542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); 543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
544 buffer.FlushCachedWrites(); 544 buffer.FlushCachedWrites();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index fd71bf186..4742bcbe9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -13,6 +13,7 @@ add_library(video_core STATIC
13 buffer_cache/buffer_base.h 13 buffer_cache/buffer_base.h
14 buffer_cache/buffer_cache.cpp 14 buffer_cache/buffer_cache.cpp
15 buffer_cache/buffer_cache.h 15 buffer_cache/buffer_cache.h
16 cache_types.h
16 cdma_pusher.cpp 17 cdma_pusher.cpp
17 cdma_pusher.h 18 cdma_pusher.h
18 compatible_formats.cpp 19 compatible_formats.cpp
@@ -51,6 +52,8 @@ add_library(video_core STATIC
51 engines/puller.cpp 52 engines/puller.cpp
52 engines/puller.h 53 engines/puller.h
53 framebuffer_config.h 54 framebuffer_config.h
55 fsr.cpp
56 fsr.h
54 host1x/codecs/codec.cpp 57 host1x/codecs/codec.cpp
55 host1x/codecs/codec.h 58 host1x/codecs/codec.h
56 host1x/codecs/h264.cpp 59 host1x/codecs/h264.cpp
@@ -84,6 +87,7 @@ add_library(video_core STATIC
84 gpu.h 87 gpu.h
85 gpu_thread.cpp 88 gpu_thread.cpp
86 gpu_thread.h 89 gpu_thread.h
90 invalidation_accumulator.h
87 memory_manager.cpp 91 memory_manager.cpp
88 memory_manager.h 92 memory_manager.h
89 precompiled_headers.h 93 precompiled_headers.h
@@ -98,6 +102,8 @@ add_library(video_core STATIC
98 renderer_null/null_rasterizer.h 102 renderer_null/null_rasterizer.h
99 renderer_null/renderer_null.cpp 103 renderer_null/renderer_null.cpp
100 renderer_null/renderer_null.h 104 renderer_null/renderer_null.h
105 renderer_opengl/blit_image.cpp
106 renderer_opengl/blit_image.h
101 renderer_opengl/gl_buffer_cache.cpp 107 renderer_opengl/gl_buffer_cache.cpp
102 renderer_opengl/gl_buffer_cache.h 108 renderer_opengl/gl_buffer_cache.h
103 renderer_opengl/gl_compute_pipeline.cpp 109 renderer_opengl/gl_compute_pipeline.cpp
@@ -106,6 +112,8 @@ add_library(video_core STATIC
106 renderer_opengl/gl_device.h 112 renderer_opengl/gl_device.h
107 renderer_opengl/gl_fence_manager.cpp 113 renderer_opengl/gl_fence_manager.cpp
108 renderer_opengl/gl_fence_manager.h 114 renderer_opengl/gl_fence_manager.h
115 renderer_opengl/gl_fsr.cpp
116 renderer_opengl/gl_fsr.h
109 renderer_opengl/gl_graphics_pipeline.cpp 117 renderer_opengl/gl_graphics_pipeline.cpp
110 renderer_opengl/gl_graphics_pipeline.h 118 renderer_opengl/gl_graphics_pipeline.h
111 renderer_opengl/gl_rasterizer.cpp 119 renderer_opengl/gl_rasterizer.cpp
@@ -189,6 +197,8 @@ add_library(video_core STATIC
189 renderer_vulkan/vk_texture_cache.cpp 197 renderer_vulkan/vk_texture_cache.cpp
190 renderer_vulkan/vk_texture_cache.h 198 renderer_vulkan/vk_texture_cache.h
191 renderer_vulkan/vk_texture_cache_base.cpp 199 renderer_vulkan/vk_texture_cache_base.cpp
200 renderer_vulkan/vk_turbo_mode.cpp
201 renderer_vulkan/vk_turbo_mode.h
192 renderer_vulkan/vk_update_descriptor.cpp 202 renderer_vulkan/vk_update_descriptor.cpp
193 renderer_vulkan/vk_update_descriptor.h 203 renderer_vulkan/vk_update_descriptor.h
194 shader_cache.cpp 204 shader_cache.cpp
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index 92d77eef2..c47b7d866 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -430,7 +430,7 @@ private:
430 if (query_begin >= SizeBytes() || size < 0) { 430 if (query_begin >= SizeBytes() || size < 0) {
431 return; 431 return;
432 } 432 }
433 u64* const untracked_words = Array<Type::Untracked>(); 433 [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>();
434 u64* const state_words = Array<type>(); 434 u64* const state_words = Array<type>();
435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); 435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; 436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
@@ -483,7 +483,7 @@ private:
483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0}); 483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
484 } 484 }
485 // Exclude CPU modified pages when visiting GPU pages 485 // Exclude CPU modified pages when visiting GPU pages
486 const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0); 486 const u64 word = current_word;
487 u64 page = page_begin; 487 u64 page = page_begin;
488 page_begin = 0; 488 page_begin = 0;
489 489
@@ -531,7 +531,7 @@ private:
531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { 531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
532 static_assert(type != Type::Untracked); 532 static_assert(type != Type::Untracked);
533 533
534 const u64* const untracked_words = Array<Type::Untracked>(); 534 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
535 const u64* const state_words = Array<type>(); 535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1; 536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD; 537 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -539,8 +539,7 @@ private:
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); 539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; 540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { 541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
542 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; 542 const u64 word = state_words[word_index];
543 const u64 word = state_words[word_index] & ~off_word;
544 if (word == 0) { 543 if (word == 0) {
545 continue; 544 continue;
546 } 545 }
@@ -564,7 +563,7 @@ private:
564 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { 563 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
565 static_assert(type != Type::Untracked); 564 static_assert(type != Type::Untracked);
566 565
567 const u64* const untracked_words = Array<Type::Untracked>(); 566 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
568 const u64* const state_words = Array<type>(); 567 const u64* const state_words = Array<type>();
569 const u64 num_query_words = size / BYTES_PER_WORD + 1; 568 const u64 num_query_words = size / BYTES_PER_WORD + 1;
570 const u64 word_begin = offset / BYTES_PER_WORD; 569 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -574,8 +573,7 @@ private:
574 u64 begin = std::numeric_limits<u64>::max(); 573 u64 begin = std::numeric_limits<u64>::max();
575 u64 end = 0; 574 u64 end = 0;
576 for (u64 word_index = word_begin; word_index < word_end; ++word_index) { 575 for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
577 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; 576 const u64 word = state_words[word_index];
578 const u64 word = state_words[word_index] & ~off_word;
579 if (word == 0) { 577 if (word == 0) {
580 continue; 578 continue;
581 } 579 }
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 6c8d98946..627917ab6 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -200,7 +200,16 @@ public:
200 /// Return true when a CPU region is modified from the CPU 200 /// Return true when a CPU region is modified from the CPU
201 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); 201 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
202 202
203 std::mutex mutex; 203 void SetDrawIndirect(
204 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
205 current_draw_indirect = current_draw_indirect_;
206 }
207
208 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
209
210 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
211
212 std::recursive_mutex mutex;
204 Runtime& runtime; 213 Runtime& runtime;
205 214
206private: 215private:
@@ -272,6 +281,8 @@ private:
272 281
273 void BindHostVertexBuffers(); 282 void BindHostVertexBuffers();
274 283
284 void BindHostDrawIndirectBuffers();
285
275 void BindHostGraphicsUniformBuffers(size_t stage); 286 void BindHostGraphicsUniformBuffers(size_t stage);
276 287
277 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind); 288 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
@@ -298,6 +309,8 @@ private:
298 309
299 void UpdateVertexBuffer(u32 index); 310 void UpdateVertexBuffer(u32 index);
300 311
312 void UpdateDrawIndirect();
313
301 void UpdateUniformBuffers(size_t stage); 314 void UpdateUniformBuffers(size_t stage);
302 315
303 void UpdateStorageBuffers(size_t stage); 316 void UpdateStorageBuffers(size_t stage);
@@ -372,6 +385,8 @@ private:
372 SlotVector<Buffer> slot_buffers; 385 SlotVector<Buffer> slot_buffers;
373 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; 386 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
374 387
388 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
389
375 u32 last_index_count = 0; 390 u32 last_index_count = 0;
376 391
377 Binding index_buffer; 392 Binding index_buffer;
@@ -380,6 +395,8 @@ private:
380 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers; 395 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
381 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers; 396 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
382 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers; 397 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
398 Binding count_buffer_binding;
399 Binding indirect_buffer_binding;
383 400
384 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers; 401 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
385 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers; 402 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
@@ -666,13 +683,17 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
666 BindHostIndexBuffer(); 683 BindHostIndexBuffer();
667 } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { 684 } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
668 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 685 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
669 if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { 686 if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
670 runtime.BindQuadArrayIndexBuffer(draw_state.vertex_buffer.first, 687 draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
671 draw_state.vertex_buffer.count); 688 runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
689 draw_state.vertex_buffer.count);
672 } 690 }
673 } 691 }
674 BindHostVertexBuffers(); 692 BindHostVertexBuffers();
675 BindHostTransformFeedbackBuffers(); 693 BindHostTransformFeedbackBuffers();
694 if (current_draw_indirect) {
695 BindHostDrawIndirectBuffers();
696 }
676} 697}
677 698
678template <class P> 699template <class P>
@@ -822,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
822template <class P> 843template <class P>
823void BufferCache<P>::CommitAsyncFlushesHigh() { 844void BufferCache<P>::CommitAsyncFlushesHigh() {
824 AccumulateFlushes(); 845 AccumulateFlushes();
846
825 if (committed_ranges.empty()) { 847 if (committed_ranges.empty()) {
826 return; 848 return;
827 } 849 }
@@ -868,7 +890,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
868 buffer_id, 890 buffer_id,
869 }); 891 });
870 // Align up to avoid cache conflicts 892 // Align up to avoid cache conflicts
871 constexpr u64 align = 256ULL; 893 constexpr u64 align = 8ULL;
872 constexpr u64 mask = ~(align - 1ULL); 894 constexpr u64 mask = ~(align - 1ULL);
873 total_size_bytes += (new_size + align - 1) & mask; 895 total_size_bytes += (new_size + align - 1) & mask;
874 largest_copy = std::max(largest_copy, new_size); 896 largest_copy = std::max(largest_copy, new_size);
@@ -1041,6 +1063,19 @@ void BufferCache<P>::BindHostVertexBuffers() {
1041} 1063}
1042 1064
1043template <class P> 1065template <class P>
1066void BufferCache<P>::BindHostDrawIndirectBuffers() {
1067 const auto bind_buffer = [this](const Binding& binding) {
1068 Buffer& buffer = slot_buffers[binding.buffer_id];
1069 TouchBuffer(buffer, binding.buffer_id);
1070 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
1071 };
1072 if (current_draw_indirect->include_count) {
1073 bind_buffer(count_buffer_binding);
1074 }
1075 bind_buffer(indirect_buffer_binding);
1076}
1077
1078template <class P>
1044void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) { 1079void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
1045 u32 dirty = ~0U; 1080 u32 dirty = ~0U;
1046 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 1081 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
@@ -1271,6 +1306,9 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
1271 UpdateStorageBuffers(stage); 1306 UpdateStorageBuffers(stage);
1272 UpdateTextureBuffers(stage); 1307 UpdateTextureBuffers(stage);
1273 } 1308 }
1309 if (current_draw_indirect) {
1310 UpdateDrawIndirect();
1311 }
1274 } while (has_deleted_buffers); 1312 } while (has_deleted_buffers);
1275} 1313}
1276 1314
@@ -1288,7 +1326,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
1288 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 1326 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
1289 const auto& index_array = draw_state.index_buffer; 1327 const auto& index_array = draw_state.index_buffer;
1290 auto& flags = maxwell3d->dirty.flags; 1328 auto& flags = maxwell3d->dirty.flags;
1291 if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { 1329 if (!flags[Dirty::IndexBuffer]) {
1292 return; 1330 return;
1293 } 1331 }
1294 flags[Dirty::IndexBuffer] = false; 1332 flags[Dirty::IndexBuffer] = false;
@@ -1361,6 +1399,27 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1361} 1399}
1362 1400
1363template <class P> 1401template <class P>
1402void BufferCache<P>::UpdateDrawIndirect() {
1403 const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) {
1404 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1405 if (!cpu_addr) {
1406 binding = NULL_BINDING;
1407 return;
1408 }
1409 binding = Binding{
1410 .cpu_addr = *cpu_addr,
1411 .size = static_cast<u32>(size),
1412 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1413 };
1414 };
1415 if (current_draw_indirect->include_count) {
1416 update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding);
1417 }
1418 update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size,
1419 indirect_buffer_binding);
1420}
1421
1422template <class P>
1364void BufferCache<P>::UpdateUniformBuffers(size_t stage) { 1423void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
1365 ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) { 1424 ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
1366 Binding& binding = uniform_buffers[stage][index]; 1425 Binding& binding = uniform_buffers[stage][index];
@@ -1879,14 +1938,21 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
1879 bool is_written) const { 1938 bool is_written) const {
1880 const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr); 1939 const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr);
1881 const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8); 1940 const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8);
1882 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); 1941 const u32 alignment = runtime.GetStorageBufferAlignment();
1942
1943 const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);
1944 const u32 aligned_size =
1945 Common::AlignUp(static_cast<u32>(gpu_addr - aligned_gpu_addr) + size, alignment);
1946
1947 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
1883 if (!cpu_addr || size == 0) { 1948 if (!cpu_addr || size == 0) {
1884 return NULL_BINDING; 1949 return NULL_BINDING;
1885 } 1950 }
1886 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); 1951
1952 const VAddr cpu_end = Common::AlignUp(*cpu_addr + aligned_size, Core::Memory::YUZU_PAGESIZE);
1887 const Binding binding{ 1953 const Binding binding{
1888 .cpu_addr = *cpu_addr, 1954 .cpu_addr = *cpu_addr,
1889 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), 1955 .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *cpu_addr),
1890 .buffer_id = BufferId{}, 1956 .buffer_id = BufferId{},
1891 }; 1957 };
1892 return binding; 1958 return binding;
@@ -1940,4 +2006,16 @@ bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index)
1940 } 2006 }
1941} 2007}
1942 2008
2009template <class P>
2010std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() {
2011 auto& buffer = slot_buffers[count_buffer_binding.buffer_id];
2012 return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr));
2013}
2014
2015template <class P>
2016std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() {
2017 auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id];
2018 return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr));
2019}
2020
1943} // namespace VideoCommon 2021} // namespace VideoCommon
diff --git a/src/video_core/cache_types.h b/src/video_core/cache_types.h
new file mode 100644
index 000000000..1a5db3c55
--- /dev/null
+++ b/src/video_core/cache_types.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2020 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 "common/common_types.h"
8
9namespace VideoCommon {
10
11enum class CacheType : u32 {
12 None = 0,
13 TextureCache = 1 << 0,
14 QueryCache = 1 << 1,
15 BufferCache = 1 << 2,
16 ShaderCache = 1 << 3,
17 NoTextureCache = QueryCache | BufferCache | ShaderCache,
18 NoBufferCache = TextureCache | QueryCache | ShaderCache,
19 NoQueryCache = TextureCache | BufferCache | ShaderCache,
20 All = TextureCache | QueryCache | BufferCache | ShaderCache,
21};
22DECLARE_ENUM_FLAG_OPERATORS(CacheType)
23
24} // namespace VideoCommon
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 322de2606..551929824 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -61,7 +61,7 @@ bool DmaPusher::Step() {
61 } else { 61 } else {
62 const CommandListHeader command_list_header{ 62 const CommandListHeader command_list_header{
63 command_list.command_lists[dma_pushbuffer_subindex++]}; 63 command_list.command_lists[dma_pushbuffer_subindex++]};
64 const GPUVAddr dma_get = command_list_header.addr; 64 dma_state.dma_get = command_list_header.addr;
65 65
66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { 66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
67 // We've gone through the current list, remove it from the queue 67 // We've gone through the current list, remove it from the queue
@@ -75,12 +75,22 @@ bool DmaPusher::Step() {
75 75
76 // Push buffer non-empty, read a word 76 // Push buffer non-empty, read a word
77 command_headers.resize_destructive(command_list_header.size); 77 command_headers.resize_destructive(command_list_header.size);
78 if (Settings::IsGPULevelHigh()) { 78 constexpr u32 MacroRegistersStart = 0xE00;
79 memory_manager.ReadBlock(dma_get, command_headers.data(), 79 if (dma_state.method < MacroRegistersStart) {
80 command_list_header.size * sizeof(u32)); 80 if (Settings::IsGPULevelHigh()) {
81 memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
82 command_list_header.size * sizeof(u32));
83 } else {
84 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
85 command_list_header.size * sizeof(u32));
86 }
81 } else { 87 } else {
82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), 88 const size_t copy_size = command_list_header.size * sizeof(u32);
83 command_list_header.size * sizeof(u32)); 89 if (subchannels[dma_state.subchannel]) {
90 subchannels[dma_state.subchannel]->current_dirty =
91 memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size);
92 }
93 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
84 } 94 }
85 ProcessCommands(command_headers); 95 ProcessCommands(command_headers);
86 } 96 }
@@ -94,6 +104,7 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
94 104
95 if (dma_state.method_count) { 105 if (dma_state.method_count) {
96 // Data word of methods command 106 // Data word of methods command
107 dma_state.dma_word_offset = static_cast<u32>(index * sizeof(u32));
97 if (dma_state.non_incrementing) { 108 if (dma_state.non_incrementing) {
98 const u32 max_write = static_cast<u32>( 109 const u32 max_write = static_cast<u32>(
99 std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index); 110 std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
@@ -132,6 +143,8 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
132 case SubmissionMode::Inline: 143 case SubmissionMode::Inline:
133 dma_state.method = command_header.method; 144 dma_state.method = command_header.method;
134 dma_state.subchannel = command_header.subchannel; 145 dma_state.subchannel = command_header.subchannel;
146 dma_state.dma_word_offset = static_cast<u64>(
147 -static_cast<s64>(dma_state.dma_get)); // negate to set address as 0
135 CallMethod(command_header.arg_count); 148 CallMethod(command_header.arg_count);
136 dma_state.non_incrementing = true; 149 dma_state.non_incrementing = true;
137 dma_increment_once = false; 150 dma_increment_once = false;
@@ -164,8 +177,14 @@ void DmaPusher::CallMethod(u32 argument) const {
164 dma_state.method_count, 177 dma_state.method_count,
165 }); 178 });
166 } else { 179 } else {
167 subchannels[dma_state.subchannel]->CallMethod(dma_state.method, argument, 180 auto subchannel = subchannels[dma_state.subchannel];
168 dma_state.is_last_call); 181 if (!subchannel->execution_mask[dma_state.method]) [[likely]] {
182 subchannel->method_sink.emplace_back(dma_state.method, argument);
183 return;
184 }
185 subchannel->ConsumeSink();
186 subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
187 subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
169 } 188 }
170} 189}
171 190
@@ -174,8 +193,11 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
174 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, 193 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
175 dma_state.method_count); 194 dma_state.method_count);
176 } else { 195 } else {
177 subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, 196 auto subchannel = subchannels[dma_state.subchannel];
178 num_methods, dma_state.method_count); 197 subchannel->ConsumeSink();
198 subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
199 subchannel->CallMultiMethod(dma_state.method, base_start, num_methods,
200 dma_state.method_count);
179 } 201 }
180} 202}
181 203
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 6f00de937..1cdb690ed 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -156,6 +156,8 @@ private:
156 u32 subchannel; ///< Current subchannel 156 u32 subchannel; ///< Current subchannel
157 u32 method_count; ///< Current method count 157 u32 method_count; ///< Current method count
158 u32 length_pending; ///< Large NI command length pending 158 u32 length_pending; ///< Large NI command length pending
159 GPUVAddr dma_get; ///< Currently read segment
160 u64 dma_word_offset; ///< Current word ofset from address
159 bool non_incrementing; ///< Current command's NI flag 161 bool non_incrementing; ///< Current command's NI flag
160 bool is_last_call; 162 bool is_last_call;
161 }; 163 };
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 3a78421f6..1d22d25f1 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -51,6 +51,10 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
51 LOG_WARNING(HW_GPU, "(STUBBED) called"); 51 LOG_WARNING(HW_GPU, "(STUBBED) called");
52 break; 52 break;
53 } 53 }
54 case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
55 DrawTexture();
56 break;
57 }
54 default: 58 default:
55 break; 59 break;
56 } 60 }
@@ -91,6 +95,23 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind
91 ProcessDraw(true, num_instances); 95 ProcessDraw(true, num_instances);
92} 96}
93 97
98void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) {
99 draw_state.topology = topology;
100
101 ProcessDrawIndirect();
102}
103
104void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first,
105 u32 index_count) {
106 const auto& regs{maxwell3d->regs};
107 draw_state.topology = topology;
108 draw_state.index_buffer = regs.index_buffer;
109 draw_state.index_buffer.first = index_first;
110 draw_state.index_buffer.count = index_count;
111
112 ProcessDrawIndirect();
113}
114
94void DrawManager::SetInlineIndexBuffer(u32 index) { 115void DrawManager::SetInlineIndexBuffer(u32 index) {
95 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff)); 116 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
96 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8)); 117 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
@@ -162,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) {
162 ProcessDraw(true, 1); 183 ProcessDraw(true, 1);
163} 184}
164 185
186void DrawManager::DrawTexture() {
187 const auto& regs{maxwell3d->regs};
188 draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f;
189 draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f;
190 const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f;
191 const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f;
192 const bool lower_left{regs.window_origin.mode !=
193 Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
194 if (lower_left) {
195 draw_texture_state.dst_y0 -= dst_height;
196 }
197 draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width;
198 draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height;
199 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
200 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
201 draw_texture_state.src_x1 =
202 (static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width +
203 draw_texture_state.src_x0;
204 draw_texture_state.src_y1 =
205 (static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height +
206 draw_texture_state.src_y0;
207 draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
208 draw_texture_state.src_texture = regs.draw_texture.src_texture;
209
210 maxwell3d->rasterizer->DrawTexture();
211}
212
165void DrawManager::UpdateTopology() { 213void DrawManager::UpdateTopology() {
166 const auto& regs{maxwell3d->regs}; 214 const auto& regs{maxwell3d->regs};
167 switch (regs.primitive_topology_control) { 215 switch (regs.primitive_topology_control) {
@@ -198,4 +246,18 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
198 maxwell3d->rasterizer->Draw(draw_indexed, instance_count); 246 maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
199 } 247 }
200} 248}
249
250void DrawManager::ProcessDrawIndirect() {
251 LOG_TRACE(
252 HW_GPU,
253 "called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}",
254 draw_state.topology, indirect_state.is_indexed, indirect_state.include_count,
255 indirect_state.buffer_size, indirect_state.max_draw_counts);
256
257 UpdateTopology();
258
259 if (maxwell3d->ShouldExecute()) {
260 maxwell3d->rasterizer->DrawIndirect();
261 }
262}
201} // namespace Tegra::Engines 263} // namespace Tegra::Engines
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 0e6930a9c..7c22c49f1 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -32,6 +32,29 @@ public:
32 std::vector<u8> inline_index_draw_indexes; 32 std::vector<u8> inline_index_draw_indexes;
33 }; 33 };
34 34
35 struct DrawTextureState {
36 f32 dst_x0;
37 f32 dst_y0;
38 f32 dst_x1;
39 f32 dst_y1;
40 f32 src_x0;
41 f32 src_y0;
42 f32 src_x1;
43 f32 src_y1;
44 u32 src_sampler;
45 u32 src_texture;
46 };
47
48 struct IndirectParams {
49 bool is_indexed;
50 bool include_count;
51 GPUVAddr count_start_address;
52 GPUVAddr indirect_start_address;
53 size_t buffer_size;
54 size_t max_draw_counts;
55 size_t stride;
56 };
57
35 explicit DrawManager(Maxwell3D* maxwell_3d); 58 explicit DrawManager(Maxwell3D* maxwell_3d);
36 59
37 void ProcessMethodCall(u32 method, u32 argument); 60 void ProcessMethodCall(u32 method, u32 argument);
@@ -46,10 +69,26 @@ public:
46 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, 69 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
47 u32 base_instance, u32 num_instances); 70 u32 base_instance, u32 num_instances);
48 71
72 void DrawArrayIndirect(PrimitiveTopology topology);
73
74 void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
75
49 const State& GetDrawState() const { 76 const State& GetDrawState() const {
50 return draw_state; 77 return draw_state;
51 } 78 }
52 79
80 const DrawTextureState& GetDrawTextureState() const {
81 return draw_texture_state;
82 }
83
84 IndirectParams& GetIndirectParams() {
85 return indirect_state;
86 }
87
88 const IndirectParams& GetIndirectParams() const {
89 return indirect_state;
90 }
91
53private: 92private:
54 void SetInlineIndexBuffer(u32 index); 93 void SetInlineIndexBuffer(u32 index);
55 94
@@ -59,11 +98,17 @@ private:
59 98
60 void DrawIndexSmall(u32 argument); 99 void DrawIndexSmall(u32 argument);
61 100
101 void DrawTexture();
102
62 void UpdateTopology(); 103 void UpdateTopology();
63 104
64 void ProcessDraw(bool draw_indexed, u32 instance_count); 105 void ProcessDraw(bool draw_indexed, u32 instance_count);
65 106
107 void ProcessDrawIndirect();
108
66 Maxwell3D* maxwell3d{}; 109 Maxwell3D* maxwell3d{};
67 State draw_state{}; 110 State draw_state{};
111 DrawTextureState draw_texture_state{};
112 IndirectParams indirect_state{};
68}; 113};
69} // namespace Tegra::Engines 114} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h
index 26cde8584..392322358 100644
--- a/src/video_core/engines/engine_interface.h
+++ b/src/video_core/engines/engine_interface.h
@@ -3,6 +3,10 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <bitset>
7#include <limits>
8#include <vector>
9
6#include "common/common_types.h" 10#include "common/common_types.h"
7 11
8namespace Tegra::Engines { 12namespace Tegra::Engines {
@@ -17,6 +21,26 @@ public:
17 /// Write multiple values to the register identified by method. 21 /// Write multiple values to the register identified by method.
18 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 22 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
19 u32 methods_pending) = 0; 23 u32 methods_pending) = 0;
24
25 void ConsumeSink() {
26 if (method_sink.empty()) {
27 return;
28 }
29 ConsumeSinkImpl();
30 }
31
32 std::bitset<std::numeric_limits<u16>::max()> execution_mask{};
33 std::vector<std::pair<u32, u32>> method_sink{};
34 bool current_dirty{};
35 GPUVAddr current_dma_segment;
36
37protected:
38 virtual void ConsumeSinkImpl() {
39 for (auto [method, value] : method_sink) {
40 CallMethod(method, value, true);
41 }
42 method_sink.clear();
43 }
20}; 44};
21 45
22} // namespace Tegra::Engines 46} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index cea1dd8b0..7f5a0c29d 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -76,7 +76,7 @@ void State::ProcessData(std::span<const u8> read_buffer) {
76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, 76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
77 x_elements, regs.line_count, regs.dest.BlockHeight(), 77 x_elements, regs.line_count, regs.dest.BlockHeight(),
78 regs.dest.BlockDepth(), regs.line_length_in); 78 regs.dest.BlockDepth(), regs.line_length_in);
79 memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); 79 memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size);
80 } 80 }
81} 81}
82 82
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index c6478ae85..a126c359c 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -6,6 +6,7 @@
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "video_core/engines/fermi_2d.h" 7#include "video_core/engines/fermi_2d.h"
8#include "video_core/engines/sw_blitter/blitter.h" 8#include "video_core/engines/sw_blitter/blitter.h"
9#include "video_core/memory_manager.h"
9#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
10#include "video_core/surface.h" 11#include "video_core/surface.h"
11#include "video_core/textures/decoders.h" 12#include "video_core/textures/decoders.h"
@@ -20,11 +21,14 @@ namespace Tegra::Engines {
20 21
21using namespace Texture; 22using namespace Texture;
22 23
23Fermi2D::Fermi2D(MemoryManager& memory_manager_) { 24Fermi2D::Fermi2D(MemoryManager& memory_manager_) : memory_manager{memory_manager_} {
24 sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_); 25 sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager);
25 // Nvidia's OpenGL driver seems to assume these values 26 // Nvidia's OpenGL driver seems to assume these values
26 regs.src.depth = 1; 27 regs.src.depth = 1;
27 regs.dst.depth = 1; 28 regs.dst.depth = 1;
29
30 execution_mask.reset();
31 execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
28} 32}
29 33
30Fermi2D::~Fermi2D() = default; 34Fermi2D::~Fermi2D() = default;
@@ -49,6 +53,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
49 } 53 }
50} 54}
51 55
56void Fermi2D::ConsumeSinkImpl() {
57 for (auto [method, value] : method_sink) {
58 regs.reg_array[method] = value;
59 }
60 method_sink.clear();
61}
62
52void Fermi2D::Blit() { 63void Fermi2D::Blit() {
53 MICROPROFILE_SCOPE(GPU_BlitEngine); 64 MICROPROFILE_SCOPE(GPU_BlitEngine);
54 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", 65 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
@@ -94,6 +105,7 @@ void Fermi2D::Blit() {
94 config.src_x0 = 0; 105 config.src_x0 = 0;
95 } 106 }
96 107
108 memory_manager.FlushCaching();
97 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { 109 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
98 sw_blitter->Blit(src, regs.dst, config); 110 sw_blitter->Blit(src, regs.dst, config);
99 } 111 }
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 100b21bac..705b323e1 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -305,10 +305,13 @@ public:
305private: 305private:
306 VideoCore::RasterizerInterface* rasterizer = nullptr; 306 VideoCore::RasterizerInterface* rasterizer = nullptr;
307 std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter; 307 std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter;
308 MemoryManager& memory_manager;
308 309
309 /// Performs the copy from the source surface to the destination surface as configured in the 310 /// Performs the copy from the source surface to the destination surface as configured in the
310 /// registers. 311 /// registers.
311 void Blit(); 312 void Blit();
313
314 void ConsumeSinkImpl() override;
312}; 315};
313 316
314#define ASSERT_REG_POSITION(field_name, position) \ 317#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index e5c622155..601095f03 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -14,7 +14,12 @@
14namespace Tegra::Engines { 14namespace Tegra::Engines {
15 15
16KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) 16KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
17 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {} 17 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
18 execution_mask.reset();
19 execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
20 execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
21 execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
22}
18 23
19KeplerCompute::~KeplerCompute() = default; 24KeplerCompute::~KeplerCompute() = default;
20 25
@@ -23,6 +28,13 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
23 upload_state.BindRasterizer(rasterizer); 28 upload_state.BindRasterizer(rasterizer);
24} 29}
25 30
31void KeplerCompute::ConsumeSinkImpl() {
32 for (auto [method, value] : method_sink) {
33 regs.reg_array[method] = value;
34 }
35 method_sink.clear();
36}
37
26void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 38void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
27 ASSERT_MSG(method < Regs::NUM_REGS, 39 ASSERT_MSG(method < Regs::NUM_REGS,
28 "Invalid KeplerCompute register, increase the size of the Regs structure"); 40 "Invalid KeplerCompute register, increase the size of the Regs structure");
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index e154e3f06..2092e685f 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -204,6 +204,8 @@ public:
204private: 204private:
205 void ProcessLaunch(); 205 void ProcessLaunch();
206 206
207 void ConsumeSinkImpl() override;
208
207 /// Retrieves information about a specific TIC entry from the TIC buffer. 209 /// Retrieves information about a specific TIC entry from the TIC buffer.
208 Texture::TICEntry GetTICEntry(u32 tic_index) const; 210 Texture::TICEntry GetTICEntry(u32 tic_index) const;
209 211
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 08045d1cf..c026801a3 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -18,6 +18,17 @@ KeplerMemory::~KeplerMemory() = default;
18 18
19void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { 19void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
20 upload_state.BindRasterizer(rasterizer_); 20 upload_state.BindRasterizer(rasterizer_);
21
22 execution_mask.reset();
23 execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
24 execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
25}
26
27void KeplerMemory::ConsumeSinkImpl() {
28 for (auto [method, value] : method_sink) {
29 regs.reg_array[method] = value;
30 }
31 method_sink.clear();
21} 32}
22 33
23void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 34void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 5fe7489f0..fb1eecbba 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -73,6 +73,8 @@ public:
73 } regs{}; 73 } regs{};
74 74
75private: 75private:
76 void ConsumeSinkImpl() override;
77
76 Core::System& system; 78 Core::System& system;
77 Upload::State upload_state; 79 Upload::State upload_state;
78}; 80};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 9b182b653..ae9da6290 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -4,6 +4,8 @@
4#include <cstring> 4#include <cstring>
5#include <optional> 5#include <optional>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/scope_exit.h"
8#include "common/settings.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/core_timing.h" 10#include "core/core_timing.h"
9#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
@@ -28,6 +30,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
28 regs.upload} { 30 regs.upload} {
29 dirty.flags.flip(); 31 dirty.flags.flip();
30 InitializeRegisterDefaults(); 32 InitializeRegisterDefaults();
33 execution_mask.reset();
34 for (size_t i = 0; i < execution_mask.size(); i++) {
35 execution_mask[i] = IsMethodExecutable(static_cast<u32>(i));
36 }
31} 37}
32 38
33Maxwell3D::~Maxwell3D() = default; 39Maxwell3D::~Maxwell3D() = default;
@@ -121,6 +127,72 @@ void Maxwell3D::InitializeRegisterDefaults() {
121 shadow_state = regs; 127 shadow_state = regs;
122} 128}
123 129
130bool Maxwell3D::IsMethodExecutable(u32 method) {
131 if (method >= MacroRegistersStart) {
132 return true;
133 }
134 switch (method) {
135 case MAXWELL3D_REG_INDEX(draw.end):
136 case MAXWELL3D_REG_INDEX(draw.begin):
137 case MAXWELL3D_REG_INDEX(vertex_buffer.first):
138 case MAXWELL3D_REG_INDEX(vertex_buffer.count):
139 case MAXWELL3D_REG_INDEX(index_buffer.first):
140 case MAXWELL3D_REG_INDEX(index_buffer.count):
141 case MAXWELL3D_REG_INDEX(draw_inline_index):
142 case MAXWELL3D_REG_INDEX(index_buffer32_subsequent):
143 case MAXWELL3D_REG_INDEX(index_buffer16_subsequent):
144 case MAXWELL3D_REG_INDEX(index_buffer8_subsequent):
145 case MAXWELL3D_REG_INDEX(index_buffer32_first):
146 case MAXWELL3D_REG_INDEX(index_buffer16_first):
147 case MAXWELL3D_REG_INDEX(index_buffer8_first):
148 case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
149 case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
150 case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
151 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
152 case MAXWELL3D_REG_INDEX(draw_texture.src_y0):
153 case MAXWELL3D_REG_INDEX(wait_for_idle):
154 case MAXWELL3D_REG_INDEX(shadow_ram_control):
155 case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
156 case MAXWELL3D_REG_INDEX(load_mme.instruction):
157 case MAXWELL3D_REG_INDEX(load_mme.start_address):
158 case MAXWELL3D_REG_INDEX(falcon[4]):
159 case MAXWELL3D_REG_INDEX(const_buffer.buffer):
160 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
161 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
162 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
163 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
164 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
165 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
166 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
167 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
168 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
169 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
170 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
171 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
172 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
173 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
174 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
175 case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
176 case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
177 case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
178 case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
179 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
180 case MAXWELL3D_REG_INDEX(topology_override):
181 case MAXWELL3D_REG_INDEX(clear_surface):
182 case MAXWELL3D_REG_INDEX(report_semaphore.query):
183 case MAXWELL3D_REG_INDEX(render_enable.mode):
184 case MAXWELL3D_REG_INDEX(clear_report_value):
185 case MAXWELL3D_REG_INDEX(sync_info):
186 case MAXWELL3D_REG_INDEX(launch_dma):
187 case MAXWELL3D_REG_INDEX(inline_data):
188 case MAXWELL3D_REG_INDEX(fragment_barrier):
189 case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
190 return true;
191 default:
192 return false;
193 }
194}
195
124void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 196void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
125 if (executing_macro == 0) { 197 if (executing_macro == 0) {
126 // A macro call must begin by writing the macro method's register, not its argument. 198 // A macro call must begin by writing the macro method's register, not its argument.
@@ -130,12 +202,70 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
130 } 202 }
131 203
132 macro_params.insert(macro_params.end(), base_start, base_start + amount); 204 macro_params.insert(macro_params.end(), base_start, base_start + amount);
205 for (size_t i = 0; i < amount; i++) {
206 macro_addresses.push_back(current_dma_segment + i * sizeof(u32));
207 }
208 macro_segments.emplace_back(current_dma_segment, amount);
209 current_macro_dirty |= current_dirty;
210 current_dirty = false;
133 211
134 // Call the macro when there are no more parameters in the command buffer 212 // Call the macro when there are no more parameters in the command buffer
135 if (is_last_call) { 213 if (is_last_call) {
214 ConsumeSink();
136 CallMacroMethod(executing_macro, macro_params); 215 CallMacroMethod(executing_macro, macro_params);
137 macro_params.clear(); 216 macro_params.clear();
217 macro_addresses.clear();
218 macro_segments.clear();
219 current_macro_dirty = false;
220 }
221}
222
223void Maxwell3D::RefreshParametersImpl() {
224 size_t current_index = 0;
225 for (auto& segment : macro_segments) {
226 if (segment.first == 0) {
227 current_index += segment.second;
228 continue;
229 }
230 memory_manager.ReadBlock(segment.first, &macro_params[current_index],
231 sizeof(u32) * segment.second);
232 current_index += segment.second;
233 }
234}
235
236u32 Maxwell3D::GetMaxCurrentVertices() {
237 u32 num_vertices = 0;
238 for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
239 const auto& array = regs.vertex_streams[index];
240 if (array.enable == 0) {
241 continue;
242 }
243 const auto& attribute = regs.vertex_attrib_format[index];
244 if (attribute.constant) {
245 num_vertices = std::max(num_vertices, 1U);
246 continue;
247 }
248 const auto& limit = regs.vertex_stream_limits[index];
249 const GPUVAddr gpu_addr_begin = array.Address();
250 const GPUVAddr gpu_addr_end = limit.Address() + 1;
251 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
252 num_vertices = std::max(
253 num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value()));
138 } 254 }
255 return num_vertices;
256}
257
258size_t Maxwell3D::EstimateIndexBufferSize() {
259 GPUVAddr start_address = regs.index_buffer.StartAddress();
260 GPUVAddr end_address = regs.index_buffer.EndAddress();
261 constexpr std::array<size_t, 4> max_sizes = {
262 std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
263 std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
264 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
265 return std::min<size_t>(
266 memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) /
267 byte_size,
268 static_cast<size_t>(end_address - start_address));
139} 269}
140 270
141u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { 271u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
@@ -152,6 +282,29 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
152 return argument; 282 return argument;
153} 283}
154 284
285void Maxwell3D::ConsumeSinkImpl() {
286 SCOPE_EXIT({ method_sink.clear(); });
287 const auto control = shadow_state.shadow_ram_control;
288 if (control == Regs::ShadowRamControl::Track ||
289 control == Regs::ShadowRamControl::TrackWithFilter) {
290
291 for (auto [method, value] : method_sink) {
292 shadow_state.reg_array[method] = value;
293 ProcessDirtyRegisters(method, value);
294 }
295 return;
296 }
297 if (control == Regs::ShadowRamControl::Replay) {
298 for (auto [method, value] : method_sink) {
299 ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
300 }
301 return;
302 }
303 for (auto [method, value] : method_sink) {
304 ProcessDirtyRegisters(method, value);
305 }
306}
307
155void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { 308void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
156 if (regs.reg_array[method] == argument) { 309 if (regs.reg_array[method] == argument) {
157 return; 310 return;
@@ -263,7 +416,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
263 416
264 const u32 argument = ProcessShadowRam(method, method_argument); 417 const u32 argument = ProcessShadowRam(method, method_argument);
265 ProcessDirtyRegisters(method, argument); 418 ProcessDirtyRegisters(method, argument);
266
267 ProcessMethodCall(method, argument, method_argument, is_last_call); 419 ProcessMethodCall(method, argument, method_argument, is_last_call);
268} 420}
269 421
@@ -294,9 +446,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
294 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: 446 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
295 ProcessCBMultiData(base_start, amount); 447 ProcessCBMultiData(base_start, amount);
296 break; 448 break;
297 case MAXWELL3D_REG_INDEX(inline_data): 449 case MAXWELL3D_REG_INDEX(inline_data): {
450 ASSERT(methods_pending == amount);
298 upload_state.ProcessData(base_start, amount); 451 upload_state.ProcessData(base_start, amount);
299 return; 452 return;
453 }
300 default: 454 default:
301 for (u32 i = 0; i < amount; i++) { 455 for (u32 i = 0; i < amount; i++) {
302 CallMethod(method, base_start[i], methods_pending - i <= 1); 456 CallMethod(method, base_start[i], methods_pending - i <= 1);
@@ -314,7 +468,7 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
314} 468}
315 469
316void Maxwell3D::ProcessFirmwareCall4() { 470void Maxwell3D::ProcessFirmwareCall4() {
317 LOG_WARNING(HW_GPU, "(STUBBED) called"); 471 LOG_DEBUG(HW_GPU, "(STUBBED) called");
318 472
319 // Firmware call 4 is a blob that changes some registers depending on its parameters. 473 // Firmware call 4 is a blob that changes some registers depending on its parameters.
320 // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. 474 // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
@@ -332,11 +486,6 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
332} 486}
333 487
334void Maxwell3D::ProcessQueryGet() { 488void Maxwell3D::ProcessQueryGet() {
335 // TODO(Subv): Support the other query units.
336 if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) {
337 LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented");
338 }
339
340 switch (regs.report_semaphore.query.operation) { 489 switch (regs.report_semaphore.query.operation) {
341 case Regs::ReportSemaphore::Operation::Release: 490 case Regs::ReportSemaphore::Operation::Release:
342 if (regs.report_semaphore.query.short_query != 0) { 491 if (regs.report_semaphore.query.short_query != 0) {
@@ -389,7 +538,11 @@ void Maxwell3D::ProcessQueryCondition() {
389 case Regs::RenderEnable::Override::NeverRender: 538 case Regs::RenderEnable::Override::NeverRender:
390 execute_on = false; 539 execute_on = false;
391 break; 540 break;
392 case Regs::RenderEnable::Override::UseRenderEnable: 541 case Regs::RenderEnable::Override::UseRenderEnable: {
542 if (rasterizer->AccelerateConditionalRendering()) {
543 execute_on = true;
544 return;
545 }
393 switch (regs.render_enable.mode) { 546 switch (regs.render_enable.mode) {
394 case Regs::RenderEnable::Mode::True: { 547 case Regs::RenderEnable::Mode::True: {
395 execute_on = true; 548 execute_on = true;
@@ -427,6 +580,7 @@ void Maxwell3D::ProcessQueryCondition() {
427 } 580 }
428 break; 581 break;
429 } 582 }
583 }
430} 584}
431 585
432void Maxwell3D::ProcessCounterReset() { 586void Maxwell3D::ProcessCounterReset() {
@@ -463,7 +617,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
463} 617}
464 618
465void Maxwell3D::ProcessCBBind(size_t stage_index) { 619void Maxwell3D::ProcessCBBind(size_t stage_index) {
466 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. 620 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader
621 // stage.
467 const auto& bind_data = regs.bind_groups[stage_index]; 622 const auto& bind_data = regs.bind_groups[stage_index];
468 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; 623 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
469 buffer.enabled = bind_data.valid.Value() != 0; 624 buffer.enabled = bind_data.valid.Value() != 0;
@@ -490,7 +645,7 @@ void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
490 645
491 const GPUVAddr address{buffer_address + regs.const_buffer.offset}; 646 const GPUVAddr address{buffer_address + regs.const_buffer.offset};
492 const size_t copy_size = amount * sizeof(u32); 647 const size_t copy_size = amount * sizeof(u32);
493 memory_manager.WriteBlock(address, start_base, copy_size); 648 memory_manager.WriteBlockCached(address, start_base, copy_size);
494 649
495 // Increment the current buffer position. 650 // Increment the current buffer position.
496 regs.const_buffer.offset += static_cast<u32>(copy_size); 651 regs.const_buffer.offset += static_cast<u32>(copy_size);
@@ -524,4 +679,10 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
524 return regs.reg_array[method]; 679 return regs.reg_array[method];
525} 680}
526 681
682void Maxwell3D::SetHLEReplacementAttributeType(u32 bank, u32 offset,
683 HLEReplacementAttributeType name) {
684 const u64 key = (static_cast<u64>(bank) << 32) | offset;
685 replace_table.emplace(key, name);
686}
687
527} // namespace Tegra::Engines 688} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 22b904319..c89969bb4 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -272,6 +272,7 @@ public:
272 }; 272 };
273 273
274 union { 274 union {
275 u32 raw;
275 BitField<0, 1, Mode> mode; 276 BitField<0, 1, Mode> mode;
276 BitField<4, 8, u32> pad; 277 BitField<4, 8, u32> pad;
277 }; 278 };
@@ -1217,10 +1218,12 @@ public:
1217 1218
1218 struct Window { 1219 struct Window {
1219 union { 1220 union {
1221 u32 raw_x;
1220 BitField<0, 16, u32> x_min; 1222 BitField<0, 16, u32> x_min;
1221 BitField<16, 16, u32> x_max; 1223 BitField<16, 16, u32> x_max;
1222 }; 1224 };
1223 union { 1225 union {
1226 u32 raw_y;
1224 BitField<0, 16, u32> y_min; 1227 BitField<0, 16, u32> y_min;
1225 BitField<16, 16, u32> y_max; 1228 BitField<16, 16, u32> y_max;
1226 }; 1229 };
@@ -1596,6 +1599,20 @@ public:
1596 }; 1599 };
1597 static_assert(sizeof(TIRModulationCoeff) == 0x4); 1600 static_assert(sizeof(TIRModulationCoeff) == 0x4);
1598 1601
1602 struct DrawTexture {
1603 s32 dst_x0;
1604 s32 dst_y0;
1605 s32 dst_width;
1606 s32 dst_height;
1607 s64 dx_du;
1608 s64 dy_dv;
1609 u32 src_sampler;
1610 u32 src_texture;
1611 s32 src_x0;
1612 s32 src_y0;
1613 };
1614 static_assert(sizeof(DrawTexture) == 0x30);
1615
1599 struct ReduceColorThreshold { 1616 struct ReduceColorThreshold {
1600 union { 1617 union {
1601 BitField<0, 8, u32> all_hit_once; 1618 BitField<0, 8, u32> all_hit_once;
@@ -2708,7 +2725,7 @@ public:
2708 u32 post_z_pixel_imask; ///< 0x0F1C 2725 u32 post_z_pixel_imask; ///< 0x0F1C
2709 INSERT_PADDING_BYTES_NOINIT(0x20); 2726 INSERT_PADDING_BYTES_NOINIT(0x20);
2710 ConstantColorRendering const_color_rendering; ///< 0x0F40 2727 ConstantColorRendering const_color_rendering; ///< 0x0F40
2711 s32 stencil_back_ref; ///< 0x0F54 2728 u32 stencil_back_ref; ///< 0x0F54
2712 u32 stencil_back_mask; ///< 0x0F58 2729 u32 stencil_back_mask; ///< 0x0F58
2713 u32 stencil_back_func_mask; ///< 0x0F5C 2730 u32 stencil_back_func_mask; ///< 0x0F5C
2714 INSERT_PADDING_BYTES_NOINIT(0x14); 2731 INSERT_PADDING_BYTES_NOINIT(0x14);
@@ -2748,7 +2765,7 @@ public:
2748 u32 reserved_sw_method2; ///< 0x102C 2765 u32 reserved_sw_method2; ///< 0x102C
2749 std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030 2766 std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030
2750 std::array<u32, 15> spare_nop; ///< 0x1044 2767 std::array<u32, 15> spare_nop; ///< 0x1044
2751 INSERT_PADDING_BYTES_NOINIT(0x30); 2768 DrawTexture draw_texture; ///< 0x1080
2752 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0 2769 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0
2753 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC 2770 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC
2754 std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0 2771 std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0
@@ -2832,9 +2849,9 @@ public:
2832 Blend blend; ///< 0x133C 2849 Blend blend; ///< 0x133C
2833 u32 stencil_enable; ///< 0x1380 2850 u32 stencil_enable; ///< 0x1380
2834 StencilOp stencil_front_op; ///< 0x1384 2851 StencilOp stencil_front_op; ///< 0x1384
2835 s32 stencil_front_ref; ///< 0x1394 2852 u32 stencil_front_ref; ///< 0x1394
2836 s32 stencil_front_func_mask; ///< 0x1398 2853 u32 stencil_front_func_mask; ///< 0x1398
2837 s32 stencil_front_mask; ///< 0x139C 2854 u32 stencil_front_mask; ///< 0x139C
2838 INSERT_PADDING_BYTES_NOINIT(0x4); 2855 INSERT_PADDING_BYTES_NOINIT(0x4);
2839 u32 draw_auto_start_byte_count; ///< 0x13A4 2856 u32 draw_auto_start_byte_count; ///< 0x13A4
2840 PsSaturate frag_color_clamp; ///< 0x13A8 2857 PsSaturate frag_color_clamp; ///< 0x13A8
@@ -3020,6 +3037,24 @@ public:
3020 /// Store temporary hw register values, used by some calls to restore state after a operation 3037 /// Store temporary hw register values, used by some calls to restore state after a operation
3021 Regs shadow_state; 3038 Regs shadow_state;
3022 3039
3040 // None Engine
3041 enum class EngineHint : u32 {
3042 None = 0x0,
3043 OnHLEMacro = 0x1,
3044 };
3045
3046 EngineHint engine_state{EngineHint::None};
3047
3048 enum class HLEReplacementAttributeType : u32 {
3049 BaseVertex = 0x0,
3050 BaseInstance = 0x1,
3051 DrawID = 0x2,
3052 };
3053
3054 void SetHLEReplacementAttributeType(u32 bank, u32 offset, HLEReplacementAttributeType name);
3055
3056 std::unordered_map<u64, HLEReplacementAttributeType> replace_table;
3057
3023 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); 3058 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
3024 static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable"); 3059 static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
3025 3060
@@ -3067,6 +3102,35 @@ public:
3067 std::unique_ptr<DrawManager> draw_manager; 3102 std::unique_ptr<DrawManager> draw_manager;
3068 friend class DrawManager; 3103 friend class DrawManager;
3069 3104
3105 GPUVAddr GetMacroAddress(size_t index) const {
3106 return macro_addresses[index];
3107 }
3108
3109 void RefreshParameters() {
3110 if (!current_macro_dirty) {
3111 return;
3112 }
3113 RefreshParametersImpl();
3114 }
3115
3116 bool AnyParametersDirty() const {
3117 return current_macro_dirty;
3118 }
3119
3120 u32 GetMaxCurrentVertices();
3121
3122 size_t EstimateIndexBufferSize();
3123
3124 /// Handles a write to the CLEAR_BUFFERS register.
3125 void ProcessClearBuffers(u32 layer_count);
3126
3127 /// Handles a write to the CB_BIND register.
3128 void ProcessCBBind(size_t stage_index);
3129
3130 /// Handles a write to the CB_DATA[i] register.
3131 void ProcessCBData(u32 value);
3132 void ProcessCBMultiData(const u32* start_base, u32 amount);
3133
3070private: 3134private:
3071 void InitializeRegisterDefaults(); 3135 void InitializeRegisterDefaults();
3072 3136
@@ -3076,6 +3140,8 @@ private:
3076 3140
3077 void ProcessDirtyRegisters(u32 method, u32 argument); 3141 void ProcessDirtyRegisters(u32 method, u32 argument);
3078 3142
3143 void ConsumeSinkImpl() override;
3144
3079 void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); 3145 void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
3080 3146
3081 /// Retrieves information about a specific TIC entry from the TIC buffer. 3147 /// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -3116,16 +3182,13 @@ private:
3116 /// Handles writes to syncing register. 3182 /// Handles writes to syncing register.
3117 void ProcessSyncPoint(); 3183 void ProcessSyncPoint();
3118 3184
3119 /// Handles a write to the CB_DATA[i] register.
3120 void ProcessCBData(u32 value);
3121 void ProcessCBMultiData(const u32* start_base, u32 amount);
3122
3123 /// Handles a write to the CB_BIND register.
3124 void ProcessCBBind(size_t stage_index);
3125
3126 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3185 /// Returns a query's value or an empty object if the value will be deferred through a cache.
3127 std::optional<u64> GetQueryResult(); 3186 std::optional<u64> GetQueryResult();
3128 3187
3188 void RefreshParametersImpl();
3189
3190 bool IsMethodExecutable(u32 method);
3191
3129 Core::System& system; 3192 Core::System& system;
3130 MemoryManager& memory_manager; 3193 MemoryManager& memory_manager;
3131 3194
@@ -3145,6 +3208,10 @@ private:
3145 Upload::State upload_state; 3208 Upload::State upload_state;
3146 3209
3147 bool execute_on{true}; 3210 bool execute_on{true};
3211
3212 std::vector<std::pair<GPUVAddr, size_t>> macro_segments;
3213 std::vector<GPUVAddr> macro_addresses;
3214 bool current_macro_dirty{};
3148}; 3215};
3149 3216
3150#define ASSERT_REG_POSITION(field_name, position) \ 3217#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index f73d7bf0f..7762c7d96 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -21,7 +21,10 @@ namespace Tegra::Engines {
21using namespace Texture; 21using namespace Texture;
22 22
23MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) 23MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
24 : system{system_}, memory_manager{memory_manager_} {} 24 : system{system_}, memory_manager{memory_manager_} {
25 execution_mask.reset();
26 execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
27}
25 28
26MaxwellDMA::~MaxwellDMA() = default; 29MaxwellDMA::~MaxwellDMA() = default;
27 30
@@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
29 rasterizer = rasterizer_; 32 rasterizer = rasterizer_;
30} 33}
31 34
35void MaxwellDMA::ConsumeSinkImpl() {
36 for (auto [method, value] : method_sink) {
37 regs.reg_array[method] = value;
38 }
39 method_sink.clear();
40}
41
32void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 42void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
33 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); 43 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
34 44
@@ -59,7 +69,7 @@ void MaxwellDMA::Launch() {
59 if (launch.multi_line_enable) { 69 if (launch.multi_line_enable) {
60 const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; 70 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; 71 const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
62 72 memory_manager.FlushCaching();
63 if (!is_src_pitch && !is_dst_pitch) { 73 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 74 // If both the source and the destination are in block layout, assert.
65 CopyBlockLinearToBlockLinear(); 75 CopyBlockLinearToBlockLinear();
@@ -94,6 +104,7 @@ void MaxwellDMA::Launch() {
94 reinterpret_cast<u8*>(tmp_buffer.data()), 104 reinterpret_cast<u8*>(tmp_buffer.data()),
95 regs.line_length_in * sizeof(u32)); 105 regs.line_length_in * sizeof(u32));
96 } else { 106 } else {
107 memory_manager.FlushCaching();
97 const auto convert_linear_2_blocklinear_addr = [](u64 address) { 108 const auto convert_linear_2_blocklinear_addr = [](u64 address) {
98 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | 109 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
99 ((address & 0x180) >> 1) | ((address & 0x20) << 3); 110 ((address & 0x180) >> 1) | ((address & 0x20) << 3);
@@ -111,8 +122,8 @@ void MaxwellDMA::Launch() {
111 memory_manager.ReadBlockUnsafe( 122 memory_manager.ReadBlockUnsafe(
112 convert_linear_2_blocklinear_addr(regs.offset_in + offset), 123 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
113 tmp_buffer.data(), tmp_buffer.size()); 124 tmp_buffer.data(), tmp_buffer.size());
114 memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), 125 memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(),
115 tmp_buffer.size()); 126 tmp_buffer.size());
116 } 127 }
117 } else if (is_src_pitch && !is_dst_pitch) { 128 } else if (is_src_pitch && !is_dst_pitch) {
118 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 129 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
@@ -122,7 +133,7 @@ void MaxwellDMA::Launch() {
122 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 133 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
123 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), 134 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
124 tmp_buffer.size()); 135 tmp_buffer.size());
125 memory_manager.WriteBlock( 136 memory_manager.WriteBlockCached(
126 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 137 convert_linear_2_blocklinear_addr(regs.offset_out + offset),
127 tmp_buffer.data(), tmp_buffer.size()); 138 tmp_buffer.data(), tmp_buffer.size());
128 } 139 }
@@ -131,8 +142,8 @@ void MaxwellDMA::Launch() {
131 std::vector<u8> tmp_buffer(regs.line_length_in); 142 std::vector<u8> tmp_buffer(regs.line_length_in);
132 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 143 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
133 regs.line_length_in); 144 regs.line_length_in);
134 memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), 145 memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(),
135 regs.line_length_in); 146 regs.line_length_in);
136 } 147 }
137 } 148 }
138 } 149 }
@@ -194,7 +205,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
194 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 205 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
195 regs.pitch_out); 206 regs.pitch_out);
196 207
197 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 208 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
198} 209}
199 210
200void MaxwellDMA::CopyPitchToBlockLinear() { 211void MaxwellDMA::CopyPitchToBlockLinear() {
@@ -246,7 +257,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
246 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 257 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
247 regs.pitch_in); 258 regs.pitch_in);
248 259
249 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 260 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
250} 261}
251 262
252void MaxwellDMA::FastCopyBlockLinearToPitch() { 263void MaxwellDMA::FastCopyBlockLinearToPitch() {
@@ -277,7 +288,7 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
277 regs.src_params.block_size.height, regs.src_params.block_size.depth, 288 regs.src_params.block_size.height, regs.src_params.block_size.depth,
278 regs.pitch_out); 289 regs.pitch_out);
279 290
280 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 291 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
281} 292}
282 293
283void MaxwellDMA::CopyBlockLinearToBlockLinear() { 294void MaxwellDMA::CopyBlockLinearToBlockLinear() {
@@ -337,7 +348,7 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
337 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, 348 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
338 dst.block_size.height, dst.block_size.depth, pitch); 349 dst.block_size.height, dst.block_size.depth, pitch);
339 350
340 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 351 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
341} 352}
342 353
343void MaxwellDMA::ReleaseSemaphore() { 354void MaxwellDMA::ReleaseSemaphore() {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index c88191a61..0e594fa74 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -231,6 +231,8 @@ private:
231 231
232 void ReleaseSemaphore(); 232 void ReleaseSemaphore();
233 233
234 void ConsumeSinkImpl() override;
235
234 Core::System& system; 236 Core::System& system;
235 237
236 MemoryManager& memory_manager; 238 MemoryManager& memory_manager;
diff --git a/src/video_core/fsr.cpp b/src/video_core/fsr.cpp
new file mode 100644
index 000000000..5653c64fc
--- /dev/null
+++ b/src/video_core/fsr.cpp
@@ -0,0 +1,148 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cmath>
5#include "video_core/fsr.h"
6
7namespace FSR {
8namespace {
9// Reimplementations of the constant generating functions in ffx_fsr1.h
10// GCC generated a lot of warnings when using the official header.
11u32 AU1_AH1_AF1(f32 f) {
12 static constexpr u32 base[512]{
13 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
14 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
15 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
16 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
17 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
18 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
19 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
20 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
21 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
22 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
23 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
24 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
25 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
26 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
27 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
28 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
29 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
30 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
31 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
32 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
33 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
34 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
35 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
36 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
37 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
38 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
39 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
40 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
41 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
42 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
43 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
44 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
45 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
46 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
47 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
48 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
49 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
50 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
51 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
52 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
53 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
54 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
55 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
56 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
57 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
58 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
59 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
60 };
61 static constexpr s8 shift[512]{
62 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
63 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
64 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
65 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
66 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
67 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
68 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
69 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
70 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
71 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
72 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
73 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
81 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
82 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
83 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
86 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
87 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
88 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18,
97 };
98 const u32 u = Common::BitCast<u32>(f);
99 const u32 i = u >> 23;
100 return base[i] + ((u & 0x7fffff) >> shift[i]);
101}
102
103u32 AU1_AH2_AF2(f32 a[2]) {
104 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
105}
106
107void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
108 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
109 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
110 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
111 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
112 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
113 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
114 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
115 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
116 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
117 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
118 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
119 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
120 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
121 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
122 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
123 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
124 con3[2] = con3[3] = 0;
125}
126} // Anonymous namespace
127
128void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
129 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
130 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
131 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
132 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
133 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
134 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
135 inputOffsetInPixelsX);
136 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
137 inputOffsetInPixelsY);
138}
139
140void FsrRcasCon(u32* con, f32 sharpness) {
141 sharpness = std::exp2f(-sharpness);
142 f32 hSharp[2]{sharpness, sharpness};
143 con[0] = Common::BitCast<u32>(sharpness);
144 con[1] = AU1_AH2_AF2(hSharp);
145 con[2] = 0;
146 con[3] = 0;
147}
148} // namespace FSR
diff --git a/src/video_core/fsr.h b/src/video_core/fsr.h
new file mode 100644
index 000000000..db0d4ec6f
--- /dev/null
+++ b/src/video_core/fsr.h
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_cast.h"
7#include "common/common_types.h"
8
9namespace FSR {
10// Reimplementations of the constant generating functions in ffx_fsr1.h
11// GCC generated a lot of warnings when using the official header.
12void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
13 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
14 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
15 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY);
16
17void FsrRcasCon(u32* con, f32 sharpness);
18
19} // namespace FSR
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index e6dc24f22..52cd5bb81 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -3,14 +3,19 @@
3 3
4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) 4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr)
5 5
6set(GLSL_INCLUDES 6set(FIDELITYFX_FILES
7 fidelityfx_fsr.comp
8 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h 7 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h
9 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h 8 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h
10) 9)
11 10
11set(GLSL_INCLUDES
12 fidelityfx_fsr.comp
13 ${FIDELITYFX_FILES}
14)
15
12set(SHADER_FILES 16set(SHADER_FILES
13 astc_decoder.comp 17 astc_decoder.comp
18 blit_color_float.frag
14 block_linear_unswizzle_2d.comp 19 block_linear_unswizzle_2d.comp
15 block_linear_unswizzle_3d.comp 20 block_linear_unswizzle_3d.comp
16 convert_abgr8_to_d24s8.frag 21 convert_abgr8_to_d24s8.frag
@@ -23,6 +28,9 @@ set(SHADER_FILES
23 fxaa.vert 28 fxaa.vert
24 opengl_convert_s8d24.comp 29 opengl_convert_s8d24.comp
25 opengl_copy_bc4.comp 30 opengl_copy_bc4.comp
31 opengl_fidelityfx_fsr.frag
32 opengl_fidelityfx_fsr_easu.frag
33 opengl_fidelityfx_fsr_rcas.frag
26 opengl_present.frag 34 opengl_present.frag
27 opengl_present.vert 35 opengl_present.vert
28 opengl_present_scaleforce.frag 36 opengl_present_scaleforce.frag
@@ -36,8 +44,9 @@ set(SHADER_FILES
36 smaa_blending_weight_calculation.frag 44 smaa_blending_weight_calculation.frag
37 smaa_neighborhood_blending.vert 45 smaa_neighborhood_blending.vert
38 smaa_neighborhood_blending.frag 46 smaa_neighborhood_blending.frag
39 vulkan_blit_color_float.frag
40 vulkan_blit_depth_stencil.frag 47 vulkan_blit_depth_stencil.frag
48 vulkan_color_clear.frag
49 vulkan_color_clear.vert
41 vulkan_fidelityfx_fsr_easu_fp16.comp 50 vulkan_fidelityfx_fsr_easu_fp16.comp
42 vulkan_fidelityfx_fsr_easu_fp32.comp 51 vulkan_fidelityfx_fsr_easu_fp32.comp
43 vulkan_fidelityfx_fsr_rcas_fp16.comp 52 vulkan_fidelityfx_fsr_rcas_fp16.comp
@@ -47,6 +56,7 @@ set(SHADER_FILES
47 vulkan_present_scaleforce_fp16.frag 56 vulkan_present_scaleforce_fp16.frag
48 vulkan_present_scaleforce_fp32.frag 57 vulkan_present_scaleforce_fp32.frag
49 vulkan_quad_indexed.comp 58 vulkan_quad_indexed.comp
59 vulkan_turbo_mode.comp
50 vulkan_uint8.comp 60 vulkan_uint8.comp
51) 61)
52 62
@@ -117,6 +127,25 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
117 endif() 127 endif()
118endforeach() 128endforeach()
119 129
130foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
131 get_filename_component(FILENAME ${FILEPATH} NAME)
132 string(REPLACE "." "_" HEADER_NAME ${FILENAME})
133 set(SOURCE_FILE ${FILEPATH})
134 set(SOURCE_HEADER_FILE ${SHADER_DIR}/${HEADER_NAME}.h)
135 add_custom_command(
136 OUTPUT
137 ${SOURCE_HEADER_FILE}
138 COMMAND
139 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE}
140 MAIN_DEPENDENCY
141 ${SOURCE_FILE}
142 DEPENDS
143 ${INPUT_FILE}
144 # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
145 )
146 set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
147endforeach()
148
120set(SHADER_SOURCES ${SHADER_FILES}) 149set(SHADER_SOURCES ${SHADER_FILES})
121list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) 150list(APPEND SHADER_SOURCES ${GLSL_INCLUDES})
122 151
diff --git a/src/video_core/host_shaders/vulkan_blit_color_float.frag b/src/video_core/host_shaders/blit_color_float.frag
index c0c832296..c0c832296 100644
--- a/src/video_core/host_shaders/vulkan_blit_color_float.frag
+++ b/src/video_core/host_shaders/blit_color_float.frag
diff --git a/src/video_core/host_shaders/full_screen_triangle.vert b/src/video_core/host_shaders/full_screen_triangle.vert
index 2c976b19f..d16d98995 100644
--- a/src/video_core/host_shaders/full_screen_triangle.vert
+++ b/src/video_core/host_shaders/full_screen_triangle.vert
@@ -4,13 +4,20 @@
4#version 450 4#version 450
5 5
6#ifdef VULKAN 6#ifdef VULKAN
7#define VERTEX_ID gl_VertexIndex
7#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { 8#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
8#define END_PUSH_CONSTANTS }; 9#define END_PUSH_CONSTANTS };
9#define UNIFORM(n) 10#define UNIFORM(n)
11#define FLIPY 1
10#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv 12#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
13#define VERTEX_ID gl_VertexID
11#define BEGIN_PUSH_CONSTANTS 14#define BEGIN_PUSH_CONSTANTS
12#define END_PUSH_CONSTANTS 15#define END_PUSH_CONSTANTS
16#define FLIPY -1
13#define UNIFORM(n) layout (location = n) uniform 17#define UNIFORM(n) layout (location = n) uniform
18out gl_PerVertex {
19 vec4 gl_Position;
20};
14#endif 21#endif
15 22
16BEGIN_PUSH_CONSTANTS 23BEGIN_PUSH_CONSTANTS
@@ -21,8 +28,8 @@ END_PUSH_CONSTANTS
21layout(location = 0) out vec2 texcoord; 28layout(location = 0) out vec2 texcoord;
22 29
23void main() { 30void main() {
24 float x = float((gl_VertexIndex & 1) << 2); 31 float x = float((VERTEX_ID & 1) << 2);
25 float y = float((gl_VertexIndex & 2) << 1); 32 float y = float((VERTEX_ID & 2) << 1);
26 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); 33 gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0);
27 texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset); 34 texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset);
28} 35}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
new file mode 100644
index 000000000..16d22f58e
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
@@ -0,0 +1,108 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//!#version 460 core
5#extension GL_ARB_separate_shader_objects : enable
6#extension GL_ARB_shading_language_420pack : enable
7
8#extension GL_AMD_gpu_shader_half_float : enable
9#extension GL_NV_gpu_shader5 : enable
10
11// FidelityFX Super Resolution Sample
12//
13// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
14// Permission is hereby granted, free of charge, to any person obtaining a copy
15// of this software and associated documentation files(the "Software"), to deal
16// in the Software without restriction, including without limitation the rights
17// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
18// copies of the Software, and to permit persons to whom the Software is
19// furnished to do so, subject to the following conditions :
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28// THE SOFTWARE.
29
30layout (location = 0) uniform uvec4 constants[4];
31
32#define A_GPU 1
33#define A_GLSL 1
34
35#ifdef YUZU_USE_FP16
36 #define A_HALF
37#endif
38#include "ffx_a.h"
39
40#ifndef YUZU_USE_FP16
41 layout (binding=0) uniform sampler2D InputTexture;
42 #if USE_EASU
43 #define FSR_EASU_F 1
44 AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; }
45 AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; }
46 AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; }
47 #endif
48 #if USE_RCAS
49 #define FSR_RCAS_F
50 AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); }
51 void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
52 #endif
53#else
54 layout (binding=0) uniform sampler2D InputTexture;
55 #if USE_EASU
56 #define FSR_EASU_H 1
57 AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; }
58 AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; }
59 AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; }
60 #endif
61 #if USE_RCAS
62 #define FSR_RCAS_H
63 AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); }
64 void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
65 #endif
66#endif
67
68#include "ffx_fsr1.h"
69
70#if USE_RCAS
71 layout(location = 0) in vec2 frag_texcoord;
72#endif
73layout (location = 0) out vec4 frag_color;
74
75void CurrFilter(AU2 pos)
76{
77#if USE_EASU
78 #ifndef YUZU_USE_FP16
79 AF3 c;
80 FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
81 frag_color = AF4(c, 1.0);
82 #else
83 AH3 c;
84 FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
85 frag_color = AH4(c, 1.0);
86 #endif
87#endif
88#if USE_RCAS
89 #ifndef YUZU_USE_FP16
90 AF3 c;
91 FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
92 frag_color = AF4(c, 1.0);
93 #else
94 AH3 c;
95 FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
96 frag_color = AH4(c, 1.0);
97 #endif
98#endif
99}
100
101void main()
102{
103#if USE_RCAS
104 CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
105#else
106 CurrFilter(AU2(gl_FragCoord.xy));
107#endif
108}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
new file mode 100644
index 000000000..d39f80ac1
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_EASU 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
new file mode 100644
index 000000000..cfa78ddc7
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_RCAS 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_color_clear.frag b/src/video_core/host_shaders/vulkan_color_clear.frag
new file mode 100644
index 000000000..617bf01e1
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.frag
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6layout (push_constant) uniform PushConstants {
7 vec4 clear_color;
8};
9
10layout(location = 0) out vec4 color;
11
12void main() {
13 color = clear_color;
14}
diff --git a/src/video_core/host_shaders/vulkan_color_clear.vert b/src/video_core/host_shaders/vulkan_color_clear.vert
new file mode 100644
index 000000000..d85883141
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.vert
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6void main() {
7 float x = float((gl_VertexIndex & 1) << 2);
8 float y = float((gl_VertexIndex & 2) << 1);
9 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
10}
diff --git a/src/video_core/host_shaders/vulkan_quad_indexed.comp b/src/video_core/host_shaders/vulkan_quad_indexed.comp
index a412f30ff..066fe4a9c 100644
--- a/src/video_core/host_shaders/vulkan_quad_indexed.comp
+++ b/src/video_core/host_shaders/vulkan_quad_indexed.comp
@@ -16,6 +16,7 @@ layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer {
16layout (push_constant) uniform PushConstants { 16layout (push_constant) uniform PushConstants {
17 uint base_vertex; 17 uint base_vertex;
18 int index_shift; // 0: uint8, 1: uint16, 2: uint32 18 int index_shift; // 0: uint8, 1: uint16, 2: uint32
19 int is_strip; // 0: quads 1: quadstrip
19}; 20};
20 21
21void main() { 22void main() {
@@ -28,9 +29,10 @@ void main() {
28 int flipped_shift = 2 - index_shift; 29 int flipped_shift = 2 - index_shift;
29 int mask = (1 << flipped_shift) - 1; 30 int mask = (1 << flipped_shift) - 1;
30 31
31 const int quad_swizzle[6] = int[](0, 1, 2, 0, 2, 3); 32 const int quads_swizzle[6] = int[](0, 1, 2, 0, 2, 3);
33 const int quad_strip_swizzle[6] = int[](0, 3, 1, 0, 2, 3);
32 for (uint vertex = 0; vertex < 6; ++vertex) { 34 for (uint vertex = 0; vertex < 6; ++vertex) {
33 int offset = primitive * 4 + quad_swizzle[vertex]; 35 int offset = (is_strip == 0 ? primitive * 4 + quads_swizzle[vertex] : primitive * 2 + quad_strip_swizzle[vertex]);
34 int int_offset = offset >> flipped_shift; 36 int int_offset = offset >> flipped_shift;
35 int bit_offset = (offset & mask) * index_size; 37 int bit_offset = (offset & mask) * index_size;
36 uint packed_input = input_indexes[int_offset]; 38 uint packed_input = input_indexes[int_offset];
diff --git a/src/video_core/host_shaders/vulkan_turbo_mode.comp b/src/video_core/host_shaders/vulkan_turbo_mode.comp
new file mode 100644
index 000000000..d651001d9
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_turbo_mode.comp
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6layout (local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
7
8layout (binding = 0) buffer ThreadData {
9 uint data[];
10};
11
12uint xorshift32(uint x) {
13 x ^= x << 13;
14 x ^= x >> 17;
15 x ^= x << 5;
16 return x;
17}
18
19uint getGlobalIndex() {
20 return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_WorkGroupSize.y * gl_NumWorkGroups.y;
21}
22
23void main() {
24 uint myIndex = xorshift32(getGlobalIndex());
25 uint otherIndex = xorshift32(myIndex);
26
27 uint otherValue = atomicAdd(data[otherIndex % data.length()], 0) + 1;
28 atomicAdd(data[myIndex % data.length()], otherValue);
29}
diff --git a/src/video_core/invalidation_accumulator.h b/src/video_core/invalidation_accumulator.h
new file mode 100644
index 000000000..2c2aaf7bb
--- /dev/null
+++ b/src/video_core/invalidation_accumulator.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <utility>
7#include <vector>
8
9#include "common/common_types.h"
10
11namespace VideoCommon {
12
13class InvalidationAccumulator {
14public:
15 InvalidationAccumulator() = default;
16 ~InvalidationAccumulator() = default;
17
18 void Add(GPUVAddr address, size_t size) {
19 const auto reset_values = [&]() {
20 if (has_collected) {
21 buffer.emplace_back(start_address, accumulated_size);
22 }
23 start_address = address;
24 accumulated_size = size;
25 last_collection = start_address + size;
26 };
27 if (address >= start_address && address + size <= last_collection) [[likely]] {
28 return;
29 }
30 size = ((address + size + atomicity_size_mask) & atomicity_mask) - address;
31 address = address & atomicity_mask;
32 if (!has_collected) [[unlikely]] {
33 reset_values();
34 has_collected = true;
35 return;
36 }
37 if (address != last_collection) [[unlikely]] {
38 reset_values();
39 return;
40 }
41 accumulated_size += size;
42 last_collection += size;
43 }
44
45 void Clear() {
46 buffer.clear();
47 start_address = 0;
48 last_collection = 0;
49 has_collected = false;
50 }
51
52 bool AnyAccumulated() const {
53 return has_collected;
54 }
55
56 template <typename Func>
57 void Callback(Func&& func) {
58 if (!has_collected) {
59 return;
60 }
61 buffer.emplace_back(start_address, accumulated_size);
62 for (auto& [address, size] : buffer) {
63 func(address, size);
64 }
65 }
66
67private:
68 static constexpr size_t atomicity_bits = 5;
69 static constexpr size_t atomicity_size = 1ULL << atomicity_bits;
70 static constexpr size_t atomicity_size_mask = atomicity_size - 1;
71 static constexpr size_t atomicity_mask = ~atomicity_size_mask;
72 GPUVAddr start_address{};
73 GPUVAddr last_collection{};
74 size_t accumulated_size{};
75 bool has_collected{};
76 std::vector<std::pair<VAddr, size_t>> buffer;
77};
78
79} // namespace VideoCommon
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index 505d81c1e..82ad0477d 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -12,7 +12,9 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/fs/fs.h" 13#include "common/fs/fs.h"
14#include "common/fs/path_util.h" 14#include "common/fs/path_util.h"
15#include "common/microprofile.h"
15#include "common/settings.h" 16#include "common/settings.h"
17#include "video_core/engines/maxwell_3d.h"
16#include "video_core/macro/macro.h" 18#include "video_core/macro/macro.h"
17#include "video_core/macro/macro_hle.h" 19#include "video_core/macro/macro_hle.h"
18#include "video_core/macro/macro_interpreter.h" 20#include "video_core/macro/macro_interpreter.h"
@@ -21,6 +23,8 @@
21#include "video_core/macro/macro_jit_x64.h" 23#include "video_core/macro/macro_jit_x64.h"
22#endif 24#endif
23 25
26MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192));
27
24namespace Tegra { 28namespace Tegra {
25 29
26static void Dump(u64 hash, std::span<const u32> code) { 30static void Dump(u64 hash, std::span<const u32> code) {
@@ -40,8 +44,8 @@ static void Dump(u64 hash, std::span<const u32> code) {
40 macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes()); 44 macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
41} 45}
42 46
43MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) 47MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d_)
44 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} 48 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d_)}, maxwell3d{maxwell3d_} {}
45 49
46MacroEngine::~MacroEngine() = default; 50MacroEngine::~MacroEngine() = default;
47 51
@@ -59,8 +63,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
59 if (compiled_macro != macro_cache.end()) { 63 if (compiled_macro != macro_cache.end()) {
60 const auto& cache_info = compiled_macro->second; 64 const auto& cache_info = compiled_macro->second;
61 if (cache_info.has_hle_program) { 65 if (cache_info.has_hle_program) {
66 MICROPROFILE_SCOPE(MacroHLE);
62 cache_info.hle_program->Execute(parameters, method); 67 cache_info.hle_program->Execute(parameters, method);
63 } else { 68 } else {
69 maxwell3d.RefreshParameters();
64 cache_info.lle_program->Execute(parameters, method); 70 cache_info.lle_program->Execute(parameters, method);
65 } 71 }
66 } else { 72 } else {
@@ -101,12 +107,15 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
101 } 107 }
102 } 108 }
103 109
104 if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { 110 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
111 if (!hle_program || Settings::values.disable_macro_hle) {
112 maxwell3d.RefreshParameters();
113 cache_info.lle_program->Execute(parameters, method);
114 } else {
105 cache_info.has_hle_program = true; 115 cache_info.has_hle_program = true;
106 cache_info.hle_program = std::move(hle_program); 116 cache_info.hle_program = std::move(hle_program);
117 MICROPROFILE_SCOPE(MacroHLE);
107 cache_info.hle_program->Execute(parameters, method); 118 cache_info.hle_program->Execute(parameters, method);
108 } else {
109 cache_info.lle_program->Execute(parameters, method);
110 } 119 }
111 } 120 }
112} 121}
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h
index 07d97ba39..737ced9a4 100644
--- a/src/video_core/macro/macro.h
+++ b/src/video_core/macro/macro.h
@@ -137,6 +137,7 @@ private:
137 std::unordered_map<u32, CacheInfo> macro_cache; 137 std::unordered_map<u32, CacheInfo> macro_cache;
138 std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; 138 std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
139 std::unique_ptr<HLEMacro> hle_macros; 139 std::unique_ptr<HLEMacro> hle_macros;
140 Engines::Maxwell3D& maxwell3d;
140}; 141};
141 142
142std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); 143std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 8549db2e4..6272a4652 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -1,143 +1,551 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <array> 4#include <array>
5#include <vector> 5#include <vector>
6#include "common/assert.h"
6#include "common/scope_exit.h" 7#include "common/scope_exit.h"
7#include "video_core/dirty_flags.h" 8#include "video_core/dirty_flags.h"
8#include "video_core/engines/draw_manager.h" 9#include "video_core/engines/draw_manager.h"
9#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
10#include "video_core/macro/macro.h" 11#include "video_core/macro/macro.h"
11#include "video_core/macro/macro_hle.h" 12#include "video_core/macro/macro_hle.h"
13#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
13 15
14namespace Tegra { 16namespace Tegra {
15namespace {
16 17
17using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); 18using Maxwell3D = Engines::Maxwell3D;
18 19
19// HLE'd functions 20namespace {
20void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
21 const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
22 maxwell3d.draw_manager->DrawIndex(
23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff),
24 parameters[4], parameters[1], parameters[3], parameters[5], instance_count);
25}
26 21
27void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 22bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
28 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 23 switch (topology) {
29 maxwell3d.draw_manager->DrawArray( 24 case Maxwell3D::Regs::PrimitiveTopology::Points:
30 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), 25 case Maxwell3D::Regs::PrimitiveTopology::Lines:
31 parameters[3], parameters[1], parameters[4], instance_count); 26 case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
27 case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
28 case Maxwell3D::Regs::PrimitiveTopology::Triangles:
29 case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
30 case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
31 case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
32 case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
33 case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
34 case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
35 case Maxwell3D::Regs::PrimitiveTopology::Patches:
36 return true;
37 case Maxwell3D::Regs::PrimitiveTopology::Quads:
38 case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
39 case Maxwell3D::Regs::PrimitiveTopology::Polygon:
40 default:
41 return false;
42 }
32} 43}
33 44
34void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 45class HLEMacroImpl : public CachedMacro {
35 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 46public:
36 const u32 element_base = parameters[4]; 47 explicit HLEMacroImpl(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {}
37 const u32 base_instance = parameters[5];
38 maxwell3d.regs.vertex_id_base = element_base;
39 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
40 maxwell3d.CallMethod(0x8e3, 0x640, true);
41 maxwell3d.CallMethod(0x8e4, element_base, true);
42 maxwell3d.CallMethod(0x8e5, base_instance, true);
43
44 maxwell3d.draw_manager->DrawIndex(
45 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
46 parameters[3], parameters[1], element_base, base_instance, instance_count);
47
48 maxwell3d.regs.vertex_id_base = 0x0;
49 maxwell3d.CallMethod(0x8e3, 0x640, true);
50 maxwell3d.CallMethod(0x8e4, 0x0, true);
51 maxwell3d.CallMethod(0x8e5, 0x0, true);
52}
53 48
54// Multidraw Indirect 49protected:
55void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 50 Maxwell3D& maxwell3d;
56 SCOPE_EXIT({ 51};
57 // Clean everything. 52
58 maxwell3d.regs.vertex_id_base = 0x0; 53/*
59 maxwell3d.CallMethod(0x8e3, 0x640, true); 54 * @note: these macros have two versions, a normal and extended version, with the extended version
60 maxwell3d.CallMethod(0x8e4, 0x0, true); 55 * also assigning the base vertex/instance.
61 maxwell3d.CallMethod(0x8e5, 0x0, true); 56 */
57template <bool extended>
58class HLE_DrawArraysIndirect final : public HLEMacroImpl {
59public:
60 explicit HLE_DrawArraysIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
61
62 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
63 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
64 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
65 Fallback(parameters);
66 return;
67 }
68
69 auto& params = maxwell3d.draw_manager->GetIndirectParams();
70 params.is_indexed = false;
71 params.include_count = false;
72 params.count_start_address = 0;
73 params.indirect_start_address = maxwell3d.GetMacroAddress(1);
74 params.buffer_size = 4 * sizeof(u32);
75 params.max_draw_counts = 1;
76 params.stride = 0;
77
78 if constexpr (extended) {
79 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
80 maxwell3d.SetHLEReplacementAttributeType(
81 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
82 }
83
84 maxwell3d.draw_manager->DrawArrayIndirect(topology);
85
86 if constexpr (extended) {
87 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
88 maxwell3d.replace_table.clear();
89 }
90 }
91
92private:
93 void Fallback(const std::vector<u32>& parameters) {
94 SCOPE_EXIT({
95 if (extended) {
96 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
97 maxwell3d.replace_table.clear();
98 }
99 });
100 maxwell3d.RefreshParameters();
101 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
102
103 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
104 const u32 vertex_first = parameters[3];
105 const u32 vertex_count = parameters[1];
106
107 if (!IsTopologySafe(topology) &&
108 static_cast<size_t>(maxwell3d.GetMaxCurrentVertices()) <
109 static_cast<size_t>(vertex_first) + static_cast<size_t>(vertex_count)) {
110 ASSERT_MSG(false, "Faulty draw!");
111 return;
112 }
113
114 const u32 base_instance = parameters[4];
115 if constexpr (extended) {
116 maxwell3d.regs.global_base_instance_index = base_instance;
117 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
118 maxwell3d.SetHLEReplacementAttributeType(
119 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
120 }
121
122 maxwell3d.draw_manager->DrawArray(topology, vertex_first, vertex_count, base_instance,
123 instance_count);
124
125 if constexpr (extended) {
126 maxwell3d.regs.global_base_instance_index = 0;
127 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
128 maxwell3d.replace_table.clear();
129 }
130 }
131};
132
133/*
134 * @note: these macros have two versions, a normal and extended version, with the extended version
135 * also assigning the base vertex/instance.
136 */
137template <bool extended>
138class HLE_DrawIndexedIndirect final : public HLEMacroImpl {
139public:
140 explicit HLE_DrawIndexedIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
141
142 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
143 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
144 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
145 Fallback(parameters);
146 return;
147 }
148
149 const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
150 const u32 element_base = parameters[4];
151 const u32 base_instance = parameters[5];
152 maxwell3d.regs.vertex_id_base = element_base;
153 maxwell3d.regs.global_base_vertex_index = element_base;
154 maxwell3d.regs.global_base_instance_index = base_instance;
155 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
156 if constexpr (extended) {
157 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
158 maxwell3d.SetHLEReplacementAttributeType(
159 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
160 maxwell3d.SetHLEReplacementAttributeType(
161 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
162 }
163 auto& params = maxwell3d.draw_manager->GetIndirectParams();
164 params.is_indexed = true;
165 params.include_count = false;
166 params.count_start_address = 0;
167 params.indirect_start_address = maxwell3d.GetMacroAddress(1);
168 params.buffer_size = 5 * sizeof(u32);
169 params.max_draw_counts = 1;
170 params.stride = 0;
62 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 171 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
63 }); 172 maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
64 const u32 start_indirect = parameters[0]; 173 maxwell3d.regs.vertex_id_base = 0x0;
65 const u32 end_indirect = parameters[1]; 174 maxwell3d.regs.global_base_vertex_index = 0x0;
66 if (start_indirect >= end_indirect) { 175 maxwell3d.regs.global_base_instance_index = 0x0;
67 // Nothing to do. 176 if constexpr (extended) {
68 return; 177 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
69 } 178 maxwell3d.replace_table.clear();
70 const u32 padding = parameters[3]; 179 }
71 const std::size_t max_draws = parameters[4]; 180 }
72 181
73 const u32 indirect_words = 5 + padding; 182private:
74 const std::size_t first_draw = start_indirect; 183 void Fallback(const std::vector<u32>& parameters) {
75 const std::size_t effective_draws = end_indirect - start_indirect; 184 maxwell3d.RefreshParameters();
76 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); 185 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
77 186 const u32 element_base = parameters[4];
78 for (std::size_t index = first_draw; index < last_draw; index++) { 187 const u32 base_instance = parameters[5];
79 const std::size_t base = index * indirect_words + 5; 188 maxwell3d.regs.vertex_id_base = element_base;
80 const u32 base_vertex = parameters[base + 3]; 189 maxwell3d.regs.global_base_vertex_index = element_base;
81 const u32 base_instance = parameters[base + 4]; 190 maxwell3d.regs.global_base_instance_index = base_instance;
82 maxwell3d.regs.vertex_id_base = base_vertex;
83 maxwell3d.CallMethod(0x8e3, 0x640, true);
84 maxwell3d.CallMethod(0x8e4, base_vertex, true);
85 maxwell3d.CallMethod(0x8e5, base_instance, true);
86 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 191 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
192 if constexpr (extended) {
193 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
194 maxwell3d.SetHLEReplacementAttributeType(
195 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
196 maxwell3d.SetHLEReplacementAttributeType(
197 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
198 }
199
87 maxwell3d.draw_manager->DrawIndex( 200 maxwell3d.draw_manager->DrawIndex(
88 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]), 201 static_cast<Tegra::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), parameters[3],
89 parameters[base + 2], parameters[base], base_vertex, base_instance, 202 parameters[1], element_base, base_instance, instance_count);
90 parameters[base + 1]); 203
204 maxwell3d.regs.vertex_id_base = 0x0;
205 maxwell3d.regs.global_base_vertex_index = 0x0;
206 maxwell3d.regs.global_base_instance_index = 0x0;
207 if constexpr (extended) {
208 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
209 maxwell3d.replace_table.clear();
210 }
91 } 211 }
92} 212};
93 213
94// Multi-layer Clear 214class HLE_MultiLayerClear final : public HLEMacroImpl {
95void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 215public:
96 ASSERT(parameters.size() == 1); 216 explicit HLE_MultiLayerClear(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
97 217
98 const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]}; 218 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
99 const u32 rt_index = clear_params.RT; 219 maxwell3d.RefreshParameters();
100 const u32 num_layers = maxwell3d.regs.rt[rt_index].depth; 220 ASSERT(parameters.size() == 1);
101 ASSERT(clear_params.layer == 0);
102 221
103 maxwell3d.regs.clear_surface.raw = clear_params.raw; 222 const Maxwell3D::Regs::ClearSurface clear_params{parameters[0]};
104 maxwell3d.draw_manager->Clear(num_layers); 223 const u32 rt_index = clear_params.RT;
105} 224 const u32 num_layers = maxwell3d.regs.rt[rt_index].depth;
225 ASSERT(clear_params.layer == 0);
226
227 maxwell3d.regs.clear_surface.raw = clear_params.raw;
228 maxwell3d.draw_manager->Clear(num_layers);
229 }
230};
231
232class HLE_MultiDrawIndexedIndirectCount final : public HLEMacroImpl {
233public:
234 explicit HLE_MultiDrawIndexedIndirectCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
235
236 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
237 const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
238 if (!IsTopologySafe(topology)) {
239 Fallback(parameters);
240 return;
241 }
242
243 const u32 start_indirect = parameters[0];
244 const u32 end_indirect = parameters[1];
245 if (start_indirect >= end_indirect) {
246 // Nothing to do.
247 return;
248 }
106 249
107constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{ 250 const u32 padding = parameters[3]; // padding is in words
108 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
109 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
110 {0x0217920100488FF7, &HLE_0217920100488FF7},
111 {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164},
112 {0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B},
113}};
114 251
115class HLEMacroImpl final : public CachedMacro { 252 // size of each indirect segment
253 const u32 indirect_words = 5 + padding;
254 const u32 stride = indirect_words * sizeof(u32);
255 const std::size_t draw_count = end_indirect - start_indirect;
256 const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
257 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
258 auto& params = maxwell3d.draw_manager->GetIndirectParams();
259 params.is_indexed = true;
260 params.include_count = true;
261 params.count_start_address = maxwell3d.GetMacroAddress(4);
262 params.indirect_start_address = maxwell3d.GetMacroAddress(5);
263 params.buffer_size = stride * draw_count;
264 params.max_draw_counts = draw_count;
265 params.stride = stride;
266 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
267 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
268 maxwell3d.SetHLEReplacementAttributeType(
269 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
270 maxwell3d.SetHLEReplacementAttributeType(
271 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
272 maxwell3d.SetHLEReplacementAttributeType(0, 0x648,
273 Maxwell3D::HLEReplacementAttributeType::DrawID);
274 maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
275 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
276 maxwell3d.replace_table.clear();
277 }
278
279private:
280 void Fallback(const std::vector<u32>& parameters) {
281 SCOPE_EXIT({
282 // Clean everything.
283 maxwell3d.regs.vertex_id_base = 0x0;
284 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
285 maxwell3d.replace_table.clear();
286 });
287 maxwell3d.RefreshParameters();
288 const u32 start_indirect = parameters[0];
289 const u32 end_indirect = parameters[1];
290 if (start_indirect >= end_indirect) {
291 // Nothing to do.
292 return;
293 }
294 const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
295 const u32 padding = parameters[3];
296 const std::size_t max_draws = parameters[4];
297
298 const u32 indirect_words = 5 + padding;
299 const std::size_t first_draw = start_indirect;
300 const std::size_t effective_draws = end_indirect - start_indirect;
301 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
302
303 for (std::size_t index = first_draw; index < last_draw; index++) {
304 const std::size_t base = index * indirect_words + 5;
305 const u32 base_vertex = parameters[base + 3];
306 const u32 base_instance = parameters[base + 4];
307 maxwell3d.regs.vertex_id_base = base_vertex;
308 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
309 maxwell3d.SetHLEReplacementAttributeType(
310 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
311 maxwell3d.SetHLEReplacementAttributeType(
312 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
313 maxwell3d.CallMethod(0x8e3, 0x648, true);
314 maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
315 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
316 maxwell3d.draw_manager->DrawIndex(topology, parameters[base + 2], parameters[base],
317 base_vertex, base_instance, parameters[base + 1]);
318 }
319 }
320};
321
322class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl {
323public:
324 explicit HLE_C713C83D8F63CCF3(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
325
326 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
327 maxwell3d.RefreshParameters();
328 const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
329 const u32 address = maxwell3d.regs.shadow_scratch[24];
330 auto& const_buffer = maxwell3d.regs.const_buffer;
331 const_buffer.size = 0x7000;
332 const_buffer.address_high = (address >> 24) & 0xFF;
333 const_buffer.address_low = address << 8;
334 const_buffer.offset = offset;
335 }
336};
337
338class HLE_D7333D26E0A93EDE final : public HLEMacroImpl {
339public:
340 explicit HLE_D7333D26E0A93EDE(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
341
342 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
343 maxwell3d.RefreshParameters();
344 const size_t index = parameters[0];
345 const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
346 const u32 size = maxwell3d.regs.shadow_scratch[47 + index];
347 auto& const_buffer = maxwell3d.regs.const_buffer;
348 const_buffer.size = size;
349 const_buffer.address_high = (address >> 24) & 0xFF;
350 const_buffer.address_low = address << 8;
351 }
352};
353
354class HLE_BindShader final : public HLEMacroImpl {
355public:
356 explicit HLE_BindShader(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
357
358 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
359 maxwell3d.RefreshParameters();
360 auto& regs = maxwell3d.regs;
361 const u32 index = parameters[0];
362 if ((parameters[1] - regs.shadow_scratch[28 + index]) == 0) {
363 return;
364 }
365
366 regs.pipelines[index & 0xF].offset = parameters[2];
367 maxwell3d.dirty.flags[VideoCommon::Dirty::Shaders] = true;
368 regs.shadow_scratch[28 + index] = parameters[1];
369 regs.shadow_scratch[34 + index] = parameters[2];
370
371 const u32 address = parameters[4];
372 auto& const_buffer = regs.const_buffer;
373 const_buffer.size = 0x10000;
374 const_buffer.address_high = (address >> 24) & 0xFF;
375 const_buffer.address_low = address << 8;
376
377 const size_t bind_group_id = parameters[3] & 0x7F;
378 auto& bind_group = regs.bind_groups[bind_group_id];
379 bind_group.raw_config = 0x11;
380 maxwell3d.ProcessCBBind(bind_group_id);
381 }
382};
383
384class HLE_SetRasterBoundingBox final : public HLEMacroImpl {
385public:
386 explicit HLE_SetRasterBoundingBox(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
387
388 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
389 maxwell3d.RefreshParameters();
390 const u32 raster_mode = parameters[0];
391 auto& regs = maxwell3d.regs;
392 const u32 raster_enabled = maxwell3d.regs.conservative_raster_enable;
393 const u32 scratch_data = maxwell3d.regs.shadow_scratch[52];
394 regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
395 regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
396 }
397};
398
399template <size_t base_size>
400class HLE_ClearConstBuffer final : public HLEMacroImpl {
401public:
402 explicit HLE_ClearConstBuffer(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
403
404 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
405 maxwell3d.RefreshParameters();
406 static constexpr std::array<u32, base_size> zeroes{};
407 auto& regs = maxwell3d.regs;
408 regs.const_buffer.size = static_cast<u32>(base_size);
409 regs.const_buffer.address_high = parameters[0];
410 regs.const_buffer.address_low = parameters[1];
411 regs.const_buffer.offset = 0;
412 maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
413 }
414};
415
416class HLE_ClearMemory final : public HLEMacroImpl {
116public: 417public:
117 explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) 418 explicit HLE_ClearMemory(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
118 : maxwell3d{maxwell3d_}, func{func_} {} 419
420 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
421 maxwell3d.RefreshParameters();
119 422
120 void Execute(const std::vector<u32>& parameters, u32 method) override { 423 const u32 needed_memory = parameters[2] / sizeof(u32);
121 func(maxwell3d, parameters); 424 if (needed_memory > zero_memory.size()) {
425 zero_memory.resize(needed_memory, 0);
426 }
427 auto& regs = maxwell3d.regs;
428 regs.upload.line_length_in = parameters[2];
429 regs.upload.line_count = 1;
430 regs.upload.dest.address_high = parameters[0];
431 regs.upload.dest.address_low = parameters[1];
432 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
433 maxwell3d.CallMultiMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
434 zero_memory.data(), needed_memory, needed_memory);
122 } 435 }
123 436
124private: 437private:
125 Engines::Maxwell3D& maxwell3d; 438 std::vector<u32> zero_memory;
126 HLEFunction func; 439};
440
441class HLE_TransformFeedbackSetup final : public HLEMacroImpl {
442public:
443 explicit HLE_TransformFeedbackSetup(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
444
445 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
446 maxwell3d.RefreshParameters();
447
448 auto& regs = maxwell3d.regs;
449 regs.transform_feedback_enabled = 1;
450 regs.transform_feedback.buffers[0].start_offset = 0;
451 regs.transform_feedback.buffers[1].start_offset = 0;
452 regs.transform_feedback.buffers[2].start_offset = 0;
453 regs.transform_feedback.buffers[3].start_offset = 0;
454
455 regs.upload.line_length_in = 4;
456 regs.upload.line_count = 1;
457 regs.upload.dest.address_high = parameters[0];
458 regs.upload.dest.address_low = parameters[1];
459 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
460 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
461 regs.transform_feedback.controls[0].stride, true);
462 }
127}; 463};
128 464
129} // Anonymous namespace 465} // Anonymous namespace
130 466
131HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} 467HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
468 builders.emplace(0x0D61FC9FAAC9FCADULL,
469 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
470 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
471 return std::make_unique<HLE_DrawArraysIndirect<false>>(maxwell3d__);
472 }));
473 builders.emplace(0x8A4D173EB99A8603ULL,
474 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
475 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
476 return std::make_unique<HLE_DrawArraysIndirect<true>>(maxwell3d__);
477 }));
478 builders.emplace(0x771BB18C62444DA0ULL,
479 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
480 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
481 return std::make_unique<HLE_DrawIndexedIndirect<false>>(maxwell3d__);
482 }));
483 builders.emplace(0x0217920100488FF7ULL,
484 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
485 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
486 return std::make_unique<HLE_DrawIndexedIndirect<true>>(maxwell3d__);
487 }));
488 builders.emplace(0x3F5E74B9C9A50164ULL,
489 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
490 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
491 return std::make_unique<HLE_MultiDrawIndexedIndirectCount>(
492 maxwell3d__);
493 }));
494 builders.emplace(0xEAD26C3E2109B06BULL,
495 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
496 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
497 return std::make_unique<HLE_MultiLayerClear>(maxwell3d__);
498 }));
499 builders.emplace(0xC713C83D8F63CCF3ULL,
500 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
501 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
502 return std::make_unique<HLE_C713C83D8F63CCF3>(maxwell3d__);
503 }));
504 builders.emplace(0xD7333D26E0A93EDEULL,
505 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
506 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
507 return std::make_unique<HLE_D7333D26E0A93EDE>(maxwell3d__);
508 }));
509 builders.emplace(0xEB29B2A09AA06D38ULL,
510 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
511 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
512 return std::make_unique<HLE_BindShader>(maxwell3d__);
513 }));
514 builders.emplace(0xDB1341DBEB4C8AF7ULL,
515 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
516 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
517 return std::make_unique<HLE_SetRasterBoundingBox>(maxwell3d__);
518 }));
519 builders.emplace(0x6C97861D891EDf7EULL,
520 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
521 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
522 return std::make_unique<HLE_ClearConstBuffer<0x5F00>>(maxwell3d__);
523 }));
524 builders.emplace(0xD246FDDF3A6173D7ULL,
525 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
526 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
527 return std::make_unique<HLE_ClearConstBuffer<0x7000>>(maxwell3d__);
528 }));
529 builders.emplace(0xEE4D0004BEC8ECF4ULL,
530 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
531 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
532 return std::make_unique<HLE_ClearMemory>(maxwell3d__);
533 }));
534 builders.emplace(0xFC0CF27F5FFAA661ULL,
535 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
536 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
537 return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__);
538 }));
539}
540
132HLEMacro::~HLEMacro() = default; 541HLEMacro::~HLEMacro() = default;
133 542
134std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { 543std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const {
135 const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), 544 const auto it = builders.find(hash);
136 [hash](const auto& pair) { return pair.first == hash; }); 545 if (it == builders.end()) {
137 if (it == hle_funcs.end()) {
138 return nullptr; 546 return nullptr;
139 } 547 }
140 return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); 548 return it->second(maxwell3d);
141} 549}
142 550
143} // namespace Tegra 551} // namespace Tegra
diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h
index 625332c9d..33f92fab1 100644
--- a/src/video_core/macro/macro_hle.h
+++ b/src/video_core/macro/macro_hle.h
@@ -3,7 +3,10 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
8#include <unordered_map>
9
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
9namespace Tegra { 12namespace Tegra {
@@ -23,6 +26,8 @@ public:
23 26
24private: 27private:
25 Engines::Maxwell3D& maxwell3d; 28 Engines::Maxwell3D& maxwell3d;
29 std::unordered_map<u64, std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>>
30 builders;
26}; 31};
27 32
28} // namespace Tegra 33} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 8c8dfcca6..83924475b 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
12#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
13#include "core/memory.h" 13#include "core/memory.h"
14#include "video_core/invalidation_accumulator.h"
14#include "video_core/memory_manager.h" 15#include "video_core/memory_manager.h"
15#include "video_core/rasterizer_interface.h" 16#include "video_core/rasterizer_interface.h"
16#include "video_core/renderer_base.h" 17#include "video_core/renderer_base.h"
@@ -25,7 +26,9 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
25 address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, 26 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 entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
27 page_bits != big_page_bits ? page_bits : 0}, 28 page_bits != big_page_bits ? page_bits : 0},
28 unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { 29 kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
30 1, std::memory_order_acq_rel)},
31 accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} {
29 address_space_size = 1ULL << address_space_bits; 32 address_space_size = 1ULL << address_space_bits;
30 page_size = 1ULL << page_bits; 33 page_size = 1ULL << page_bits;
31 page_mask = page_size - 1ULL; 34 page_mask = page_size - 1ULL;
@@ -41,11 +44,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
41 big_entries.resize(big_page_table_size / 32, 0); 44 big_entries.resize(big_page_table_size / 32, 0);
42 big_page_table_cpu.resize(big_page_table_size); 45 big_page_table_cpu.resize(big_page_table_size);
43 big_page_continous.resize(big_page_table_size / continous_bits, 0); 46 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); 47 entries.resize(page_table_size / 32, 0);
48 kinds.resize(page_table_size / 32, kind_valus);
49} 48}
50 49
51MemoryManager::~MemoryManager() = default; 50MemoryManager::~MemoryManager() = default;
@@ -83,38 +82,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
83} 82}
84 83
85PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { 84PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
86 auto entry = GetEntry<true>(gpu_addr); 85 return kind_map.GetValueAt(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 }
92}
93
94template <bool is_big_page>
95PTEKind MemoryManager::GetKind(size_t position) const {
96 if constexpr (is_big_page) {
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 }
105}
106
107template <bool is_big_page>
108void MemoryManager::SetKind(size_t position, PTEKind kind) {
109 if constexpr (is_big_page) {
110 position = position >> big_page_bits;
111 const size_t sub_index = position % 32;
112 big_kinds[position / 32][sub_index] = kind;
113 } else {
114 position = position >> page_bits;
115 const size_t sub_index = position % 32;
116 kinds[position / 32][sub_index] = kind;
117 }
118} 86}
119 87
120inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { 88inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
@@ -141,7 +109,6 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
141 const GPUVAddr current_gpu_addr = gpu_addr + offset; 109 const GPUVAddr current_gpu_addr = gpu_addr + offset;
142 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); 110 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
143 SetEntry<false>(current_gpu_addr, entry_type); 111 SetEntry<false>(current_gpu_addr, entry_type);
144 SetKind<false>(current_gpu_addr, kind);
145 if (current_entry_type != entry_type) { 112 if (current_entry_type != entry_type) {
146 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); 113 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size);
147 } 114 }
@@ -153,6 +120,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
153 } 120 }
154 remaining_size -= page_size; 121 remaining_size -= page_size;
155 } 122 }
123 kind_map.Map(gpu_addr, gpu_addr + size, kind);
156 return gpu_addr; 124 return gpu_addr;
157} 125}
158 126
@@ -164,7 +132,6 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
164 const GPUVAddr current_gpu_addr = gpu_addr + offset; 132 const GPUVAddr current_gpu_addr = gpu_addr + offset;
165 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); 133 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
166 SetEntry<true>(current_gpu_addr, entry_type); 134 SetEntry<true>(current_gpu_addr, entry_type);
167 SetKind<true>(current_gpu_addr, kind);
168 if (current_entry_type != entry_type) { 135 if (current_entry_type != entry_type) {
169 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); 136 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size);
170 } 137 }
@@ -193,6 +160,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
193 } 160 }
194 remaining_size -= big_page_size; 161 remaining_size -= big_page_size;
195 } 162 }
163 kind_map.Map(gpu_addr, gpu_addr + size, kind);
196 return gpu_addr; 164 return gpu_addr;
197} 165}
198 166
@@ -219,15 +187,12 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
219 if (size == 0) { 187 if (size == 0) {
220 return; 188 return;
221 } 189 }
222 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); 190 GetSubmappedRangeImpl<false>(gpu_addr, size, page_stash);
223
224 for (const auto& [map_addr, map_size] : submapped_ranges) {
225 // Flush and invalidate through the GPU interface, to be asynchronous if possible.
226 const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
227 ASSERT(cpu_addr);
228 191
229 rasterizer->UnmapMemory(*cpu_addr, map_size); 192 for (const auto& [map_addr, map_size] : page_stash) {
193 rasterizer->UnmapMemory(map_addr, map_size);
230 } 194 }
195 page_stash.clear();
231 196
232 BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); 197 BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
233 PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); 198 PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
@@ -325,9 +290,15 @@ template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typenam
325inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, 290inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
326 FuncMapped&& func_mapped, FuncReserved&& func_reserved, 291 FuncMapped&& func_mapped, FuncReserved&& func_reserved,
327 FuncUnmapped&& func_unmapped) const { 292 FuncUnmapped&& func_unmapped) const {
328 static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>; 293 using FuncMappedReturn =
329 static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>; 294 typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type;
330 static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>; 295 using FuncReservedReturn =
296 typename std::invoke_result<FuncReserved, std::size_t, std::size_t, std::size_t>::type;
297 using FuncUnmappedReturn =
298 typename std::invoke_result<FuncUnmapped, std::size_t, std::size_t, std::size_t>::type;
299 static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMappedReturn, bool>;
300 static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReservedReturn, bool>;
301 static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmappedReturn, bool>;
331 u64 used_page_size; 302 u64 used_page_size;
332 u64 used_page_mask; 303 u64 used_page_mask;
333 u64 used_page_bits; 304 u64 used_page_bits;
@@ -384,8 +355,8 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
384} 355}
385 356
386template <bool is_safe> 357template <bool is_safe>
387void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, 358void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
388 std::size_t size) const { 359 [[maybe_unused]] VideoCommon::CacheType which) const {
389 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, 360 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
390 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { 361 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
391 std::memset(dest_buffer, 0, copy_amount); 362 std::memset(dest_buffer, 0, copy_amount);
@@ -395,7 +366,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
395 const VAddr cpu_addr_base = 366 const VAddr cpu_addr_base =
396 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 367 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
397 if constexpr (is_safe) { 368 if constexpr (is_safe) {
398 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 369 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
399 } 370 }
400 u8* physical = memory.GetPointer(cpu_addr_base); 371 u8* physical = memory.GetPointer(cpu_addr_base);
401 std::memcpy(dest_buffer, physical, copy_amount); 372 std::memcpy(dest_buffer, physical, copy_amount);
@@ -405,7 +376,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
405 const VAddr cpu_addr_base = 376 const VAddr cpu_addr_base =
406 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 377 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
407 if constexpr (is_safe) { 378 if constexpr (is_safe) {
408 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 379 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
409 } 380 }
410 if (!IsBigPageContinous(page_index)) [[unlikely]] { 381 if (!IsBigPageContinous(page_index)) [[unlikely]] {
411 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); 382 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
@@ -423,18 +394,19 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
423 MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); 394 MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
424} 395}
425 396
426void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { 397void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
427 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size); 398 VideoCommon::CacheType which) const {
399 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
428} 400}
429 401
430void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 402void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
431 const std::size_t size) const { 403 const std::size_t size) const {
432 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size); 404 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
433} 405}
434 406
435template <bool is_safe> 407template <bool is_safe>
436void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, 408void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
437 std::size_t size) { 409 [[maybe_unused]] VideoCommon::CacheType which) {
438 auto just_advance = [&]([[maybe_unused]] std::size_t page_index, 410 auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
439 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { 411 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
440 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; 412 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
@@ -443,7 +415,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
443 const VAddr cpu_addr_base = 415 const VAddr cpu_addr_base =
444 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 416 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
445 if constexpr (is_safe) { 417 if constexpr (is_safe) {
446 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 418 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
447 } 419 }
448 u8* physical = memory.GetPointer(cpu_addr_base); 420 u8* physical = memory.GetPointer(cpu_addr_base);
449 std::memcpy(physical, src_buffer, copy_amount); 421 std::memcpy(physical, src_buffer, copy_amount);
@@ -453,7 +425,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
453 const VAddr cpu_addr_base = 425 const VAddr cpu_addr_base =
454 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 426 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
455 if constexpr (is_safe) { 427 if constexpr (is_safe) {
456 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 428 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
457 } 429 }
458 if (!IsBigPageContinous(page_index)) [[unlikely]] { 430 if (!IsBigPageContinous(page_index)) [[unlikely]] {
459 memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); 431 memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
@@ -471,16 +443,24 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
471 MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); 443 MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
472} 444}
473 445
474void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { 446void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
475 WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size); 447 VideoCommon::CacheType which) {
448 WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which);
476} 449}
477 450
478void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, 451void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
479 std::size_t size) { 452 std::size_t size) {
480 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size); 453 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
454}
455
456void MemoryManager::WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer,
457 std::size_t size) {
458 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
459 accumulator->Add(gpu_dest_addr, size);
481} 460}
482 461
483void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { 462void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size,
463 VideoCommon::CacheType which) const {
484 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 464 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
485 [[maybe_unused]] std::size_t offset, 465 [[maybe_unused]] std::size_t offset,
486 [[maybe_unused]] std::size_t copy_amount) {}; 466 [[maybe_unused]] std::size_t copy_amount) {};
@@ -488,12 +468,12 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
488 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 468 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
489 const VAddr cpu_addr_base = 469 const VAddr cpu_addr_base =
490 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 470 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
491 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 471 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
492 }; 472 };
493 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 473 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
494 const VAddr cpu_addr_base = 474 const VAddr cpu_addr_base =
495 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 475 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
496 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 476 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
497 }; 477 };
498 auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, 478 auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
499 std::size_t copy_amount) { 479 std::size_t copy_amount) {
@@ -503,7 +483,8 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
503 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); 483 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
504} 484}
505 485
506bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { 486bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
487 VideoCommon::CacheType which) const {
507 bool result = false; 488 bool result = false;
508 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 489 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
509 [[maybe_unused]] std::size_t offset, 490 [[maybe_unused]] std::size_t offset,
@@ -512,13 +493,13 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const {
512 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 493 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
513 const VAddr cpu_addr_base = 494 const VAddr cpu_addr_base =
514 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 495 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
515 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); 496 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
516 return result; 497 return result;
517 }; 498 };
518 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 499 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
519 const VAddr cpu_addr_base = 500 const VAddr cpu_addr_base =
520 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 501 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
521 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); 502 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
522 return result; 503 return result;
523 }; 504 };
524 auto check_short_pages = [&](std::size_t page_index, std::size_t offset, 505 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
@@ -571,7 +552,12 @@ size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
571 return range_so_far; 552 return range_so_far;
572} 553}
573 554
574void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { 555size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
556 return kind_map.GetContinousSizeFrom(gpu_addr);
557}
558
559void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
560 VideoCommon::CacheType which) const {
575 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 561 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
576 [[maybe_unused]] std::size_t offset, 562 [[maybe_unused]] std::size_t offset,
577 [[maybe_unused]] std::size_t copy_amount) {}; 563 [[maybe_unused]] std::size_t copy_amount) {};
@@ -579,12 +565,12 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
579 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 565 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
580 const VAddr cpu_addr_base = 566 const VAddr cpu_addr_base =
581 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 567 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
582 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 568 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
583 }; 569 };
584 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 570 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
585 const VAddr cpu_addr_base = 571 const VAddr cpu_addr_base =
586 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 572 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
587 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 573 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
588 }; 574 };
589 auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, 575 auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
590 std::size_t copy_amount) { 576 std::size_t copy_amount) {
@@ -594,14 +580,15 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
594 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); 580 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
595} 581}
596 582
597void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { 583void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
584 VideoCommon::CacheType which) {
598 std::vector<u8> tmp_buffer(size); 585 std::vector<u8> tmp_buffer(size);
599 ReadBlock(gpu_src_addr, tmp_buffer.data(), size); 586 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
600 587
601 // The output block must be flushed in case it has data modified from the GPU. 588 // The output block must be flushed in case it has data modified from the GPU.
602 // Fixes NPC geometry in Zombie Panic in Wonderland DX 589 // Fixes NPC geometry in Zombie Panic in Wonderland DX
603 FlushRegion(gpu_dest_addr, size); 590 FlushRegion(gpu_dest_addr, size, which);
604 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); 591 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which);
605} 592}
606 593
607bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { 594bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
@@ -681,7 +668,17 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
681std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( 668std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
682 GPUVAddr gpu_addr, std::size_t size) const { 669 GPUVAddr gpu_addr, std::size_t size) const {
683 std::vector<std::pair<GPUVAddr, std::size_t>> result{}; 670 std::vector<std::pair<GPUVAddr, std::size_t>> result{};
684 std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{}; 671 GetSubmappedRangeImpl<true>(gpu_addr, size, result);
672 return result;
673}
674
675template <bool is_gpu_address>
676void MemoryManager::GetSubmappedRangeImpl(
677 GPUVAddr gpu_addr, std::size_t size,
678 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>&
679 result) const {
680 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>
681 last_segment{};
685 std::optional<VAddr> old_page_addr{}; 682 std::optional<VAddr> old_page_addr{};
686 const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index, 683 const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
687 [[maybe_unused]] std::size_t offset, 684 [[maybe_unused]] std::size_t offset,
@@ -703,8 +700,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
703 } 700 }
704 old_page_addr = {cpu_addr_base + copy_amount}; 701 old_page_addr = {cpu_addr_base + copy_amount};
705 if (!last_segment) { 702 if (!last_segment) {
706 const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; 703 if constexpr (is_gpu_address) {
707 last_segment = {new_base_addr, copy_amount}; 704 const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset;
705 last_segment = {new_base_addr, copy_amount};
706 } else {
707 last_segment = {cpu_addr_base, copy_amount};
708 }
708 } else { 709 } else {
709 last_segment->second += copy_amount; 710 last_segment->second += copy_amount;
710 } 711 }
@@ -721,8 +722,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
721 } 722 }
722 old_page_addr = {cpu_addr_base + copy_amount}; 723 old_page_addr = {cpu_addr_base + copy_amount};
723 if (!last_segment) { 724 if (!last_segment) {
724 const GPUVAddr new_base_addr = (page_index << page_bits) + offset; 725 if constexpr (is_gpu_address) {
725 last_segment = {new_base_addr, copy_amount}; 726 const GPUVAddr new_base_addr = (page_index << page_bits) + offset;
727 last_segment = {new_base_addr, copy_amount};
728 } else {
729 last_segment = {cpu_addr_base, copy_amount};
730 }
726 } else { 731 } else {
727 last_segment->second += copy_amount; 732 last_segment->second += copy_amount;
728 } 733 }
@@ -733,7 +738,18 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
733 }; 738 };
734 MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages); 739 MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
735 split(0, 0, 0); 740 split(0, 0, 0);
736 return result; 741}
742
743void MemoryManager::FlushCaching() {
744 if (!accumulator->AnyAccumulated()) {
745 return;
746 }
747 accumulator->Callback([this](GPUVAddr addr, size_t size) {
748 GetSubmappedRangeImpl<false>(addr, size, page_stash);
749 });
750 rasterizer->InnerInvalidation(page_stash);
751 page_stash.clear();
752 accumulator->Clear();
737} 753}
738 754
739} // namespace Tegra 755} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index ab4bc9ec6..9ebfb6179 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -10,13 +10,19 @@
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/multi_level_page_table.h" 12#include "common/multi_level_page_table.h"
13#include "common/range_map.h"
13#include "common/virtual_buffer.h" 14#include "common/virtual_buffer.h"
15#include "video_core/cache_types.h"
14#include "video_core/pte_kind.h" 16#include "video_core/pte_kind.h"
15 17
16namespace VideoCore { 18namespace VideoCore {
17class RasterizerInterface; 19class RasterizerInterface;
18} 20}
19 21
22namespace VideoCommon {
23class InvalidationAccumulator;
24}
25
20namespace Core { 26namespace Core {
21class DeviceMemory; 27class DeviceMemory;
22namespace Memory { 28namespace Memory {
@@ -59,9 +65,12 @@ public:
59 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory 65 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory
60 * Flushes and Invalidations, respectively to each operation. 66 * Flushes and Invalidations, respectively to each operation.
61 */ 67 */
62 void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 68 void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
63 void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 69 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
64 void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); 70 void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
71 VideoCommon::CacheType which = VideoCommon::CacheType::All);
72 void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
73 VideoCommon::CacheType which = VideoCommon::CacheType::All);
65 74
66 /** 75 /**
67 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and 76 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@@ -75,6 +84,7 @@ public:
75 */ 84 */
76 void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 85 void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
77 void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 86 void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
87 void WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
78 88
79 /** 89 /**
80 * Checks if a gpu region can be simply read with a pointer. 90 * Checks if a gpu region can be simply read with a pointer.
@@ -104,11 +114,14 @@ public:
104 GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); 114 GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true);
105 void Unmap(GPUVAddr gpu_addr, std::size_t size); 115 void Unmap(GPUVAddr gpu_addr, std::size_t size);
106 116
107 void FlushRegion(GPUVAddr gpu_addr, size_t size) const; 117 void FlushRegion(GPUVAddr gpu_addr, size_t size,
118 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
108 119
109 void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; 120 void InvalidateRegion(GPUVAddr gpu_addr, size_t size,
121 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
110 122
111 bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; 123 bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
124 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
112 125
113 size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; 126 size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const;
114 127
@@ -118,16 +131,23 @@ public:
118 131
119 PTEKind GetPageKind(GPUVAddr gpu_addr) const; 132 PTEKind GetPageKind(GPUVAddr gpu_addr) const;
120 133
134 size_t GetMemoryLayoutSize(GPUVAddr gpu_addr,
135 size_t max_size = std::numeric_limits<size_t>::max()) const;
136
137 void FlushCaching();
138
121private: 139private:
122 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> 140 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
123 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
124 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; 142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
125 143
126 template <bool is_safe> 144 template <bool is_safe>
127 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
146 VideoCommon::CacheType which) const;
128 147
129 template <bool is_safe> 148 template <bool is_safe>
130 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 149 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
150 VideoCommon::CacheType which);
131 151
132 template <bool is_big_page> 152 template <bool is_big_page>
133 [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { 153 [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
@@ -141,6 +161,12 @@ private:
141 inline bool IsBigPageContinous(size_t big_page_index) const; 161 inline bool IsBigPageContinous(size_t big_page_index) const;
142 inline void SetBigPageContinous(size_t big_page_index, bool value); 162 inline void SetBigPageContinous(size_t big_page_index, bool value);
143 163
164 template <bool is_gpu_address>
165 void GetSubmappedRangeImpl(
166 GPUVAddr gpu_addr, std::size_t size,
167 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>&
168 result) const;
169
144 Core::System& system; 170 Core::System& system;
145 Core::Memory::Memory& memory; 171 Core::Memory::Memory& memory;
146 Core::DeviceMemory& device_memory; 172 Core::DeviceMemory& device_memory;
@@ -183,23 +209,17 @@ private:
183 template <bool is_big_page> 209 template <bool is_big_page>
184 inline void SetEntry(size_t position, EntryType entry); 210 inline void SetEntry(size_t position, EntryType entry);
185 211
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; 212 Common::MultiLevelPageTable<u32> page_table;
213 Common::RangeMap<GPUVAddr, PTEKind> kind_map;
196 Common::VirtualBuffer<u32> big_page_table_cpu; 214 Common::VirtualBuffer<u32> big_page_table_cpu;
197 215
198 std::vector<u64> big_page_continous; 216 std::vector<u64> big_page_continous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
199 218
200 constexpr static size_t continous_bits = 64; 219 constexpr static size_t continous_bits = 64;
201 220
202 const size_t unique_identifier; 221 const size_t unique_identifier;
222 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
203 223
204 static std::atomic<size_t> unique_identifier_generator; 224 static std::atomic<size_t> unique_identifier_generator;
205}; 225};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index b6907463c..33e2610bc 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -6,8 +6,10 @@
6#include <functional> 6#include <functional>
7#include <optional> 7#include <optional>
8#include <span> 8#include <span>
9#include <utility>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/polyfill_thread.h" 11#include "common/polyfill_thread.h"
12#include "video_core/cache_types.h"
11#include "video_core/engines/fermi_2d.h" 13#include "video_core/engines/fermi_2d.h"
12#include "video_core/gpu.h" 14#include "video_core/gpu.h"
13 15
@@ -42,6 +44,12 @@ public:
42 /// Dispatches a draw invocation 44 /// Dispatches a draw invocation
43 virtual void Draw(bool is_indexed, u32 instance_count) = 0; 45 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 46
47 /// Dispatches an indirect draw invocation
48 virtual void DrawIndirect() {}
49
50 /// Dispatches an draw texture invocation
51 virtual void DrawTexture() = 0;
52
45 /// Clear the current framebuffer 53 /// Clear the current framebuffer
46 virtual void Clear(u32 layer_count) = 0; 54 virtual void Clear(u32 layer_count) = 0;
47 55
@@ -80,13 +88,22 @@ public:
80 virtual void FlushAll() = 0; 88 virtual void FlushAll() = 0;
81 89
82 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 90 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
83 virtual void FlushRegion(VAddr addr, u64 size) = 0; 91 virtual void FlushRegion(VAddr addr, u64 size,
92 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
84 93
85 /// Check if the the specified memory area requires flushing to CPU Memory. 94 /// Check if the the specified memory area requires flushing to CPU Memory.
86 virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; 95 virtual bool MustFlushRegion(VAddr addr, u64 size,
96 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
87 97
88 /// Notify rasterizer that any caches of the specified region should be invalidated 98 /// Notify rasterizer that any caches of the specified region should be invalidated
89 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 99 virtual void InvalidateRegion(VAddr addr, u64 size,
100 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
101
102 virtual void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) {
103 for (const auto& [cpu_addr, size] : sequences) {
104 InvalidateRegion(cpu_addr, size);
105 }
106 }
90 107
91 /// Notify rasterizer that any caches of the specified region are desync with guest 108 /// Notify rasterizer that any caches of the specified region are desync with guest
92 virtual void OnCPUWrite(VAddr addr, u64 size) = 0; 109 virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
@@ -102,7 +119,8 @@ public:
102 119
103 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 120 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
104 /// and invalidated 121 /// and invalidated
105 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 122 virtual void FlushAndInvalidateRegion(
123 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
106 124
107 /// Notify the host renderer to wait for previous primitive and compute operations. 125 /// Notify the host renderer to wait for previous primitive and compute operations.
108 virtual void WaitForIdle() = 0; 126 virtual void WaitForIdle() = 0;
@@ -119,6 +137,10 @@ public:
119 /// Notify rasterizer that a frame is about to finish 137 /// Notify rasterizer that a frame is about to finish
120 virtual void TickFrame() = 0; 138 virtual void TickFrame() = 0;
121 139
140 virtual bool AccelerateConditionalRendering() {
141 return false;
142 }
143
122 /// Attempt to use a faster method to perform a surface copy 144 /// Attempt to use a faster method to perform a surface copy
123 [[nodiscard]] virtual bool AccelerateSurfaceCopy( 145 [[nodiscard]] virtual bool AccelerateSurfaceCopy(
124 const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, 146 const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst,
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index 9734d84bc..2b5c7defa 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -21,6 +21,7 @@ RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gp
21RasterizerNull::~RasterizerNull() = default; 21RasterizerNull::~RasterizerNull() = default;
22 22
23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} 23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
24void RasterizerNull::DrawTexture() {}
24void RasterizerNull::Clear(u32 layer_count) {} 25void RasterizerNull::Clear(u32 layer_count) {}
25void RasterizerNull::DispatchCompute() {} 26void RasterizerNull::DispatchCompute() {}
26void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} 27void RasterizerNull::ResetCounter(VideoCore::QueryType type) {}
@@ -39,11 +40,11 @@ void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr
39 u32 size) {} 40 u32 size) {}
40void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} 41void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {}
41void RasterizerNull::FlushAll() {} 42void RasterizerNull::FlushAll() {}
42void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} 43void RasterizerNull::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
43bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { 44bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {
44 return false; 45 return false;
45} 46}
46void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} 47void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
47void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} 48void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
48void RasterizerNull::InvalidateGPUCache() {} 49void RasterizerNull::InvalidateGPUCache() {}
49void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} 50void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
@@ -61,7 +62,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) {
61} 62}
62void RasterizerNull::SignalReference() {} 63void RasterizerNull::SignalReference() {}
63void RasterizerNull::ReleaseFences() {} 64void RasterizerNull::ReleaseFences() {}
64void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} 65void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
65void RasterizerNull::WaitForIdle() {} 66void RasterizerNull::WaitForIdle() {}
66void RasterizerNull::FragmentBarrier() {} 67void RasterizerNull::FragmentBarrier() {}
67void RasterizerNull::TiledCacheBarrier() {} 68void RasterizerNull::TiledCacheBarrier() {}
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index ecf77ba42..51f896e43 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -31,6 +31,7 @@ public:
31 ~RasterizerNull() override; 31 ~RasterizerNull() override;
32 32
33 void Draw(bool is_indexed, u32 instance_count) override; 33 void Draw(bool is_indexed, u32 instance_count) override;
34 void DrawTexture() override;
34 void Clear(u32 layer_count) override; 35 void Clear(u32 layer_count) override;
35 void DispatchCompute() override; 36 void DispatchCompute() override;
36 void ResetCounter(VideoCore::QueryType type) override; 37 void ResetCounter(VideoCore::QueryType type) override;
@@ -38,9 +39,12 @@ public:
38 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 39 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
39 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 40 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
40 void FlushAll() override; 41 void FlushAll() override;
41 void FlushRegion(VAddr addr, u64 size) override; 42 void FlushRegion(VAddr addr, u64 size,
42 bool MustFlushRegion(VAddr addr, u64 size) override; 43 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
43 void InvalidateRegion(VAddr addr, u64 size) override; 44 bool MustFlushRegion(VAddr addr, u64 size,
45 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
46 void InvalidateRegion(VAddr addr, u64 size,
47 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
44 void OnCPUWrite(VAddr addr, u64 size) override; 48 void OnCPUWrite(VAddr addr, u64 size) override;
45 void InvalidateGPUCache() override; 49 void InvalidateGPUCache() override;
46 void UnmapMemory(VAddr addr, u64 size) override; 50 void UnmapMemory(VAddr addr, u64 size) override;
@@ -50,7 +54,8 @@ public:
50 void SignalSyncPoint(u32 value) override; 54 void SignalSyncPoint(u32 value) override;
51 void SignalReference() override; 55 void SignalReference() override;
52 void ReleaseFences() override; 56 void ReleaseFences() override;
53 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 57 void FlushAndInvalidateRegion(
58 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
54 void WaitForIdle() override; 59 void WaitForIdle() override;
55 void FragmentBarrier() override; 60 void FragmentBarrier() override;
56 void TiledCacheBarrier() override; 61 void TiledCacheBarrier() override;
diff --git a/src/video_core/renderer_opengl/blit_image.cpp b/src/video_core/renderer_opengl/blit_image.cpp
new file mode 100644
index 000000000..9a560a73b
--- /dev/null
+++ b/src/video_core/renderer_opengl/blit_image.cpp
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5
6#include "video_core/host_shaders/blit_color_float_frag.h"
7#include "video_core/host_shaders/full_screen_triangle_vert.h"
8#include "video_core/renderer_opengl/blit_image.h"
9#include "video_core/renderer_opengl/gl_shader_manager.h"
10#include "video_core/renderer_opengl/gl_shader_util.h"
11
12namespace OpenGL {
13
14BlitImageHelper::BlitImageHelper(ProgramManager& program_manager_)
15 : program_manager(program_manager_),
16 full_screen_vert(CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER)),
17 blit_color_to_color_frag(
18 CreateProgram(HostShaders::BLIT_COLOR_FLOAT_FRAG, GL_FRAGMENT_SHADER)) {}
19
20BlitImageHelper::~BlitImageHelper() = default;
21
22void BlitImageHelper::BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
23 const Region2D& dst_region, const Region2D& src_region,
24 const Extent3D& src_size) {
25 glEnable(GL_CULL_FACE);
26 glDisable(GL_COLOR_LOGIC_OP);
27 glDisable(GL_DEPTH_TEST);
28 glDisable(GL_STENCIL_TEST);
29 glDisable(GL_POLYGON_OFFSET_FILL);
30 glDisable(GL_RASTERIZER_DISCARD);
31 glDisable(GL_ALPHA_TEST);
32 glDisablei(GL_BLEND, 0);
33 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
34 glCullFace(GL_BACK);
35 glFrontFace(GL_CW);
36 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
37 glDepthRangeIndexed(0, 0.0, 0.0);
38
39 program_manager.BindPresentPrograms(full_screen_vert.handle, blit_color_to_color_frag.handle);
40 glProgramUniform2f(full_screen_vert.handle, 0,
41 static_cast<float>(src_region.end.x - src_region.start.x) /
42 static_cast<float>(src_size.width),
43 static_cast<float>(src_region.end.y - src_region.start.y) /
44 static_cast<float>(src_size.height));
45 glProgramUniform2f(full_screen_vert.handle, 1,
46 static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
47 static_cast<float>(src_region.start.y) /
48 static_cast<float>(src_size.height));
49 glViewport(std::min(dst_region.start.x, dst_region.end.x),
50 std::min(dst_region.start.y, dst_region.end.y),
51 std::abs(dst_region.end.x - dst_region.start.x),
52 std::abs(dst_region.end.y - dst_region.start.y));
53 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
54 glBindSampler(0, src_sampler);
55 glBindTextureUnit(0, src_image_view);
56 glClear(GL_COLOR_BUFFER_BIT);
57 glDrawArrays(GL_TRIANGLES, 0, 3);
58}
59} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/blit_image.h b/src/video_core/renderer_opengl/blit_image.h
new file mode 100644
index 000000000..5a2b12d16
--- /dev/null
+++ b/src/video_core/renderer_opengl/blit_image.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <glad/glad.h>
7
8#include "video_core/engines/fermi_2d.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/texture_cache/types.h"
11
12namespace OpenGL {
13
14using VideoCommon::Extent3D;
15using VideoCommon::Offset2D;
16using VideoCommon::Region2D;
17
18class ProgramManager;
19class Framebuffer;
20class ImageView;
21
22class BlitImageHelper {
23public:
24 explicit BlitImageHelper(ProgramManager& program_manager);
25 ~BlitImageHelper();
26
27 void BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
28 const Region2D& dst_region, const Region2D& src_region,
29 const Extent3D& src_size);
30
31private:
32 ProgramManager& program_manager;
33
34 OGLProgram full_screen_vert;
35 OGLProgram blit_color_to_color_frag;
36};
37
38} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index a8c3f8b67..bb1962073 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -160,6 +160,10 @@ public:
160 return device.CanReportMemoryUsage(); 160 return device.CanReportMemoryUsage();
161 } 161 }
162 162
163 u32 GetStorageBufferAlignment() const {
164 return static_cast<u32>(device.GetShaderStorageBufferAlignment());
165 }
166
163private: 167private:
164 static constexpr std::array PABO_LUT{ 168 static constexpr std::array PABO_LUT{
165 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 169 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index cee5c3247..22ed16ebf 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -166,6 +166,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
166 has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64"); 166 has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64");
167 has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float; 167 has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float;
168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; 168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
169 has_draw_texture = GLAD_GL_NV_draw_texture;
169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; 170 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
170 need_fastmath_off = is_nvidia; 171 need_fastmath_off = is_nvidia;
171 can_report_memory = GLAD_GL_NVX_gpu_memory_info; 172 can_report_memory = GLAD_GL_NVX_gpu_memory_info;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 2a72d84be..3ff8cad83 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -4,6 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include <cstddef> 6#include <cstddef>
7#include <string>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
9#include "shader_recompiler/stage.h" 11#include "shader_recompiler/stage.h"
@@ -146,6 +148,10 @@ public:
146 return has_sparse_texture_2; 148 return has_sparse_texture_2;
147 } 149 }
148 150
151 bool HasDrawTexture() const {
152 return has_draw_texture;
153 }
154
149 bool IsWarpSizePotentiallyLargerThanGuest() const { 155 bool IsWarpSizePotentiallyLargerThanGuest() const {
150 return warp_size_potentially_larger_than_guest; 156 return warp_size_potentially_larger_than_guest;
151 } 157 }
@@ -216,6 +222,7 @@ private:
216 bool has_shader_int64{}; 222 bool has_shader_int64{};
217 bool has_amd_shader_half_float{}; 223 bool has_amd_shader_half_float{};
218 bool has_sparse_texture_2{}; 224 bool has_sparse_texture_2{};
225 bool has_draw_texture{};
219 bool warp_size_potentially_larger_than_guest{}; 226 bool warp_size_potentially_larger_than_guest{};
220 bool need_fastmath_off{}; 227 bool need_fastmath_off{};
221 bool has_cbuf_ftou_bug{}; 228 bool has_cbuf_ftou_bug{};
diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp
new file mode 100644
index 000000000..77262dcf1
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/renderer_opengl/gl_fsr.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
9
10namespace OpenGL {
11using namespace FSR;
12
13using FsrConstants = std::array<u32, 4 * 4>;
14
15FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
16 std::string_view fsr_rcas_source)
17 : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
18 fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
19 fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
20 glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
21 glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
22}
23
24FSR::~FSR() = default;
25
26void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
27 u32 input_image_width, u32 input_image_height,
28 const Common::Rectangle<int>& crop_rect) {
29
30 const auto output_image_width = screen.GetWidth();
31 const auto output_image_height = screen.GetHeight();
32
33 if (fsr_intermediate_tex.handle) {
34 GLint fsr_tex_width, fsr_tex_height;
35 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
36 &fsr_tex_width);
37 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
38 &fsr_tex_height);
39 if (static_cast<u32>(fsr_tex_width) != output_image_width ||
40 static_cast<u32>(fsr_tex_height) != output_image_height) {
41 fsr_intermediate_tex.Release();
42 }
43 }
44 if (!fsr_intermediate_tex.handle) {
45 fsr_intermediate_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
47 output_image_height);
48 glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
49 fsr_intermediate_tex.handle, 0);
50 }
51
52 GLint old_draw_fb;
53 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
54
55 glFrontFace(GL_CW);
56 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
57 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
58 static_cast<GLfloat>(output_image_height));
59
60 FsrConstants constants;
61 FsrEasuConOffset(
62 constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
63
64 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
65 static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
66 static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
67 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
68
69 glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
70
71 program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
72 glDrawArrays(GL_TRIANGLES, 0, 3);
73
74 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
75 glBindTextureUnit(0, fsr_intermediate_tex.handle);
76
77 const float sharpening =
78 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
79
80 FsrRcasCon(constants.data(), sharpening);
81 glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
82}
83
84void FSR::InitBuffers() {
85 fsr_framebuffer.Create();
86}
87
88void FSR::ReleaseBuffers() {
89 fsr_framebuffer.Release();
90 fsr_intermediate_tex.Release();
91}
92
93const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
94 return fsr_rcas_frag;
95}
96
97bool FSR::AreBuffersInitialized() const noexcept {
98 return fsr_framebuffer.handle;
99}
100
101} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h
new file mode 100644
index 000000000..1f6ae3115
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
20 std::string_view fsr_rcas_source);
21 ~FSR();
22
23 void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
24 u32 input_image_width, u32 input_image_height,
25 const Common::Rectangle<int>& crop_rect);
26
27 void InitBuffers();
28
29 void ReleaseBuffers();
30
31 [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
32
33 [[nodiscard]] bool AreBuffersInitialized() const noexcept;
34
35private:
36 OGLFramebuffer fsr_framebuffer;
37 OGLProgram fsr_vertex;
38 OGLProgram fsr_easu_frag;
39 OGLProgram fsr_rcas_frag;
40 OGLTexture fsr_intermediate_tex;
41};
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index ea53ddb46..1c06b3655 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -40,6 +40,7 @@ struct GraphicsPipelineKey {
40 BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; 40 BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive;
41 BitField<8, 2, Maxwell::Tessellation::Spacing> 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 BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
43 }; 44 };
44 std::array<u32, 3> padding; 45 std::array<u32, 3> padding;
45 VideoCommon::TransformFeedbackState xfb_state; 46 VideoCommon::TransformFeedbackState xfb_state;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a44b8c454..7bced675c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -64,7 +64,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, 64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
65 state_tracker, gpu.ShaderNotify()), 65 state_tracker, gpu.ShaderNotify()),
66 query_cache(*this), accelerate_dma(buffer_cache), 66 query_cache(*this), accelerate_dma(buffer_cache),
67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} 67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
68 blit_image(program_manager_) {}
68 69
69RasterizerOpenGL::~RasterizerOpenGL() = default; 70RasterizerOpenGL::~RasterizerOpenGL() = default;
70 71
@@ -139,6 +140,7 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
139void RasterizerOpenGL::Clear(u32 layer_count) { 140void RasterizerOpenGL::Clear(u32 layer_count) {
140 MICROPROFILE_SCOPE(OpenGL_Clears); 141 MICROPROFILE_SCOPE(OpenGL_Clears);
141 142
143 gpu_memory->FlushCaching();
142 const auto& regs = maxwell3d->regs; 144 const auto& regs = maxwell3d->regs;
143 bool use_color{}; 145 bool use_color{};
144 bool use_depth{}; 146 bool use_depth{};
@@ -202,10 +204,12 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
202 ++num_queued_commands; 204 ++num_queued_commands;
203} 205}
204 206
205void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { 207template <typename Func>
208void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
206 MICROPROFILE_SCOPE(OpenGL_Drawing); 209 MICROPROFILE_SCOPE(OpenGL_Drawing);
207 210
208 SCOPE_EXIT({ gpu.TickWork(); }); 211 SCOPE_EXIT({ gpu.TickWork(); });
212 gpu_memory->FlushCaching();
209 query_cache.UpdateCounters(); 213 query_cache.UpdateCounters();
210 214
211 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 215 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -226,49 +230,140 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
226 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology); 230 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
227 BeginTransformFeedback(pipeline, primitive_mode); 231 BeginTransformFeedback(pipeline, primitive_mode);
228 232
229 const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance); 233 draw_func(primitive_mode);
230 const GLsizei num_instances = static_cast<GLsizei>(instance_count); 234
231 if (is_indexed) { 235 EndTransformFeedback();
232 const GLint base_vertex = static_cast<GLint>(draw_state.base_index); 236
233 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count); 237 ++num_queued_commands;
234 const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); 238 has_written_global_memory |= pipeline->WritesGlobalMemory();
235 const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format); 239}
236 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { 240
237 glDrawElements(primitive_mode, num_vertices, format, offset); 241void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
238 } else if (num_instances == 1 && base_instance == 0) { 242 PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
239 glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex); 243 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
240 } else if (base_vertex == 0 && base_instance == 0) { 244 const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance);
241 glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances); 245 const GLsizei num_instances = static_cast<GLsizei>(instance_count);
242 } else if (base_vertex == 0) { 246 if (is_indexed) {
243 glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset, 247 const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
244 num_instances, base_instance); 248 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
245 } else if (base_instance == 0) { 249 const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
246 glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset, 250 const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
247 num_instances, base_vertex); 251 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
252 glDrawElements(primitive_mode, num_vertices, format, offset);
253 } else if (num_instances == 1 && base_instance == 0) {
254 glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
255 } else if (base_vertex == 0 && base_instance == 0) {
256 glDrawElementsInstanced(primitive_mode, num_vertices, format, offset,
257 num_instances);
258 } else if (base_vertex == 0) {
259 glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
260 num_instances, base_instance);
261 } else if (base_instance == 0) {
262 glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
263 num_instances, base_vertex);
264 } else {
265 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
266 offset, num_instances, base_vertex,
267 base_instance);
268 }
248 } else { 269 } else {
249 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format, 270 const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
250 offset, num_instances, base_vertex, 271 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
251 base_instance); 272 if (num_instances == 1 && base_instance == 0) {
273 glDrawArrays(primitive_mode, base_vertex, num_vertices);
274 } else if (base_instance == 0) {
275 glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
276 } else {
277 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
278 num_instances, base_instance);
279 }
252 } 280 }
253 } else { 281 });
254 const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first); 282}
255 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count); 283
256 if (num_instances == 1 && base_instance == 0) { 284void RasterizerOpenGL::DrawIndirect() {
257 glDrawArrays(primitive_mode, base_vertex, num_vertices); 285 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
258 } else if (base_instance == 0) { 286 buffer_cache.SetDrawIndirect(&params);
259 glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances); 287 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
288 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
289 const GLvoid* const gl_offset =
290 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
291 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->Handle());
292 if (params.include_count) {
293 const auto [draw_buffer, offset_base] = buffer_cache.GetDrawIndirectCount();
294 glBindBuffer(GL_PARAMETER_BUFFER, draw_buffer->Handle());
295
296 if (params.is_indexed) {
297 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
298 glMultiDrawElementsIndirectCount(primitive_mode, format, gl_offset,
299 static_cast<GLintptr>(offset_base),
300 static_cast<GLsizei>(params.max_draw_counts),
301 static_cast<GLsizei>(params.stride));
302 } else {
303 glMultiDrawArraysIndirectCount(primitive_mode, gl_offset,
304 static_cast<GLintptr>(offset_base),
305 static_cast<GLsizei>(params.max_draw_counts),
306 static_cast<GLsizei>(params.stride));
307 }
308 return;
309 }
310 if (params.is_indexed) {
311 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
312 glMultiDrawElementsIndirect(primitive_mode, format, gl_offset,
313 static_cast<GLsizei>(params.max_draw_counts),
314 static_cast<GLsizei>(params.stride));
260 } else { 315 } else {
261 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, 316 glMultiDrawArraysIndirect(primitive_mode, gl_offset,
262 num_instances, base_instance); 317 static_cast<GLsizei>(params.max_draw_counts),
318 static_cast<GLsizei>(params.stride));
263 } 319 }
320 });
321 buffer_cache.SetDrawIndirect(nullptr);
322}
323
324void RasterizerOpenGL::DrawTexture() {
325 MICROPROFILE_SCOPE(OpenGL_Drawing);
326
327 SCOPE_EXIT({ gpu.TickWork(); });
328 query_cache.UpdateCounters();
329
330 texture_cache.SynchronizeGraphicsDescriptors();
331 texture_cache.UpdateRenderTargets(false);
332
333 SyncState();
334
335 const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
336 const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
337 const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
338
339 if (device.HasDrawTexture()) {
340 state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
341
342 glDrawTextureNV(texture.DefaultHandle(), sampler->Handle(), draw_texture_state.dst_x0,
343 draw_texture_state.dst_y0, draw_texture_state.dst_x1,
344 draw_texture_state.dst_y1, 0,
345 draw_texture_state.src_x0 / static_cast<float>(texture.size.width),
346 draw_texture_state.src_y0 / static_cast<float>(texture.size.height),
347 draw_texture_state.src_x1 / static_cast<float>(texture.size.width),
348 draw_texture_state.src_y1 / static_cast<float>(texture.size.height));
349 } else {
350 Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
351 .y = static_cast<s32>(draw_texture_state.dst_y0)},
352 Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
353 .y = static_cast<s32>(draw_texture_state.dst_y1)}};
354 Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
355 .y = static_cast<s32>(draw_texture_state.src_y0)},
356 Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
357 .y = static_cast<s32>(draw_texture_state.src_y1)}};
358 blit_image.BlitColor(texture_cache.GetFramebuffer()->Handle(), texture.DefaultHandle(),
359 sampler->Handle(), dst_region, src_region, texture.size);
264 } 360 }
265 EndTransformFeedback();
266 361
267 ++num_queued_commands; 362 ++num_queued_commands;
268 has_written_global_memory |= pipeline->WritesGlobalMemory();
269} 363}
270 364
271void RasterizerOpenGL::DispatchCompute() { 365void RasterizerOpenGL::DispatchCompute() {
366 gpu_memory->FlushCaching();
272 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; 367 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()};
273 if (!pipeline) { 368 if (!pipeline) {
274 return; 369 return;
@@ -302,46 +397,60 @@ void RasterizerOpenGL::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
302 397
303void RasterizerOpenGL::FlushAll() {} 398void RasterizerOpenGL::FlushAll() {}
304 399
305void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 400void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
306 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 401 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
307 if (addr == 0 || size == 0) { 402 if (addr == 0 || size == 0) {
308 return; 403 return;
309 } 404 }
310 { 405 if (True(which & VideoCommon::CacheType::TextureCache)) {
311 std::scoped_lock lock{texture_cache.mutex}; 406 std::scoped_lock lock{texture_cache.mutex};
312 texture_cache.DownloadMemory(addr, size); 407 texture_cache.DownloadMemory(addr, size);
313 } 408 }
314 { 409 if ((True(which & VideoCommon::CacheType::BufferCache))) {
315 std::scoped_lock lock{buffer_cache.mutex}; 410 std::scoped_lock lock{buffer_cache.mutex};
316 buffer_cache.DownloadMemory(addr, size); 411 buffer_cache.DownloadMemory(addr, size);
317 } 412 }
318 query_cache.FlushRegion(addr, size); 413 if ((True(which & VideoCommon::CacheType::QueryCache))) {
414 query_cache.FlushRegion(addr, size);
415 }
319} 416}
320 417
321bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { 418bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
322 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 419 if ((True(which & VideoCommon::CacheType::BufferCache))) {
420 std::scoped_lock lock{buffer_cache.mutex};
421 if (buffer_cache.IsRegionGpuModified(addr, size)) {
422 return true;
423 }
424 }
323 if (!Settings::IsGPULevelHigh()) { 425 if (!Settings::IsGPULevelHigh()) {
324 return buffer_cache.IsRegionGpuModified(addr, size); 426 return false;
427 }
428 if (True(which & VideoCommon::CacheType::TextureCache)) {
429 std::scoped_lock lock{texture_cache.mutex};
430 return texture_cache.IsRegionGpuModified(addr, size);
325 } 431 }
326 return texture_cache.IsRegionGpuModified(addr, size) || 432 return false;
327 buffer_cache.IsRegionGpuModified(addr, size);
328} 433}
329 434
330void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 435void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
331 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 436 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
332 if (addr == 0 || size == 0) { 437 if (addr == 0 || size == 0) {
333 return; 438 return;
334 } 439 }
335 { 440 if (True(which & VideoCommon::CacheType::TextureCache)) {
336 std::scoped_lock lock{texture_cache.mutex}; 441 std::scoped_lock lock{texture_cache.mutex};
337 texture_cache.WriteMemory(addr, size); 442 texture_cache.WriteMemory(addr, size);
338 } 443 }
339 { 444 if (True(which & VideoCommon::CacheType::BufferCache)) {
340 std::scoped_lock lock{buffer_cache.mutex}; 445 std::scoped_lock lock{buffer_cache.mutex};
341 buffer_cache.WriteMemory(addr, size); 446 buffer_cache.WriteMemory(addr, size);
342 } 447 }
343 shader_cache.InvalidateRegion(addr, size); 448 if (True(which & VideoCommon::CacheType::ShaderCache)) {
344 query_cache.InvalidateRegion(addr, size); 449 shader_cache.InvalidateRegion(addr, size);
450 }
451 if (True(which & VideoCommon::CacheType::QueryCache)) {
452 query_cache.InvalidateRegion(addr, size);
453 }
345} 454}
346 455
347void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { 456void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
@@ -408,11 +517,12 @@ void RasterizerOpenGL::ReleaseFences() {
408 fence_manager.WaitPendingFences(); 517 fence_manager.WaitPendingFences();
409} 518}
410 519
411void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 520void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size,
521 VideoCommon::CacheType which) {
412 if (Settings::IsGPULevelExtreme()) { 522 if (Settings::IsGPULevelExtreme()) {
413 FlushRegion(addr, size); 523 FlushRegion(addr, size, which);
414 } 524 }
415 InvalidateRegion(addr, size); 525 InvalidateRegion(addr, size, which);
416} 526}
417 527
418void RasterizerOpenGL::WaitForIdle() { 528void RasterizerOpenGL::WaitForIdle() {
@@ -460,6 +570,22 @@ void RasterizerOpenGL::TickFrame() {
460 } 570 }
461} 571}
462 572
573bool RasterizerOpenGL::AccelerateConditionalRendering() {
574 gpu_memory->FlushCaching();
575 if (Settings::IsGPULevelHigh()) {
576 // Reimplement Host conditional rendering.
577 return false;
578 }
579 // Medium / Low Hack: stub any checks on queries writen into the buffer cache.
580 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
581 Maxwell::ReportSemaphore::Compare cmp;
582 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
583 VideoCommon::CacheType::BufferCache)) {
584 return true;
585 }
586 return false;
587}
588
463bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 589bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
464 const Tegra::Engines::Fermi2D::Surface& dst, 590 const Tegra::Engines::Fermi2D::Surface& dst,
465 const Tegra::Engines::Fermi2D::Config& copy_config) { 591 const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -481,7 +607,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
481 } 607 }
482 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); 608 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
483 { 609 {
484 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 610 std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
485 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 611 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
486 buffer_cache.WriteMemory(*cpu_addr, copy_size); 612 buffer_cache.WriteMemory(*cpu_addr, copy_size);
487 } 613 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index fc183c3ca..0c45832ae 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -16,6 +16,7 @@
16#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
17#include "video_core/rasterizer_accelerated.h" 17#include "video_core/rasterizer_accelerated.h"
18#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
19#include "video_core/renderer_opengl/blit_image.h"
19#include "video_core/renderer_opengl/gl_buffer_cache.h" 20#include "video_core/renderer_opengl/gl_buffer_cache.h"
20#include "video_core/renderer_opengl/gl_device.h" 21#include "video_core/renderer_opengl/gl_device.h"
21#include "video_core/renderer_opengl/gl_fence_manager.h" 22#include "video_core/renderer_opengl/gl_fence_manager.h"
@@ -69,6 +70,8 @@ public:
69 ~RasterizerOpenGL() override; 70 ~RasterizerOpenGL() override;
70 71
71 void Draw(bool is_indexed, u32 instance_count) override; 72 void Draw(bool is_indexed, u32 instance_count) override;
73 void DrawIndirect() override;
74 void DrawTexture() override;
72 void Clear(u32 layer_count) override; 75 void Clear(u32 layer_count) override;
73 void DispatchCompute() override; 76 void DispatchCompute() override;
74 void ResetCounter(VideoCore::QueryType type) override; 77 void ResetCounter(VideoCore::QueryType type) override;
@@ -76,9 +79,12 @@ public:
76 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 79 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
77 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 80 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
78 void FlushAll() override; 81 void FlushAll() override;
79 void FlushRegion(VAddr addr, u64 size) override; 82 void FlushRegion(VAddr addr, u64 size,
80 bool MustFlushRegion(VAddr addr, u64 size) override; 83 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
81 void InvalidateRegion(VAddr addr, u64 size) override; 84 bool MustFlushRegion(VAddr addr, u64 size,
85 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
86 void InvalidateRegion(VAddr addr, u64 size,
87 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
82 void OnCPUWrite(VAddr addr, u64 size) override; 88 void OnCPUWrite(VAddr addr, u64 size) override;
83 void InvalidateGPUCache() override; 89 void InvalidateGPUCache() override;
84 void UnmapMemory(VAddr addr, u64 size) override; 90 void UnmapMemory(VAddr addr, u64 size) override;
@@ -88,12 +94,14 @@ public:
88 void SignalSyncPoint(u32 value) override; 94 void SignalSyncPoint(u32 value) override;
89 void SignalReference() override; 95 void SignalReference() override;
90 void ReleaseFences() override; 96 void ReleaseFences() override;
91 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 97 void FlushAndInvalidateRegion(
98 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
92 void WaitForIdle() override; 99 void WaitForIdle() override;
93 void FragmentBarrier() override; 100 void FragmentBarrier() override;
94 void TiledCacheBarrier() override; 101 void TiledCacheBarrier() override;
95 void FlushCommands() override; 102 void FlushCommands() override;
96 void TickFrame() override; 103 void TickFrame() override;
104 bool AccelerateConditionalRendering() override;
97 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 105 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
98 const Tegra::Engines::Fermi2D::Surface& dst, 106 const Tegra::Engines::Fermi2D::Surface& dst,
99 const Tegra::Engines::Fermi2D::Config& copy_config) override; 107 const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -121,6 +129,9 @@ private:
121 static constexpr size_t MAX_IMAGES = 48; 129 static constexpr size_t MAX_IMAGES = 48;
122 static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES; 130 static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES;
123 131
132 template <typename Func>
133 void PrepareDraw(bool is_indexed, Func&&);
134
124 /// Syncs state to match guest's 135 /// Syncs state to match guest's
125 void SyncState(); 136 void SyncState();
126 137
@@ -215,6 +226,8 @@ private:
215 AccelerateDMA accelerate_dma; 226 AccelerateDMA accelerate_dma;
216 FenceManagerOpenGL fence_manager; 227 FenceManagerOpenGL fence_manager;
217 228
229 BlitImageHelper blit_image;
230
218 boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices; 231 boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices;
219 std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids; 232 std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids;
220 boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles; 233 boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index f8868a012..7dd854e0f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
51using VideoCommon::SerializePipeline; 51using VideoCommon::SerializePipeline;
52using Context = ShaderContext::Context; 52using Context = ShaderContext::Context;
53 53
54constexpr u32 CACHE_VERSION = 7; 54constexpr u32 CACHE_VERSION = 9;
55 55
56template <typename Container> 56template <typename Container>
57auto MakeSpan(Container& container) { 57auto MakeSpan(Container& container) {
@@ -236,6 +236,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
236 .needs_demote_reorder = device.IsAmd(), 236 .needs_demote_reorder = device.IsAmd(),
237 .support_snorm_render_buffer = false, 237 .support_snorm_render_buffer = false,
238 .support_viewport_index_layer = device.HasVertexViewportLayer(), 238 .support_viewport_index_layer = device.HasVertexViewportLayer(),
239 .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
240 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
239 } { 241 } {
240 if (use_asynchronous_shaders) { 242 if (use_asynchronous_shaders) {
241 workers = CreateWorkers(); 243 workers = CreateWorkers();
@@ -350,6 +352,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
350 regs.tessellation.params.output_primitives.Value() == 352 regs.tessellation.params.output_primitives.Value() ==
351 Maxwell::Tessellation::OutputPrimitives::Triangles_CW); 353 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
352 graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); 354 graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
355 graphics_key.app_stage.Assign(maxwell3d->engine_state);
353 if (graphics_key.xfb_enabled) { 356 if (graphics_key.xfb_enabled) {
354 SetXfbState(graphics_key.xfb_state, regs); 357 SetXfbState(graphics_key.xfb_state, regs);
355 } 358 }
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index d9c29d8b7..98841ae65 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -1,2 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <glad/glad.h>
5
6#include "video_core/renderer_opengl/gl_shader_manager.h"
7
8namespace OpenGL {
9
10static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
11 GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
12 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
13};
14
15ProgramManager::ProgramManager(const Device& device) {
16 glCreateProgramPipelines(1, &pipeline.handle);
17 if (device.UseAssemblyShaders()) {
18 glEnable(GL_COMPUTE_PROGRAM_NV);
19 }
20}
21
22void ProgramManager::BindComputeProgram(GLuint program) {
23 glUseProgram(program);
24 is_compute_bound = true;
25}
26
27void ProgramManager::BindComputeAssemblyProgram(GLuint program) {
28 if (current_assembly_compute_program != program) {
29 current_assembly_compute_program = program;
30 glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
31 }
32 UnbindPipeline();
33}
34
35void ProgramManager::BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
36 static constexpr std::array<GLenum, 5> stage_enums{
37 GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
38 GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
39 };
40 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
41 if (current_programs[stage] != programs[stage].handle) {
42 current_programs[stage] = programs[stage].handle;
43 glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
44 }
45 }
46 BindPipeline();
47}
48
49void ProgramManager::BindPresentPrograms(GLuint vertex, GLuint fragment) {
50 if (current_programs[0] != vertex) {
51 current_programs[0] = vertex;
52 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
53 }
54 if (current_programs[4] != fragment) {
55 current_programs[4] = fragment;
56 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
57 }
58 glUseProgramStages(
59 pipeline.handle,
60 GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
61 current_programs[1] = 0;
62 current_programs[2] = 0;
63 current_programs[3] = 0;
64
65 if (current_stage_mask != 0) {
66 current_stage_mask = 0;
67 for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
68 glDisable(program_type);
69 }
70 }
71 BindPipeline();
72}
73
74void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
75 u32 stage_mask) {
76 const u32 changed_mask = current_stage_mask ^ stage_mask;
77 current_stage_mask = stage_mask;
78
79 if (changed_mask != 0) {
80 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
81 if (((changed_mask >> stage) & 1) != 0) {
82 if (((stage_mask >> stage) & 1) != 0) {
83 glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
84 } else {
85 glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
86 }
87 }
88 }
89 }
90 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
91 if (current_programs[stage] != programs[stage].handle) {
92 current_programs[stage] = programs[stage].handle;
93 glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
94 }
95 }
96 UnbindPipeline();
97}
98
99void ProgramManager::RestoreGuestCompute() {}
100
101void ProgramManager::BindPipeline() {
102 if (!is_pipeline_bound) {
103 is_pipeline_bound = true;
104 glBindProgramPipeline(pipeline.handle);
105 }
106 UnbindCompute();
107}
108
109void ProgramManager::UnbindPipeline() {
110 if (is_pipeline_bound) {
111 is_pipeline_bound = false;
112 glBindProgramPipeline(0);
113 }
114 UnbindCompute();
115}
116
117void ProgramManager::UnbindCompute() {
118 if (is_compute_bound) {
119 is_compute_bound = false;
120 glUseProgram(0);
121 }
122}
123} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index a84f5aeb3..07ffab77f 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -6,8 +6,6 @@
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8 8
9#include <glad/glad.h>
10
11#include "video_core/renderer_opengl/gl_device.h" 9#include "video_core/renderer_opengl/gl_device.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 10#include "video_core/renderer_opengl/gl_resource_manager.h"
13 11
@@ -16,121 +14,28 @@ namespace OpenGL {
16class ProgramManager { 14class ProgramManager {
17 static constexpr size_t NUM_STAGES = 5; 15 static constexpr size_t NUM_STAGES = 5;
18 16
19 static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
20 GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
21 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
22 };
23
24public: 17public:
25 explicit ProgramManager(const Device& device) { 18 explicit ProgramManager(const Device& device);
26 glCreateProgramPipelines(1, &pipeline.handle); 19
27 if (device.UseAssemblyShaders()) { 20 void BindComputeProgram(GLuint program);
28 glEnable(GL_COMPUTE_PROGRAM_NV); 21
29 } 22 void BindComputeAssemblyProgram(GLuint program);
30 } 23
31 24 void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs);
32 void BindComputeProgram(GLuint program) { 25
33 glUseProgram(program); 26 void BindPresentPrograms(GLuint vertex, GLuint fragment);
34 is_compute_bound = true;
35 }
36
37 void BindComputeAssemblyProgram(GLuint program) {
38 if (current_assembly_compute_program != program) {
39 current_assembly_compute_program = program;
40 glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
41 }
42 UnbindPipeline();
43 }
44
45 void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
46 static constexpr std::array<GLenum, 5> stage_enums{
47 GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
48 GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
49 };
50 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
51 if (current_programs[stage] != programs[stage].handle) {
52 current_programs[stage] = programs[stage].handle;
53 glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
54 }
55 }
56 BindPipeline();
57 }
58
59 void BindPresentPrograms(GLuint vertex, GLuint fragment) {
60 if (current_programs[0] != vertex) {
61 current_programs[0] = vertex;
62 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
63 }
64 if (current_programs[4] != fragment) {
65 current_programs[4] = fragment;
66 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
67 }
68 glUseProgramStages(
69 pipeline.handle,
70 GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
71 current_programs[1] = 0;
72 current_programs[2] = 0;
73 current_programs[3] = 0;
74
75 if (current_stage_mask != 0) {
76 current_stage_mask = 0;
77 for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
78 glDisable(program_type);
79 }
80 }
81 BindPipeline();
82 }
83 27
84 void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, 28 void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
85 u32 stage_mask) { 29 u32 stage_mask);
86 const u32 changed_mask = current_stage_mask ^ stage_mask; 30
87 current_stage_mask = stage_mask; 31 void RestoreGuestCompute();
88
89 if (changed_mask != 0) {
90 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
91 if (((changed_mask >> stage) & 1) != 0) {
92 if (((stage_mask >> stage) & 1) != 0) {
93 glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
94 } else {
95 glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
96 }
97 }
98 }
99 }
100 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
101 if (current_programs[stage] != programs[stage].handle) {
102 current_programs[stage] = programs[stage].handle;
103 glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
104 }
105 }
106 UnbindPipeline();
107 }
108
109 void RestoreGuestCompute() {}
110 32
111private: 33private:
112 void BindPipeline() { 34 void BindPipeline();
113 if (!is_pipeline_bound) { 35
114 is_pipeline_bound = true; 36 void UnbindPipeline();
115 glBindProgramPipeline(pipeline.handle); 37
116 } 38 void UnbindCompute();
117 UnbindCompute();
118 }
119
120 void UnbindPipeline() {
121 if (is_pipeline_bound) {
122 is_pipeline_bound = false;
123 glBindProgramPipeline(0);
124 }
125 UnbindCompute();
126 }
127
128 void UnbindCompute() {
129 if (is_compute_bound) {
130 is_compute_bound = false;
131 glUseProgram(0);
132 }
133 }
134 39
135 OGLPipeline pipeline; 40 OGLPipeline pipeline;
136 bool is_pipeline_bound{}; 41 bool is_pipeline_bound{};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 113528e9b..5d9d370f2 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -354,6 +354,7 @@ struct TextureCacheParams {
354 static constexpr bool FRAMEBUFFER_BLITS = true; 354 static constexpr bool FRAMEBUFFER_BLITS = true;
355 static constexpr bool HAS_EMULATED_COPIES = true; 355 static constexpr bool HAS_EMULATED_COPIES = true;
356 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 356 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
357 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false;
357 358
358 using Runtime = OpenGL::TextureCacheRuntime; 359 using Runtime = OpenGL::TextureCacheRuntime;
359 using Image = OpenGL::Image; 360 using Image = OpenGL::Image;
@@ -361,6 +362,7 @@ struct TextureCacheParams {
361 using ImageView = OpenGL::ImageView; 362 using ImageView = OpenGL::ImageView;
362 using Sampler = OpenGL::Sampler; 363 using Sampler = OpenGL::Sampler;
363 using Framebuffer = OpenGL::Framebuffer; 364 using Framebuffer = OpenGL::Framebuffer;
365 using AsyncBuffer = u32;
364}; 366};
365 367
366using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; 368using TextureCache = VideoCommon::TextureCache<TextureCacheParams>;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bc75680f0..2a74c1d05 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -17,8 +17,14 @@
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/memory.h" 18#include "core/memory.h"
19#include "core/telemetry_session.h" 19#include "core/telemetry_session.h"
20#include "video_core/host_shaders/ffx_a_h.h"
21#include "video_core/host_shaders/ffx_fsr1_h.h"
22#include "video_core/host_shaders/full_screen_triangle_vert.h"
20#include "video_core/host_shaders/fxaa_frag.h" 23#include "video_core/host_shaders/fxaa_frag.h"
21#include "video_core/host_shaders/fxaa_vert.h" 24#include "video_core/host_shaders/fxaa_vert.h"
25#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
26#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
27#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
22#include "video_core/host_shaders/opengl_present_frag.h" 28#include "video_core/host_shaders/opengl_present_frag.h"
23#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" 29#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
24#include "video_core/host_shaders/opengl_present_vert.h" 30#include "video_core/host_shaders/opengl_present_vert.h"
@@ -31,6 +37,7 @@
31#include "video_core/host_shaders/smaa_edge_detection_vert.h" 37#include "video_core/host_shaders/smaa_edge_detection_vert.h"
32#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" 38#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
33#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" 39#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
40#include "video_core/renderer_opengl/gl_fsr.h"
34#include "video_core/renderer_opengl/gl_rasterizer.h" 41#include "video_core/renderer_opengl/gl_rasterizer.h"
35#include "video_core/renderer_opengl/gl_shader_manager.h" 42#include "video_core/renderer_opengl/gl_shader_manager.h"
36#include "video_core/renderer_opengl/gl_shader_util.h" 43#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -268,12 +275,17 @@ void RendererOpenGL::InitOpenGLObjects() {
268 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); 275 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
269 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); 276 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
270 277
271 const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) { 278 const auto replace_include = [](std::string& shader_source, std::string_view include_name,
272 std::string shader_source{specialized_source}; 279 std::string_view include_content) {
273 constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\""; 280 const std::string include_string = fmt::format("#include \"{}\"", include_name);
274 const std::size_t pos = shader_source.find(include_string); 281 const std::size_t pos = shader_source.find(include_string);
275 ASSERT(pos != std::string::npos); 282 ASSERT(pos != std::string::npos);
276 shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL); 283 shader_source.replace(pos, include_string.size(), include_content);
284 };
285
286 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
287 std::string shader_source{specialized_source};
288 replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
277 return CreateProgram(shader_source, stage); 289 return CreateProgram(shader_source, stage);
278 }; 290 };
279 291
@@ -298,14 +310,32 @@ void RendererOpenGL::InitOpenGLObjects() {
298 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), 310 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
299 GL_FRAGMENT_SHADER); 311 GL_FRAGMENT_SHADER);
300 312
313 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
314 replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
315 replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
316
317 std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
318 std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
319 replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
320 replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
321
322 fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
323 fsr_rcas_frag_source);
324
301 // Generate presentation sampler 325 // Generate presentation sampler
302 present_sampler.Create(); 326 present_sampler.Create();
303 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 327 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
304 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 328 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
329 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
330 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
331 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
305 332
306 present_sampler_nn.Create(); 333 present_sampler_nn.Create();
307 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 334 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
308 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 335 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
336 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
337 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
338 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
309 339
310 // Generate VBO handle for drawing 340 // Generate VBO handle for drawing
311 vertex_buffer.Create(); 341 vertex_buffer.Create();
@@ -442,7 +472,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
442 472
443 glBindTextureUnit(0, screen_info.display_texture); 473 glBindTextureUnit(0, screen_info.display_texture);
444 474
445 const auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); 475 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
476 if (anti_aliasing > Settings::AntiAliasing::LastAA) {
477 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
478 anti_aliasing = Settings::AntiAliasing::None;
479 Settings::values.anti_aliasing.SetValue(anti_aliasing);
480 }
481
446 if (anti_aliasing != Settings::AntiAliasing::None) { 482 if (anti_aliasing != Settings::AntiAliasing::None) {
447 glEnablei(GL_SCISSOR_TEST, 0); 483 glEnablei(GL_SCISSOR_TEST, 0);
448 auto viewport_width = screen_info.texture.width; 484 auto viewport_width = screen_info.texture.width;
@@ -519,6 +555,31 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
519 555
520 glBindTextureUnit(0, aa_texture.handle); 556 glBindTextureUnit(0, aa_texture.handle);
521 } 557 }
558 glDisablei(GL_SCISSOR_TEST, 0);
559
560 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
561 if (!fsr->AreBuffersInitialized()) {
562 fsr->InitBuffers();
563 }
564
565 auto crop_rect = framebuffer_crop_rect;
566 if (crop_rect.GetWidth() == 0) {
567 crop_rect.right = framebuffer_width;
568 }
569 if (crop_rect.GetHeight() == 0) {
570 crop_rect.bottom = framebuffer_height;
571 }
572 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
573 const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
574 const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
575 glBindSampler(0, present_sampler.handle);
576 fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
577 } else {
578 if (fsr->AreBuffersInitialized()) {
579 fsr->ReleaseBuffers();
580 }
581 }
582
522 const std::array ortho_matrix = 583 const std::array ortho_matrix =
523 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); 584 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
524 585
@@ -534,10 +595,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
534 case Settings::ScalingFilter::ScaleForce: 595 case Settings::ScalingFilter::ScaleForce:
535 return present_scaleforce_fragment.handle; 596 return present_scaleforce_fragment.handle;
536 case Settings::ScalingFilter::Fsr: 597 case Settings::ScalingFilter::Fsr:
537 LOG_WARNING( 598 return fsr->GetPresentFragmentProgram().handle;
538 Render_OpenGL,
539 "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
540 return present_scaleforce_fragment.handle;
541 default: 599 default:
542 return present_bilinear_fragment.handle; 600 return present_bilinear_fragment.handle;
543 } 601 }
@@ -572,15 +630,18 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
572 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); 630 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
573 f32 scale_v = 631 f32 scale_v =
574 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); 632 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
575 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 633
576 // (e.g. handheld mode) on a 1920x1080 framebuffer. 634 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
577 if (framebuffer_crop_rect.GetWidth() > 0) { 635 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
578 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 636 // (e.g. handheld mode) on a 1920x1080 framebuffer.
579 static_cast<f32>(screen_info.texture.width); 637 if (framebuffer_crop_rect.GetWidth() > 0) {
580 } 638 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
581 if (framebuffer_crop_rect.GetHeight() > 0) { 639 static_cast<f32>(screen_info.texture.width);
582 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / 640 }
583 static_cast<f32>(screen_info.texture.height); 641 if (framebuffer_crop_rect.GetHeight() > 0) {
642 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
643 static_cast<f32>(screen_info.texture.height);
644 }
584 } 645 }
585 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && 646 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
586 !screen_info.was_accelerated) { 647 !screen_info.was_accelerated) {
@@ -606,7 +667,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
606 } else { 667 } else {
607 glDisable(GL_FRAMEBUFFER_SRGB); 668 glDisable(GL_FRAMEBUFFER_SRGB);
608 } 669 }
609 glDisablei(GL_SCISSOR_TEST, 0);
610 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), 670 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
611 static_cast<GLfloat>(layout.height)); 671 static_cast<GLfloat>(layout.height));
612 672
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cc97d7b26..f1d5fd954 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,6 +10,7 @@
10 10
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 12#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_fsr.h"
13#include "video_core/renderer_opengl/gl_rasterizer.h" 14#include "video_core/renderer_opengl/gl_rasterizer.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
15#include "video_core/renderer_opengl/gl_shader_manager.h" 16#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -141,6 +142,8 @@ private:
141 OGLTexture smaa_edges_tex; 142 OGLTexture smaa_edges_tex;
142 OGLTexture smaa_blend_tex; 143 OGLTexture smaa_blend_tex;
143 144
145 std::unique_ptr<FSR> fsr;
146
144 /// OpenGL framebuffer data 147 /// OpenGL framebuffer data
145 std::vector<u8> gl_framebuffer_data; 148 std::vector<u8> gl_framebuffer_data;
146 149
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 3f2b139e0..cf2964a3f 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -4,14 +4,16 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/settings.h" 6#include "common/settings.h"
7#include "video_core/host_shaders/blit_color_float_frag_spv.h"
7#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" 8#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
8#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" 9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
9#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
10#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" 11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
11#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" 12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
12#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
13#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
15#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h"
16#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h"
15#include "video_core/renderer_vulkan/blit_image.h" 17#include "video_core/renderer_vulkan/blit_image.h"
16#include "video_core/renderer_vulkan/maxwell_to_vk.h" 18#include "video_core/renderer_vulkan/maxwell_to_vk.h"
17#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -69,10 +71,11 @@ constexpr VkDescriptorSetLayoutCreateInfo TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CRE
69 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()), 71 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()),
70 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(), 72 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(),
71}; 73};
72constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{ 74template <VkShaderStageFlags stageFlags, size_t size>
73 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, 75inline constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{
76 .stageFlags = stageFlags,
74 .offset = 0, 77 .offset = 0,
75 .size = sizeof(PushConstants), 78 .size = static_cast<u32>(size),
76}; 79};
77constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{ 80constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{
78 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 81 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -125,10 +128,8 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE
125 .alphaToCoverageEnable = VK_FALSE, 128 .alphaToCoverageEnable = VK_FALSE,
126 .alphaToOneEnable = VK_FALSE, 129 .alphaToOneEnable = VK_FALSE,
127}; 130};
128constexpr std::array DYNAMIC_STATES{ 131constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
129 VK_DYNAMIC_STATE_VIEWPORT, 132 VK_DYNAMIC_STATE_BLEND_CONSTANTS};
130 VK_DYNAMIC_STATE_SCISSOR,
131};
132constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ 133constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
133 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 134 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
134 .pNext = nullptr, 135 .pNext = nullptr,
@@ -205,15 +206,15 @@ inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
205}; 206};
206 207
207constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo( 208constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo(
208 const VkDescriptorSetLayout* set_layout) { 209 const VkDescriptorSetLayout* set_layout, vk::Span<VkPushConstantRange> push_constants) {
209 return VkPipelineLayoutCreateInfo{ 210 return VkPipelineLayoutCreateInfo{
210 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 211 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
211 .pNext = nullptr, 212 .pNext = nullptr,
212 .flags = 0, 213 .flags = 0,
213 .setLayoutCount = 1, 214 .setLayoutCount = (set_layout != nullptr ? 1u : 0u),
214 .pSetLayouts = set_layout, 215 .pSetLayouts = set_layout,
215 .pushConstantRangeCount = 1, 216 .pushConstantRangeCount = push_constants.size(),
216 .pPushConstantRanges = &PUSH_CONSTANT_RANGE, 217 .pPushConstantRanges = push_constants.data(),
217 }; 218 };
218} 219}
219 220
@@ -302,8 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
302 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr); 303 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
303} 304}
304 305
305void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, 306void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
306 const Region2D& src_region) {
307 const VkOffset2D offset{ 307 const VkOffset2D offset{
308 .x = std::min(dst_region.start.x, dst_region.end.x), 308 .x = std::min(dst_region.start.x, dst_region.end.x),
309 .y = std::min(dst_region.start.y, dst_region.end.y), 309 .y = std::min(dst_region.start.y, dst_region.end.y),
@@ -325,15 +325,23 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
325 .offset = offset, 325 .offset = offset,
326 .extent = extent, 326 .extent = extent,
327 }; 327 };
328 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x); 328 cmdbuf.SetViewport(0, viewport);
329 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y); 329 cmdbuf.SetScissor(0, scissor);
330}
331
332void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
333 const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
334 BindBlitState(cmdbuf, dst_region);
335 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) /
336 static_cast<float>(src_size.width);
337 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) /
338 static_cast<float>(src_size.height);
330 const PushConstants push_constants{ 339 const PushConstants push_constants{
331 .tex_scale = {scale_x, scale_y}, 340 .tex_scale = {scale_x, scale_y},
332 .tex_offset = {static_cast<float>(src_region.start.x), 341 .tex_offset = {static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
333 static_cast<float>(src_region.start.y)}, 342 static_cast<float>(src_region.start.y) /
343 static_cast<float>(src_size.height)},
334 }; 344 };
335 cmdbuf.SetViewport(0, viewport);
336 cmdbuf.SetScissor(0, scissor);
337 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); 345 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
338} 346}
339 347
@@ -347,6 +355,51 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
347 .height = is_rescaled ? resolution.ScaleUp(height) : height, 355 .height = is_rescaled ? resolution.ScaleUp(height) : height,
348 }; 356 };
349} 357}
358
359void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
360 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
361 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
362 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
363 const VkImageMemoryBarrier barrier{
364 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
365 .pNext = nullptr,
366 .srcAccessMask = flags,
367 .dstAccessMask = flags,
368 .oldLayout = source_layout,
369 .newLayout = target_layout,
370 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
371 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
372 .image = image,
373 .subresourceRange{
374 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
375 .baseMipLevel = 0,
376 .levelCount = 1,
377 .baseArrayLayer = 0,
378 .layerCount = 1,
379 },
380 };
381 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
382 0, barrier);
383}
384
385void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
386 const VkRenderPass render_pass = framebuffer->RenderPass();
387 const VkFramebuffer framebuffer_handle = framebuffer->Handle();
388 const VkExtent2D render_area = framebuffer->RenderArea();
389 const VkRenderPassBeginInfo renderpass_bi{
390 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
391 .pNext = nullptr,
392 .renderPass = render_pass,
393 .framebuffer = framebuffer_handle,
394 .renderArea{
395 .offset{},
396 .extent = render_area,
397 },
398 .clearValueCount = 0,
399 .pClearValues = nullptr,
400 };
401 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
402}
350} // Anonymous namespace 403} // Anonymous namespace
351 404
352BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, 405BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
@@ -360,13 +413,20 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
360 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, 413 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)},
361 two_textures_descriptor_allocator{ 414 two_textures_descriptor_allocator{
362 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, 415 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)},
363 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout( 416 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
364 PipelineLayoutCreateInfo(one_texture_set_layout.address()))), 417 one_texture_set_layout.address(),
365 two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( 418 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
366 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 419 two_textures_pipeline_layout(
420 device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
421 two_textures_set_layout.address(),
422 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
423 clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
424 nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
367 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 425 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
368 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 426 blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
369 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), 427 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
428 clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
429 clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
370 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 430 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
371 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 431 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
372 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 432 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
@@ -404,6 +464,32 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
404 scheduler.InvalidateState(); 464 scheduler.InvalidateState();
405} 465}
406 466
467void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
468 VkImage src_image, VkSampler src_sampler,
469 const Region2D& dst_region, const Region2D& src_region,
470 const Extent3D& src_size) {
471 const BlitImagePipelineKey key{
472 .renderpass = dst_framebuffer->RenderPass(),
473 .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
474 };
475 const VkPipelineLayout layout = *one_texture_pipeline_layout;
476 const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
477 scheduler.RequestOutsideRenderPassOperationContext();
478 scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
479 src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
480 TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL);
481 BeginRenderPass(cmdbuf, dst_framebuffer);
482 const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
483 UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);
484 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
485 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
486 nullptr);
487 BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
488 cmdbuf.Draw(3, 1, 0, 0);
489 cmdbuf.EndRenderPass();
490 });
491}
492
407void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, 493void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
408 VkImageView src_depth_view, VkImageView src_stencil_view, 494 VkImageView src_depth_view, VkImageView src_stencil_view,
409 const Region2D& dst_region, const Region2D& src_region, 495 const Region2D& dst_region, const Region2D& src_region,
@@ -479,6 +565,30 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
479 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); 565 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
480} 566}
481 567
568void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
569 const std::array<f32, 4>& clear_color,
570 const Region2D& dst_region) {
571 const BlitImagePipelineKey key{
572 .renderpass = dst_framebuffer->RenderPass(),
573 .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
574 };
575 const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
576 const VkPipelineLayout layout = *clear_color_pipeline_layout;
577 scheduler.RequestRenderpass(dst_framebuffer);
578 scheduler.Record(
579 [pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) {
580 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
581 const std::array blend_color = {
582 (color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f,
583 (color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f};
584 cmdbuf.SetBlendConstants(blend_color.data());
585 BindBlitState(cmdbuf, dst_region);
586 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color);
587 cmdbuf.Draw(3, 1, 0, 0);
588 });
589 scheduler.InvalidateState();
590}
591
482void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 592void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
483 const ImageView& src_image_view) { 593 const ImageView& src_image_view) {
484 const VkPipelineLayout layout = *one_texture_pipeline_layout; 594 const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -654,6 +764,58 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
654 return *blit_depth_stencil_pipelines.back(); 764 return *blit_depth_stencil_pipelines.back();
655} 765}
656 766
767VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
768 const auto it = std::ranges::find(clear_color_keys, key);
769 if (it != clear_color_keys.end()) {
770 return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
771 }
772 clear_color_keys.push_back(key);
773 const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
774 const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
775 .blendEnable = VK_TRUE,
776 .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
777 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
778 .colorBlendOp = VK_BLEND_OP_ADD,
779 .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
780 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
781 .alphaBlendOp = VK_BLEND_OP_ADD,
782 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
783 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
784 };
785 const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
786 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
787 .pNext = nullptr,
788 .flags = 0,
789 .logicOpEnable = VK_FALSE,
790 .logicOp = VK_LOGIC_OP_CLEAR,
791 .attachmentCount = 1,
792 .pAttachments = &color_blend_attachment_state,
793 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
794 };
795 clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
796 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
797 .pNext = nullptr,
798 .flags = 0,
799 .stageCount = static_cast<u32>(stages.size()),
800 .pStages = stages.data(),
801 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
802 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
803 .pTessellationState = nullptr,
804 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
805 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
806 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
807 .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
808 .pColorBlendState = &color_blend_state_generic_create_info,
809 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
810 .layout = *clear_color_pipeline_layout,
811 .renderPass = key.renderpass,
812 .subpass = 0,
813 .basePipelineHandle = VK_NULL_HANDLE,
814 .basePipelineIndex = 0,
815 }));
816 return *clear_color_pipelines.back();
817}
818
657void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, 819void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
658 bool is_target_depth) { 820 bool is_target_depth) {
659 if (pipeline) { 821 if (pipeline) {
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 5df679fb4..2976a7d91 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -10,6 +10,8 @@
10 10
11namespace Vulkan { 11namespace Vulkan {
12 12
13using VideoCommon::Extent3D;
14using VideoCommon::Offset2D;
13using VideoCommon::Region2D; 15using VideoCommon::Region2D;
14 16
15class Device; 17class Device;
@@ -36,6 +38,10 @@ public:
36 Tegra::Engines::Fermi2D::Filter filter, 38 Tegra::Engines::Fermi2D::Filter filter,
37 Tegra::Engines::Fermi2D::Operation operation); 39 Tegra::Engines::Fermi2D::Operation operation);
38 40
41 void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
42 VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
43 const Region2D& src_region, const Extent3D& src_size);
44
39 void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view, 45 void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view,
40 VkImageView src_stencil_view, const Region2D& dst_region, 46 VkImageView src_stencil_view, const Region2D& dst_region,
41 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, 47 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
@@ -55,6 +61,9 @@ public:
55 61
56 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); 62 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
57 63
64 void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
65 const std::array<f32, 4>& clear_color, const Region2D& dst_region);
66
58private: 67private:
59 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 68 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
60 const ImageView& src_image_view); 69 const ImageView& src_image_view);
@@ -66,6 +75,8 @@ private:
66 75
67 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); 76 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
68 77
78 [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
79
69 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); 80 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
70 81
71 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 82 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
@@ -91,9 +102,12 @@ private:
91 DescriptorAllocator two_textures_descriptor_allocator; 102 DescriptorAllocator two_textures_descriptor_allocator;
92 vk::PipelineLayout one_texture_pipeline_layout; 103 vk::PipelineLayout one_texture_pipeline_layout;
93 vk::PipelineLayout two_textures_pipeline_layout; 104 vk::PipelineLayout two_textures_pipeline_layout;
105 vk::PipelineLayout clear_color_pipeline_layout;
94 vk::ShaderModule full_screen_vert; 106 vk::ShaderModule full_screen_vert;
95 vk::ShaderModule blit_color_to_color_frag; 107 vk::ShaderModule blit_color_to_color_frag;
96 vk::ShaderModule blit_depth_stencil_frag; 108 vk::ShaderModule blit_depth_stencil_frag;
109 vk::ShaderModule clear_color_vert;
110 vk::ShaderModule clear_color_frag;
97 vk::ShaderModule convert_depth_to_float_frag; 111 vk::ShaderModule convert_depth_to_float_frag;
98 vk::ShaderModule convert_float_to_depth_frag; 112 vk::ShaderModule convert_float_to_depth_frag;
99 vk::ShaderModule convert_abgr8_to_d24s8_frag; 113 vk::ShaderModule convert_abgr8_to_d24s8_frag;
@@ -106,6 +120,8 @@ private:
106 std::vector<vk::Pipeline> blit_color_pipelines; 120 std::vector<vk::Pipeline> blit_color_pipelines;
107 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys; 121 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys;
108 std::vector<vk::Pipeline> blit_depth_stencil_pipelines; 122 std::vector<vk::Pipeline> blit_depth_stencil_pipelines;
123 std::vector<BlitImagePipelineKey> clear_color_keys;
124 std::vector<vk::Pipeline> clear_color_pipelines;
109 vk::Pipeline convert_d32_to_r32_pipeline; 125 vk::Pipeline convert_d32_to_r32_pipeline;
110 vk::Pipeline convert_r32_to_d32_pipeline; 126 vk::Pipeline convert_r32_to_d32_pipeline;
111 vk::Pipeline convert_d16_to_r16_pipeline; 127 vk::Pipeline convert_d16_to_r16_pipeline;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index e62b36822..f8398b511 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -48,43 +48,30 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
48} 48}
49} // Anonymous namespace 49} // Anonymous namespace
50 50
51void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, 51void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
52 bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
53 const Maxwell& regs = maxwell3d.regs; 52 const Maxwell& regs = maxwell3d.regs;
54 const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; 53 const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
55 const std::array enabled_lut{
56 regs.polygon_offset_point_enable,
57 regs.polygon_offset_line_enable,
58 regs.polygon_offset_fill_enable,
59 };
60 const u32 topology_index = static_cast<u32>(topology_);
61 54
62 raw1 = 0; 55 raw1 = 0;
63 extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); 56 extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
64 dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); 57 extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
58 extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
59 extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
60 extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
61 dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
65 xfb_enabled.Assign(regs.transform_feedback_enabled != 0); 62 xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
66 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
67 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
68 depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
69 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
70 regs.viewport_clip_control.geometry_clip ==
71 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
72 regs.viewport_clip_control.geometry_clip ==
73 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
74 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 63 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
75 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); 64 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
76 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
77 tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); 65 tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
78 tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); 66 tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
79 tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == 67 tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
80 Maxwell::Tessellation::OutputPrimitives::Triangles_CW); 68 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
81 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 69 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
82 logic_op.Assign(PackLogicOp(regs.logic_op.op));
83 topology.Assign(topology_); 70 topology.Assign(topology_);
84 msaa_mode.Assign(regs.anti_alias_samples_mode); 71 msaa_mode.Assign(regs.anti_alias_samples_mode);
85 72
86 raw2 = 0; 73 raw2 = 0;
87 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 74
88 const auto test_func = 75 const auto test_func =
89 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; 76 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
90 alpha_test_func.Assign(PackComparisonOp(test_func)); 77 alpha_test_func.Assign(PackComparisonOp(test_func));
@@ -97,6 +84,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
97 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); 84 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
98 alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); 85 alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0);
99 alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); 86 alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0);
87 app_stage.Assign(maxwell3d.engine_state);
100 88
101 for (size_t i = 0; i < regs.rt.size(); ++i) { 89 for (size_t i = 0; i < regs.rt.size(); ++i) {
102 color_formats[i] = static_cast<u8>(regs.rt[i].format); 90 color_formats[i] = static_cast<u8>(regs.rt[i].format);
@@ -105,7 +93,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
105 point_size = Common::BitCast<u32>(regs.point_size); 93 point_size = Common::BitCast<u32>(regs.point_size);
106 94
107 if (maxwell3d.dirty.flags[Dirty::VertexInput]) { 95 if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
108 if (has_dynamic_vertex_input) { 96 if (features.has_dynamic_vertex_input) {
109 // Dirty flag will be reset by the command buffer update 97 // Dirty flag will be reset by the command buffer update
110 static constexpr std::array LUT{ 98 static constexpr std::array LUT{
111 0u, // Invalid 99 0u, // Invalid
@@ -144,12 +132,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
144 } 132 }
145 } 133 }
146 } 134 }
147 if (maxwell3d.dirty.flags[Dirty::Blending]) {
148 maxwell3d.dirty.flags[Dirty::Blending] = false;
149 for (size_t index = 0; index < attachments.size(); ++index) {
150 attachments[index].Refresh(regs, index);
151 }
152 }
153 if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) { 135 if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) {
154 maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false; 136 maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false;
155 const auto& transform = regs.viewport_transform; 137 const auto& transform = regs.viewport_transform;
@@ -157,8 +139,27 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
157 return static_cast<u16>(viewport.swizzle.raw); 139 return static_cast<u16>(viewport.swizzle.raw);
158 }); 140 });
159 } 141 }
142 dynamic_state.raw1 = 0;
143 dynamic_state.raw2 = 0;
160 if (!extended_dynamic_state) { 144 if (!extended_dynamic_state) {
161 dynamic_state.Refresh(regs); 145 dynamic_state.Refresh(regs);
146 std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
147 return static_cast<u16>(array.stride.Value());
148 });
149 }
150 if (!extended_dynamic_state_2_extra) {
151 dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
152 }
153 if (!extended_dynamic_state_3_blend) {
154 if (maxwell3d.dirty.flags[Dirty::Blending]) {
155 maxwell3d.dirty.flags[Dirty::Blending] = false;
156 for (size_t index = 0; index < attachments.size(); ++index) {
157 attachments[index].Refresh(regs, index);
158 }
159 }
160 }
161 if (!extended_dynamic_state_3_enables) {
162 dynamic_state.Refresh3(regs);
162 } 163 }
163 if (xfb_enabled) { 164 if (xfb_enabled) {
164 RefreshXfbState(xfb_state, regs); 165 RefreshXfbState(xfb_state, regs);
@@ -175,12 +176,11 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
175 mask_a.Assign(mask.A); 176 mask_a.Assign(mask.A);
176 177
177 // TODO: C++20 Use templated lambda to deduplicate code 178 // TODO: C++20 Use templated lambda to deduplicate code
179 if (!regs.blend.enable[index]) {
180 return;
181 }
178 182
179 if (!regs.blend_per_target_enabled) { 183 const auto setup_blend = [&]<typename T>(const T& src) {
180 if (!regs.blend.enable[index]) {
181 return;
182 }
183 const auto& src = regs.blend;
184 equation_rgb.Assign(PackBlendEquation(src.color_op)); 184 equation_rgb.Assign(PackBlendEquation(src.color_op));
185 equation_a.Assign(PackBlendEquation(src.alpha_op)); 185 equation_a.Assign(PackBlendEquation(src.alpha_op));
186 factor_source_rgb.Assign(PackBlendFactor(src.color_source)); 186 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
@@ -188,20 +188,13 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
188 factor_source_a.Assign(PackBlendFactor(src.alpha_source)); 188 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
189 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); 189 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
190 enable.Assign(1); 190 enable.Assign(1);
191 return; 191 };
192 }
193 192
194 if (!regs.blend.enable[index]) { 193 if (!regs.blend_per_target_enabled) {
194 setup_blend(regs.blend);
195 return; 195 return;
196 } 196 }
197 const auto& src = regs.blend_per_target[index]; 197 setup_blend(regs.blend_per_target[index]);
198 equation_rgb.Assign(PackBlendEquation(src.color_op));
199 equation_a.Assign(PackBlendEquation(src.alpha_op));
200 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
201 factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
202 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
203 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
204 enable.Assign(1);
205} 198}
206 199
207void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { 200void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
@@ -211,8 +204,6 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
211 packed_front_face = 1 - packed_front_face; 204 packed_front_face = 1 - packed_front_face;
212 } 205 }
213 206
214 raw1 = 0;
215 raw2 = 0;
216 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); 207 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
217 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); 208 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
218 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); 209 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
@@ -236,9 +227,37 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
236 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); 227 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
237 cull_face.Assign(PackCullFace(regs.gl_cull_face)); 228 cull_face.Assign(PackCullFace(regs.gl_cull_face));
238 cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); 229 cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0);
239 std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { 230}
240 return static_cast<u16>(array.stride.Value()); 231
241 }); 232void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
233 Maxwell::PrimitiveTopology topology_,
234 bool base_feautures_supported) {
235 logic_op.Assign(PackLogicOp(regs.logic_op.op));
236
237 if (base_feautures_supported) {
238 return;
239 }
240
241 const std::array enabled_lut{
242 regs.polygon_offset_point_enable,
243 regs.polygon_offset_line_enable,
244 regs.polygon_offset_fill_enable,
245 };
246 const u32 topology_index = static_cast<u32>(topology_);
247
248 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
249 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
250 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
251}
252
253void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) {
254 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
255 depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
256 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
257 regs.viewport_clip_control.geometry_clip ==
258 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
259 regs.viewport_clip_control.geometry_clip ==
260 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
242} 261}
243 262
244size_t FixedPipelineState::Hash() const noexcept { 263size_t FixedPipelineState::Hash() const noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index ab79fb8f3..98ea20b42 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -17,6 +17,15 @@ namespace Vulkan {
17 17
18using Maxwell = Tegra::Engines::Maxwell3D::Regs; 18using Maxwell = Tegra::Engines::Maxwell3D::Regs;
19 19
20struct DynamicFeatures {
21 bool has_extended_dynamic_state;
22 bool has_extended_dynamic_state_2;
23 bool has_extended_dynamic_state_2_extra;
24 bool has_extended_dynamic_state_3_blend;
25 bool has_extended_dynamic_state_3_enables;
26 bool has_dynamic_vertex_input;
27};
28
20struct FixedPipelineState { 29struct FixedPipelineState {
21 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; 30 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
22 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; 31 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
@@ -133,6 +142,17 @@ struct FixedPipelineState {
133 struct DynamicState { 142 struct DynamicState {
134 union { 143 union {
135 u32 raw1; 144 u32 raw1;
145 BitField<0, 2, u32> cull_face;
146 BitField<2, 1, u32> cull_enable;
147 BitField<3, 1, u32> primitive_restart_enable;
148 BitField<4, 1, u32> depth_bias_enable;
149 BitField<5, 1, u32> rasterize_enable;
150 BitField<6, 4, u32> logic_op;
151 BitField<10, 1, u32> logic_op_enable;
152 BitField<11, 1, u32> depth_clamp_disabled;
153 };
154 union {
155 u32 raw2;
136 StencilFace<0> front; 156 StencilFace<0> front;
137 StencilFace<12> back; 157 StencilFace<12> back;
138 BitField<24, 1, u32> stencil_enable; 158 BitField<24, 1, u32> stencil_enable;
@@ -142,15 +162,11 @@ struct FixedPipelineState {
142 BitField<28, 1, u32> front_face; 162 BitField<28, 1, u32> front_face;
143 BitField<29, 3, u32> depth_test_func; 163 BitField<29, 3, u32> depth_test_func;
144 }; 164 };
145 union {
146 u32 raw2;
147 BitField<0, 2, u32> cull_face;
148 BitField<2, 1, u32> cull_enable;
149 };
150 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
151 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
152 165
153 void Refresh(const Maxwell& regs); 166 void Refresh(const Maxwell& regs);
167 void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
168 bool base_feautures_supported);
169 void Refresh3(const Maxwell& regs);
154 170
155 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 171 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
156 return UnpackComparisonOp(depth_test_func); 172 return UnpackComparisonOp(depth_test_func);
@@ -168,25 +184,24 @@ struct FixedPipelineState {
168 union { 184 union {
169 u32 raw1; 185 u32 raw1;
170 BitField<0, 1, u32> extended_dynamic_state; 186 BitField<0, 1, u32> extended_dynamic_state;
171 BitField<1, 1, u32> dynamic_vertex_input; 187 BitField<1, 1, u32> extended_dynamic_state_2;
172 BitField<2, 1, u32> xfb_enabled; 188 BitField<2, 1, u32> extended_dynamic_state_2_extra;
173 BitField<3, 1, u32> primitive_restart_enable; 189 BitField<3, 1, u32> extended_dynamic_state_3_blend;
174 BitField<4, 1, u32> depth_bias_enable; 190 BitField<4, 1, u32> extended_dynamic_state_3_enables;
175 BitField<5, 1, u32> depth_clamp_disabled; 191 BitField<5, 1, u32> dynamic_vertex_input;
176 BitField<6, 1, u32> ndc_minus_one_to_one; 192 BitField<6, 1, u32> xfb_enabled;
177 BitField<7, 2, u32> polygon_mode; 193 BitField<7, 1, u32> ndc_minus_one_to_one;
178 BitField<9, 5, u32> patch_control_points_minus_one; 194 BitField<8, 2, u32> polygon_mode;
179 BitField<14, 2, u32> tessellation_primitive; 195 BitField<10, 2, u32> tessellation_primitive;
180 BitField<16, 2, u32> tessellation_spacing; 196 BitField<12, 2, u32> tessellation_spacing;
181 BitField<18, 1, u32> tessellation_clockwise; 197 BitField<14, 1, u32> tessellation_clockwise;
182 BitField<19, 1, u32> logic_op_enable; 198 BitField<15, 5, u32> patch_control_points_minus_one;
183 BitField<20, 4, u32> logic_op; 199
184 BitField<24, 4, Maxwell::PrimitiveTopology> topology; 200 BitField<24, 4, Maxwell::PrimitiveTopology> topology;
185 BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; 201 BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
186 }; 202 };
187 union { 203 union {
188 u32 raw2; 204 u32 raw2;
189 BitField<0, 1, u32> rasterize_enable;
190 BitField<1, 3, u32> alpha_test_func; 205 BitField<1, 3, u32> alpha_test_func;
191 BitField<4, 1, u32> early_z; 206 BitField<4, 1, u32> early_z;
192 BitField<5, 1, u32> depth_enabled; 207 BitField<5, 1, u32> depth_enabled;
@@ -197,25 +212,28 @@ struct FixedPipelineState {
197 BitField<14, 1, u32> smooth_lines; 212 BitField<14, 1, u32> smooth_lines;
198 BitField<15, 1, u32> alpha_to_coverage_enabled; 213 BitField<15, 1, u32> alpha_to_coverage_enabled;
199 BitField<16, 1, u32> alpha_to_one_enabled; 214 BitField<16, 1, u32> alpha_to_one_enabled;
215 BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
200 }; 216 };
201 std::array<u8, Maxwell::NumRenderTargets> color_formats; 217 std::array<u8, Maxwell::NumRenderTargets> color_formats;
202 218
203 u32 alpha_test_ref; 219 u32 alpha_test_ref;
204 u32 point_size; 220 u32 point_size;
205 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
206 std::array<u16, Maxwell::NumViewports> viewport_swizzles; 221 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
207 union { 222 union {
208 u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state 223 u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
209 u64 enabled_divisors; 224 u64 enabled_divisors;
210 }; 225 };
226
227 DynamicState dynamic_state;
228 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
211 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; 229 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
212 std::array<u32, Maxwell::NumVertexArrays> binding_divisors; 230 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
231 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
232 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
213 233
214 DynamicState dynamic_state;
215 VideoCommon::TransformFeedbackState xfb_state; 234 VideoCommon::TransformFeedbackState xfb_state;
216 235
217 void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state, 236 void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features);
218 bool has_dynamic_vertex_input);
219 237
220 size_t Hash() const noexcept; 238 size_t Hash() const noexcept;
221 239
@@ -230,13 +248,17 @@ struct FixedPipelineState {
230 // When transform feedback is enabled, use the whole struct 248 // When transform feedback is enabled, use the whole struct
231 return sizeof(*this); 249 return sizeof(*this);
232 } 250 }
233 if (dynamic_vertex_input) { 251 if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
234 // Exclude dynamic state and attributes 252 // Exclude dynamic state and attributes
253 return offsetof(FixedPipelineState, dynamic_state);
254 }
255 if (dynamic_vertex_input) {
256 // Exclude dynamic state
235 return offsetof(FixedPipelineState, attributes); 257 return offsetof(FixedPipelineState, attributes);
236 } 258 }
237 if (extended_dynamic_state) { 259 if (extended_dynamic_state) {
238 // Exclude dynamic state 260 // Exclude dynamic state
239 return offsetof(FixedPipelineState, dynamic_state); 261 return offsetof(FixedPipelineState, vertex_strides);
240 } 262 }
241 // Default 263 // Default
242 return offsetof(FixedPipelineState, xfb_state); 264 return offsetof(FixedPipelineState, xfb_state);
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 3e03c5cd6..ca52e2389 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -301,6 +301,8 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
301 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; 301 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
302 case Maxwell::PrimitiveTopology::Lines: 302 case Maxwell::PrimitiveTopology::Lines:
303 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; 303 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
304 case Maxwell::PrimitiveTopology::LineLoop:
305 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
304 case Maxwell::PrimitiveTopology::LineStrip: 306 case Maxwell::PrimitiveTopology::LineStrip:
305 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; 307 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
306 case Maxwell::PrimitiveTopology::Triangles: 308 case Maxwell::PrimitiveTopology::Triangles:
@@ -309,15 +311,28 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
309 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; 311 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
310 case Maxwell::PrimitiveTopology::TriangleFan: 312 case Maxwell::PrimitiveTopology::TriangleFan:
311 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; 313 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
314 case Maxwell::PrimitiveTopology::LinesAdjacency:
315 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
316 case Maxwell::PrimitiveTopology::LineStripAdjacency:
317 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
318 case Maxwell::PrimitiveTopology::TrianglesAdjacency:
319 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
320 case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
321 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
312 case Maxwell::PrimitiveTopology::Quads: 322 case Maxwell::PrimitiveTopology::Quads:
313 // TODO(Rodrigo): Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT whenever it releases 323 case Maxwell::PrimitiveTopology::QuadStrip:
324 // TODO: Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT/VK_PRIMITIVE_TOPOLOGY_QUAD_STRIP_EXT
325 // whenever it releases
314 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 326 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
315 case Maxwell::PrimitiveTopology::Patches: 327 case Maxwell::PrimitiveTopology::Patches:
316 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; 328 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
317 default: 329 case Maxwell::PrimitiveTopology::Polygon:
318 UNIMPLEMENTED_MSG("Unimplemented topology={}", topology); 330 LOG_WARNING(Render_Vulkan, "Draw mode is Polygon with a polygon mode of lines should be a "
319 return {}; 331 "single body and not a bunch of triangles.");
332 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
320 } 333 }
334 UNIMPLEMENTED_MSG("Unimplemented topology={}", topology);
335 return {};
321} 336}
322 337
323VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, 338VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index f502a7d09..2a8d9e377 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -60,24 +60,13 @@ std::string GetDriverVersion(const Device& device) {
60 return GetReadableVersion(version); 60 return GetReadableVersion(version);
61} 61}
62 62
63std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) { 63std::string BuildCommaSeparatedExtensions(
64 std::sort(std::begin(available_extensions), std::end(available_extensions)); 64 const std::set<std::string, std::less<>>& available_extensions) {
65 65 return fmt::format("{}", fmt::join(available_extensions, ","));
66 static constexpr std::size_t AverageExtensionSize = 64;
67 std::string separated_extensions;
68 separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
69
70 const auto end = std::end(available_extensions);
71 for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
72 if (const bool is_last = extension + 1 == end; is_last) {
73 separated_extensions += *extension;
74 } else {
75 separated_extensions += fmt::format("{},", *extension);
76 }
77 }
78 return separated_extensions;
79} 66}
80 67
68} // Anonymous namespace
69
81Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 70Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
82 VkSurfaceKHR surface) { 71 VkSurfaceKHR surface) {
83 const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices(); 72 const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices();
@@ -89,7 +78,6 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl
89 const vk::PhysicalDevice physical_device(devices[device_index], dld); 78 const vk::PhysicalDevice physical_device(devices[device_index], dld);
90 return Device(*instance, physical_device, surface, dld); 79 return Device(*instance, physical_device, surface, dld);
91} 80}
92} // Anonymous namespace
93 81
94RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, 82RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
95 Core::Frontend::EmuWindow& emu_window, 83 Core::Frontend::EmuWindow& emu_window,
@@ -98,7 +86,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
98 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), 86 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
99 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), 87 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
100 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 88 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
101 true, Settings::values.renderer_debug.GetValue())), 89 Settings::values.renderer_debug.GetValue())),
102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 90 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
103 surface(CreateSurface(instance, render_window)), 91 surface(CreateSurface(instance, render_window)),
104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 92 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
@@ -109,6 +97,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
109 screen_info), 97 screen_info),
110 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, 98 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
111 state_tracker, scheduler) { 99 state_tracker, scheduler) {
100 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
101 turbo_mode.emplace(instance, dld);
102 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
103 }
112 Report(); 104 Report();
113} catch (const vk::Exception& exception) { 105} catch (const vk::Exception& exception) {
114 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); 106 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
@@ -116,6 +108,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
116} 108}
117 109
118RendererVulkan::~RendererVulkan() { 110RendererVulkan::~RendererVulkan() {
111 scheduler.RegisterOnSubmit([] {});
119 void(device.GetLogical().WaitIdle()); 112 void(device.GetLogical().WaitIdle());
120} 113}
121 114
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index e7bfecb20..009e75e0d 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -13,6 +13,7 @@
13#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_state_tracker.h" 14#include "video_core/renderer_vulkan/vk_state_tracker.h"
15#include "video_core/renderer_vulkan/vk_swapchain.h" 15#include "video_core/renderer_vulkan/vk_swapchain.h"
16#include "video_core/renderer_vulkan/vk_turbo_mode.h"
16#include "video_core/vulkan_common/vulkan_device.h" 17#include "video_core/vulkan_common/vulkan_device.h"
17#include "video_core/vulkan_common/vulkan_memory_allocator.h" 18#include "video_core/vulkan_common/vulkan_memory_allocator.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -31,6 +32,9 @@ class GPU;
31 32
32namespace Vulkan { 33namespace Vulkan {
33 34
35Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
36 VkSurfaceKHR surface);
37
34class RendererVulkan final : public VideoCore::RendererBase { 38class RendererVulkan final : public VideoCore::RendererBase {
35public: 39public:
36 explicit RendererVulkan(Core::TelemetrySession& telemtry_session, 40 explicit RendererVulkan(Core::TelemetrySession& telemtry_session,
@@ -74,6 +78,7 @@ private:
74 Swapchain swapchain; 78 Swapchain swapchain;
75 BlitScreen blit_screen; 79 BlitScreen blit_screen;
76 RasterizerVulkan rasterizer; 80 RasterizerVulkan rasterizer;
81 std::optional<TurboMode> turbo_mode;
77}; 82};
78 83
79} // namespace Vulkan 84} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 84d36fea6..1cfb4c2ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -51,21 +51,13 @@ size_t BytesPerIndex(VkIndexType index_type) {
51 } 51 }
52} 52}
53 53
54template <typename T>
55std::array<T, 6> MakeQuadIndices(u32 quad, u32 first) {
56 std::array<T, 6> indices{0, 1, 2, 0, 2, 3};
57 for (T& index : indices) {
58 index = static_cast<T>(first + index + quad * 4);
59 }
60 return indices;
61}
62
63vk::Buffer CreateBuffer(const Device& device, u64 size) { 54vk::Buffer CreateBuffer(const Device& device, u64 size) {
64 VkBufferUsageFlags flags = 55 VkBufferUsageFlags flags =
65 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 56 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
66 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | 57 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
67 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 58 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
68 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; 59 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
60 VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
69 if (device.IsExtTransformFeedbackSupported()) { 61 if (device.IsExtTransformFeedbackSupported()) {
70 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 62 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
71 } 63 }
@@ -123,6 +115,187 @@ VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat
123 return *views.back().handle; 115 return *views.back().handle;
124} 116}
125 117
118class QuadIndexBuffer {
119public:
120 QuadIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
121 Scheduler& scheduler_, StagingBufferPool& staging_pool_)
122 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
123 staging_pool{staging_pool_} {}
124
125 virtual ~QuadIndexBuffer() = default;
126
127 void UpdateBuffer(u32 num_indices_) {
128 if (num_indices_ <= num_indices) {
129 return;
130 }
131
132 scheduler.Finish();
133
134 num_indices = num_indices_;
135 index_type = IndexTypeFromNumElements(device, num_indices);
136
137 const u32 num_quads = GetQuadsNum(num_indices);
138 const u32 num_triangle_indices = num_quads * 6;
139 const u32 num_first_offset_copies = 4;
140 const size_t bytes_per_index = BytesPerIndex(index_type);
141 const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
142 buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
143 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
144 .pNext = nullptr,
145 .flags = 0,
146 .size = size_bytes,
147 .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
148 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
149 .queueFamilyIndexCount = 0,
150 .pQueueFamilyIndices = nullptr,
151 });
152 if (device.HasDebuggingToolAttached()) {
153 buffer.SetObjectNameEXT("Quad LUT");
154 }
155 memory_commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
156
157 const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload);
158 u8* staging_data = staging.mapped_span.data();
159 const size_t quad_size = bytes_per_index * 6;
160
161 for (u32 first = 0; first < num_first_offset_copies; ++first) {
162 for (u32 quad = 0; quad < num_quads; ++quad) {
163 MakeAndUpdateIndices(staging_data, quad_size, quad, first);
164 staging_data += quad_size;
165 }
166 }
167
168 scheduler.RequestOutsideRenderPassOperationContext();
169 scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset,
170 dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) {
171 const VkBufferCopy copy{
172 .srcOffset = src_offset,
173 .dstOffset = 0,
174 .size = size_bytes,
175 };
176 const VkBufferMemoryBarrier write_barrier{
177 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
178 .pNext = nullptr,
179 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
180 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
181 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
182 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
183 .buffer = dst_buffer,
184 .offset = 0,
185 .size = size_bytes,
186 };
187 cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy);
188 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
189 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier);
190 });
191 }
192
193 void BindBuffer(u32 first) {
194 const VkIndexType index_type_ = index_type;
195 const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices);
196 const size_t offset =
197 (sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type);
198 scheduler.Record([buffer = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) {
199 cmdbuf.BindIndexBuffer(buffer, offset, index_type_);
200 });
201 }
202
203protected:
204 virtual u32 GetQuadsNum(u32 num_indices) const = 0;
205
206 virtual void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) = 0;
207
208 const Device& device;
209 MemoryAllocator& memory_allocator;
210 Scheduler& scheduler;
211 StagingBufferPool& staging_pool;
212
213 vk::Buffer buffer{};
214 MemoryCommit memory_commit{};
215 VkIndexType index_type{};
216 u32 num_indices = 0;
217};
218
219class QuadArrayIndexBuffer : public QuadIndexBuffer {
220public:
221 QuadArrayIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
222 Scheduler& scheduler_, StagingBufferPool& staging_pool_)
223 : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
224
225 ~QuadArrayIndexBuffer() = default;
226
227private:
228 u32 GetQuadsNum(u32 num_indices_) const override {
229 return num_indices_ / 4;
230 }
231
232 template <typename T>
233 static std::array<T, 6> MakeIndices(u32 quad, u32 first) {
234 std::array<T, 6> indices{0, 1, 2, 0, 2, 3};
235 for (T& index : indices) {
236 index = static_cast<T>(first + index + quad * 4);
237 }
238 return indices;
239 }
240
241 void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) {
242 switch (index_type) {
243 case VK_INDEX_TYPE_UINT8_EXT:
244 std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), quad_size);
245 break;
246 case VK_INDEX_TYPE_UINT16:
247 std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), quad_size);
248 break;
249 case VK_INDEX_TYPE_UINT32:
250 std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), quad_size);
251 break;
252 default:
253 ASSERT(false);
254 break;
255 }
256 }
257};
258
259class QuadStripIndexBuffer : public QuadIndexBuffer {
260public:
261 QuadStripIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_,
262 Scheduler& scheduler_, StagingBufferPool& staging_pool_)
263 : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {}
264
265 ~QuadStripIndexBuffer() = default;
266
267private:
268 u32 GetQuadsNum(u32 num_indices_) const override {
269 return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0;
270 }
271
272 template <typename T>
273 static std::array<T, 6> MakeIndices(u32 quad, u32 first) {
274 std::array<T, 6> indices{0, 3, 1, 0, 2, 3};
275 for (T& index : indices) {
276 index = static_cast<T>(first + index + quad * 2);
277 }
278 return indices;
279 }
280
281 void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) {
282 switch (index_type) {
283 case VK_INDEX_TYPE_UINT8_EXT:
284 std::memcpy(staging_data, MakeIndices<u8>(quad, first).data(), quad_size);
285 break;
286 case VK_INDEX_TYPE_UINT16:
287 std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), quad_size);
288 break;
289 case VK_INDEX_TYPE_UINT32:
290 std::memcpy(staging_data, MakeIndices<u32>(quad, first).data(), quad_size);
291 break;
292 default:
293 ASSERT(false);
294 break;
295 }
296 }
297};
298
126BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_, 299BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_,
127 Scheduler& scheduler_, StagingBufferPool& staging_pool_, 300 Scheduler& scheduler_, StagingBufferPool& staging_pool_,
128 UpdateDescriptorQueue& update_descriptor_queue_, 301 UpdateDescriptorQueue& update_descriptor_queue_,
@@ -130,7 +303,12 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
130 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, 303 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
131 staging_pool{staging_pool_}, update_descriptor_queue{update_descriptor_queue_}, 304 staging_pool{staging_pool_}, update_descriptor_queue{update_descriptor_queue_},
132 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 305 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
133 quad_index_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue) {} 306 quad_index_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue) {
307 quad_array_index_buffer = std::make_shared<QuadArrayIndexBuffer>(device_, memory_allocator_,
308 scheduler_, staging_pool_);
309 quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
310 scheduler_, staging_pool_);
311}
134 312
135StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) { 313StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
136 return staging_pool.Request(size, MemoryUsage::Upload); 314 return staging_pool.Request(size, MemoryUsage::Upload);
@@ -152,12 +330,19 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
152 return device.CanReportMemoryUsage(); 330 return device.CanReportMemoryUsage();
153} 331}
154 332
333u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
334 return static_cast<u32>(device.GetStorageBufferAlignment());
335}
336
155void BufferCacheRuntime::Finish() { 337void BufferCacheRuntime::Finish() {
156 scheduler.Finish(); 338 scheduler.Finish();
157} 339}
158 340
159void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, 341void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
160 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 342 std::span<const VideoCommon::BufferCopy> copies, bool barrier) {
343 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
344 return;
345 }
161 static constexpr VkMemoryBarrier READ_BARRIER{ 346 static constexpr VkMemoryBarrier READ_BARRIER{
162 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 347 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
163 .pNext = nullptr, 348 .pNext = nullptr,
@@ -216,6 +401,9 @@ void BufferCacheRuntime::PostCopyBarrier() {
216} 401}
217 402
218void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) { 403void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) {
404 if (dest_buffer == VK_NULL_HANDLE) {
405 return;
406 }
219 static constexpr VkMemoryBarrier READ_BARRIER{ 407 static constexpr VkMemoryBarrier READ_BARRIER{
220 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 408 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
221 .pNext = nullptr, 409 .pNext = nullptr,
@@ -245,10 +433,11 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
245 VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format); 433 VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format);
246 VkDeviceSize vk_offset = offset; 434 VkDeviceSize vk_offset = offset;
247 VkBuffer vk_buffer = buffer; 435 VkBuffer vk_buffer = buffer;
248 if (topology == PrimitiveTopology::Quads) { 436 if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) {
249 vk_index_type = VK_INDEX_TYPE_UINT32; 437 vk_index_type = VK_INDEX_TYPE_UINT32;
250 std::tie(vk_buffer, vk_offset) = 438 std::tie(vk_buffer, vk_offset) =
251 quad_index_pass.Assemble(index_format, num_indices, base_vertex, buffer, offset); 439 quad_index_pass.Assemble(index_format, num_indices, base_vertex, buffer, offset,
440 topology == PrimitiveTopology::QuadStrip);
252 } else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) { 441 } else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) {
253 vk_index_type = VK_INDEX_TYPE_UINT16; 442 vk_index_type = VK_INDEX_TYPE_UINT16;
254 std::tie(vk_buffer, vk_offset) = uint8_pass.Assemble(num_indices, buffer, offset); 443 std::tie(vk_buffer, vk_offset) = uint8_pass.Assemble(num_indices, buffer, offset);
@@ -263,7 +452,7 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
263 }); 452 });
264} 453}
265 454
266void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { 455void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) {
267 if (count == 0) { 456 if (count == 0) {
268 ReserveNullBuffer(); 457 ReserveNullBuffer();
269 scheduler.Record([this](vk::CommandBuffer cmdbuf) { 458 scheduler.Record([this](vk::CommandBuffer cmdbuf) {
@@ -271,16 +460,14 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) {
271 }); 460 });
272 return; 461 return;
273 } 462 }
274 ReserveQuadArrayLUT(first + count, true); 463
275 464 if (topology == PrimitiveTopology::Quads) {
276 // The LUT has the indices 0, 1, 2, and 3 copied as an array 465 quad_array_index_buffer->UpdateBuffer(first + count);
277 // To apply these 'first' offsets we can apply an offset based on the modulus. 466 quad_array_index_buffer->BindBuffer(first);
278 const VkIndexType index_type = quad_array_lut_index_type; 467 } else if (topology == PrimitiveTopology::QuadStrip) {
279 const size_t sub_first_offset = static_cast<size_t>(first % 4) * (current_num_indices / 4); 468 quad_strip_index_buffer->UpdateBuffer(first + count);
280 const size_t offset = (sub_first_offset + first / 4) * 6ULL * BytesPerIndex(index_type); 469 quad_strip_index_buffer->BindBuffer(first);
281 scheduler.Record([buffer = *quad_array_lut, index_type, offset](vk::CommandBuffer cmdbuf) { 470 }
282 cmdbuf.BindIndexBuffer(buffer, offset, index_type);
283 });
284} 471}
285 472
286void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, 473void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size,
@@ -296,6 +483,11 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
296 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); 483 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
297 }); 484 });
298 } else { 485 } else {
486 if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
487 ReserveNullBuffer();
488 buffer = *null_buffer;
489 offset = 0;
490 }
299 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { 491 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
300 cmdbuf.BindVertexBuffer(index, buffer, offset); 492 cmdbuf.BindVertexBuffer(index, buffer, offset);
301 }); 493 });
@@ -323,83 +515,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
323 }); 515 });
324} 516}
325 517
326void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) {
327 if (num_indices <= current_num_indices) {
328 return;
329 }
330 if (wait_for_idle) {
331 scheduler.Finish();
332 }
333 current_num_indices = num_indices;
334 quad_array_lut_index_type = IndexTypeFromNumElements(device, num_indices);
335
336 const u32 num_quads = num_indices / 4;
337 const u32 num_triangle_indices = num_quads * 6;
338 const u32 num_first_offset_copies = 4;
339 const size_t bytes_per_index = BytesPerIndex(quad_array_lut_index_type);
340 const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
341 quad_array_lut = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
342 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
343 .pNext = nullptr,
344 .flags = 0,
345 .size = size_bytes,
346 .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
347 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
348 .queueFamilyIndexCount = 0,
349 .pQueueFamilyIndices = nullptr,
350 });
351 if (device.HasDebuggingToolAttached()) {
352 quad_array_lut.SetObjectNameEXT("Quad LUT");
353 }
354 quad_array_lut_commit = memory_allocator.Commit(quad_array_lut, MemoryUsage::DeviceLocal);
355
356 const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload);
357 u8* staging_data = staging.mapped_span.data();
358 const size_t quad_size = bytes_per_index * 6;
359 for (u32 first = 0; first < num_first_offset_copies; ++first) {
360 for (u32 quad = 0; quad < num_quads; ++quad) {
361 switch (quad_array_lut_index_type) {
362 case VK_INDEX_TYPE_UINT8_EXT:
363 std::memcpy(staging_data, MakeQuadIndices<u8>(quad, first).data(), quad_size);
364 break;
365 case VK_INDEX_TYPE_UINT16:
366 std::memcpy(staging_data, MakeQuadIndices<u16>(quad, first).data(), quad_size);
367 break;
368 case VK_INDEX_TYPE_UINT32:
369 std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size);
370 break;
371 default:
372 ASSERT(false);
373 break;
374 }
375 staging_data += quad_size;
376 }
377 }
378 scheduler.RequestOutsideRenderPassOperationContext();
379 scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset,
380 dst_buffer = *quad_array_lut, size_bytes](vk::CommandBuffer cmdbuf) {
381 const VkBufferCopy copy{
382 .srcOffset = src_offset,
383 .dstOffset = 0,
384 .size = size_bytes,
385 };
386 const VkBufferMemoryBarrier write_barrier{
387 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
388 .pNext = nullptr,
389 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
390 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
391 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
392 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
393 .buffer = dst_buffer,
394 .offset = 0,
395 .size = size_bytes,
396 };
397 cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy);
398 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
399 0, write_barrier);
400 });
401}
402
403void BufferCacheRuntime::ReserveNullBuffer() { 518void BufferCacheRuntime::ReserveNullBuffer() {
404 if (null_buffer) { 519 if (null_buffer) {
405 return; 520 return;
@@ -417,6 +532,7 @@ void BufferCacheRuntime::ReserveNullBuffer() {
417 if (device.IsExtTransformFeedbackSupported()) { 532 if (device.IsExtTransformFeedbackSupported()) {
418 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 533 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
419 } 534 }
535 create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
420 null_buffer = device.GetLogical().CreateBuffer(create_info); 536 null_buffer = device.GetLogical().CreateBuffer(create_info);
421 if (device.HasDebuggingToolAttached()) { 537 if (device.HasDebuggingToolAttached()) {
422 null_buffer.SetObjectNameEXT("Null buffer"); 538 null_buffer.SetObjectNameEXT("Null buffer");
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index a15c8b39b..06539c733 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -50,6 +50,9 @@ private:
50 std::vector<BufferView> views; 50 std::vector<BufferView> views;
51}; 51};
52 52
53class QuadArrayIndexBuffer;
54class QuadStripIndexBuffer;
55
53class BufferCacheRuntime { 56class BufferCacheRuntime {
54 friend Buffer; 57 friend Buffer;
55 58
@@ -70,6 +73,8 @@ public:
70 73
71 bool CanReportMemoryUsage() const; 74 bool CanReportMemoryUsage() const;
72 75
76 u32 GetStorageBufferAlignment() const;
77
73 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 78 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
74 79
75 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 80 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
@@ -86,7 +91,7 @@ public:
86 void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices, 91 void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices,
87 u32 base_vertex, VkBuffer buffer, u32 offset, u32 size); 92 u32 base_vertex, VkBuffer buffer, u32 offset, u32 size);
88 93
89 void BindQuadArrayIndexBuffer(u32 first, u32 count); 94 void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
90 95
91 void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); 96 void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
92 97
@@ -118,8 +123,6 @@ private:
118 update_descriptor_queue.AddBuffer(buffer, offset, size); 123 update_descriptor_queue.AddBuffer(buffer, offset, size);
119 } 124 }
120 125
121 void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle);
122
123 void ReserveNullBuffer(); 126 void ReserveNullBuffer();
124 127
125 const Device& device; 128 const Device& device;
@@ -128,10 +131,8 @@ private:
128 StagingBufferPool& staging_pool; 131 StagingBufferPool& staging_pool;
129 UpdateDescriptorQueue& update_descriptor_queue; 132 UpdateDescriptorQueue& update_descriptor_queue;
130 133
131 vk::Buffer quad_array_lut; 134 std::shared_ptr<QuadArrayIndexBuffer> quad_array_index_buffer;
132 MemoryCommit quad_array_lut_commit; 135 std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
133 VkIndexType quad_array_lut_index_type{};
134 u32 current_num_indices = 0;
135 136
136 vk::Buffer null_buffer; 137 vk::Buffer null_buffer;
137 MemoryCommit null_buffer_commit; 138 MemoryCommit null_buffer_commit;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 2c00979d7..1a316b6eb 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -245,7 +245,7 @@ QuadIndexedPass::QuadIndexedPass(const Device& device_, Scheduler& scheduler_,
245 UpdateDescriptorQueue& update_descriptor_queue_) 245 UpdateDescriptorQueue& update_descriptor_queue_)
246 : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, 246 : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS,
247 INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, 247 INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO,
248 COMPUTE_PUSH_CONSTANT_RANGE<sizeof(u32) * 2>, VULKAN_QUAD_INDEXED_COMP_SPV), 248 COMPUTE_PUSH_CONSTANT_RANGE<sizeof(u32) * 3>, VULKAN_QUAD_INDEXED_COMP_SPV),
249 scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, 249 scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
250 update_descriptor_queue{update_descriptor_queue_} {} 250 update_descriptor_queue{update_descriptor_queue_} {}
251 251
@@ -253,7 +253,7 @@ QuadIndexedPass::~QuadIndexedPass() = default;
253 253
254std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( 254std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
255 Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, u32 base_vertex, 255 Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, u32 base_vertex,
256 VkBuffer src_buffer, u32 src_offset) { 256 VkBuffer src_buffer, u32 src_offset, bool is_strip) {
257 const u32 index_shift = [index_format] { 257 const u32 index_shift = [index_format] {
258 switch (index_format) { 258 switch (index_format) {
259 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedByte: 259 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedByte:
@@ -267,7 +267,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
267 return 2; 267 return 2;
268 }(); 268 }();
269 const u32 input_size = num_vertices << index_shift; 269 const u32 input_size = num_vertices << index_shift;
270 const u32 num_tri_vertices = (num_vertices / 4) * 6; 270 const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
271 271
272 const std::size_t staging_size = num_tri_vertices * sizeof(u32); 272 const std::size_t staging_size = num_tri_vertices * sizeof(u32);
273 const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); 273 const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
@@ -278,8 +278,8 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
278 const void* const descriptor_data{update_descriptor_queue.UpdateData()}; 278 const void* const descriptor_data{update_descriptor_queue.UpdateData()};
279 279
280 scheduler.RequestOutsideRenderPassOperationContext(); 280 scheduler.RequestOutsideRenderPassOperationContext();
281 scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, 281 scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, index_shift,
282 index_shift](vk::CommandBuffer cmdbuf) { 282 is_strip](vk::CommandBuffer cmdbuf) {
283 static constexpr u32 DISPATCH_SIZE = 1024; 283 static constexpr u32 DISPATCH_SIZE = 1024;
284 static constexpr VkMemoryBarrier WRITE_BARRIER{ 284 static constexpr VkMemoryBarrier WRITE_BARRIER{
285 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 285 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
@@ -287,7 +287,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
287 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 287 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
288 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, 288 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
289 }; 289 };
290 const std::array<u32, 2> push_constants{base_vertex, index_shift}; 290 const std::array<u32, 3> push_constants{base_vertex, index_shift, is_strip ? 1u : 0u};
291 const VkDescriptorSet set = descriptor_allocator.Commit(); 291 const VkDescriptorSet set = descriptor_allocator.Commit();
292 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); 292 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
293 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); 293 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 5d32e3caf..c4c8fa081 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -74,7 +74,7 @@ public:
74 74
75 std::pair<VkBuffer, VkDeviceSize> Assemble( 75 std::pair<VkBuffer, VkDeviceSize> Assemble(
76 Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, 76 Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices,
77 u32 base_vertex, VkBuffer src_buffer, u32 src_offset); 77 u32 base_vertex, VkBuffer src_buffer, u32 src_offset, bool is_strip);
78 78
79private: 79private:
80 Scheduler& scheduler; 80 Scheduler& scheduler;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 04a3a861e..2a0f0dbf0 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -24,13 +24,15 @@ using Shader::ImageBufferDescriptor;
24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
25using Tegra::Texture::TexturePair; 25using Tegra::Texture::TexturePair;
26 26
27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_,
28 DescriptorPool& descriptor_pool,
28 UpdateDescriptorQueue& update_descriptor_queue_, 29 UpdateDescriptorQueue& update_descriptor_queue_,
29 Common::ThreadWorker* thread_worker, 30 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics, 31 PipelineStatistics* pipeline_statistics,
31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 32 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
32 vk::ShaderModule spv_module_) 33 vk::ShaderModule spv_module_)
33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 34 : device{device_}, pipeline_cache(pipeline_cache_),
35 update_descriptor_queue{update_descriptor_queue_}, info{info_},
34 spv_module(std::move(spv_module_)) { 36 spv_module(std::move(spv_module_)) {
35 if (shader_notify) { 37 if (shader_notify) {
36 shader_notify->MarkShaderBuilding(); 38 shader_notify->MarkShaderBuilding();
@@ -56,23 +58,27 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
56 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 58 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 59 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 } 60 }
59 pipeline = device.GetLogical().CreateComputePipeline({ 61 pipeline = device.GetLogical().CreateComputePipeline(
60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 62 {
61 .pNext = nullptr, 63 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
62 .flags = flags, 64 .pNext = nullptr,
63 .stage{ 65 .flags = flags,
64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 66 .stage{
65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 67 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
66 .flags = 0, 68 .pNext =
67 .stage = VK_SHADER_STAGE_COMPUTE_BIT, 69 device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
68 .module = *spv_module, 70 .flags = 0,
69 .pName = "main", 71 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
70 .pSpecializationInfo = nullptr, 72 .module = *spv_module,
73 .pName = "main",
74 .pSpecializationInfo = nullptr,
75 },
76 .layout = *pipeline_layout,
77 .basePipelineHandle = 0,
78 .basePipelineIndex = 0,
71 }, 79 },
72 .layout = *pipeline_layout, 80 *pipeline_cache);
73 .basePipelineHandle = 0, 81
74 .basePipelineIndex = 0,
75 });
76 if (pipeline_statistics) { 82 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline); 83 pipeline_statistics->Collect(*pipeline);
78 } 84 }
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index d70837fc5..78d77027f 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -28,7 +28,8 @@ class Scheduler;
28 28
29class ComputePipeline { 29class ComputePipeline {
30public: 30public:
31 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 31 explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache,
32 DescriptorPool& descriptor_pool,
32 UpdateDescriptorQueue& update_descriptor_queue, 33 UpdateDescriptorQueue& update_descriptor_queue,
33 Common::ThreadWorker* thread_worker, 34 Common::ThreadWorker* thread_worker,
34 PipelineStatistics* pipeline_statistics, 35 PipelineStatistics* pipeline_statistics,
@@ -46,6 +47,7 @@ public:
46 47
47private: 48private:
48 const Device& device; 49 const Device& device;
50 vk::PipelineCache& pipeline_cache;
49 UpdateDescriptorQueue& update_descriptor_queue; 51 UpdateDescriptorQueue& update_descriptor_queue;
50 Shader::Info info; 52 Shader::Info info;
51 53
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index 33daa8c1c..df972cd54 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -1,12 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cmath>
5#include "common/bit_cast.h"
6#include "common/common_types.h" 4#include "common/common_types.h"
7#include "common/div_ceil.h" 5#include "common/div_ceil.h"
8#include "common/settings.h" 6#include "common/settings.h"
9 7
8#include "video_core/fsr.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" 9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" 10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" 11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
@@ -17,146 +16,7 @@
17#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
18 17
19namespace Vulkan { 18namespace Vulkan {
20namespace { 19using namespace FSR;
21// Reimplementations of the constant generating functions in ffx_fsr1.h
22// GCC generated a lot of warnings when using the official header.
23u32 AU1_AH1_AF1(f32 f) {
24 static constexpr u32 base[512]{
25 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
26 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
27 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
28 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
29 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
30 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
32 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
35 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
36 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
37 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
38 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
39 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
40 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
41 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
42 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
43 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
44 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
45 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
46 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
47 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
48 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
49 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
50 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
51 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
52 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
53 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
54 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
55 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
56 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
57 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
58 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
59 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
60 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
61 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
62 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
63 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
64 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
65 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
66 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
67 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
68 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
69 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
70 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
71 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
72 };
73 static constexpr s8 shift[512]{
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
81 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
82 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
83 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
86 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
87 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
88 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
97 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
98 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
99 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
100 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
101 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
102 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
103 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
104 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
105 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
106 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
107 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
108 0x18, 0x18,
109 };
110 const u32 u = Common::BitCast<u32>(f);
111 const u32 i = u >> 23;
112 return base[i] + ((u & 0x7fffff) >> shift[i]);
113}
114
115u32 AU1_AH2_AF2(f32 a[2]) {
116 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
117}
118
119void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
120 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
121 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
122 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
123 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
124 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
125 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
126 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
127 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
128 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
129 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
130 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
131 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
132 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
133 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
134 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
135 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
136 con3[2] = con3[3] = 0;
137}
138
139void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
140 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
141 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
142 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
143 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
144 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
145 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
146 inputOffsetInPixelsX);
147 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
148 inputOffsetInPixelsY);
149}
150
151void FsrRcasCon(u32* con, f32 sharpness) {
152 sharpness = std::exp2f(-sharpness);
153 f32 hSharp[2]{sharpness, sharpness};
154 con[0] = Common::BitCast<u32>(sharpness);
155 con[1] = AU1_AH2_AF2(hSharp);
156 con[2] = 0;
157 con[3] = 0;
158}
159} // Anonymous namespace
160 20
161FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, 21FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
162 VkExtent2D output_size_) 22 VkExtent2D output_size_)
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 515d8d869..f91bb5a1d 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -201,6 +201,22 @@ struct SimpleVertexSpec {
201 static constexpr bool has_images = false; 201 static constexpr bool has_images = false;
202}; 202};
203 203
204struct SimpleStorageSpec {
205 static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
206 static constexpr bool has_storage_buffers = true;
207 static constexpr bool has_texture_buffers = false;
208 static constexpr bool has_image_buffers = false;
209 static constexpr bool has_images = false;
210};
211
212struct SimpleImageSpec {
213 static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
214 static constexpr bool has_storage_buffers = false;
215 static constexpr bool has_texture_buffers = false;
216 static constexpr bool has_image_buffers = false;
217 static constexpr bool has_images = true;
218};
219
204struct DefaultSpec { 220struct DefaultSpec {
205 static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true}; 221 static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
206 static constexpr bool has_storage_buffers = true; 222 static constexpr bool has_storage_buffers = true;
@@ -211,19 +227,21 @@ struct DefaultSpec {
211 227
212ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules, 228ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules,
213 const std::array<Shader::Info, NUM_STAGES>& infos) { 229 const std::array<Shader::Info, NUM_STAGES>& infos) {
214 return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, DefaultSpec>(modules, infos); 230 return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, SimpleStorageSpec, SimpleImageSpec,
231 DefaultSpec>(modules, infos);
215} 232}
216} // Anonymous namespace 233} // Anonymous namespace
217 234
218GraphicsPipeline::GraphicsPipeline( 235GraphicsPipeline::GraphicsPipeline(
219 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 236 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
220 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 237 vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify,
238 const Device& device_, DescriptorPool& descriptor_pool,
221 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 239 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
222 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, 240 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
223 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, 241 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
224 const std::array<const Shader::Info*, NUM_STAGES>& infos) 242 const std::array<const Shader::Info*, NUM_STAGES>& infos)
225 : key{key_}, device{device_}, texture_cache{texture_cache_}, 243 : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
226 buffer_cache{buffer_cache_}, scheduler{scheduler_}, 244 pipeline_cache(pipeline_cache_), scheduler{scheduler_},
227 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { 245 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} {
228 if (shader_notify) { 246 if (shader_notify) {
229 shader_notify->MarkShaderBuilding(); 247 shader_notify->MarkShaderBuilding();
@@ -524,6 +542,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
524 FixedPipelineState::DynamicState dynamic{}; 542 FixedPipelineState::DynamicState dynamic{};
525 if (!key.state.extended_dynamic_state) { 543 if (!key.state.extended_dynamic_state) {
526 dynamic = key.state.dynamic_state; 544 dynamic = key.state.dynamic_state;
545 } else {
546 dynamic.raw1 = key.state.dynamic_state.raw1;
527 } 547 }
528 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; 548 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 549 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
@@ -561,7 +581,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
561 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 581 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
562 vertex_bindings.push_back({ 582 vertex_bindings.push_back({
563 .binding = static_cast<u32>(index), 583 .binding = static_cast<u32>(index),
564 .stride = dynamic.vertex_strides[index], 584 .stride = key.state.vertex_strides[index],
565 .inputRate = rate, 585 .inputRate = rate,
566 }); 586 });
567 if (instanced) { 587 if (instanced) {
@@ -625,12 +645,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
625 .pNext = nullptr, 645 .pNext = nullptr,
626 .flags = 0, 646 .flags = 0,
627 .topology = input_assembly_topology, 647 .topology = input_assembly_topology,
628 .primitiveRestartEnable = key.state.primitive_restart_enable != 0 && 648 .primitiveRestartEnable =
629 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 649 dynamic.primitive_restart_enable != 0 &&
630 device.IsTopologyListPrimitiveRestartSupported()) || 650 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
631 SupportsPrimitiveRestart(input_assembly_topology) || 651 device.IsTopologyListPrimitiveRestartSupported()) ||
632 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 652 SupportsPrimitiveRestart(input_assembly_topology) ||
633 device.IsPatchListPrimitiveRestartSupported())), 653 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
654 device.IsPatchListPrimitiveRestartSupported()))
655 ? VK_TRUE
656 : VK_FALSE,
634 }; 657 };
635 const VkPipelineTessellationStateCreateInfo tessellation_ci{ 658 const VkPipelineTessellationStateCreateInfo tessellation_ci{
636 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 659 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -672,15 +695,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
672 .pNext = nullptr, 695 .pNext = nullptr,
673 .flags = 0, 696 .flags = 0,
674 .depthClampEnable = 697 .depthClampEnable =
675 static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), 698 static_cast<VkBool32>(dynamic.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
676 .rasterizerDiscardEnable = 699 .rasterizerDiscardEnable =
677 static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), 700 static_cast<VkBool32>(dynamic.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
678 .polygonMode = 701 .polygonMode =
679 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)), 702 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)),
680 .cullMode = static_cast<VkCullModeFlags>( 703 .cullMode = static_cast<VkCullModeFlags>(
681 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), 704 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
682 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), 705 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
683 .depthBiasEnable = key.state.depth_bias_enable, 706 .depthBiasEnable = (dynamic.depth_bias_enable != 0 ? VK_TRUE : VK_FALSE),
684 .depthBiasConstantFactor = 0.0f, 707 .depthBiasConstantFactor = 0.0f,
685 .depthBiasClamp = 0.0f, 708 .depthBiasClamp = 0.0f,
686 .depthBiasSlopeFactor = 0.0f, 709 .depthBiasSlopeFactor = 0.0f,
@@ -782,13 +805,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
782 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 805 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
783 .pNext = nullptr, 806 .pNext = nullptr,
784 .flags = 0, 807 .flags = 0,
785 .logicOpEnable = key.state.logic_op_enable != 0, 808 .logicOpEnable = dynamic.logic_op_enable != 0,
786 .logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()), 809 .logicOp = static_cast<VkLogicOp>(dynamic.logic_op.Value()),
787 .attachmentCount = static_cast<u32>(cb_attachments.size()), 810 .attachmentCount = static_cast<u32>(cb_attachments.size()),
788 .pAttachments = cb_attachments.data(), 811 .pAttachments = cb_attachments.data(),
789 .blendConstants = {}, 812 .blendConstants = {},
790 }; 813 };
791 static_vector<VkDynamicState, 19> dynamic_states{ 814 static_vector<VkDynamicState, 28> dynamic_states{
792 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, 815 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
793 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 816 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
794 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 817 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
@@ -811,6 +834,32 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
811 dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); 834 dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
812 } 835 }
813 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); 836 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
837 if (key.state.extended_dynamic_state_2) {
838 static constexpr std::array extended2{
839 VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
840 VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
841 VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
842 };
843 dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
844 }
845 if (key.state.extended_dynamic_state_2_extra) {
846 dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
847 }
848 if (key.state.extended_dynamic_state_3_blend) {
849 static constexpr std::array extended3{
850 VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
851 VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
852 VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
853 };
854 dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
855 }
856 if (key.state.extended_dynamic_state_3_enables) {
857 static constexpr std::array extended3{
858 VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
859 VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
860 };
861 dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
862 }
814 } 863 }
815 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ 864 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
816 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 865 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
@@ -849,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
849 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 898 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
850 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 899 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
851 } 900 }
852 pipeline = device.GetLogical().CreateGraphicsPipeline({ 901 pipeline = device.GetLogical().CreateGraphicsPipeline(
853 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 902 {
854 .pNext = nullptr, 903 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
855 .flags = flags, 904 .pNext = nullptr,
856 .stageCount = static_cast<u32>(shader_stages.size()), 905 .flags = flags,
857 .pStages = shader_stages.data(), 906 .stageCount = static_cast<u32>(shader_stages.size()),
858 .pVertexInputState = &vertex_input_ci, 907 .pStages = shader_stages.data(),
859 .pInputAssemblyState = &input_assembly_ci, 908 .pVertexInputState = &vertex_input_ci,
860 .pTessellationState = &tessellation_ci, 909 .pInputAssemblyState = &input_assembly_ci,
861 .pViewportState = &viewport_ci, 910 .pTessellationState = &tessellation_ci,
862 .pRasterizationState = &rasterization_ci, 911 .pViewportState = &viewport_ci,
863 .pMultisampleState = &multisample_ci, 912 .pRasterizationState = &rasterization_ci,
864 .pDepthStencilState = &depth_stencil_ci, 913 .pMultisampleState = &multisample_ci,
865 .pColorBlendState = &color_blend_ci, 914 .pDepthStencilState = &depth_stencil_ci,
866 .pDynamicState = &dynamic_state_ci, 915 .pColorBlendState = &color_blend_ci,
867 .layout = *pipeline_layout, 916 .pDynamicState = &dynamic_state_ci,
868 .renderPass = render_pass, 917 .layout = *pipeline_layout,
869 .subpass = 0, 918 .renderPass = render_pass,
870 .basePipelineHandle = nullptr, 919 .subpass = 0,
871 .basePipelineIndex = 0, 920 .basePipelineHandle = nullptr,
872 }); 921 .basePipelineIndex = 0,
922 },
923 *pipeline_cache);
873} 924}
874 925
875void GraphicsPipeline::Validate() { 926void GraphicsPipeline::Validate() {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 1ed2967be..67c657d0e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -70,16 +70,14 @@ 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(Scheduler& scheduler, BufferCache& buffer_cache, 73 explicit GraphicsPipeline(
74 TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, 74 Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
75 const Device& device, DescriptorPool& descriptor_pool, 75 vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify,
76 UpdateDescriptorQueue& update_descriptor_queue, 76 const Device& device, DescriptorPool& descriptor_pool,
77 Common::ThreadWorker* worker_thread, 77 UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* worker_thread,
78 PipelineStatistics* pipeline_statistics, 78 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
79 RenderPassCache& render_pass_cache, 79 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
80 const GraphicsPipelineCacheKey& key, 80 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);
83 81
84 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
85 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; 83 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
@@ -133,6 +131,7 @@ private:
133 const Device& device; 131 const Device& device;
134 TextureCache& texture_cache; 132 TextureCache& texture_cache;
135 BufferCache& buffer_cache; 133 BufferCache& buffer_cache;
134 vk::PipelineCache& pipeline_cache;
136 Scheduler& scheduler; 135 Scheduler& scheduler;
137 UpdateDescriptorQueue& update_descriptor_queue; 136 UpdateDescriptorQueue& update_descriptor_queue;
138 137
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index e7262420c..7e69b11d8 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -54,7 +54,8 @@ using VideoCommon::FileEnvironment;
54using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
55using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
56 56
57constexpr u32 CACHE_VERSION = 8; 57constexpr u32 CACHE_VERSION = 10;
58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
58 59
59template <typename Container> 60template <typename Container>
60auto MakeSpan(Container& container) { 61auto MakeSpan(Container& container) {
@@ -284,6 +285,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
284 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, 285 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_},
285 texture_cache{texture_cache_}, shader_notify{shader_notify_}, 286 texture_cache{texture_cache_}, shader_notify{shader_notify_},
286 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 287 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
288 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
287 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), 289 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
288 serialization_thread(1, "VkPipelineSerialization") { 290 serialization_thread(1, "VkPipelineSerialization") {
289 const auto& float_control{device.FloatControlProperties()}; 291 const auto& float_control{device.FloatControlProperties()};
@@ -329,6 +331,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
329 .need_declared_frag_colors = false, 331 .need_declared_frag_colors = false,
330 332
331 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, 333 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
334 .has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
332 .has_broken_unsigned_image_offsets = false, 335 .has_broken_unsigned_image_offsets = false,
333 .has_broken_signed_operations = false, 336 .has_broken_signed_operations = false,
334 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, 337 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
@@ -341,6 +344,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
341 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, 344 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE,
342 .support_snorm_render_buffer = true, 345 .support_snorm_render_buffer = true,
343 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 346 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
347 .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
348 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
344 }; 349 };
345 350
346 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { 351 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
@@ -351,9 +356,23 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
351 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", 356 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
352 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); 357 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
353 } 358 }
359
360 dynamic_features = DynamicFeatures{
361 .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
362 .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
363 .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
364 .has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
365 .has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
366 .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
367 };
354} 368}
355 369
356PipelineCache::~PipelineCache() = default; 370PipelineCache::~PipelineCache() {
371 if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
372 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
373 CACHE_VERSION);
374 }
375}
357 376
358GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { 377GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
359 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 378 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
@@ -362,8 +381,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
362 current_pipeline = nullptr; 381 current_pipeline = nullptr;
363 return nullptr; 382 return nullptr;
364 } 383 }
365 graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), 384 graphics_key.state.Refresh(*maxwell3d, dynamic_features);
366 device.IsExtVertexInputDynamicStateSupported());
367 385
368 if (current_pipeline) { 386 if (current_pipeline) {
369 GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; 387 GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
@@ -410,6 +428,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
410 } 428 }
411 pipeline_cache_filename = base_dir / "vulkan.bin"; 429 pipeline_cache_filename = base_dir / "vulkan.bin";
412 430
431 if (use_vulkan_pipeline_cache) {
432 vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin";
433 vulkan_pipeline_cache =
434 LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION);
435 }
436
413 struct { 437 struct {
414 std::mutex mutex; 438 std::mutex mutex;
415 size_t total{}; 439 size_t total{};
@@ -439,14 +463,21 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
439 }); 463 });
440 ++state.total; 464 ++state.total;
441 }}; 465 }};
442 const bool extended_dynamic_state = device.IsExtExtendedDynamicStateSupported();
443 const bool dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported();
444 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { 466 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
445 GraphicsPipelineCacheKey key; 467 GraphicsPipelineCacheKey key;
446 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 468 file.read(reinterpret_cast<char*>(&key), sizeof(key));
447 469
448 if ((key.state.extended_dynamic_state != 0) != extended_dynamic_state || 470 if ((key.state.extended_dynamic_state != 0) !=
449 (key.state.dynamic_vertex_input != 0) != dynamic_vertex_input) { 471 dynamic_features.has_extended_dynamic_state ||
472 (key.state.extended_dynamic_state_2 != 0) !=
473 dynamic_features.has_extended_dynamic_state_2 ||
474 (key.state.extended_dynamic_state_2_extra != 0) !=
475 dynamic_features.has_extended_dynamic_state_2_extra ||
476 (key.state.extended_dynamic_state_3_blend != 0) !=
477 dynamic_features.has_extended_dynamic_state_3_blend ||
478 (key.state.extended_dynamic_state_3_enables != 0) !=
479 dynamic_features.has_extended_dynamic_state_3_enables ||
480 (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
450 return; 481 return;
451 } 482 }
452 workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { 483 workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable {
@@ -481,6 +512,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
481 512
482 workers.WaitForRequests(stop_loading); 513 workers.WaitForRequests(stop_loading);
483 514
515 if (use_vulkan_pipeline_cache) {
516 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
517 CACHE_VERSION);
518 }
519
484 if (state.statistics) { 520 if (state.statistics) {
485 state.statistics->Report(); 521 state.statistics->Report();
486 } 522 }
@@ -601,10 +637,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
601 previous_stage = &program; 637 previous_stage = &program;
602 } 638 }
603 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 639 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
604 return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache, 640 return std::make_unique<GraphicsPipeline>(
605 &shader_notify, device, descriptor_pool, 641 scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device,
606 update_descriptor_queue, thread_worker, statistics, 642 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
607 render_pass_cache, key, std::move(modules), infos); 643 std::move(modules), infos);
608 644
609} catch (const Shader::Exception& exception) { 645} catch (const Shader::Exception& exception) {
610 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 646 LOG_ERROR(Render_Vulkan, "{}", exception.what());
@@ -674,13 +710,108 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
674 spv_module.SetObjectNameEXT(name.c_str()); 710 spv_module.SetObjectNameEXT(name.c_str());
675 } 711 }
676 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 712 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
677 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 713 return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool,
678 thread_worker, statistics, &shader_notify, 714 update_descriptor_queue, thread_worker, statistics,
679 program.info, std::move(spv_module)); 715 &shader_notify, program.info, std::move(spv_module));
680 716
681} catch (const Shader::Exception& exception) { 717} catch (const Shader::Exception& exception) {
682 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 718 LOG_ERROR(Render_Vulkan, "{}", exception.what());
683 return nullptr; 719 return nullptr;
684} 720}
685 721
722void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename,
723 const vk::PipelineCache& pipeline_cache,
724 u32 cache_version) try {
725 std::ofstream file(filename, std::ios::binary);
726 file.exceptions(std::ifstream::failbit);
727 if (!file.is_open()) {
728 LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}",
729 Common::FS::PathToUTF8String(filename));
730 return;
731 }
732 file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size())
733 .write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version));
734
735 size_t cache_size = 0;
736 std::vector<char> cache_data;
737 if (pipeline_cache) {
738 pipeline_cache.Read(&cache_size, nullptr);
739 cache_data.resize(cache_size);
740 pipeline_cache.Read(&cache_size, cache_data.data());
741 }
742 file.write(cache_data.data(), cache_size);
743
744 LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}",
745 Common::FS::PathToUTF8String(filename));
746
747} catch (const std::ios_base::failure& e) {
748 LOG_ERROR(Common_Filesystem, "{}", e.what());
749 if (!Common::FS::RemoveFile(filename)) {
750 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
751 Common::FS::PathToUTF8String(filename));
752 }
753}
754
755vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename,
756 u32 expected_cache_version) {
757 const auto create_pipeline_cache = [this](size_t data_size, const void* data) {
758 VkPipelineCacheCreateInfo pipeline_cache_ci = {
759 .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
760 .pNext = nullptr,
761 .flags = 0,
762 .initialDataSize = data_size,
763 .pInitialData = data};
764 return device.GetLogical().CreatePipelineCache(pipeline_cache_ci);
765 };
766 try {
767 std::ifstream file(filename, std::ios::binary | std::ios::ate);
768 if (!file.is_open()) {
769 return create_pipeline_cache(0, nullptr);
770 }
771 file.exceptions(std::ifstream::failbit);
772 const auto end{file.tellg()};
773 file.seekg(0, std::ios::beg);
774
775 std::array<char, 8> magic_number;
776 u32 cache_version;
777 file.read(magic_number.data(), magic_number.size())
778 .read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version));
779 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) {
780 file.close();
781 if (Common::FS::RemoveFile(filename)) {
782 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) {
783 LOG_ERROR(Common_Filesystem, "Invalid Vulkan driver pipeline cache file");
784 }
785 if (cache_version != expected_cache_version) {
786 LOG_INFO(Common_Filesystem, "Deleting old Vulkan driver pipeline cache");
787 }
788 } else {
789 LOG_ERROR(Common_Filesystem,
790 "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"",
791 Common::FS::PathToUTF8String(filename));
792 }
793 return create_pipeline_cache(0, nullptr);
794 }
795
796 static constexpr size_t header_size = magic_number.size() + sizeof(cache_version);
797 const size_t cache_size = static_cast<size_t>(end) - header_size;
798 std::vector<char> cache_data(cache_size);
799 file.read(cache_data.data(), cache_size);
800
801 LOG_INFO(Render_Vulkan,
802 "Loaded Vulkan driver pipeline cache: ", Common::FS::PathToUTF8String(filename));
803
804 return create_pipeline_cache(cache_size, cache_data.data());
805
806 } catch (const std::ios_base::failure& e) {
807 LOG_ERROR(Common_Filesystem, "{}", e.what());
808 if (!Common::FS::RemoveFile(filename)) {
809 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
810 Common::FS::PathToUTF8String(filename));
811 }
812
813 return create_pipeline_cache(0, nullptr);
814 }
815}
816
686} // namespace Vulkan 817} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 61f9e9366..5171912d7 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -135,6 +135,12 @@ private:
135 PipelineStatistics* statistics, 135 PipelineStatistics* statistics,
136 bool build_in_parallel); 136 bool build_in_parallel);
137 137
138 void SerializeVulkanPipelineCache(const std::filesystem::path& filename,
139 const vk::PipelineCache& pipeline_cache, u32 cache_version);
140
141 vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
142 u32 expected_cache_version);
143
138 const Device& device; 144 const Device& device;
139 Scheduler& scheduler; 145 Scheduler& scheduler;
140 DescriptorPool& descriptor_pool; 146 DescriptorPool& descriptor_pool;
@@ -144,6 +150,7 @@ private:
144 TextureCache& texture_cache; 150 TextureCache& texture_cache;
145 VideoCore::ShaderNotify& shader_notify; 151 VideoCore::ShaderNotify& shader_notify;
146 bool use_asynchronous_shaders{}; 152 bool use_asynchronous_shaders{};
153 bool use_vulkan_pipeline_cache{};
147 154
148 GraphicsPipelineCacheKey graphics_key{}; 155 GraphicsPipelineCacheKey graphics_key{};
149 GraphicsPipeline* current_pipeline{}; 156 GraphicsPipeline* current_pipeline{};
@@ -158,8 +165,12 @@ private:
158 165
159 std::filesystem::path pipeline_cache_filename; 166 std::filesystem::path pipeline_cache_filename;
160 167
168 std::filesystem::path vulkan_pipeline_cache_filename;
169 vk::PipelineCache vulkan_pipeline_cache;
170
161 Common::ThreadWorker workers; 171 Common::ThreadWorker workers;
162 Common::ThreadWorker serialization_thread; 172 Common::ThreadWorker serialization_thread;
173 DynamicFeatures dynamic_features;
163}; 174};
164 175
165} // namespace Vulkan 176} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 4b7126c30..719edbcfb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -138,12 +138,16 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
138 .first_index = is_indexed ? draw_state.index_buffer.first : 0, 138 .first_index = is_indexed ? draw_state.index_buffer.first : 0,
139 .is_indexed = is_indexed, 139 .is_indexed = is_indexed,
140 }; 140 };
141 // 6 triangle vertices per quad, base vertex is part of the index
142 // See BindQuadIndexBuffer for more details
141 if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { 143 if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) {
142 // 6 triangle vertices per quad, base vertex is part of the index
143 // See BindQuadArrayIndexBuffer for more details
144 params.num_vertices = (params.num_vertices / 4) * 6; 144 params.num_vertices = (params.num_vertices / 4) * 6;
145 params.base_vertex = 0; 145 params.base_vertex = 0;
146 params.is_indexed = true; 146 params.is_indexed = true;
147 } else if (draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
148 params.num_vertices = (params.num_vertices - 2) / 2 * 6;
149 params.base_vertex = 0;
150 params.is_indexed = true;
147 } 151 }
148 return params; 152 return params;
149} 153}
@@ -176,11 +180,13 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
176 180
177RasterizerVulkan::~RasterizerVulkan() = default; 181RasterizerVulkan::~RasterizerVulkan() = default;
178 182
179void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { 183template <typename Func>
184void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
180 MICROPROFILE_SCOPE(Vulkan_Drawing); 185 MICROPROFILE_SCOPE(Vulkan_Drawing);
181 186
182 SCOPE_EXIT({ gpu.TickWork(); }); 187 SCOPE_EXIT({ gpu.TickWork(); });
183 FlushWork(); 188 FlushWork();
189 gpu_memory->FlushCaching();
184 190
185 query_cache.UpdateCounters(); 191 query_cache.UpdateCounters();
186 192
@@ -197,26 +203,103 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
197 203
198 UpdateDynamicStates(); 204 UpdateDynamicStates();
199 205
200 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 206 draw_func();
201 const u32 num_instances{instance_count}; 207
202 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; 208 EndTransformFeedback();
203 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { 209}
204 if (draw_params.is_indexed) { 210
205 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, 211void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
206 draw_params.first_index, draw_params.base_vertex, 212 PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
207 draw_params.base_instance); 213 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
208 } else { 214 const u32 num_instances{instance_count};
209 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, 215 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
210 draw_params.base_vertex, draw_params.base_instance); 216 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
217 if (draw_params.is_indexed) {
218 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
219 draw_params.first_index, draw_params.base_vertex,
220 draw_params.base_instance);
221 } else {
222 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
223 draw_params.base_vertex, draw_params.base_instance);
224 }
225 });
226 });
227}
228
229void RasterizerVulkan::DrawIndirect() {
230 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
231 buffer_cache.SetDrawIndirect(&params);
232 PrepareDraw(params.is_indexed, [this, &params] {
233 const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
234 const auto& buffer = indirect_buffer.first;
235 const auto& offset = indirect_buffer.second;
236 if (params.include_count) {
237 const auto count = buffer_cache.GetDrawIndirectCount();
238 const auto& draw_buffer = count.first;
239 const auto& offset_base = count.second;
240 scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
241 buffer_obj = buffer->Handle(), offset_base, offset,
242 params](vk::CommandBuffer cmdbuf) {
243 if (params.is_indexed) {
244 cmdbuf.DrawIndexedIndirectCount(
245 buffer_obj, offset, draw_buffer_obj, offset_base,
246 static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride));
247 } else {
248 cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base,
249 static_cast<u32>(params.max_draw_counts),
250 static_cast<u32>(params.stride));
251 }
252 });
253 return;
211 } 254 }
255 scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
256 if (params.is_indexed) {
257 cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
258 static_cast<u32>(params.max_draw_counts),
259 static_cast<u32>(params.stride));
260 } else {
261 cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts),
262 static_cast<u32>(params.stride));
263 }
264 });
212 }); 265 });
213 EndTransformFeedback(); 266 buffer_cache.SetDrawIndirect(nullptr);
267}
268
269void RasterizerVulkan::DrawTexture() {
270 MICROPROFILE_SCOPE(Vulkan_Drawing);
271
272 SCOPE_EXIT({ gpu.TickWork(); });
273 FlushWork();
274
275 query_cache.UpdateCounters();
276
277 texture_cache.SynchronizeGraphicsDescriptors();
278 texture_cache.UpdateRenderTargets(false);
279
280 UpdateDynamicStates();
281
282 const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
283 const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
284 const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
285 Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
286 .y = static_cast<s32>(draw_texture_state.dst_y0)},
287 Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
288 .y = static_cast<s32>(draw_texture_state.dst_y1)}};
289 Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
290 .y = static_cast<s32>(draw_texture_state.src_y0)},
291 Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
292 .y = static_cast<s32>(draw_texture_state.src_y1)}};
293 blit_image.BlitColor(texture_cache.GetFramebuffer(), texture.RenderTarget(),
294 texture.ImageHandle(), sampler->Handle(), dst_region, src_region,
295 texture.size);
214} 296}
215 297
216void RasterizerVulkan::Clear(u32 layer_count) { 298void RasterizerVulkan::Clear(u32 layer_count) {
217 MICROPROFILE_SCOPE(Vulkan_Clearing); 299 MICROPROFILE_SCOPE(Vulkan_Clearing);
218 300
219 FlushWork(); 301 FlushWork();
302 gpu_memory->FlushCaching();
220 303
221 query_cache.UpdateCounters(); 304 query_cache.UpdateCounters();
222 305
@@ -311,7 +394,15 @@ void RasterizerVulkan::Clear(u32 layer_count) {
311 cmdbuf.ClearAttachments(attachment, clear_rect); 394 cmdbuf.ClearAttachments(attachment, clear_rect);
312 }); 395 });
313 } else { 396 } else {
314 UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); 397 u8 color_mask = static_cast<u8>(regs.clear_surface.R | regs.clear_surface.G << 1 |
398 regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
399 Region2D dst_region = {
400 Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
401 Offset2D{.x = clear_rect.rect.offset.x +
402 static_cast<s32>(clear_rect.rect.extent.width),
403 .y = clear_rect.rect.offset.y +
404 static_cast<s32>(clear_rect.rect.extent.height)}};
405 blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
315 } 406 }
316 } 407 }
317 408
@@ -341,6 +432,7 @@ void RasterizerVulkan::Clear(u32 layer_count) {
341 432
342void RasterizerVulkan::DispatchCompute() { 433void RasterizerVulkan::DispatchCompute() {
343 FlushWork(); 434 FlushWork();
435 gpu_memory->FlushCaching();
344 436
345 ComputePipeline* const pipeline{pipeline_cache.CurrentComputePipeline()}; 437 ComputePipeline* const pipeline{pipeline_cache.CurrentComputePipeline()};
346 if (!pipeline) { 438 if (!pipeline) {
@@ -375,44 +467,79 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in
375 467
376void RasterizerVulkan::FlushAll() {} 468void RasterizerVulkan::FlushAll() {}
377 469
378void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { 470void RasterizerVulkan::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
379 if (addr == 0 || size == 0) { 471 if (addr == 0 || size == 0) {
380 return; 472 return;
381 } 473 }
382 { 474 if (True(which & VideoCommon::CacheType::TextureCache)) {
383 std::scoped_lock lock{texture_cache.mutex}; 475 std::scoped_lock lock{texture_cache.mutex};
384 texture_cache.DownloadMemory(addr, size); 476 texture_cache.DownloadMemory(addr, size);
385 } 477 }
386 { 478 if ((True(which & VideoCommon::CacheType::BufferCache))) {
387 std::scoped_lock lock{buffer_cache.mutex}; 479 std::scoped_lock lock{buffer_cache.mutex};
388 buffer_cache.DownloadMemory(addr, size); 480 buffer_cache.DownloadMemory(addr, size);
389 } 481 }
390 query_cache.FlushRegion(addr, size); 482 if ((True(which & VideoCommon::CacheType::QueryCache))) {
483 query_cache.FlushRegion(addr, size);
484 }
391} 485}
392 486
393bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { 487bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
394 std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; 488 if ((True(which & VideoCommon::CacheType::BufferCache))) {
489 std::scoped_lock lock{buffer_cache.mutex};
490 if (buffer_cache.IsRegionGpuModified(addr, size)) {
491 return true;
492 }
493 }
395 if (!Settings::IsGPULevelHigh()) { 494 if (!Settings::IsGPULevelHigh()) {
396 return buffer_cache.IsRegionGpuModified(addr, size); 495 return false;
397 } 496 }
398 return texture_cache.IsRegionGpuModified(addr, size) || 497 if (True(which & VideoCommon::CacheType::TextureCache)) {
399 buffer_cache.IsRegionGpuModified(addr, size); 498 std::scoped_lock lock{texture_cache.mutex};
499 return texture_cache.IsRegionGpuModified(addr, size);
500 }
501 return false;
400} 502}
401 503
402void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { 504void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
403 if (addr == 0 || size == 0) { 505 if (addr == 0 || size == 0) {
404 return; 506 return;
405 } 507 }
406 { 508 if (True(which & VideoCommon::CacheType::TextureCache)) {
407 std::scoped_lock lock{texture_cache.mutex}; 509 std::scoped_lock lock{texture_cache.mutex};
408 texture_cache.WriteMemory(addr, size); 510 texture_cache.WriteMemory(addr, size);
409 } 511 }
410 { 512 if ((True(which & VideoCommon::CacheType::BufferCache))) {
411 std::scoped_lock lock{buffer_cache.mutex}; 513 std::scoped_lock lock{buffer_cache.mutex};
412 buffer_cache.WriteMemory(addr, size); 514 buffer_cache.WriteMemory(addr, size);
413 } 515 }
414 pipeline_cache.InvalidateRegion(addr, size); 516 if ((True(which & VideoCommon::CacheType::QueryCache))) {
415 query_cache.InvalidateRegion(addr, size); 517 query_cache.InvalidateRegion(addr, size);
518 }
519 if ((True(which & VideoCommon::CacheType::ShaderCache))) {
520 pipeline_cache.InvalidateRegion(addr, size);
521 }
522}
523
524void RasterizerVulkan::InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) {
525 {
526 std::scoped_lock lock{texture_cache.mutex};
527 for (const auto& [addr, size] : sequences) {
528 texture_cache.WriteMemory(addr, size);
529 }
530 }
531 {
532 std::scoped_lock lock{buffer_cache.mutex};
533 for (const auto& [addr, size] : sequences) {
534 buffer_cache.WriteMemory(addr, size);
535 }
536 }
537 {
538 for (const auto& [addr, size] : sequences) {
539 query_cache.InvalidateRegion(addr, size);
540 pipeline_cache.InvalidateRegion(addr, size);
541 }
542 }
416} 543}
417 544
418void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { 545void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
@@ -477,11 +604,12 @@ void RasterizerVulkan::ReleaseFences() {
477 fence_manager.WaitPendingFences(); 604 fence_manager.WaitPendingFences();
478} 605}
479 606
480void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { 607void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size,
608 VideoCommon::CacheType which) {
481 if (Settings::IsGPULevelExtreme()) { 609 if (Settings::IsGPULevelExtreme()) {
482 FlushRegion(addr, size); 610 FlushRegion(addr, size, which);
483 } 611 }
484 InvalidateRegion(addr, size); 612 InvalidateRegion(addr, size, which);
485} 613}
486 614
487void RasterizerVulkan::WaitForIdle() { 615void RasterizerVulkan::WaitForIdle() {
@@ -537,6 +665,22 @@ void RasterizerVulkan::TickFrame() {
537 } 665 }
538} 666}
539 667
668bool RasterizerVulkan::AccelerateConditionalRendering() {
669 gpu_memory->FlushCaching();
670 if (Settings::IsGPULevelHigh()) {
671 // TODO(Blinkhawk): Reimplement Host conditional rendering.
672 return false;
673 }
674 // Medium / Low Hack: stub any checks on queries writen into the buffer cache.
675 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
676 Maxwell::ReportSemaphore::Compare cmp;
677 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
678 VideoCommon::CacheType::BufferCache)) {
679 return true;
680 }
681 return false;
682}
683
540bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 684bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
541 const Tegra::Engines::Fermi2D::Surface& dst, 685 const Tegra::Engines::Fermi2D::Surface& dst,
542 const Tegra::Engines::Fermi2D::Config& copy_config) { 686 const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -557,7 +701,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
557 } 701 }
558 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); 702 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
559 { 703 {
560 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 704 std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
561 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 705 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
562 buffer_cache.WriteMemory(*cpu_addr, copy_size); 706 buffer_cache.WriteMemory(*cpu_addr, copy_size);
563 } 707 }
@@ -635,16 +779,35 @@ void RasterizerVulkan::UpdateDynamicStates() {
635 UpdateLineWidth(regs); 779 UpdateLineWidth(regs);
636 if (device.IsExtExtendedDynamicStateSupported()) { 780 if (device.IsExtExtendedDynamicStateSupported()) {
637 UpdateCullMode(regs); 781 UpdateCullMode(regs);
638 UpdateDepthBoundsTestEnable(regs);
639 UpdateDepthTestEnable(regs);
640 UpdateDepthWriteEnable(regs);
641 UpdateDepthCompareOp(regs); 782 UpdateDepthCompareOp(regs);
642 UpdateFrontFace(regs); 783 UpdateFrontFace(regs);
643 UpdateStencilOp(regs); 784 UpdateStencilOp(regs);
644 UpdateStencilTestEnable(regs); 785
645 if (device.IsExtVertexInputDynamicStateSupported()) { 786 if (device.IsExtVertexInputDynamicStateSupported()) {
646 UpdateVertexInput(regs); 787 UpdateVertexInput(regs);
647 } 788 }
789
790 if (state_tracker.TouchStateEnable()) {
791 UpdateDepthBoundsTestEnable(regs);
792 UpdateDepthTestEnable(regs);
793 UpdateDepthWriteEnable(regs);
794 UpdateStencilTestEnable(regs);
795 if (device.IsExtExtendedDynamicState2Supported()) {
796 UpdatePrimitiveRestartEnable(regs);
797 UpdateRasterizerDiscardEnable(regs);
798 UpdateDepthBiasEnable(regs);
799 }
800 if (device.IsExtExtendedDynamicState3EnablesSupported()) {
801 UpdateLogicOpEnable(regs);
802 UpdateDepthClampEnable(regs);
803 }
804 }
805 if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
806 UpdateLogicOp(regs);
807 }
808 if (device.IsExtExtendedDynamicState3Supported()) {
809 UpdateBlending(regs);
810 }
648 } 811 }
649} 812}
650 813
@@ -785,32 +948,92 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
785 if (!state_tracker.TouchStencilProperties()) { 948 if (!state_tracker.TouchStencilProperties()) {
786 return; 949 return;
787 } 950 }
788 if (regs.stencil_two_side_enable) { 951 bool update_references = state_tracker.TouchStencilReference();
789 // Separate values per face 952 bool update_write_mask = state_tracker.TouchStencilWriteMask();
790 scheduler.Record( 953 bool update_compare_masks = state_tracker.TouchStencilCompare();
791 [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask, 954 if (state_tracker.TouchStencilSide(regs.stencil_two_side_enable != 0)) {
792 front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref, 955 update_references = true;
793 back_write_mask = regs.stencil_back_mask, 956 update_write_mask = true;
794 back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { 957 update_compare_masks = true;
958 }
959 if (update_references) {
960 [&]() {
961 if (regs.stencil_two_side_enable) {
962 if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) &&
963 !state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) {
964 return;
965 }
966 } else {
967 if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
968 return;
969 }
970 }
971 scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref,
972 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
973 const bool set_back = two_sided && front_ref != back_ref;
795 // Front face 974 // Front face
796 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref); 975 cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
797 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask); 976 : VK_STENCIL_FACE_FRONT_AND_BACK,
798 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT, front_test_mask); 977 front_ref);
799 978 if (set_back) {
800 // Back face 979 cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
801 cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref); 980 }
802 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
803 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
804 }); 981 });
805 } else { 982 }();
806 // Front face defines both faces 983 }
807 scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask, 984 if (update_write_mask) {
808 test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { 985 [&]() {
809 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); 986 if (regs.stencil_two_side_enable) {
810 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); 987 if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) &&
811 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); 988 !state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
812 }); 989 return;
990 }
991 } else {
992 if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
993 return;
994 }
995 }
996 scheduler.Record([front_write_mask = regs.stencil_front_mask,
997 back_write_mask = regs.stencil_back_mask,
998 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
999 const bool set_back = two_sided && front_write_mask != back_write_mask;
1000 // Front face
1001 cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
1002 : VK_STENCIL_FACE_FRONT_AND_BACK,
1003 front_write_mask);
1004 if (set_back) {
1005 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
1006 }
1007 });
1008 }();
1009 }
1010 if (update_compare_masks) {
1011 [&]() {
1012 if (regs.stencil_two_side_enable) {
1013 if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) &&
1014 !state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
1015 return;
1016 }
1017 } else {
1018 if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
1019 return;
1020 }
1021 }
1022 scheduler.Record([front_test_mask = regs.stencil_front_func_mask,
1023 back_test_mask = regs.stencil_back_func_mask,
1024 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
1025 const bool set_back = two_sided && front_test_mask != back_test_mask;
1026 // Front face
1027 cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
1028 : VK_STENCIL_FACE_FRONT_AND_BACK,
1029 front_test_mask);
1030 if (set_back) {
1031 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
1032 }
1033 });
1034 }();
813 } 1035 }
1036 state_tracker.ClearStencilReset();
814} 1037}
815 1038
816void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { 1039void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -864,6 +1087,82 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r
864 }); 1087 });
865} 1088}
866 1089
1090void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1091 if (!state_tracker.TouchPrimitiveRestartEnable()) {
1092 return;
1093 }
1094 scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
1095 cmdbuf.SetPrimitiveRestartEnableEXT(enable);
1096 });
1097}
1098
1099void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1100 if (!state_tracker.TouchRasterizerDiscardEnable()) {
1101 return;
1102 }
1103 scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
1104 cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
1105 });
1106}
1107
1108void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1109 if (!state_tracker.TouchDepthBiasEnable()) {
1110 return;
1111 }
1112 constexpr size_t POINT = 0;
1113 constexpr size_t LINE = 1;
1114 constexpr size_t POLYGON = 2;
1115 static constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
1116 POINT, // Points
1117 LINE, // Lines
1118 LINE, // LineLoop
1119 LINE, // LineStrip
1120 POLYGON, // Triangles
1121 POLYGON, // TriangleStrip
1122 POLYGON, // TriangleFan
1123 POLYGON, // Quads
1124 POLYGON, // QuadStrip
1125 POLYGON, // Polygon
1126 LINE, // LinesAdjacency
1127 LINE, // LineStripAdjacency
1128 POLYGON, // TrianglesAdjacency
1129 POLYGON, // TriangleStripAdjacency
1130 POLYGON, // Patches
1131 };
1132 const std::array enabled_lut{
1133 regs.polygon_offset_point_enable,
1134 regs.polygon_offset_line_enable,
1135 regs.polygon_offset_fill_enable,
1136 };
1137 const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
1138 const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
1139 scheduler.Record(
1140 [enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
1141}
1142
1143void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1144 if (!state_tracker.TouchLogicOpEnable()) {
1145 return;
1146 }
1147 scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
1148 cmdbuf.SetLogicOpEnableEXT(enable != 0);
1149 });
1150}
1151
1152void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1153 if (!state_tracker.TouchDepthClampEnable()) {
1154 return;
1155 }
1156 bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
1157 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
1158 regs.viewport_clip_control.geometry_clip ==
1159 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
1160 regs.viewport_clip_control.geometry_clip ==
1161 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
1162 scheduler.Record(
1163 [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
1164}
1165
867void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { 1166void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
868 if (!state_tracker.TouchDepthCompareOp()) { 1167 if (!state_tracker.TouchDepthCompareOp()) {
869 return; 1168 return;
@@ -921,6 +1220,78 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
921 } 1220 }
922} 1221}
923 1222
1223void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1224 if (!state_tracker.TouchLogicOp()) {
1225 return;
1226 }
1227 const auto op_value = static_cast<u32>(regs.logic_op.op);
1228 auto op = op_value >= 0x1500 && op_value < 0x1510 ? static_cast<VkLogicOp>(op_value - 0x1500)
1229 : VK_LOGIC_OP_NO_OP;
1230 scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
1231}
1232
1233void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
1234 if (!state_tracker.TouchBlending()) {
1235 return;
1236 }
1237
1238 if (state_tracker.TouchColorMask()) {
1239 std::array<VkColorComponentFlags, Maxwell::NumRenderTargets> setup_masks{};
1240 for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
1241 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
1242 auto& current = setup_masks[index];
1243 if (mask.R) {
1244 current |= VK_COLOR_COMPONENT_R_BIT;
1245 }
1246 if (mask.G) {
1247 current |= VK_COLOR_COMPONENT_G_BIT;
1248 }
1249 if (mask.B) {
1250 current |= VK_COLOR_COMPONENT_B_BIT;
1251 }
1252 if (mask.A) {
1253 current |= VK_COLOR_COMPONENT_A_BIT;
1254 }
1255 }
1256 scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) {
1257 cmdbuf.SetColorWriteMaskEXT(0, setup_masks);
1258 });
1259 }
1260
1261 if (state_tracker.TouchBlendEnable()) {
1262 std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
1263 std::ranges::transform(
1264 regs.blend.enable, setup_enables.begin(),
1265 [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
1266 scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
1267 cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
1268 });
1269 }
1270
1271 if (state_tracker.TouchBlendEquations()) {
1272 std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
1273 for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
1274 const auto blend_setup = [&]<typename T>(const T& guest_blend) {
1275 auto& host_blend = setup_blends[index];
1276 host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
1277 host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
1278 host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
1279 host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
1280 host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
1281 host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
1282 };
1283 if (!regs.blend_per_target_enabled) {
1284 blend_setup(regs.blend);
1285 continue;
1286 }
1287 blend_setup(regs.blend_per_target[index]);
1288 }
1289 scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
1290 cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
1291 });
1292 }
1293}
1294
924void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { 1295void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
925 if (!state_tracker.TouchStencilTestEnable()) { 1296 if (!state_tracker.TouchStencilTestEnable()) {
926 return; 1297 return;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index ee483cfd9..a0508b57c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -65,6 +65,8 @@ public:
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, u32 instance_count) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void DrawIndirect() override;
69 void DrawTexture() override;
68 void Clear(u32 layer_count) override; 70 void Clear(u32 layer_count) override;
69 void DispatchCompute() override; 71 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 72 void ResetCounter(VideoCore::QueryType type) override;
@@ -72,9 +74,13 @@ public:
72 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 74 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
73 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 75 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
74 void FlushAll() override; 76 void FlushAll() override;
75 void FlushRegion(VAddr addr, u64 size) override; 77 void FlushRegion(VAddr addr, u64 size,
76 bool MustFlushRegion(VAddr addr, u64 size) override; 78 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
77 void InvalidateRegion(VAddr addr, u64 size) override; 79 bool MustFlushRegion(VAddr addr, u64 size,
80 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
81 void InvalidateRegion(VAddr addr, u64 size,
82 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
83 void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override;
78 void OnCPUWrite(VAddr addr, u64 size) override; 84 void OnCPUWrite(VAddr addr, u64 size) override;
79 void InvalidateGPUCache() override; 85 void InvalidateGPUCache() override;
80 void UnmapMemory(VAddr addr, u64 size) override; 86 void UnmapMemory(VAddr addr, u64 size) override;
@@ -84,12 +90,14 @@ public:
84 void SignalSyncPoint(u32 value) override; 90 void SignalSyncPoint(u32 value) override;
85 void SignalReference() override; 91 void SignalReference() override;
86 void ReleaseFences() override; 92 void ReleaseFences() override;
87 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 93 void FlushAndInvalidateRegion(
94 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
88 void WaitForIdle() override; 95 void WaitForIdle() override;
89 void FragmentBarrier() override; 96 void FragmentBarrier() override;
90 void TiledCacheBarrier() override; 97 void TiledCacheBarrier() override;
91 void FlushCommands() override; 98 void FlushCommands() override;
92 void TickFrame() override; 99 void TickFrame() override;
100 bool AccelerateConditionalRendering() override;
93 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 101 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
94 const Tegra::Engines::Fermi2D::Surface& dst, 102 const Tegra::Engines::Fermi2D::Surface& dst,
95 const Tegra::Engines::Fermi2D::Config& copy_config) override; 103 const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -114,6 +122,9 @@ private:
114 122
115 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); 123 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float);
116 124
125 template <typename Func>
126 void PrepareDraw(bool is_indexed, Func&&);
127
117 void FlushWork(); 128 void FlushWork();
118 129
119 void UpdateDynamicStates(); 130 void UpdateDynamicStates();
@@ -135,9 +146,16 @@ private:
135 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); 146 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
136 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); 147 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
137 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); 148 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
149 void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
150 void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
151 void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
152 void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
153 void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);
138 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); 154 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
139 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); 155 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
140 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); 156 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
157 void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
158 void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs);
141 159
142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); 160 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
143 161
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index c2e53a5d5..e03685af1 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -213,6 +213,11 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
213 .signalSemaphoreCount = num_signal_semaphores, 213 .signalSemaphoreCount = num_signal_semaphores,
214 .pSignalSemaphores = signal_semaphores.data(), 214 .pSignalSemaphores = signal_semaphores.data(),
215 }; 215 };
216
217 if (on_submit) {
218 on_submit();
219 }
220
216 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) { 221 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
217 case VK_SUCCESS: 222 case VK_SUCCESS:
218 break; 223 break;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 3858c506c..bd4cb0f7e 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -5,6 +5,7 @@
5 5
6#include <condition_variable> 6#include <condition_variable>
7#include <cstddef> 7#include <cstddef>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <thread> 10#include <thread>
10#include <utility> 11#include <utility>
@@ -66,6 +67,11 @@ public:
66 query_cache = &query_cache_; 67 query_cache = &query_cache_;
67 } 68 }
68 69
70 // Registers a callback to perform on queue submission.
71 void RegisterOnSubmit(std::function<void()>&& func) {
72 on_submit = std::move(func);
73 }
74
69 /// Send work to a separate thread. 75 /// Send work to a separate thread.
70 template <typename T> 76 template <typename T>
71 void Record(T&& command) { 77 void Record(T&& command) {
@@ -216,6 +222,7 @@ private:
216 vk::CommandBuffer current_cmdbuf; 222 vk::CommandBuffer current_cmdbuf;
217 223
218 std::unique_ptr<CommandChunk> chunk; 224 std::unique_ptr<CommandChunk> chunk;
225 std::function<void()> on_submit;
219 226
220 State state; 227 State state;
221 228
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 06f68d09a..74ca77216 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -1,5 +1,5 @@
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-3.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <utility> 5#include <utility>
@@ -94,7 +94,8 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
94 .flags = 0, 94 .flags = 0,
95 .size = STREAM_BUFFER_SIZE, 95 .size = STREAM_BUFFER_SIZE,
96 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | 96 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
97 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, 97 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
98 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
98 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 99 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
99 .queueFamilyIndexCount = 0, 100 .queueFamilyIndexCount = 0,
100 .pQueueFamilyIndices = nullptr, 101 .pQueueFamilyIndices = nullptr,
@@ -142,11 +143,23 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
142 143
143StagingBufferPool::~StagingBufferPool() = default; 144StagingBufferPool::~StagingBufferPool() = default;
144 145
145StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { 146StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
146 if (usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { 147 if (!deferred && usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) {
147 return GetStreamBuffer(size); 148 return GetStreamBuffer(size);
148 } 149 }
149 return GetStagingBuffer(size, usage); 150 return GetStagingBuffer(size, usage, deferred);
151}
152
153void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
154 auto& entries = GetCache(ref.usage)[ref.log2_level].entries;
155 const auto is_this_one = [&ref](const StagingBuffer& entry) {
156 return entry.index == ref.index;
157 };
158 auto it = std::find_if(entries.begin(), entries.end(), is_this_one);
159 ASSERT(it != entries.end());
160 ASSERT(it->deferred);
161 it->tick = scheduler.CurrentTick();
162 it->deferred = false;
150} 163}
151 164
152void StagingBufferPool::TickFrame() { 165void StagingBufferPool::TickFrame() {
@@ -187,6 +200,9 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
187 .buffer = *stream_buffer, 200 .buffer = *stream_buffer,
188 .offset = static_cast<VkDeviceSize>(offset), 201 .offset = static_cast<VkDeviceSize>(offset),
189 .mapped_span = std::span<u8>(stream_pointer + offset, size), 202 .mapped_span = std::span<u8>(stream_pointer + offset, size),
203 .usage{},
204 .log2_level{},
205 .index{},
190 }; 206 };
191} 207}
192 208
@@ -196,19 +212,21 @@ bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end)
196 [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; }); 212 [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
197}; 213};
198 214
199StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage) { 215StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
200 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { 216 bool deferred) {
217 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
201 return *ref; 218 return *ref;
202 } 219 }
203 return CreateStagingBuffer(size, usage); 220 return CreateStagingBuffer(size, usage, deferred);
204} 221}
205 222
206std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, 223std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
207 MemoryUsage usage) { 224 MemoryUsage usage,
225 bool deferred) {
208 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; 226 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
209 227
210 const auto is_free = [this](const StagingBuffer& entry) { 228 const auto is_free = [this](const StagingBuffer& entry) {
211 return scheduler.IsFree(entry.tick); 229 return !entry.deferred && scheduler.IsFree(entry.tick);
212 }; 230 };
213 auto& entries = cache_level.entries; 231 auto& entries = cache_level.entries;
214 const auto hint_it = entries.begin() + cache_level.iterate_index; 232 const auto hint_it = entries.begin() + cache_level.iterate_index;
@@ -220,11 +238,14 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
220 } 238 }
221 } 239 }
222 cache_level.iterate_index = std::distance(entries.begin(), it) + 1; 240 cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
223 it->tick = scheduler.CurrentTick(); 241 it->tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick();
242 ASSERT(!it->deferred);
243 it->deferred = deferred;
224 return it->Ref(); 244 return it->Ref();
225} 245}
226 246
227StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { 247StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
248 bool deferred) {
228 const u32 log2 = Common::Log2Ceil64(size); 249 const u32 log2 = Common::Log2Ceil64(size);
229 vk::Buffer buffer = device.GetLogical().CreateBuffer({ 250 vk::Buffer buffer = device.GetLogical().CreateBuffer({
230 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 251 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -233,7 +254,8 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
233 .size = 1ULL << log2, 254 .size = 1ULL << log2,
234 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 255 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
235 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 256 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
236 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 257 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
258 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
237 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 259 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
238 .queueFamilyIndexCount = 0, 260 .queueFamilyIndexCount = 0,
239 .pQueueFamilyIndices = nullptr, 261 .pQueueFamilyIndices = nullptr,
@@ -249,7 +271,11 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
249 .buffer = std::move(buffer), 271 .buffer = std::move(buffer),
250 .commit = std::move(commit), 272 .commit = std::move(commit),
251 .mapped_span = mapped_span, 273 .mapped_span = mapped_span,
252 .tick = scheduler.CurrentTick(), 274 .usage = usage,
275 .log2_level = log2,
276 .index = unique_ids++,
277 .tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick(),
278 .deferred = deferred,
253 }); 279 });
254 return entry.Ref(); 280 return entry.Ref();
255} 281}
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 91dc84da8..4fd15f11a 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -1,5 +1,5 @@
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-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -20,6 +20,9 @@ struct StagingBufferRef {
20 VkBuffer buffer; 20 VkBuffer buffer;
21 VkDeviceSize offset; 21 VkDeviceSize offset;
22 std::span<u8> mapped_span; 22 std::span<u8> mapped_span;
23 MemoryUsage usage;
24 u32 log2_level;
25 u64 index;
23}; 26};
24 27
25class StagingBufferPool { 28class StagingBufferPool {
@@ -30,7 +33,8 @@ public:
30 Scheduler& scheduler); 33 Scheduler& scheduler);
31 ~StagingBufferPool(); 34 ~StagingBufferPool();
32 35
33 StagingBufferRef Request(size_t size, MemoryUsage usage); 36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
37 void FreeDeferred(StagingBufferRef& ref);
34 38
35 void TickFrame(); 39 void TickFrame();
36 40
@@ -44,13 +48,20 @@ private:
44 vk::Buffer buffer; 48 vk::Buffer buffer;
45 MemoryCommit commit; 49 MemoryCommit commit;
46 std::span<u8> mapped_span; 50 std::span<u8> mapped_span;
51 MemoryUsage usage;
52 u32 log2_level;
53 u64 index;
47 u64 tick = 0; 54 u64 tick = 0;
55 bool deferred{};
48 56
49 StagingBufferRef Ref() const noexcept { 57 StagingBufferRef Ref() const noexcept {
50 return { 58 return {
51 .buffer = *buffer, 59 .buffer = *buffer,
52 .offset = 0, 60 .offset = 0,
53 .mapped_span = mapped_span, 61 .mapped_span = mapped_span,
62 .usage = usage,
63 .log2_level = log2_level,
64 .index = index,
54 }; 65 };
55 } 66 }
56 }; 67 };
@@ -68,11 +79,12 @@ private:
68 79
69 bool AreRegionsActive(size_t region_begin, size_t region_end) const; 80 bool AreRegionsActive(size_t region_begin, size_t region_end) const;
70 81
71 StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage); 82 StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
72 83
73 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); 84 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
85 bool deferred);
74 86
75 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); 87 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
76 88
77 StagingBuffersCache& GetCache(MemoryUsage usage); 89 StagingBuffersCache& GetCache(MemoryUsage usage);
78 90
@@ -99,6 +111,7 @@ private:
99 111
100 size_t current_delete_level = 0; 112 size_t current_delete_level = 0;
101 u64 buffer_index = 0; 113 u64 buffer_index = 0;
114 u64 unique_ids{};
102}; 115};
103 116
104} // namespace Vulkan 117} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index edb41b171..d56558a83 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -27,10 +27,37 @@ using Flags = Maxwell3D::DirtyState::Flags;
27 27
28Flags MakeInvalidationFlags() { 28Flags MakeInvalidationFlags() {
29 static constexpr int INVALIDATION_FLAGS[]{ 29 static constexpr int INVALIDATION_FLAGS[]{
30 Viewports, Scissors, DepthBias, BlendConstants, DepthBounds, 30 Viewports,
31 StencilProperties, LineWidth, CullMode, DepthBoundsEnable, DepthTestEnable, 31 Scissors,
32 DepthWriteEnable, DepthCompareOp, FrontFace, StencilOp, StencilTestEnable, 32 DepthBias,
33 VertexBuffers, VertexInput, 33 BlendConstants,
34 DepthBounds,
35 StencilProperties,
36 StencilReference,
37 StencilWriteMask,
38 StencilCompare,
39 LineWidth,
40 CullMode,
41 DepthBoundsEnable,
42 DepthTestEnable,
43 DepthWriteEnable,
44 DepthCompareOp,
45 FrontFace,
46 StencilOp,
47 StencilTestEnable,
48 VertexBuffers,
49 VertexInput,
50 StateEnable,
51 PrimitiveRestartEnable,
52 RasterizerDiscardEnable,
53 DepthBiasEnable,
54 LogicOpEnable,
55 DepthClampEnable,
56 LogicOp,
57 Blending,
58 ColorMask,
59 BlendEquations,
60 BlendEnable,
34 }; 61 };
35 Flags flags{}; 62 Flags flags{};
36 for (const int flag : INVALIDATION_FLAGS) { 63 for (const int flag : INVALIDATION_FLAGS) {
@@ -75,14 +102,17 @@ void SetupDirtyDepthBounds(Tables& tables) {
75} 102}
76 103
77void SetupDirtyStencilProperties(Tables& tables) { 104void SetupDirtyStencilProperties(Tables& tables) {
78 auto& table = tables[0]; 105 const auto setup = [&](size_t position, u8 flag) {
79 table[OFF(stencil_two_side_enable)] = StencilProperties; 106 tables[0][position] = flag;
80 table[OFF(stencil_front_ref)] = StencilProperties; 107 tables[1][position] = StencilProperties;
81 table[OFF(stencil_front_mask)] = StencilProperties; 108 };
82 table[OFF(stencil_front_func_mask)] = StencilProperties; 109 tables[0][OFF(stencil_two_side_enable)] = StencilProperties;
83 table[OFF(stencil_back_ref)] = StencilProperties; 110 setup(OFF(stencil_front_ref), StencilReference);
84 table[OFF(stencil_back_mask)] = StencilProperties; 111 setup(OFF(stencil_front_mask), StencilWriteMask);
85 table[OFF(stencil_back_func_mask)] = StencilProperties; 112 setup(OFF(stencil_front_func_mask), StencilCompare);
113 setup(OFF(stencil_back_ref), StencilReference);
114 setup(OFF(stencil_back_mask), StencilWriteMask);
115 setup(OFF(stencil_back_func_mask), StencilCompare);
86} 116}
87 117
88void SetupDirtyLineWidth(Tables& tables) { 118void SetupDirtyLineWidth(Tables& tables) {
@@ -96,16 +126,22 @@ void SetupDirtyCullMode(Tables& tables) {
96 table[OFF(gl_cull_test_enabled)] = CullMode; 126 table[OFF(gl_cull_test_enabled)] = CullMode;
97} 127}
98 128
99void SetupDirtyDepthBoundsEnable(Tables& tables) { 129void SetupDirtyStateEnable(Tables& tables) {
100 tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; 130 const auto setup = [&](size_t position, u8 flag) {
101} 131 tables[0][position] = flag;
102 132 tables[1][position] = StateEnable;
103void SetupDirtyDepthTestEnable(Tables& tables) { 133 };
104 tables[0][OFF(depth_test_enable)] = DepthTestEnable; 134 setup(OFF(depth_bounds_enable), DepthBoundsEnable);
105} 135 setup(OFF(depth_test_enable), DepthTestEnable);
106 136 setup(OFF(depth_write_enabled), DepthWriteEnable);
107void SetupDirtyDepthWriteEnable(Tables& tables) { 137 setup(OFF(stencil_enable), StencilTestEnable);
108 tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; 138 setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable);
139 setup(OFF(rasterize_enable), RasterizerDiscardEnable);
140 setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
141 setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
142 setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);
143 setup(OFF(logic_op.enable), LogicOpEnable);
144 setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable);
109} 145}
110 146
111void SetupDirtyDepthCompareOp(Tables& tables) { 147void SetupDirtyDepthCompareOp(Tables& tables) {
@@ -133,16 +169,22 @@ void SetupDirtyStencilOp(Tables& tables) {
133 tables[1][OFF(stencil_two_side_enable)] = StencilOp; 169 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
134} 170}
135 171
136void SetupDirtyStencilTestEnable(Tables& tables) {
137 tables[0][OFF(stencil_enable)] = StencilTestEnable;
138}
139
140void SetupDirtyBlending(Tables& tables) { 172void SetupDirtyBlending(Tables& tables) {
141 tables[0][OFF(color_mask_common)] = Blending; 173 tables[0][OFF(color_mask_common)] = Blending;
174 tables[1][OFF(color_mask_common)] = ColorMask;
142 tables[0][OFF(blend_per_target_enabled)] = Blending; 175 tables[0][OFF(blend_per_target_enabled)] = Blending;
176 tables[1][OFF(blend_per_target_enabled)] = BlendEquations;
143 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); 177 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending);
178 FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMask);
144 FillBlock(tables[0], OFF(blend), NUM(blend), Blending); 179 FillBlock(tables[0], OFF(blend), NUM(blend), Blending);
180 FillBlock(tables[1], OFF(blend), NUM(blend), BlendEquations);
181 FillBlock(tables[1], OFF(blend.enable), NUM(blend.enable), BlendEnable);
145 FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); 182 FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending);
183 FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendEquations);
184}
185
186void SetupDirtySpecialOps(Tables& tables) {
187 tables[0][OFF(logic_op.op)] = LogicOp;
146} 188}
147 189
148void SetupDirtyViewportSwizzles(Tables& tables) { 190void SetupDirtyViewportSwizzles(Tables& tables) {
@@ -185,17 +227,15 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
185 SetupDirtyStencilProperties(tables); 227 SetupDirtyStencilProperties(tables);
186 SetupDirtyLineWidth(tables); 228 SetupDirtyLineWidth(tables);
187 SetupDirtyCullMode(tables); 229 SetupDirtyCullMode(tables);
188 SetupDirtyDepthBoundsEnable(tables); 230 SetupDirtyStateEnable(tables);
189 SetupDirtyDepthTestEnable(tables);
190 SetupDirtyDepthWriteEnable(tables);
191 SetupDirtyDepthCompareOp(tables); 231 SetupDirtyDepthCompareOp(tables);
192 SetupDirtyFrontFace(tables); 232 SetupDirtyFrontFace(tables);
193 SetupDirtyStencilOp(tables); 233 SetupDirtyStencilOp(tables);
194 SetupDirtyStencilTestEnable(tables);
195 SetupDirtyBlending(tables); 234 SetupDirtyBlending(tables);
196 SetupDirtyViewportSwizzles(tables); 235 SetupDirtyViewportSwizzles(tables);
197 SetupDirtyVertexAttributes(tables); 236 SetupDirtyVertexAttributes(tables);
198 SetupDirtyVertexBindings(tables); 237 SetupDirtyVertexBindings(tables);
238 SetupDirtySpecialOps(tables);
199} 239}
200 240
201void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { 241void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
@@ -204,6 +244,8 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
204 244
205void StateTracker::InvalidateState() { 245void StateTracker::InvalidateState() {
206 flags->set(); 246 flags->set();
247 current_topology = INVALID_TOPOLOGY;
248 stencil_reset = true;
207} 249}
208 250
209StateTracker::StateTracker() 251StateTracker::StateTracker()
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 2296dea60..8010ad26c 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -35,6 +35,9 @@ enum : u8 {
35 BlendConstants, 35 BlendConstants,
36 DepthBounds, 36 DepthBounds,
37 StencilProperties, 37 StencilProperties,
38 StencilReference,
39 StencilWriteMask,
40 StencilCompare,
38 LineWidth, 41 LineWidth,
39 42
40 CullMode, 43 CullMode,
@@ -45,8 +48,18 @@ enum : u8 {
45 FrontFace, 48 FrontFace,
46 StencilOp, 49 StencilOp,
47 StencilTestEnable, 50 StencilTestEnable,
51 PrimitiveRestartEnable,
52 RasterizerDiscardEnable,
53 DepthBiasEnable,
54 StateEnable,
55 LogicOp,
56 LogicOpEnable,
57 DepthClampEnable,
48 58
49 Blending, 59 Blending,
60 BlendEnable,
61 BlendEquations,
62 ColorMask,
50 ViewportSwizzles, 63 ViewportSwizzles,
51 64
52 Last, 65 Last,
@@ -64,6 +77,7 @@ public:
64 void InvalidateCommandBufferState() { 77 void InvalidateCommandBufferState() {
65 (*flags) |= invalidation_flags; 78 (*flags) |= invalidation_flags;
66 current_topology = INVALID_TOPOLOGY; 79 current_topology = INVALID_TOPOLOGY;
80 stencil_reset = true;
67 } 81 }
68 82
69 void InvalidateViewports() { 83 void InvalidateViewports() {
@@ -103,6 +117,57 @@ public:
103 return Exchange(Dirty::StencilProperties, false); 117 return Exchange(Dirty::StencilProperties, false);
104 } 118 }
105 119
120 bool TouchStencilReference() {
121 return Exchange(Dirty::StencilReference, false);
122 }
123
124 bool TouchStencilWriteMask() {
125 return Exchange(Dirty::StencilWriteMask, false);
126 }
127
128 bool TouchStencilCompare() {
129 return Exchange(Dirty::StencilCompare, false);
130 }
131
132 template <typename T>
133 bool ExchangeCheck(T& old_value, T new_value) {
134 bool result = old_value != new_value;
135 old_value = new_value;
136 return result;
137 }
138
139 bool TouchStencilSide(bool two_sided_stencil_new) {
140 return ExchangeCheck(two_sided_stencil, two_sided_stencil_new) || stencil_reset;
141 }
142
143 bool CheckStencilReferenceFront(u32 new_value) {
144 return ExchangeCheck(front.ref, new_value) || stencil_reset;
145 }
146
147 bool CheckStencilReferenceBack(u32 new_value) {
148 return ExchangeCheck(back.ref, new_value) || stencil_reset;
149 }
150
151 bool CheckStencilWriteMaskFront(u32 new_value) {
152 return ExchangeCheck(front.write_mask, new_value) || stencil_reset;
153 }
154
155 bool CheckStencilWriteMaskBack(u32 new_value) {
156 return ExchangeCheck(back.write_mask, new_value) || stencil_reset;
157 }
158
159 bool CheckStencilCompareMaskFront(u32 new_value) {
160 return ExchangeCheck(front.compare_mask, new_value) || stencil_reset;
161 }
162
163 bool CheckStencilCompareMaskBack(u32 new_value) {
164 return ExchangeCheck(back.compare_mask, new_value) || stencil_reset;
165 }
166
167 void ClearStencilReset() {
168 stencil_reset = false;
169 }
170
106 bool TouchLineWidth() const { 171 bool TouchLineWidth() const {
107 return Exchange(Dirty::LineWidth, false); 172 return Exchange(Dirty::LineWidth, false);
108 } 173 }
@@ -111,6 +176,10 @@ public:
111 return Exchange(Dirty::CullMode, false); 176 return Exchange(Dirty::CullMode, false);
112 } 177 }
113 178
179 bool TouchStateEnable() {
180 return Exchange(Dirty::StateEnable, false);
181 }
182
114 bool TouchDepthBoundsTestEnable() { 183 bool TouchDepthBoundsTestEnable() {
115 return Exchange(Dirty::DepthBoundsEnable, false); 184 return Exchange(Dirty::DepthBoundsEnable, false);
116 } 185 }
@@ -123,6 +192,26 @@ public:
123 return Exchange(Dirty::DepthWriteEnable, false); 192 return Exchange(Dirty::DepthWriteEnable, false);
124 } 193 }
125 194
195 bool TouchPrimitiveRestartEnable() {
196 return Exchange(Dirty::PrimitiveRestartEnable, false);
197 }
198
199 bool TouchRasterizerDiscardEnable() {
200 return Exchange(Dirty::RasterizerDiscardEnable, false);
201 }
202
203 bool TouchDepthBiasEnable() {
204 return Exchange(Dirty::DepthBiasEnable, false);
205 }
206
207 bool TouchLogicOpEnable() {
208 return Exchange(Dirty::LogicOpEnable, false);
209 }
210
211 bool TouchDepthClampEnable() {
212 return Exchange(Dirty::DepthClampEnable, false);
213 }
214
126 bool TouchDepthCompareOp() { 215 bool TouchDepthCompareOp() {
127 return Exchange(Dirty::DepthCompareOp, false); 216 return Exchange(Dirty::DepthCompareOp, false);
128 } 217 }
@@ -135,10 +224,30 @@ public:
135 return Exchange(Dirty::StencilOp, false); 224 return Exchange(Dirty::StencilOp, false);
136 } 225 }
137 226
227 bool TouchBlending() {
228 return Exchange(Dirty::Blending, false);
229 }
230
231 bool TouchBlendEnable() {
232 return Exchange(Dirty::BlendEnable, false);
233 }
234
235 bool TouchBlendEquations() {
236 return Exchange(Dirty::BlendEquations, false);
237 }
238
239 bool TouchColorMask() {
240 return Exchange(Dirty::ColorMask, false);
241 }
242
138 bool TouchStencilTestEnable() { 243 bool TouchStencilTestEnable() {
139 return Exchange(Dirty::StencilTestEnable, false); 244 return Exchange(Dirty::StencilTestEnable, false);
140 } 245 }
141 246
247 bool TouchLogicOp() {
248 return Exchange(Dirty::LogicOp, false);
249 }
250
142 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { 251 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
143 const bool has_changed = current_topology != new_topology; 252 const bool has_changed = current_topology != new_topology;
144 current_topology = new_topology; 253 current_topology = new_topology;
@@ -160,10 +269,20 @@ private:
160 return is_dirty; 269 return is_dirty;
161 } 270 }
162 271
272 struct StencilProperties {
273 u32 ref = 0;
274 u32 write_mask = 0;
275 u32 compare_mask = 0;
276 };
277
163 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; 278 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
164 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; 279 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags;
165 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 280 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
166 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; 281 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
282 bool two_sided_stencil = false;
283 StencilProperties front{};
284 StencilProperties back{};
285 bool stencil_reset = false;
167}; 286};
168 287
169} // namespace Vulkan 288} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index a65bbeb1c..d39372ec4 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -812,8 +812,12 @@ StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) {
812 return staging_buffer_pool.Request(size, MemoryUsage::Upload); 812 return staging_buffer_pool.Request(size, MemoryUsage::Upload);
813} 813}
814 814
815StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) { 815StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
816 return staging_buffer_pool.Request(size, MemoryUsage::Download); 816 return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred);
817}
818
819void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
820 staging_buffer_pool.FreeDeferred(ref);
817} 821}
818 822
819bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { 823bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 7ec0df134..1f27a3589 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -51,7 +51,9 @@ public:
51 51
52 StagingBufferRef UploadStagingBuffer(size_t size); 52 StagingBufferRef UploadStagingBuffer(size_t size);
53 53
54 StagingBufferRef DownloadStagingBuffer(size_t size); 54 StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
55
56 void FreeDeferredStagingBuffer(StagingBufferRef& ref);
55 57
56 void TickFrame(); 58 void TickFrame();
57 59
@@ -347,6 +349,7 @@ struct TextureCacheParams {
347 static constexpr bool FRAMEBUFFER_BLITS = false; 349 static constexpr bool FRAMEBUFFER_BLITS = false;
348 static constexpr bool HAS_EMULATED_COPIES = false; 350 static constexpr bool HAS_EMULATED_COPIES = false;
349 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 351 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
352 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
350 353
351 using Runtime = Vulkan::TextureCacheRuntime; 354 using Runtime = Vulkan::TextureCacheRuntime;
352 using Image = Vulkan::Image; 355 using Image = Vulkan::Image;
@@ -354,6 +357,7 @@ struct TextureCacheParams {
354 using ImageView = Vulkan::ImageView; 357 using ImageView = Vulkan::ImageView;
355 using Sampler = Vulkan::Sampler; 358 using Sampler = Vulkan::Sampler;
356 using Framebuffer = Vulkan::Framebuffer; 359 using Framebuffer = Vulkan::Framebuffer;
360 using AsyncBuffer = Vulkan::StagingBufferRef;
357}; 361};
358 362
359using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; 363using TextureCache = VideoCommon::TextureCache<TextureCacheParams>;
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
new file mode 100644
index 000000000..c42594149
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
@@ -0,0 +1,222 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/literals.h"
5#include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.h"
6#include "video_core/renderer_vulkan/renderer_vulkan.h"
7#include "video_core/renderer_vulkan/vk_shader_util.h"
8#include "video_core/renderer_vulkan/vk_turbo_mode.h"
9#include "video_core/vulkan_common/vulkan_device.h"
10
11namespace Vulkan {
12
13using namespace Common::Literals;
14
15TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld)
16 : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} {
17 {
18 std::scoped_lock lk{m_submission_lock};
19 m_submission_time = std::chrono::steady_clock::now();
20 }
21 m_thread = std::jthread([&](auto stop_token) { Run(stop_token); });
22}
23
24TurboMode::~TurboMode() = default;
25
26void TurboMode::QueueSubmitted() {
27 std::scoped_lock lk{m_submission_lock};
28 m_submission_time = std::chrono::steady_clock::now();
29 m_submission_cv.notify_one();
30}
31
32void TurboMode::Run(std::stop_token stop_token) {
33 auto& dld = m_device.GetLogical();
34
35 // Allocate buffer. 2MiB should be sufficient.
36 auto buffer = dld.CreateBuffer(VkBufferCreateInfo{
37 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
38 .pNext = nullptr,
39 .flags = 0,
40 .size = 2_MiB,
41 .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
42 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
43 .queueFamilyIndexCount = 0,
44 .pQueueFamilyIndices = nullptr,
45 });
46
47 // Commit some device local memory for the buffer.
48 auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
49
50 // Create the descriptor pool to contain our descriptor.
51 constexpr VkDescriptorPoolSize pool_size{
52 .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
53 .descriptorCount = 1,
54 };
55
56 auto descriptor_pool = dld.CreateDescriptorPool(VkDescriptorPoolCreateInfo{
57 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
58 .pNext = nullptr,
59 .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
60 .maxSets = 1,
61 .poolSizeCount = 1,
62 .pPoolSizes = &pool_size,
63 });
64
65 // Create the descriptor set layout from the pool.
66 constexpr VkDescriptorSetLayoutBinding layout_binding{
67 .binding = 0,
68 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
69 .descriptorCount = 1,
70 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
71 .pImmutableSamplers = nullptr,
72 };
73
74 auto descriptor_set_layout = dld.CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{
75 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
76 .pNext = nullptr,
77 .flags = 0,
78 .bindingCount = 1,
79 .pBindings = &layout_binding,
80 });
81
82 // Actually create the descriptor set.
83 auto descriptor_set = descriptor_pool.Allocate(VkDescriptorSetAllocateInfo{
84 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
85 .pNext = nullptr,
86 .descriptorPool = *descriptor_pool,
87 .descriptorSetCount = 1,
88 .pSetLayouts = descriptor_set_layout.address(),
89 });
90
91 // Create the shader.
92 auto shader = BuildShader(m_device, VULKAN_TURBO_MODE_COMP_SPV);
93
94 // Create the pipeline layout.
95 auto pipeline_layout = dld.CreatePipelineLayout(VkPipelineLayoutCreateInfo{
96 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
97 .pNext = nullptr,
98 .flags = 0,
99 .setLayoutCount = 1,
100 .pSetLayouts = descriptor_set_layout.address(),
101 .pushConstantRangeCount = 0,
102 .pPushConstantRanges = nullptr,
103 });
104
105 // Actually create the pipeline.
106 const VkPipelineShaderStageCreateInfo shader_stage{
107 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
108 .pNext = nullptr,
109 .flags = 0,
110 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
111 .module = *shader,
112 .pName = "main",
113 .pSpecializationInfo = nullptr,
114 };
115
116 auto pipeline = dld.CreateComputePipeline(VkComputePipelineCreateInfo{
117 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
118 .pNext = nullptr,
119 .flags = 0,
120 .stage = shader_stage,
121 .layout = *pipeline_layout,
122 .basePipelineHandle = VK_NULL_HANDLE,
123 .basePipelineIndex = 0,
124 });
125
126 // Create a fence to wait on.
127 auto fence = dld.CreateFence(VkFenceCreateInfo{
128 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
129 .pNext = nullptr,
130 .flags = 0,
131 });
132
133 // Create a command pool to allocate a command buffer from.
134 auto command_pool = dld.CreateCommandPool(VkCommandPoolCreateInfo{
135 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
136 .pNext = nullptr,
137 .flags =
138 VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
139 .queueFamilyIndex = m_device.GetGraphicsFamily(),
140 });
141
142 // Create a single command buffer.
143 auto cmdbufs = command_pool.Allocate(1, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
144 auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()};
145
146 while (!stop_token.stop_requested()) {
147 // Reset the fence.
148 fence.Reset();
149
150 // Update descriptor set.
151 const VkDescriptorBufferInfo buffer_info{
152 .buffer = *buffer,
153 .offset = 0,
154 .range = VK_WHOLE_SIZE,
155 };
156
157 const VkWriteDescriptorSet buffer_write{
158 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
159 .pNext = nullptr,
160 .dstSet = descriptor_set[0],
161 .dstBinding = 0,
162 .dstArrayElement = 0,
163 .descriptorCount = 1,
164 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
165 .pImageInfo = nullptr,
166 .pBufferInfo = &buffer_info,
167 .pTexelBufferView = nullptr,
168 };
169
170 dld.UpdateDescriptorSets(std::array{buffer_write}, {});
171
172 // Set up the command buffer.
173 cmdbuf.Begin(VkCommandBufferBeginInfo{
174 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
175 .pNext = nullptr,
176 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
177 .pInheritanceInfo = nullptr,
178 });
179
180 // Clear the buffer.
181 cmdbuf.FillBuffer(*buffer, 0, VK_WHOLE_SIZE, 0);
182
183 // Bind descriptor set.
184 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
185 descriptor_set, {});
186
187 // Bind the pipeline.
188 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
189
190 // Dispatch.
191 cmdbuf.Dispatch(64, 64, 1);
192
193 // Finish.
194 cmdbuf.End();
195
196 const VkSubmitInfo submit_info{
197 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
198 .pNext = nullptr,
199 .waitSemaphoreCount = 0,
200 .pWaitSemaphores = nullptr,
201 .pWaitDstStageMask = nullptr,
202 .commandBufferCount = 1,
203 .pCommandBuffers = cmdbuf.address(),
204 .signalSemaphoreCount = 0,
205 .pSignalSemaphores = nullptr,
206 };
207
208 m_device.GetGraphicsQueue().Submit(std::array{submit_info}, *fence);
209
210 // Wait for completion.
211 fence.Wait();
212
213 // Wait for the next graphics queue submission if necessary.
214 std::unique_lock lk{m_submission_lock};
215 Common::CondvarWait(m_submission_cv, lk, stop_token, [this] {
216 return (std::chrono::steady_clock::now() - m_submission_time) <=
217 std::chrono::milliseconds{100};
218 });
219 }
220}
221
222} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h
new file mode 100644
index 000000000..99b5ac50b
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7#include <mutex>
8
9#include "common/polyfill_thread.h"
10#include "video_core/vulkan_common/vulkan_device.h"
11#include "video_core/vulkan_common/vulkan_memory_allocator.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class TurboMode {
17public:
18 explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld);
19 ~TurboMode();
20
21 void QueueSubmitted();
22
23private:
24 void Run(std::stop_token stop_token);
25
26 Device m_device;
27 MemoryAllocator m_allocator;
28 std::mutex m_submission_lock;
29 std::condition_variable_any m_submission_cv;
30 std::chrono::time_point<std::chrono::steady_clock> m_submission_time{};
31
32 std::jthread m_thread;
33};
34
35} // namespace Vulkan
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 958810747..574760f80 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -202,12 +202,15 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
202 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 202 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
203 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; 203 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
204 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; 204 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
205 const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
205 206
206 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) 207 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
207 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) 208 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
208 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats), 209 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
209 sizeof(num_texture_pixel_formats)) 210 sizeof(num_texture_pixel_formats))
210 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 211 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
212 .write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
213 sizeof(num_cbuf_replacement_values))
211 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) 214 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
212 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) 215 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
213 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 216 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
@@ -229,6 +232,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
229 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 232 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
230 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 233 .write(reinterpret_cast<const char*>(&type), sizeof(type));
231 } 234 }
235 for (const auto& [key, type] : cbuf_replacements) {
236 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
237 .write(reinterpret_cast<const char*>(&type), sizeof(type));
238 }
232 if (stage == Shader::Stage::Compute) { 239 if (stage == Shader::Stage::Compute) {
233 file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size)) 240 file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size))
234 .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size)); 241 .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size));
@@ -318,6 +325,9 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
318 ASSERT(local_size <= std::numeric_limits<u32>::max()); 325 ASSERT(local_size <= std::numeric_limits<u32>::max());
319 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size; 326 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size;
320 texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; 327 texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot;
328 is_propietary_driver = texture_bound == 2;
329 has_hle_engine_state =
330 maxwell3d->engine_state == Tegra::Engines::Maxwell3D::EngineHint::OnHLEMacro;
321} 331}
322 332
323u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { 333u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
@@ -331,6 +341,32 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
331 return value; 341 return value;
332} 342}
333 343
344std::optional<Shader::ReplaceConstant> GraphicsEnvironment::GetReplaceConstBuffer(u32 bank,
345 u32 offset) {
346 if (!has_hle_engine_state) {
347 return std::nullopt;
348 }
349 const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
350 auto it = maxwell3d->replace_table.find(key);
351 if (it == maxwell3d->replace_table.end()) {
352 return std::nullopt;
353 }
354 const auto converted_value = [](Tegra::Engines::Maxwell3D::HLEReplacementAttributeType name) {
355 switch (name) {
356 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseVertex:
357 return Shader::ReplaceConstant::BaseVertex;
358 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseInstance:
359 return Shader::ReplaceConstant::BaseInstance;
360 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::DrawID:
361 return Shader::ReplaceConstant::DrawID;
362 default:
363 UNREACHABLE();
364 }
365 }(it->second);
366 cbuf_replacements.emplace(key, converted_value);
367 return converted_value;
368}
369
334Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 370Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
335 const auto& regs{maxwell3d->regs}; 371 const auto& regs{maxwell3d->regs};
336 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; 372 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
@@ -366,6 +402,7 @@ ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_com
366 stage = Shader::Stage::Compute; 402 stage = Shader::Stage::Compute;
367 local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc; 403 local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc;
368 texture_bound = kepler_compute->regs.tex_cb_index; 404 texture_bound = kepler_compute->regs.tex_cb_index;
405 is_propietary_driver = texture_bound == 2;
369 shared_memory_size = qmd.shared_alloc; 406 shared_memory_size = qmd.shared_alloc;
370 workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}; 407 workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z};
371} 408}
@@ -409,11 +446,14 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
409 u64 num_texture_types{}; 446 u64 num_texture_types{};
410 u64 num_texture_pixel_formats{}; 447 u64 num_texture_pixel_formats{};
411 u64 num_cbuf_values{}; 448 u64 num_cbuf_values{};
449 u64 num_cbuf_replacement_values{};
412 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) 450 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
413 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) 451 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
414 .read(reinterpret_cast<char*>(&num_texture_pixel_formats), 452 .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
415 sizeof(num_texture_pixel_formats)) 453 sizeof(num_texture_pixel_formats))
416 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 454 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
455 .read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
456 sizeof(num_cbuf_replacement_values))
417 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) 457 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
418 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) 458 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
419 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 459 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
@@ -444,6 +484,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
444 .read(reinterpret_cast<char*>(&value), sizeof(value)); 484 .read(reinterpret_cast<char*>(&value), sizeof(value));
445 cbuf_values.emplace(key, value); 485 cbuf_values.emplace(key, value);
446 } 486 }
487 for (size_t i = 0; i < num_cbuf_replacement_values; ++i) {
488 u64 key;
489 Shader::ReplaceConstant value;
490 file.read(reinterpret_cast<char*>(&key), sizeof(key))
491 .read(reinterpret_cast<char*>(&value), sizeof(value));
492 cbuf_replacements.emplace(key, value);
493 }
447 if (stage == Shader::Stage::Compute) { 494 if (stage == Shader::Stage::Compute) {
448 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) 495 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
449 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); 496 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
@@ -455,6 +502,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
455 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); 502 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
456 } 503 }
457 } 504 }
505 is_propietary_driver = texture_bound == 2;
458} 506}
459 507
460void FileEnvironment::Dump(u64 hash) { 508void FileEnvironment::Dump(u64 hash) {
@@ -512,6 +560,16 @@ std::array<u32, 3> FileEnvironment::WorkgroupSize() const {
512 return workgroup_size; 560 return workgroup_size;
513} 561}
514 562
563std::optional<Shader::ReplaceConstant> FileEnvironment::GetReplaceConstBuffer(u32 bank,
564 u32 offset) {
565 const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
566 auto it = cbuf_replacements.find(key);
567 if (it == cbuf_replacements.end()) {
568 return std::nullopt;
569 }
570 return it->second;
571}
572
515void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 573void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
516 const std::filesystem::path& filename, u32 cache_version) try { 574 const std::filesystem::path& filename, u32 cache_version) try {
517 std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app); 575 std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app);
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 1342fab1e..d75987a52 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -60,6 +60,10 @@ public:
60 60
61 void Serialize(std::ofstream& file) const; 61 void Serialize(std::ofstream& file) const;
62 62
63 bool HasHLEMacroState() const override {
64 return has_hle_engine_state;
65 }
66
63protected: 67protected:
64 std::optional<u64> TryFindSize(); 68 std::optional<u64> TryFindSize();
65 69
@@ -73,6 +77,7 @@ protected:
73 std::unordered_map<u32, Shader::TextureType> texture_types; 77 std::unordered_map<u32, Shader::TextureType> texture_types;
74 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; 78 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
75 std::unordered_map<u64, u32> cbuf_values; 79 std::unordered_map<u64, u32> cbuf_values;
80 std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
76 81
77 u32 local_memory_size{}; 82 u32 local_memory_size{};
78 u32 texture_bound{}; 83 u32 texture_bound{};
@@ -89,6 +94,7 @@ protected:
89 u32 viewport_transform_state = 1; 94 u32 viewport_transform_state = 1;
90 95
91 bool has_unbound_instructions = false; 96 bool has_unbound_instructions = false;
97 bool has_hle_engine_state = false;
92}; 98};
93 99
94class GraphicsEnvironment final : public GenericEnvironment { 100class GraphicsEnvironment final : public GenericEnvironment {
@@ -109,6 +115,8 @@ public:
109 115
110 u32 ReadViewportTransformState() override; 116 u32 ReadViewportTransformState() override;
111 117
118 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
119
112private: 120private:
113 Tegra::Engines::Maxwell3D* maxwell3d{}; 121 Tegra::Engines::Maxwell3D* maxwell3d{};
114 size_t stage_index{}; 122 size_t stage_index{};
@@ -131,6 +139,11 @@ public:
131 139
132 u32 ReadViewportTransformState() override; 140 u32 ReadViewportTransformState() override;
133 141
142 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
143 [[maybe_unused]] u32 bank, [[maybe_unused]] u32 offset) override {
144 return std::nullopt;
145 }
146
134private: 147private:
135 Tegra::Engines::KeplerCompute* kepler_compute{}; 148 Tegra::Engines::KeplerCompute* kepler_compute{};
136}; 149};
@@ -166,6 +179,13 @@ public:
166 179
167 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; 180 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
168 181
182 [[nodiscard]] std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank,
183 u32 offset) override;
184
185 [[nodiscard]] bool HasHLEMacroState() const override {
186 return cbuf_replacements.size() != 0;
187 }
188
169 void Dump(u64 hash) override; 189 void Dump(u64 hash) override;
170 190
171private: 191private:
@@ -173,6 +193,7 @@ private:
173 std::unordered_map<u32, Shader::TextureType> texture_types; 193 std::unordered_map<u32, Shader::TextureType> texture_types;
174 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; 194 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
175 std::unordered_map<u64, u32> cbuf_values; 195 std::unordered_map<u64, u32> cbuf_values;
196 std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
176 std::array<u32, 3> workgroup_size{}; 197 std::array<u32, 3> workgroup_size{};
177 u32 local_memory_size{}; 198 u32 local_memory_size{};
178 u32 shared_memory_size{}; 199 u32 shared_memory_size{};
diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h
index ee4240288..1bad83fb4 100644
--- a/src/video_core/texture_cache/descriptor_table.h
+++ b/src/video_core/texture_cache/descriptor_table.h
@@ -19,9 +19,7 @@ public:
19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} 19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}
20 20
21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { 21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { 22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; }
23 return false;
24 }
25 Refresh(gpu_addr, limit); 23 Refresh(gpu_addr, limit);
26 return true; 24 return true;
27 } 25 }
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 852ec2519..e9100091e 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
101 break; 101 break;
102 } 102 }
103 if (num_samples > 1) {
104 size.width *= NumSamplesX(config.msaa_mode);
105 size.height *= NumSamplesY(config.msaa_mode);
106 }
103 if (type != ImageType::Linear) { 107 if (type != ImageType::Linear) {
104 // FIXME: Call this without passing *this 108 // FIXME: Call this without passing *this
105 layer_stride = CalculateLayerStride(*this); 109 layer_stride = CalculateLayerStride(*this);
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index d552bccf0..203ac1b11 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -51,4 +51,48 @@ namespace VideoCommon {
51 return 1; 51 return 1;
52} 52}
53 53
54[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
55 using Tegra::Texture::MsaaMode;
56 switch (msaa_mode) {
57 case MsaaMode::Msaa1x1:
58 return 1;
59 case MsaaMode::Msaa2x1:
60 case MsaaMode::Msaa2x1_D3D:
61 case MsaaMode::Msaa2x2:
62 case MsaaMode::Msaa2x2_VC4:
63 case MsaaMode::Msaa2x2_VC12:
64 return 2;
65 case MsaaMode::Msaa4x2:
66 case MsaaMode::Msaa4x2_D3D:
67 case MsaaMode::Msaa4x2_VC8:
68 case MsaaMode::Msaa4x2_VC24:
69 case MsaaMode::Msaa4x4:
70 return 4;
71 }
72 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
73 return 1;
74}
75
76[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
77 using Tegra::Texture::MsaaMode;
78 switch (msaa_mode) {
79 case MsaaMode::Msaa1x1:
80 case MsaaMode::Msaa2x1:
81 case MsaaMode::Msaa2x1_D3D:
82 return 1;
83 case MsaaMode::Msaa2x2:
84 case MsaaMode::Msaa2x2_VC4:
85 case MsaaMode::Msaa2x2_VC12:
86 case MsaaMode::Msaa4x2:
87 case MsaaMode::Msaa4x2_D3D:
88 case MsaaMode::Msaa4x2_VC8:
89 case MsaaMode::Msaa4x2_VC24:
90 return 2;
91 case MsaaMode::Msaa4x4:
92 return 4;
93 }
94 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
95 return 1;
96}
97
54} // namespace VideoCommon 98} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 1e2aad76a..9df6a2903 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -29,7 +29,7 @@ struct SlotId {
29}; 29};
30 30
31template <class T> 31template <class T>
32requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> 32 requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
33class SlotVector { 33class SlotVector {
34public: 34public:
35 class Iterator { 35 class Iterator {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 8e68a2e53..1b01990a4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -39,6 +39,12 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
39 sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); 39 sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear);
40 sampler_descriptor.cubemap_anisotropy.Assign(1); 40 sampler_descriptor.cubemap_anisotropy.Assign(1);
41 41
42 // These values were chosen based on typical peak swizzle data sizes seen in some titles
43 static constexpr size_t SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 8_MiB;
44 static constexpr size_t UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 1_MiB;
45 swizzle_data_buffer.resize_destructive(SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY);
46 unswizzle_data_buffer.resize_destructive(UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY);
47
42 // Make sure the first index is reserved for the null resources 48 // Make sure the first index is reserved for the null resources
43 // This way the null resource becomes a compile time constant 49 // This way the null resource becomes a compile time constant
44 void(slot_images.insert(NullImageParams{})); 50 void(slot_images.insert(NullImageParams{}));
@@ -90,7 +96,8 @@ void TextureCache<P>::RunGarbageCollector() {
90 const auto copies = FullDownloadCopies(image.info); 96 const auto copies = FullDownloadCopies(image.info);
91 image.DownloadMemory(map, copies); 97 image.DownloadMemory(map, copies);
92 runtime.Finish(); 98 runtime.Finish();
93 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 99 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
100 swizzle_data_buffer);
94 } 101 }
95 if (True(image.flags & ImageFlagBits::Tracked)) { 102 if (True(image.flags & ImageFlagBits::Tracked)) {
96 UntrackImage(image, image_id); 103 UntrackImage(image, image_id);
@@ -142,6 +149,13 @@ typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept {
142} 149}
143 150
144template <class P> 151template <class P>
152typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept {
153 const auto image_view_id = VisitImageView(channel_state->graphics_image_table,
154 channel_state->graphics_image_view_ids, index);
155 return slot_image_views[image_view_id];
156}
157
158template <class P>
145void TextureCache<P>::MarkModification(ImageId id) noexcept { 159void TextureCache<P>::MarkModification(ImageId id) noexcept {
146 MarkModification(slot_images[id]); 160 MarkModification(slot_images[id]);
147} 161}
@@ -461,7 +475,8 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
461 const auto copies = FullDownloadCopies(image.info); 475 const auto copies = FullDownloadCopies(image.info);
462 image.DownloadMemory(map, copies); 476 image.DownloadMemory(map, copies);
463 runtime.Finish(); 477 runtime.Finish();
464 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 478 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
479 swizzle_data_buffer);
465 } 480 }
466} 481}
467 482
@@ -638,7 +653,28 @@ bool TextureCache<P>::ShouldWaitAsyncFlushes() const noexcept {
638template <class P> 653template <class P>
639void TextureCache<P>::CommitAsyncFlushes() { 654void TextureCache<P>::CommitAsyncFlushes() {
640 // This is intentionally passing the value by copy 655 // This is intentionally passing the value by copy
641 committed_downloads.push(uncommitted_downloads); 656 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
657 const std::span<const ImageId> download_ids = uncommitted_downloads;
658 if (download_ids.empty()) {
659 committed_downloads.emplace_back(std::move(uncommitted_downloads));
660 uncommitted_downloads.clear();
661 async_buffers.emplace_back(std::optional<AsyncBuffer>{});
662 return;
663 }
664 size_t total_size_bytes = 0;
665 for (const ImageId image_id : download_ids) {
666 total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
667 }
668 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true);
669 for (const ImageId image_id : download_ids) {
670 Image& image = slot_images[image_id];
671 const auto copies = FullDownloadCopies(image.info);
672 image.DownloadMemory(download_map, copies);
673 download_map.offset += Common::AlignUp(image.unswizzled_size_bytes, 64);
674 }
675 async_buffers.emplace_back(download_map);
676 }
677 committed_downloads.emplace_back(std::move(uncommitted_downloads));
642 uncommitted_downloads.clear(); 678 uncommitted_downloads.clear();
643} 679}
644 680
@@ -647,36 +683,58 @@ void TextureCache<P>::PopAsyncFlushes() {
647 if (committed_downloads.empty()) { 683 if (committed_downloads.empty()) {
648 return; 684 return;
649 } 685 }
650 const std::span<const ImageId> download_ids = committed_downloads.front(); 686 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
651 if (download_ids.empty()) { 687 const std::span<const ImageId> download_ids = committed_downloads.front();
652 committed_downloads.pop(); 688 if (download_ids.empty()) {
653 return; 689 committed_downloads.pop_front();
654 } 690 async_buffers.pop_front();
655 size_t total_size_bytes = 0; 691 return;
656 for (const ImageId image_id : download_ids) { 692 }
657 total_size_bytes += slot_images[image_id].unswizzled_size_bytes; 693 auto download_map = *async_buffers.front();
658 } 694 std::span<u8> download_span = download_map.mapped_span;
659 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes); 695 for (size_t i = download_ids.size(); i > 0; i--) {
660 const size_t original_offset = download_map.offset; 696 const ImageBase& image = slot_images[download_ids[i - 1]];
661 for (const ImageId image_id : download_ids) { 697 const auto copies = FullDownloadCopies(image.info);
662 Image& image = slot_images[image_id]; 698 download_map.offset -= Common::AlignUp(image.unswizzled_size_bytes, 64);
663 const auto copies = FullDownloadCopies(image.info); 699 std::span<u8> download_span_alt = download_span.subspan(download_map.offset);
664 image.DownloadMemory(download_map, copies); 700 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span_alt,
665 download_map.offset += image.unswizzled_size_bytes; 701 swizzle_data_buffer);
666 } 702 }
667 // Wait for downloads to finish 703 runtime.FreeDeferredStagingBuffer(download_map);
668 runtime.Finish(); 704 committed_downloads.pop_front();
669 705 async_buffers.pop_front();
670 download_map.offset = original_offset; 706 } else {
671 std::span<u8> download_span = download_map.mapped_span; 707 const std::span<const ImageId> download_ids = committed_downloads.front();
672 for (const ImageId image_id : download_ids) { 708 if (download_ids.empty()) {
673 const ImageBase& image = slot_images[image_id]; 709 committed_downloads.pop_front();
674 const auto copies = FullDownloadCopies(image.info); 710 return;
675 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); 711 }
676 download_map.offset += image.unswizzled_size_bytes; 712 size_t total_size_bytes = 0;
677 download_span = download_span.subspan(image.unswizzled_size_bytes); 713 for (const ImageId image_id : download_ids) {
714 total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
715 }
716 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
717 const size_t original_offset = download_map.offset;
718 for (const ImageId image_id : download_ids) {
719 Image& image = slot_images[image_id];
720 const auto copies = FullDownloadCopies(image.info);
721 image.DownloadMemory(download_map, copies);
722 download_map.offset += image.unswizzled_size_bytes;
723 }
724 // Wait for downloads to finish
725 runtime.Finish();
726 download_map.offset = original_offset;
727 std::span<u8> download_span = download_map.mapped_span;
728 for (const ImageId image_id : download_ids) {
729 const ImageBase& image = slot_images[image_id];
730 const auto copies = FullDownloadCopies(image.info);
731 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
732 swizzle_data_buffer);
733 download_map.offset += image.unswizzled_size_bytes;
734 download_span = download_span.subspan(image.unswizzled_size_bytes);
735 }
736 committed_downloads.pop_front();
678 } 737 }
679 committed_downloads.pop();
680} 738}
681 739
682template <class P> 740template <class P>
@@ -731,16 +789,25 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
731 const GPUVAddr gpu_addr = image.gpu_addr; 789 const GPUVAddr gpu_addr = image.gpu_addr;
732 790
733 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { 791 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
734 gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); 792 gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
793 VideoCommon::CacheType::NoTextureCache);
735 const auto uploads = FullUploadSwizzles(image.info); 794 const auto uploads = FullUploadSwizzles(image.info);
736 runtime.AccelerateImageUpload(image, staging, uploads); 795 runtime.AccelerateImageUpload(image, staging, uploads);
737 } else if (True(image.flags & ImageFlagBits::Converted)) { 796 return;
738 std::vector<u8> unswizzled_data(image.unswizzled_size_bytes); 797 }
739 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); 798 const size_t guest_size_bytes = image.guest_size_bytes;
740 ConvertImage(unswizzled_data, image.info, mapped_span, copies); 799 swizzle_data_buffer.resize_destructive(guest_size_bytes);
800 gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes);
801
802 if (True(image.flags & ImageFlagBits::Converted)) {
803 unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
804 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer,
805 unswizzle_data_buffer);
806 ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies);
741 image.UploadMemory(staging, copies); 807 image.UploadMemory(staging, copies);
742 } else { 808 } else {
743 const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); 809 const auto copies =
810 UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span);
744 image.UploadMemory(staging, copies); 811 image.UploadMemory(staging, copies);
745 } 812 }
746} 813}
@@ -910,7 +977,7 @@ void TextureCache<P>::InvalidateScale(Image& image) {
910} 977}
911 978
912template <class P> 979template <class P>
913u64 TextureCache<P>::GetScaledImageSizeBytes(ImageBase& image) { 980u64 TextureCache<P>::GetScaledImageSizeBytes(const ImageBase& image) {
914 const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale * 981 const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale *
915 Settings::values.resolution_info.up_scale); 982 Settings::values.resolution_info.up_scale);
916 const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift + 983 const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift +
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 587339a31..485eaabaa 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -17,6 +17,7 @@
17#include "common/literals.h" 17#include "common/literals.h"
18#include "common/lru_cache.h" 18#include "common/lru_cache.h"
19#include "common/polyfill_ranges.h" 19#include "common/polyfill_ranges.h"
20#include "common/scratch_buffer.h"
20#include "video_core/compatible_formats.h" 21#include "video_core/compatible_formats.h"
21#include "video_core/control/channel_state_cache.h" 22#include "video_core/control/channel_state_cache.h"
22#include "video_core/delayed_destruction_ring.h" 23#include "video_core/delayed_destruction_ring.h"
@@ -91,6 +92,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
91 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; 92 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
92 /// 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.
93 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;
95 /// True when the API can do asynchronous texture downloads.
96 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
94 97
95 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()}; 98 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()};
96 99
@@ -105,6 +108,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
105 using ImageView = typename P::ImageView; 108 using ImageView = typename P::ImageView;
106 using Sampler = typename P::Sampler; 109 using Sampler = typename P::Sampler;
107 using Framebuffer = typename P::Framebuffer; 110 using Framebuffer = typename P::Framebuffer;
111 using AsyncBuffer = typename P::AsyncBuffer;
108 112
109 struct BlitImages { 113 struct BlitImages {
110 ImageId dst_id; 114 ImageId dst_id;
@@ -125,6 +129,9 @@ public:
125 /// Return a reference to the given image view id 129 /// Return a reference to the given image view id
126 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; 130 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
127 131
132 /// Get the imageview from the graphics descriptor table in the specified index
133 [[nodiscard]] ImageView& GetImageView(u32 index) noexcept;
134
128 /// Mark an image as modified from the GPU 135 /// Mark an image as modified from the GPU
129 void MarkModification(ImageId id) noexcept; 136 void MarkModification(ImageId id) noexcept;
130 137
@@ -202,7 +209,7 @@ public:
202 /// Create channel state. 209 /// Create channel state.
203 void CreateChannel(Tegra::Control::ChannelState& channel) final override; 210 void CreateChannel(Tegra::Control::ChannelState& channel) final override;
204 211
205 std::mutex mutex; 212 std::recursive_mutex mutex;
206 213
207private: 214private:
208 /// Iterate over all page indices in a range 215 /// Iterate over all page indices in a range
@@ -368,7 +375,7 @@ private:
368 void InvalidateScale(Image& image); 375 void InvalidateScale(Image& image);
369 bool ScaleUp(Image& image); 376 bool ScaleUp(Image& image);
370 bool ScaleDown(Image& image); 377 bool ScaleDown(Image& image);
371 u64 GetScaledImageSizeBytes(ImageBase& image); 378 u64 GetScaledImageSizeBytes(const ImageBase& image);
372 379
373 Runtime& runtime; 380 Runtime& runtime;
374 381
@@ -402,7 +409,8 @@ private:
402 409
403 // TODO: This data structure is not optimal and it should be reworked 410 // TODO: This data structure is not optimal and it should be reworked
404 std::vector<ImageId> uncommitted_downloads; 411 std::vector<ImageId> uncommitted_downloads;
405 std::queue<std::vector<ImageId>> committed_downloads; 412 std::deque<std::vector<ImageId>> committed_downloads;
413 std::deque<std::optional<AsyncBuffer>> async_buffers;
406 414
407 struct LRUItemParams { 415 struct LRUItemParams {
408 using ObjectType = ImageId; 416 using ObjectType = ImageId;
@@ -417,6 +425,9 @@ private:
417 425
418 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; 426 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
419 427
428 Common::ScratchBuffer<u8> swizzle_data_buffer;
429 Common::ScratchBuffer<u8> unswizzle_data_buffer;
430
420 u64 modification_tick = 0; 431 u64 modification_tick = 0;
421 u64 frame_tick = 0; 432 u64 frame_tick = 0;
422}; 433};
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index e8c908b42..03acc68d9 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -505,7 +505,7 @@ void SwizzlePitchLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
505 505
506void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 506void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
507 const ImageInfo& info, const BufferImageCopy& copy, 507 const ImageInfo& info, const BufferImageCopy& copy,
508 std::span<const u8> input) { 508 std::span<const u8> input, Common::ScratchBuffer<u8>& tmp_buffer) {
509 const Extent3D size = info.size; 509 const Extent3D size = info.size;
510 const LevelInfo level_info = MakeLevelInfo(info); 510 const LevelInfo level_info = MakeLevelInfo(info);
511 const Extent2D tile_size = DefaultBlockSize(info.format); 511 const Extent2D tile_size = DefaultBlockSize(info.format);
@@ -534,8 +534,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
534 tile_size.height, info.tile_width_spacing); 534 tile_size.height, info.tile_width_spacing);
535 const size_t subresource_size = sizes[level]; 535 const size_t subresource_size = sizes[level];
536 536
537 const auto dst_data = std::make_unique<u8[]>(subresource_size); 537 tmp_buffer.resize_destructive(subresource_size);
538 const std::span<u8> dst(dst_data.get(), subresource_size); 538 const std::span<u8> dst(tmp_buffer);
539 539
540 for (s32 layer = 0; layer < info.resources.layers; ++layer) { 540 for (s32 layer = 0; layer < info.resources.layers; ++layer) {
541 const std::span<const u8> src = input.subspan(host_offset); 541 const std::span<const u8> src = input.subspan(host_offset);
@@ -765,8 +765,9 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
765} 765}
766 766
767std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 767std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
768 const ImageInfo& info, std::span<u8> output) { 768 const ImageInfo& info, std::span<const u8> input,
769 const size_t guest_size_bytes = CalculateGuestSizeInBytes(info); 769 std::span<u8> output) {
770 const size_t guest_size_bytes = input.size_bytes();
770 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 771 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
771 const Extent3D size = info.size; 772 const Extent3D size = info.size;
772 773
@@ -789,10 +790,6 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
789 .image_extent = size, 790 .image_extent = size,
790 }}; 791 }};
791 } 792 }
792 const auto input_data = std::make_unique<u8[]>(guest_size_bytes);
793 gpu_memory.ReadBlockUnsafe(gpu_addr, input_data.get(), guest_size_bytes);
794 const std::span<const u8> input(input_data.get(), guest_size_bytes);
795
796 const LevelInfo level_info = MakeLevelInfo(info); 793 const LevelInfo level_info = MakeLevelInfo(info);
797 const s32 num_layers = info.resources.layers; 794 const s32 num_layers = info.resources.layers;
798 const s32 num_levels = info.resources.levels; 795 const s32 num_levels = info.resources.levels;
@@ -980,13 +977,14 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {
980} 977}
981 978
982void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 979void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
983 std::span<const BufferImageCopy> copies, std::span<const u8> memory) { 980 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
981 Common::ScratchBuffer<u8>& tmp_buffer) {
984 const bool is_pitch_linear = info.type == ImageType::Linear; 982 const bool is_pitch_linear = info.type == ImageType::Linear;
985 for (const BufferImageCopy& copy : copies) { 983 for (const BufferImageCopy& copy : copies) {
986 if (is_pitch_linear) { 984 if (is_pitch_linear) {
987 SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory); 985 SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory);
988 } else { 986 } else {
989 SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory); 987 SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory, tmp_buffer);
990 } 988 }
991 } 989 }
992} 990}
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 5e28f4ab3..d103db8ae 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -7,6 +7,7 @@
7#include <span> 7#include <span>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/scratch_buffer.h"
10 11
11#include "video_core/surface.h" 12#include "video_core/surface.h"
12#include "video_core/texture_cache/image_base.h" 13#include "video_core/texture_cache/image_base.h"
@@ -59,6 +60,7 @@ struct OverlapResult {
59 60
60[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, 61[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
61 GPUVAddr gpu_addr, const ImageInfo& info, 62 GPUVAddr gpu_addr, const ImageInfo& info,
63 std::span<const u8> input,
62 std::span<u8> output); 64 std::span<u8> output);
63 65
64[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 66[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
@@ -76,7 +78,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
76[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); 78[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info);
77 79
78void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 80void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
79 std::span<const BufferImageCopy> copies, std::span<const u8> memory); 81 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
82 Common::ScratchBuffer<u8>& tmp_buffer);
80 83
81[[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info, 84[[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info,
82 const ImageInfo& overlap_info, u32 new_level, 85 const ImageInfo& overlap_info, u32 new_level,
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 85f1d13e0..5fa0d9620 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -57,7 +57,7 @@ NsightAftermathTracker::NsightAftermathTracker() {
57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( 57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, 58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, 59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { 60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, nullptr, this))) {
61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); 61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
62 return; 62 return;
63 } 63 }
@@ -83,7 +83,7 @@ void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const {
83 83
84 std::scoped_lock lock{mutex}; 84 std::scoped_lock lock{mutex};
85 85
86 GFSDK_Aftermath_ShaderHash hash; 86 GFSDK_Aftermath_ShaderBinaryHash hash;
87 if (!GFSDK_Aftermath_SUCCEED( 87 if (!GFSDK_Aftermath_SUCCEED(
88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) { 88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module"); 89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
@@ -121,8 +121,8 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
121 u32 json_size = 0; 121 u32 json_size = 0;
122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( 122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, 123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr, 124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, this,
125 this, &json_size))) { 125 &json_size))) {
126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON"); 126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
127 return; 127 return;
128 } 128 }
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index c4d31681a..23d922e5d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -74,30 +74,6 @@ enum class NvidiaArchitecture {
74 VoltaOrOlder, 74 VoltaOrOlder,
75}; 75};
76 76
77constexpr std::array REQUIRED_EXTENSIONS{
78 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
79 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
80#ifdef _WIN32
81 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
82#endif
83#ifdef __unix__
84 VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
85#endif
86};
87
88constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{
89 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
90 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
91 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
92 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
93 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
94 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
95};
96
97constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{
98 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
99};
100
101template <typename T> 77template <typename T>
102void SetNext(void**& next, T& data) { 78void SetNext(void**& next, T& data) {
103 *next = &data; 79 *next = &data;
@@ -286,24 +262,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
286 return format_properties; 262 return format_properties;
287} 263}
288 264
289std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) {
290 const std::vector extensions = physical.EnumerateDeviceExtensionProperties();
291 std::vector<std::string> supported_extensions;
292 supported_extensions.reserve(extensions.size());
293 for (const auto& extension : extensions) {
294 supported_extensions.emplace_back(extension.extensionName);
295 }
296 return supported_extensions;
297}
298
299bool IsExtensionSupported(std::span<const std::string> supported_extensions,
300 std::string_view extension) {
301 return std::ranges::find(supported_extensions, extension) != supported_extensions.end();
302}
303
304NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, 265NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
305 std::span<const std::string> exts) { 266 const std::set<std::string, std::less<>>& exts) {
306 if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { 267 if (exts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
307 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; 268 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
308 shading_rate_props.sType = 269 shading_rate_props.sType =
309 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; 270 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
@@ -316,362 +277,55 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
316 return NvidiaArchitecture::AmpereOrNewer; 277 return NvidiaArchitecture::AmpereOrNewer;
317 } 278 }
318 } 279 }
319 if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { 280 if (exts.contains(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
320 return NvidiaArchitecture::Turing; 281 return NvidiaArchitecture::Turing;
321 } 282 }
322 return NvidiaArchitecture::VoltaOrOlder; 283 return NvidiaArchitecture::VoltaOrOlder;
323} 284}
285
286std::vector<const char*> ExtensionListForVulkan(
287 const std::set<std::string, std::less<>>& extensions) {
288 std::vector<const char*> output;
289 for (const auto& extension : extensions) {
290 output.push_back(extension.c_str());
291 }
292 return output;
293}
294
324} // Anonymous namespace 295} // Anonymous namespace
325 296
326Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 297Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
327 const vk::InstanceDispatch& dld_) 298 const vk::InstanceDispatch& dld_)
328 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 299 : instance{instance_}, dld{dld_}, physical{physical_},
329 instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions(
330 physical)},
331 format_properties(GetFormatProperties(physical)) { 300 format_properties(GetFormatProperties(physical)) {
332 CheckSuitability(surface != nullptr); 301 // Get suitability and device properties.
333 SetupFamilies(surface); 302 const bool is_suitable = GetSuitability(surface != nullptr);
334 SetupFeatures();
335 SetupProperties();
336
337 const auto queue_cis = GetDeviceQueueCreateInfos();
338 const std::vector extensions = LoadExtensions(surface != nullptr);
339
340 VkPhysicalDeviceFeatures2 features2{
341 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
342 .pNext = nullptr,
343 .features{
344 .robustBufferAccess = true,
345 .fullDrawIndexUint32 = false,
346 .imageCubeArray = true,
347 .independentBlend = true,
348 .geometryShader = true,
349 .tessellationShader = true,
350 .sampleRateShading = true,
351 .dualSrcBlend = true,
352 .logicOp = true,
353 .multiDrawIndirect = false,
354 .drawIndirectFirstInstance = false,
355 .depthClamp = true,
356 .depthBiasClamp = true,
357 .fillModeNonSolid = true,
358 .depthBounds = is_depth_bounds_supported,
359 .wideLines = true,
360 .largePoints = true,
361 .alphaToOne = false,
362 .multiViewport = true,
363 .samplerAnisotropy = true,
364 .textureCompressionETC2 = false,
365 .textureCompressionASTC_LDR = is_optimal_astc_supported,
366 .textureCompressionBC = false,
367 .occlusionQueryPrecise = true,
368 .pipelineStatisticsQuery = false,
369 .vertexPipelineStoresAndAtomics = true,
370 .fragmentStoresAndAtomics = true,
371 .shaderTessellationAndGeometryPointSize = false,
372 .shaderImageGatherExtended = true,
373 .shaderStorageImageExtendedFormats = false,
374 .shaderStorageImageMultisample = is_shader_storage_image_multisample,
375 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
376 .shaderStorageImageWriteWithoutFormat = true,
377 .shaderUniformBufferArrayDynamicIndexing = false,
378 .shaderSampledImageArrayDynamicIndexing = false,
379 .shaderStorageBufferArrayDynamicIndexing = false,
380 .shaderStorageImageArrayDynamicIndexing = false,
381 .shaderClipDistance = true,
382 .shaderCullDistance = true,
383 .shaderFloat64 = is_shader_float64_supported,
384 .shaderInt64 = is_shader_int64_supported,
385 .shaderInt16 = is_shader_int16_supported,
386 .shaderResourceResidency = false,
387 .shaderResourceMinLod = false,
388 .sparseBinding = false,
389 .sparseResidencyBuffer = false,
390 .sparseResidencyImage2D = false,
391 .sparseResidencyImage3D = false,
392 .sparseResidency2Samples = false,
393 .sparseResidency4Samples = false,
394 .sparseResidency8Samples = false,
395 .sparseResidency16Samples = false,
396 .sparseResidencyAliased = false,
397 .variableMultisampleRate = false,
398 .inheritedQueries = false,
399 },
400 };
401 const void* first_next = &features2;
402 void** next = &features2.pNext;
403
404 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{
405 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
406 .pNext = nullptr,
407 .timelineSemaphore = true,
408 };
409 SetNext(next, timeline_semaphore);
410
411 VkPhysicalDevice16BitStorageFeatures bit16_storage{
412 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
413 .pNext = nullptr,
414 .storageBuffer16BitAccess = true,
415 .uniformAndStorageBuffer16BitAccess = true,
416 .storagePushConstant16 = false,
417 .storageInputOutput16 = false,
418 };
419 SetNext(next, bit16_storage);
420
421 VkPhysicalDevice8BitStorageFeatures bit8_storage{
422 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES,
423 .pNext = nullptr,
424 .storageBuffer8BitAccess = true,
425 .uniformAndStorageBuffer8BitAccess = true,
426 .storagePushConstant8 = false,
427 };
428 SetNext(next, bit8_storage);
429
430 VkPhysicalDeviceRobustness2FeaturesEXT robustness2{
431 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
432 .pNext = nullptr,
433 .robustBufferAccess2 = true,
434 .robustImageAccess2 = true,
435 .nullDescriptor = true,
436 };
437 SetNext(next, robustness2);
438
439 VkPhysicalDeviceHostQueryResetFeatures host_query_reset{
440 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES,
441 .pNext = nullptr,
442 .hostQueryReset = true,
443 };
444 SetNext(next, host_query_reset);
445
446 VkPhysicalDeviceVariablePointerFeatures variable_pointers{
447 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES,
448 .pNext = nullptr,
449 .variablePointersStorageBuffer = VK_TRUE,
450 .variablePointers = VK_TRUE,
451 };
452 SetNext(next, variable_pointers);
453
454 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{
455 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES,
456 .pNext = nullptr,
457 .shaderDemoteToHelperInvocation = true,
458 };
459 SetNext(next, demote);
460
461 VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{
462 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES,
463 .pNext = nullptr,
464 .shaderDrawParameters = true,
465 };
466 SetNext(next, draw_parameters);
467
468 VkPhysicalDeviceShaderFloat16Int8Features float16_int8;
469 if (is_int8_supported || is_float16_supported) {
470 float16_int8 = {
471 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES,
472 .pNext = nullptr,
473 .shaderFloat16 = is_float16_supported,
474 .shaderInt8 = is_int8_supported,
475 };
476 SetNext(next, float16_int8);
477 }
478 if (!is_float16_supported) {
479 LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
480 }
481 if (!is_int8_supported) {
482 LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively");
483 }
484
485 if (!nv_viewport_swizzle) {
486 LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles");
487 }
488
489 if (!nv_viewport_array2) {
490 LOG_INFO(Render_Vulkan, "Device doesn't support viewport masks");
491 }
492
493 if (!nv_geometry_shader_passthrough) {
494 LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders");
495 }
496
497 VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout;
498 if (khr_uniform_buffer_standard_layout) {
499 std430_layout = {
500 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES,
501 .pNext = nullptr,
502 .uniformBufferStandardLayout = true,
503 };
504 SetNext(next, std430_layout);
505 } else {
506 LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
507 }
508
509 VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
510 if (ext_index_type_uint8) {
511 index_type_uint8 = {
512 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT,
513 .pNext = nullptr,
514 .indexTypeUint8 = true,
515 };
516 SetNext(next, index_type_uint8);
517 } else {
518 LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
519 }
520
521 VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart;
522 if (is_topology_list_restart_supported || is_patch_list_restart_supported) {
523 primitive_topology_list_restart = {
524 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT,
525 .pNext = nullptr,
526 .primitiveTopologyListRestart = is_topology_list_restart_supported,
527 .primitiveTopologyPatchListRestart = is_patch_list_restart_supported,
528 };
529 SetNext(next, primitive_topology_list_restart);
530 } else {
531 LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart");
532 }
533
534 VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
535 if (ext_transform_feedback) {
536 transform_feedback = {
537 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT,
538 .pNext = nullptr,
539 .transformFeedback = true,
540 .geometryStreams = true,
541 };
542 SetNext(next, transform_feedback);
543 } else {
544 LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
545 }
546
547 VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border;
548 if (ext_custom_border_color) {
549 custom_border = {
550 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
551 .pNext = nullptr,
552 .customBorderColors = VK_TRUE,
553 .customBorderColorWithoutFormat = VK_TRUE,
554 };
555 SetNext(next, custom_border);
556 } else {
557 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
558 }
559
560 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
561 if (ext_extended_dynamic_state) {
562 dynamic_state = {
563 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT,
564 .pNext = nullptr,
565 .extendedDynamicState = VK_TRUE,
566 };
567 SetNext(next, dynamic_state);
568 } else {
569 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
570 }
571
572 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
573 if (ext_line_rasterization) {
574 line_raster = {
575 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT,
576 .pNext = nullptr,
577 .rectangularLines = VK_TRUE,
578 .bresenhamLines = VK_FALSE,
579 .smoothLines = VK_TRUE,
580 .stippledRectangularLines = VK_FALSE,
581 .stippledBresenhamLines = VK_FALSE,
582 .stippledSmoothLines = VK_FALSE,
583 };
584 SetNext(next, line_raster);
585 } else {
586 LOG_INFO(Render_Vulkan, "Device doesn't support smooth lines");
587 }
588
589 if (!ext_conservative_rasterization) {
590 LOG_INFO(Render_Vulkan, "Device doesn't support conservative rasterization");
591 }
592
593 VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex;
594 if (ext_provoking_vertex) {
595 provoking_vertex = {
596 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT,
597 .pNext = nullptr,
598 .provokingVertexLast = VK_TRUE,
599 .transformFeedbackPreservesProvokingVertex = VK_TRUE,
600 };
601 SetNext(next, provoking_vertex);
602 } else {
603 LOG_INFO(Render_Vulkan, "Device doesn't support provoking vertex last");
604 }
605 303
606 VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input_dynamic; 304 const VkDriverId driver_id = properties.driver.driverID;
607 if (ext_vertex_input_dynamic_state) { 305 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
608 vertex_input_dynamic = { 306 const bool is_amd_driver =
609 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT, 307 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
610 .pNext = nullptr, 308 const bool is_amd = is_amd_driver || is_radv;
611 .vertexInputDynamicState = VK_TRUE, 309 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
612 }; 310 const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
613 SetNext(next, vertex_input_dynamic); 311 const bool is_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
614 } else { 312 const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
615 LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state");
616 }
617
618 VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
619 if (ext_shader_atomic_int64) {
620 atomic_int64 = {
621 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES,
622 .pNext = nullptr,
623 .shaderBufferInt64Atomics = VK_TRUE,
624 .shaderSharedInt64Atomics = VK_TRUE,
625 };
626 SetNext(next, atomic_int64);
627 }
628 313
629 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout; 314 if (is_mvk && !is_suitable) {
630 if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) { 315 LOG_WARNING(Render_Vulkan, "Unsuitable driver is MoltenVK, continuing anyway");
631 workgroup_layout = { 316 } else if (!is_suitable) {
632 .sType = 317 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
633 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
634 .pNext = nullptr,
635 .workgroupMemoryExplicitLayout = VK_TRUE,
636 .workgroupMemoryExplicitLayoutScalarBlockLayout = VK_TRUE,
637 .workgroupMemoryExplicitLayout8BitAccess = VK_TRUE,
638 .workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
639 };
640 SetNext(next, workgroup_layout);
641 } else if (khr_workgroup_memory_explicit_layout) {
642 // TODO(lat9nq): Find a proper fix for this
643 LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
644 "yuzu bug when host driver does not support 16-bit integers");
645 khr_workgroup_memory_explicit_layout = false;
646 } 318 }
647 319
648 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; 320 SetupFamilies(surface);
649 if (khr_pipeline_executable_properties) { 321 const auto queue_cis = GetDeviceQueueCreateInfos();
650 LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
651 executable_properties = {
652 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
653 .pNext = nullptr,
654 .pipelineExecutableInfo = VK_TRUE,
655 };
656 SetNext(next, executable_properties);
657 }
658
659 if (!ext_depth_range_unrestricted) {
660 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
661 }
662 322
663 VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; 323 // GetSuitability has already configured the linked list of features for us.
664 if (ext_depth_clip_control) { 324 // Reuse it here.
665 depth_clip_control_features = { 325 const void* first_next = &features2;
666 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
667 .pNext = nullptr,
668 .depthClipControl = VK_TRUE,
669 };
670 SetNext(next, depth_clip_control_features);
671 }
672 326
673 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; 327 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv{};
674 if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { 328 if (Settings::values.enable_nsight_aftermath && extensions.device_diagnostics_config) {
675 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); 329 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
676 330
677 diagnostics_nv = { 331 diagnostics_nv = {
@@ -683,93 +337,116 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
683 }; 337 };
684 first_next = &diagnostics_nv; 338 first_next = &diagnostics_nv;
685 } 339 }
686 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
687 340
688 is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; 341 is_blit_depth_stencil_supported = TestDepthStencilBlits();
689 is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; 342 is_optimal_astc_supported = ComputeIsOptimalAstcSupported();
690 is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || 343 is_warp_potentially_bigger = !extensions.subgroup_size_control ||
691 properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; 344 properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize;
345
346 is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
347 is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
348 is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
349 properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
350
351 supports_d24_depth =
352 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
353 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
692 354
693 CollectPhysicalMemoryInfo(); 355 CollectPhysicalMemoryInfo();
694 CollectTelemetryParameters();
695 CollectToolingInfo(); 356 CollectToolingInfo();
696 357
697 if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { 358 if (is_nvidia) {
359 const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
698 const auto arch = GetNvidiaArchitecture(physical, supported_extensions); 360 const auto arch = GetNvidiaArchitecture(physical, supported_extensions);
699 switch (arch) { 361 switch (arch) {
700 case NvidiaArchitecture::AmpereOrNewer: 362 case NvidiaArchitecture::AmpereOrNewer:
701 LOG_WARNING(Render_Vulkan, "Blacklisting Ampere devices from float16 math"); 363 LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
702 is_float16_supported = false; 364 features.shader_float16_int8.shaderFloat16 = false;
703 break; 365 break;
704 case NvidiaArchitecture::Turing: 366 case NvidiaArchitecture::Turing:
705 break; 367 break;
706 case NvidiaArchitecture::VoltaOrOlder: 368 case NvidiaArchitecture::VoltaOrOlder:
707 LOG_WARNING(Render_Vulkan, "Blacklisting Volta and older from VK_KHR_push_descriptor"); 369 if (nv_major_version < 527) {
708 khr_push_descriptor = false; 370 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
371 extensions.push_descriptor = false;
372 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
373 }
709 break; 374 break;
710 } 375 }
711 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
712 if (nv_major_version >= 510) { 376 if (nv_major_version >= 510) {
713 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); 377 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
714 cant_blit_msaa = true; 378 cant_blit_msaa = true;
715 } 379 }
716 } 380 }
717 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; 381 if (extensions.extended_dynamic_state && is_radv) {
718 if (ext_extended_dynamic_state && is_radv) {
719 // Mask driver version variant 382 // Mask driver version variant
720 const u32 version = (properties.driverVersion << 3) >> 3; 383 const u32 version = (properties.properties.driverVersion << 3) >> 3;
721 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { 384 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
722 LOG_WARNING(Render_Vulkan, 385 LOG_WARNING(Render_Vulkan,
723 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); 386 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
724 ext_extended_dynamic_state = false; 387 extensions.extended_dynamic_state = false;
388 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
389 }
390 }
391 if (extensions.extended_dynamic_state2 && is_radv) {
392 const u32 version = (properties.properties.driverVersion << 3) >> 3;
393 if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
394 LOG_WARNING(
395 Render_Vulkan,
396 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
397 features.extended_dynamic_state2.extendedDynamicState2 = false;
398 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
399 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
400 extensions.extended_dynamic_state2 = false;
401 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
725 } 402 }
726 } 403 }
727 if (ext_vertex_input_dynamic_state && is_radv) { 404 if (extensions.vertex_input_dynamic_state && is_radv) {
728 // TODO(ameerj): Blacklist only offending driver versions 405 // TODO(ameerj): Blacklist only offending driver versions
729 // TODO(ameerj): Confirm if RDNA1 is affected 406 // TODO(ameerj): Confirm if RDNA1 is affected
730 const bool is_rdna2 = 407 const bool is_rdna2 =
731 IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); 408 supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
732 if (is_rdna2) { 409 if (is_rdna2) {
733 LOG_WARNING(Render_Vulkan, 410 LOG_WARNING(Render_Vulkan,
734 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); 411 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
735 ext_vertex_input_dynamic_state = false; 412 extensions.vertex_input_dynamic_state = false;
413 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
736 } 414 }
737 } 415 }
738 sets_per_pool = 64;
739 416
740 const bool is_amd = 417 sets_per_pool = 64;
741 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; 418 if (is_amd_driver) {
742 if (is_amd) {
743 // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. 419 // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
744 sets_per_pool = 96; 420 sets_per_pool = 96;
745 // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. 421 // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
746 if (!is_float16_supported) { 422 if (!features.shader_float16_int8.shaderFloat16) {
747 LOG_WARNING( 423 LOG_WARNING(Render_Vulkan,
748 Render_Vulkan, 424 "AMD GCN4 and earlier have broken VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
749 "AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
750 has_broken_cube_compatibility = true; 425 has_broken_cube_compatibility = true;
751 } 426 }
752 } 427 }
753 const bool is_amd_or_radv = is_amd || is_radv; 428 if (extensions.sampler_filter_minmax && is_amd) {
754 if (ext_sampler_filter_minmax && is_amd_or_radv) {
755 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. 429 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
756 if (!is_float16_supported) { 430 if (!features.shader_float16_int8.shaderFloat16) {
757 LOG_WARNING(Render_Vulkan, 431 LOG_WARNING(Render_Vulkan,
758 "Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax"); 432 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
759 ext_sampler_filter_minmax = false; 433 extensions.sampler_filter_minmax = false;
434 loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
760 } 435 }
761 } 436 }
762 437
763 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; 438 if (extensions.vertex_input_dynamic_state && is_intel_windows) {
764 const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; 439 const u32 version = (properties.properties.driverVersion << 3) >> 3;
765 if (ext_vertex_input_dynamic_state && is_intel_windows) { 440 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
766 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); 441 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
767 ext_vertex_input_dynamic_state = false; 442 extensions.vertex_input_dynamic_state = false;
443 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
444 }
768 } 445 }
769 if (is_float16_supported && is_intel_windows) { 446 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
770 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. 447 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
771 LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math"); 448 LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
772 is_float16_supported = false; 449 features.shader_float16_int8.shaderFloat16 = false;
773 } 450 }
774 if (is_intel_windows) { 451 if (is_intel_windows) {
775 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); 452 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
@@ -779,10 +456,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
779 LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format"); 456 LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
780 must_emulate_bgr565 = true; 457 must_emulate_bgr565 = true;
781 } 458 }
459 if (is_mvk) {
460 LOG_WARNING(Render_Vulkan,
461 "MVK driver breaks when using more than 16 vertex attributes/bindings");
462 properties.properties.limits.maxVertexInputAttributes =
463 std::min(properties.properties.limits.maxVertexInputAttributes, 16U);
464 properties.properties.limits.maxVertexInputBindings =
465 std::min(properties.properties.limits.maxVertexInputBindings, 16U);
466 }
782 467
783 supports_d24_depth = 468 logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions),
784 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, 469 first_next, dld);
785 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
786 470
787 graphics_queue = logical.GetQueue(graphics_family); 471 graphics_queue = logical.GetQueue(graphics_family);
788 present_queue = logical.GetQueue(present_family); 472 present_queue = logical.GetQueue(present_family);
@@ -837,7 +521,7 @@ void Device::SaveShader(std::span<const u32> spirv) const {
837 } 521 }
838} 522}
839 523
840bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { 524bool Device::ComputeIsOptimalAstcSupported() const {
841 // Disable for now to avoid converting ASTC twice. 525 // Disable for now to avoid converting ASTC twice.
842 static constexpr std::array astc_formats = { 526 static constexpr std::array astc_formats = {
843 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, 527 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
@@ -855,7 +539,7 @@ bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) co
855 VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_SRGB_BLOCK, 539 VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
856 VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK, 540 VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
857 }; 541 };
858 if (!features.textureCompressionASTC_LDR) { 542 if (!features.features.textureCompressionASTC_LDR) {
859 return false; 543 return false;
860 } 544 }
861 const auto format_feature_usage{ 545 const auto format_feature_usage{
@@ -893,7 +577,7 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want
893} 577}
894 578
895std::string Device::GetDriverName() const { 579std::string Device::GetDriverName() const {
896 switch (driver_id) { 580 switch (properties.driver.driverID) {
897 case VK_DRIVER_ID_AMD_PROPRIETARY: 581 case VK_DRIVER_ID_AMD_PROPRIETARY:
898 return "AMD"; 582 return "AMD";
899 case VK_DRIVER_ID_AMD_OPEN_SOURCE: 583 case VK_DRIVER_ID_AMD_OPEN_SOURCE:
@@ -909,460 +593,336 @@ std::string Device::GetDriverName() const {
909 case VK_DRIVER_ID_MESA_LLVMPIPE: 593 case VK_DRIVER_ID_MESA_LLVMPIPE:
910 return "LAVAPIPE"; 594 return "LAVAPIPE";
911 default: 595 default:
912 return vendor_name; 596 return properties.driver.driverName;
913 } 597 }
914} 598}
915 599
916static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { 600bool Device::ShouldBoostClocks() const {
917 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; 601 const auto driver_id = properties.driver.driverID;
602 const auto vendor_id = properties.properties.vendorID;
603 const auto device_id = properties.properties.deviceID;
918 604
919 if (available_version < VK_API_VERSION_1_2) { 605 const bool validated_driver =
920 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(), 606 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
921 REQUIRED_EXTENSIONS_BEFORE_1_2.end()); 607 driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
922 } 608 driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS ||
609 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
923 610
924 if (available_version < VK_API_VERSION_1_3) { 611 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;
925 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(),
926 REQUIRED_EXTENSIONS_BEFORE_1_3.end());
927 }
928 612
929 return extensions; 613 return validated_driver && !is_steam_deck;
930} 614}
931 615
932void Device::CheckSuitability(bool requires_swapchain) const { 616bool Device::GetSuitability(bool requires_swapchain) {
933 std::vector<const char*> required_extensions = 617 // Assume we will be suitable.
934 ExtensionsRequiredForInstanceVersion(instance_version); 618 bool suitable = true;
935 std::vector<const char*> available_extensions;
936 619
937 if (requires_swapchain) { 620 // Configure properties.
938 required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 621 properties.properties = physical.GetProperties();
939 } 622
623 // Set instance version.
624 instance_version = properties.properties.apiVersion;
940 625
626 // Minimum of API version 1.1 is required. (This is well-supported.)
627 ASSERT(instance_version >= VK_API_VERSION_1_1);
628
629 // Get available extensions.
941 auto extension_properties = physical.EnumerateDeviceExtensionProperties(); 630 auto extension_properties = physical.EnumerateDeviceExtensionProperties();
942 631
632 // Get the set of supported extensions.
633 supported_extensions.clear();
943 for (const VkExtensionProperties& property : extension_properties) { 634 for (const VkExtensionProperties& property : extension_properties) {
944 available_extensions.push_back(property.extensionName); 635 supported_extensions.insert(property.extensionName);
945 } 636 }
946 637
947 bool has_all_required_extensions = true; 638 // Generate list of extensions to load.
948 for (const char* requirement_name : required_extensions) { 639 loaded_extensions.clear();
949 const bool found =
950 std::ranges::any_of(available_extensions, [&](const char* extension_name) {
951 return std::strcmp(requirement_name, extension_name) == 0;
952 });
953 640
954 if (!found) { 641#define EXTENSION(prefix, macro_name, var_name) \
955 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name); 642 if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \
956 has_all_required_extensions = false; 643 loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \
957 } 644 extensions.var_name = true; \
958 } 645 }
959 646#define FEATURE_EXTENSION(prefix, struct_name, macro_name, var_name) \
960 if (!has_all_required_extensions) { 647 if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \
961 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 648 loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \
649 extensions.var_name = true; \
962 } 650 }
963 651
964 struct LimitTuple { 652 if (instance_version < VK_API_VERSION_1_2) {
965 u32 minimum; 653 FOR_EACH_VK_FEATURE_1_2(FEATURE_EXTENSION);
966 u32 value; 654 }
967 const char* name; 655 if (instance_version < VK_API_VERSION_1_3) {
968 }; 656 FOR_EACH_VK_FEATURE_1_3(FEATURE_EXTENSION);
969 const VkPhysicalDeviceLimits& limits{properties.limits};
970 const std::array limits_report{
971 LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
972 LimitTuple{16, limits.maxViewports, "maxViewports"},
973 LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"},
974 LimitTuple{8, limits.maxClipDistances, "maxClipDistances"},
975 };
976 for (const auto& tuple : limits_report) {
977 if (tuple.value < tuple.minimum) {
978 LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name,
979 tuple.minimum, tuple.value);
980 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
981 }
982 } 657 }
983 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{};
984 demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES;
985 demote.pNext = nullptr;
986 658
987 VkPhysicalDeviceVariablePointerFeatures variable_pointers{}; 659 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
988 variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES; 660 FOR_EACH_VK_EXTENSION(EXTENSION);
989 variable_pointers.pNext = &demote; 661#ifdef _WIN32
662 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
663#endif
990 664
991 VkPhysicalDeviceRobustness2FeaturesEXT robustness2{}; 665#undef FEATURE_EXTENSION
992 robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; 666#undef EXTENSION
993 robustness2.pNext = &variable_pointers;
994 667
995 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{}; 668 // Some extensions are mandatory. Check those.
996 timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; 669#define CHECK_EXTENSION(extension_name) \
997 timeline_semaphore.pNext = &robustness2; 670 if (!loaded_extensions.contains(extension_name)) { \
671 LOG_ERROR(Render_Vulkan, "Missing required extension {}", extension_name); \
672 suitable = false; \
673 }
998 674
999 VkPhysicalDevice16BitStorageFeatures bit16_storage{}; 675#define LOG_EXTENSION(extension_name) \
1000 bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; 676 if (!loaded_extensions.contains(extension_name)) { \
1001 bit16_storage.pNext = &timeline_semaphore; 677 LOG_INFO(Render_Vulkan, "Device doesn't support extension {}", extension_name); \
678 }
1002 679
1003 VkPhysicalDevice8BitStorageFeatures bit8_storage{}; 680 FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION);
1004 bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES; 681 FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION);
1005 bit8_storage.pNext = &bit16_storage; 682#ifdef _WIN32
683 FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION);
684#else
685 FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION);
686#endif
1006 687
1007 VkPhysicalDeviceHostQueryResetFeatures host_query_reset{}; 688 if (requires_swapchain) {
1008 host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES; 689 CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1009 host_query_reset.pNext = &bit8_storage; 690 }
1010 691
1011 VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{}; 692#undef LOG_EXTENSION
1012 draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; 693#undef CHECK_EXTENSION
1013 draw_parameters.pNext = &host_query_reset;
1014 694
1015 VkPhysicalDeviceFeatures2 features2{}; 695 // Generate the linked list of features to test.
1016 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 696 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1017 features2.pNext = &draw_parameters;
1018 697
1019 physical.GetFeatures2(features2); 698 // Set next pointer.
699 void** next = &features2.pNext;
1020 700
1021 const VkPhysicalDeviceFeatures& features{features2.features}; 701 // Test all features we know about. If the feature is not available in core at our
1022 std::array feature_report{ 702 // current API version, and was not enabled by an extension, skip testing the feature.
1023 std::make_pair(features.robustBufferAccess, "robustBufferAccess"), 703 // We set the structure sType explicitly here as it is zeroed by the constructor.
1024 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 704#define FEATURE(prefix, struct_name, macro_name, var_name) \
1025 std::make_pair(features.imageCubeArray, "imageCubeArray"), 705 features.var_name.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES; \
1026 std::make_pair(features.independentBlend, "independentBlend"), 706 SetNext(next, features.var_name);
1027 std::make_pair(features.depthClamp, "depthClamp"),
1028 std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
1029 std::make_pair(features.largePoints, "largePoints"),
1030 std::make_pair(features.multiViewport, "multiViewport"),
1031 std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
1032 std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"),
1033 std::make_pair(features.wideLines, "wideLines"),
1034 std::make_pair(features.geometryShader, "geometryShader"),
1035 std::make_pair(features.tessellationShader, "tessellationShader"),
1036 std::make_pair(features.sampleRateShading, "sampleRateShading"),
1037 std::make_pair(features.dualSrcBlend, "dualSrcBlend"),
1038 std::make_pair(features.logicOp, "logicOp"),
1039 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
1040 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
1041 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
1042 std::make_pair(features.shaderStorageImageWriteWithoutFormat,
1043 "shaderStorageImageWriteWithoutFormat"),
1044 std::make_pair(features.shaderClipDistance, "shaderClipDistance"),
1045 std::make_pair(features.shaderCullDistance, "shaderCullDistance"),
1046 std::make_pair(variable_pointers.variablePointers, "variablePointers"),
1047 std::make_pair(variable_pointers.variablePointersStorageBuffer,
1048 "variablePointersStorageBuffer"),
1049 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"),
1050 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"),
1051 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"),
1052 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
1053 std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"),
1054 std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"),
1055 std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess,
1056 "uniformAndStorageBuffer16BitAccess"),
1057 std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"),
1058 std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess,
1059 "uniformAndStorageBuffer8BitAccess"),
1060 std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"),
1061 std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"),
1062 };
1063 707
1064 bool has_all_required_features = true; 708#define EXT_FEATURE(prefix, struct_name, macro_name, var_name) \
1065 for (const auto& [is_supported, name] : feature_report) { 709 if (extensions.var_name) { \
1066 if (!is_supported) { 710 features.var_name.sType = \
1067 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); 711 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES_##prefix; \
1068 has_all_required_features = false; 712 SetNext(next, features.var_name); \
1069 }
1070 } 713 }
1071 714
1072 if (!has_all_required_features) { 715 FOR_EACH_VK_FEATURE_1_1(FEATURE);
1073 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 716 FOR_EACH_VK_FEATURE_EXT(EXT_FEATURE);
717 if (instance_version >= VK_API_VERSION_1_2) {
718 FOR_EACH_VK_FEATURE_1_2(FEATURE);
719 } else {
720 FOR_EACH_VK_FEATURE_1_2(EXT_FEATURE);
1074 } 721 }
1075} 722 if (instance_version >= VK_API_VERSION_1_3) {
1076 723 FOR_EACH_VK_FEATURE_1_3(FEATURE);
1077std::vector<const char*> Device::LoadExtensions(bool requires_surface) { 724 } else {
1078 std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version); 725 FOR_EACH_VK_FEATURE_1_3(EXT_FEATURE);
1079 if (requires_surface) {
1080 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1081 } 726 }
1082 727
1083 bool has_khr_shader_float16_int8{}; 728#undef EXT_FEATURE
1084 bool has_khr_workgroup_memory_explicit_layout{}; 729#undef FEATURE
1085 bool has_khr_pipeline_executable_properties{};
1086 bool has_khr_image_format_list{};
1087 bool has_khr_swapchain_mutable_format{};
1088 bool has_ext_subgroup_size_control{};
1089 bool has_ext_transform_feedback{};
1090 bool has_ext_custom_border_color{};
1091 bool has_ext_extended_dynamic_state{};
1092 bool has_ext_shader_atomic_int64{};
1093 bool has_ext_provoking_vertex{};
1094 bool has_ext_vertex_input_dynamic_state{};
1095 bool has_ext_line_rasterization{};
1096 bool has_ext_primitive_topology_list_restart{};
1097 bool has_ext_depth_clip_control{};
1098 for (const std::string& extension : supported_extensions) {
1099 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
1100 bool push) {
1101 if (extension != name) {
1102 return;
1103 }
1104 if (push) {
1105 extensions.push_back(name);
1106 }
1107 if (status) {
1108 status->get() = true;
1109 }
1110 };
1111 test(nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
1112 test(nv_viewport_array2, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, true);
1113 test(nv_geometry_shader_passthrough, VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME,
1114 true);
1115 test(khr_uniform_buffer_standard_layout,
1116 VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
1117 test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true);
1118 test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true);
1119 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
1120 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
1121 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
1122 test(has_ext_primitive_topology_list_restart,
1123 VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true);
1124 test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
1125 test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
1126 true);
1127 test(ext_tooling_info, VK_EXT_TOOLING_INFO_EXTENSION_NAME, true);
1128 test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
1129 test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
1130 true);
1131 test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
1132 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
1133 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
1134 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
1135 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
1136 test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
1137 test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
1138 false);
1139 test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
1140 test(has_khr_workgroup_memory_explicit_layout,
1141 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
1142 test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
1143 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
1144 false);
1145 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
1146 test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
1147 if (Settings::values.enable_nsight_aftermath) {
1148 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
1149 true);
1150 }
1151 if (Settings::values.renderer_shader_feedback) {
1152 test(has_khr_pipeline_executable_properties,
1153 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
1154 }
1155 }
1156 VkPhysicalDeviceFeatures2 features{};
1157 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1158
1159 VkPhysicalDeviceProperties2 physical_properties{};
1160 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1161
1162 if (has_khr_shader_float16_int8) {
1163 VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features;
1164 float16_int8_features.sType =
1165 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
1166 float16_int8_features.pNext = nullptr;
1167 features.pNext = &float16_int8_features;
1168
1169 physical.GetFeatures2(features);
1170 is_float16_supported = float16_int8_features.shaderFloat16;
1171 is_int8_supported = float16_int8_features.shaderInt8;
1172 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
1173 }
1174 if (has_ext_subgroup_size_control) {
1175 VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features;
1176 subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES;
1177 subgroup_features.pNext = nullptr;
1178 features.pNext = &subgroup_features;
1179 physical.GetFeatures2(features);
1180
1181 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties;
1182 subgroup_properties.sType =
1183 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
1184 subgroup_properties.pNext = nullptr;
1185 physical_properties.pNext = &subgroup_properties;
1186 physical.GetProperties2(physical_properties);
1187 730
1188 is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize; 731 // Perform the feature test.
732 physical.GetFeatures2(features2);
733 features.features = features2.features;
1189 734
1190 if (subgroup_features.subgroupSizeControl && 735 // Some features are mandatory. Check those.
1191 subgroup_properties.minSubgroupSize <= GuestWarpSize && 736#define CHECK_FEATURE(feature, name) \
1192 subgroup_properties.maxSubgroupSize >= GuestWarpSize) { 737 if (!features.feature.name) { \
1193 extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); 738 LOG_ERROR(Render_Vulkan, "Missing required feature {}", #name); \
1194 guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages; 739 suitable = false; \
1195 ext_subgroup_size_control = true;
1196 }
1197 } else {
1198 is_warp_potentially_bigger = true;
1199 }
1200 if (has_ext_provoking_vertex) {
1201 VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex;
1202 provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
1203 provoking_vertex.pNext = nullptr;
1204 features.pNext = &provoking_vertex;
1205 physical.GetFeatures2(features);
1206
1207 if (provoking_vertex.provokingVertexLast &&
1208 provoking_vertex.transformFeedbackPreservesProvokingVertex) {
1209 extensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
1210 ext_provoking_vertex = true;
1211 }
1212 }
1213 if (has_ext_vertex_input_dynamic_state) {
1214 VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input;
1215 vertex_input.sType =
1216 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT;
1217 vertex_input.pNext = nullptr;
1218 features.pNext = &vertex_input;
1219 physical.GetFeatures2(features);
1220
1221 if (vertex_input.vertexInputDynamicState) {
1222 extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
1223 ext_vertex_input_dynamic_state = true;
1224 }
1225 }
1226 if (has_ext_shader_atomic_int64) {
1227 VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
1228 atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES;
1229 atomic_int64.pNext = nullptr;
1230 features.pNext = &atomic_int64;
1231 physical.GetFeatures2(features);
1232
1233 if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) {
1234 extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
1235 ext_shader_atomic_int64 = true;
1236 }
1237 } 740 }
1238 if (has_ext_transform_feedback) {
1239 VkPhysicalDeviceTransformFeedbackFeaturesEXT tfb_features;
1240 tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
1241 tfb_features.pNext = nullptr;
1242 features.pNext = &tfb_features;
1243 physical.GetFeatures2(features);
1244
1245 VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties;
1246 tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
1247 tfb_properties.pNext = nullptr;
1248 physical_properties.pNext = &tfb_properties;
1249 physical.GetProperties2(physical_properties);
1250 741
1251 if (tfb_features.transformFeedback && tfb_features.geometryStreams && 742#define LOG_FEATURE(feature, name) \
1252 tfb_properties.maxTransformFeedbackStreams >= 4 && 743 if (!features.feature.name) { \
1253 tfb_properties.maxTransformFeedbackBuffers && tfb_properties.transformFeedbackQueries && 744 LOG_INFO(Render_Vulkan, "Device doesn't support feature {}", #name); \
1254 tfb_properties.transformFeedbackDraw) {
1255 extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
1256 ext_transform_feedback = true;
1257 }
1258 }
1259 if (has_ext_custom_border_color) {
1260 VkPhysicalDeviceCustomBorderColorFeaturesEXT border_features;
1261 border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
1262 border_features.pNext = nullptr;
1263 features.pNext = &border_features;
1264 physical.GetFeatures2(features);
1265
1266 if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) {
1267 extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
1268 ext_custom_border_color = true;
1269 }
1270 } 745 }
1271 if (has_ext_extended_dynamic_state) { 746
1272 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state; 747 FOR_EACH_VK_RECOMMENDED_FEATURE(LOG_FEATURE);
1273 extended_dynamic_state.sType = 748 FOR_EACH_VK_MANDATORY_FEATURE(CHECK_FEATURE);
1274 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; 749
1275 extended_dynamic_state.pNext = nullptr; 750#undef LOG_FEATURE
1276 features.pNext = &extended_dynamic_state; 751#undef CHECK_FEATURE
1277 physical.GetFeatures2(features); 752
1278 753 // Generate linked list of properties.
1279 if (extended_dynamic_state.extendedDynamicState) { 754 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1280 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 755
1281 ext_extended_dynamic_state = true; 756 // Set next pointer.
1282 } 757 next = &properties2.pNext;
758
759 // Get driver info.
760 properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
761 SetNext(next, properties.driver);
762
763 // Retrieve relevant extension properties.
764 if (extensions.shader_float_controls) {
765 properties.float_controls.sType =
766 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
767 SetNext(next, properties.float_controls);
1283 } 768 }
1284 if (has_ext_line_rasterization) { 769 if (extensions.push_descriptor) {
1285 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; 770 properties.push_descriptor.sType =
1286 line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; 771 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
1287 line_raster.pNext = nullptr; 772 SetNext(next, properties.push_descriptor);
1288 features.pNext = &line_raster;
1289 physical.GetFeatures2(features);
1290 if (line_raster.rectangularLines && line_raster.smoothLines) {
1291 extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
1292 ext_line_rasterization = true;
1293 }
1294 } 773 }
1295 if (has_ext_depth_clip_control) { 774 if (extensions.subgroup_size_control) {
1296 VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; 775 properties.subgroup_size_control.sType =
1297 depth_clip_control_features.sType = 776 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
1298 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT; 777 SetNext(next, properties.subgroup_size_control);
1299 depth_clip_control_features.pNext = nullptr;
1300 features.pNext = &depth_clip_control_features;
1301 physical.GetFeatures2(features);
1302
1303 if (depth_clip_control_features.depthClipControl) {
1304 extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
1305 ext_depth_clip_control = true;
1306 }
1307 } 778 }
1308 if (has_khr_workgroup_memory_explicit_layout) { 779 if (extensions.transform_feedback) {
1309 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; 780 properties.transform_feedback.sType =
1310 layout.sType = 781 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
1311 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR; 782 SetNext(next, properties.transform_feedback);
1312 layout.pNext = nullptr;
1313 features.pNext = &layout;
1314 physical.GetFeatures2(features);
1315
1316 if (layout.workgroupMemoryExplicitLayout &&
1317 layout.workgroupMemoryExplicitLayout8BitAccess &&
1318 layout.workgroupMemoryExplicitLayout16BitAccess &&
1319 layout.workgroupMemoryExplicitLayoutScalarBlockLayout) {
1320 extensions.push_back(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
1321 khr_workgroup_memory_explicit_layout = true;
1322 }
1323 } 783 }
1324 if (has_khr_pipeline_executable_properties) { 784
1325 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; 785 // Perform the property fetch.
1326 executable_properties.sType = 786 physical.GetProperties2(properties2);
1327 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; 787 properties.properties = properties2.properties;
1328 executable_properties.pNext = nullptr; 788
1329 features.pNext = &executable_properties; 789 // Unload extensions if feature support is insufficient.
1330 physical.GetFeatures2(features); 790 RemoveUnsuitableExtensions();
1331 791
1332 if (executable_properties.pipelineExecutableInfo) { 792 // Check limits.
1333 extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 793 struct Limit {
1334 khr_pipeline_executable_properties = true; 794 u32 minimum;
795 u32 value;
796 const char* name;
797 };
798
799 const VkPhysicalDeviceLimits& limits{properties.properties.limits};
800 const std::array limits_report{
801 Limit{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
802 Limit{16, limits.maxViewports, "maxViewports"},
803 Limit{8, limits.maxColorAttachments, "maxColorAttachments"},
804 Limit{8, limits.maxClipDistances, "maxClipDistances"},
805 };
806
807 for (const auto& [min, value, name] : limits_report) {
808 if (value < min) {
809 LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", name, min, value);
810 suitable = false;
1335 } 811 }
1336 } 812 }
1337 if (has_ext_primitive_topology_list_restart) {
1338 VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{};
1339 primitive_topology_list_restart.sType =
1340 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
1341 primitive_topology_list_restart.pNext = nullptr;
1342 features.pNext = &primitive_topology_list_restart;
1343 physical.GetFeatures2(features);
1344
1345 is_topology_list_restart_supported =
1346 primitive_topology_list_restart.primitiveTopologyListRestart;
1347 is_patch_list_restart_supported =
1348 primitive_topology_list_restart.primitiveTopologyPatchListRestart;
1349 }
1350 if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
1351 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
1352 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
1353 khr_swapchain_mutable_format = true;
1354 }
1355 if (khr_push_descriptor) {
1356 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
1357 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
1358 push_descriptor.pNext = nullptr;
1359 813
1360 physical_properties.pNext = &push_descriptor; 814 // Return whether we were suitable.
1361 physical.GetProperties2(physical_properties); 815 return suitable;
816}
1362 817
1363 max_push_descriptors = push_descriptor.maxPushDescriptors; 818void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
819 if (loaded_extensions.contains(extension_name) && !is_suitable) {
820 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
821 loaded_extensions.erase(extension_name);
1364 } 822 }
1365 return extensions; 823}
824
825void Device::RemoveUnsuitableExtensions() {
826 // VK_EXT_custom_border_color
827 extensions.custom_border_color = features.custom_border_color.customBorderColors &&
828 features.custom_border_color.customBorderColorWithoutFormat;
829 RemoveExtensionIfUnsuitable(extensions.custom_border_color,
830 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
831
832 // VK_EXT_depth_clip_control
833 extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
834 RemoveExtensionIfUnsuitable(extensions.depth_clip_control,
835 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
836
837 // VK_EXT_extended_dynamic_state
838 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
839 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state,
840 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
841
842 // VK_EXT_extended_dynamic_state2
843 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
844 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2,
845 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
846
847 // VK_EXT_extended_dynamic_state3
848 dynamic_state3_blending =
849 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable &&
850 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation &&
851 features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask;
852 dynamic_state3_enables =
853 features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable &&
854 features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable;
855
856 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
857 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
858 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
859 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3,
860 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
861
862 // VK_EXT_provoking_vertex
863 extensions.provoking_vertex =
864 features.provoking_vertex.provokingVertexLast &&
865 features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
866 RemoveExtensionIfUnsuitable(extensions.provoking_vertex,
867 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
868
869 // VK_KHR_shader_atomic_int64
870 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
871 features.shader_atomic_int64.shaderSharedInt64Atomics;
872 RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64,
873 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
874
875 // VK_EXT_shader_demote_to_helper_invocation
876 extensions.shader_demote_to_helper_invocation =
877 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
878 RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation,
879 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
880
881 // VK_EXT_subgroup_size_control
882 extensions.subgroup_size_control =
883 features.subgroup_size_control.subgroupSizeControl &&
884 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
885 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
886 RemoveExtensionIfUnsuitable(extensions.subgroup_size_control,
887 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
888
889 // VK_EXT_transform_feedback
890 extensions.transform_feedback =
891 features.transform_feedback.transformFeedback &&
892 features.transform_feedback.geometryStreams &&
893 properties.transform_feedback.maxTransformFeedbackStreams >= 4 &&
894 properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
895 properties.transform_feedback.transformFeedbackQueries &&
896 properties.transform_feedback.transformFeedbackDraw;
897 RemoveExtensionIfUnsuitable(extensions.transform_feedback,
898 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
899
900 // VK_EXT_vertex_input_dynamic_state
901 extensions.vertex_input_dynamic_state =
902 features.vertex_input_dynamic_state.vertexInputDynamicState;
903 RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state,
904 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
905
906 // VK_KHR_pipeline_executable_properties
907 if (Settings::values.renderer_shader_feedback.GetValue()) {
908 extensions.pipeline_executable_properties =
909 features.pipeline_executable_properties.pipelineExecutableInfo;
910 RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties,
911 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
912 } else {
913 extensions.pipeline_executable_properties = false;
914 loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
915 }
916
917 // VK_KHR_workgroup_memory_explicit_layout
918 extensions.workgroup_memory_explicit_layout =
919 features.features.shaderInt16 &&
920 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
921 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
922 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
923 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
924 RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout,
925 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
1366} 926}
1367 927
1368void Device::SetupFamilies(VkSurfaceKHR surface) { 928void Device::SetupFamilies(VkSurfaceKHR surface) {
@@ -1392,55 +952,12 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
1392 LOG_ERROR(Render_Vulkan, "Device lacks a present queue"); 952 LOG_ERROR(Render_Vulkan, "Device lacks a present queue");
1393 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 953 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
1394 } 954 }
1395 graphics_family = *graphics; 955 if (graphics) {
1396 present_family = *present; 956 graphics_family = *graphics;
1397} 957 }
1398 958 if (present) {
1399void Device::SetupFeatures() { 959 present_family = *present;
1400 const VkPhysicalDeviceFeatures features{physical.GetFeatures()}; 960 }
1401 is_depth_bounds_supported = features.depthBounds;
1402 is_formatless_image_load_supported = features.shaderStorageImageReadWithoutFormat;
1403 is_shader_float64_supported = features.shaderFloat64;
1404 is_shader_int64_supported = features.shaderInt64;
1405 is_shader_int16_supported = features.shaderInt16;
1406 is_shader_storage_image_multisample = features.shaderStorageImageMultisample;
1407 is_blit_depth_stencil_supported = TestDepthStencilBlits();
1408 is_optimal_astc_supported = IsOptimalAstcSupported(features);
1409
1410 const VkPhysicalDeviceLimits& limits{properties.limits};
1411 max_vertex_input_attributes = limits.maxVertexInputAttributes;
1412 max_vertex_input_bindings = limits.maxVertexInputBindings;
1413}
1414
1415void Device::SetupProperties() {
1416 float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
1417
1418 VkPhysicalDeviceProperties2KHR properties2{};
1419 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1420 properties2.pNext = &float_controls;
1421
1422 physical.GetProperties2(properties2);
1423}
1424
1425void Device::CollectTelemetryParameters() {
1426 VkPhysicalDeviceDriverProperties driver{
1427 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
1428 .pNext = nullptr,
1429 .driverID = {},
1430 .driverName = {},
1431 .driverInfo = {},
1432 .conformanceVersion = {},
1433 };
1434
1435 VkPhysicalDeviceProperties2 device_properties{
1436 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
1437 .pNext = &driver,
1438 .properties = {},
1439 };
1440 physical.GetProperties2(device_properties);
1441
1442 driver_id = driver.driverID;
1443 vendor_name = driver.driverName;
1444} 961}
1445 962
1446u64 Device::GetDeviceMemoryUsage() const { 963u64 Device::GetDeviceMemoryUsage() const {
@@ -1458,7 +975,8 @@ u64 Device::GetDeviceMemoryUsage() const {
1458void Device::CollectPhysicalMemoryInfo() { 975void Device::CollectPhysicalMemoryInfo() {
1459 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; 976 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
1460 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; 977 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1461 const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr); 978 const auto mem_info =
979 physical.GetMemoryProperties(extensions.memory_budget ? &budget : nullptr);
1462 const auto& mem_properties = mem_info.memoryProperties; 980 const auto& mem_properties = mem_info.memoryProperties;
1463 const size_t num_properties = mem_properties.memoryHeapCount; 981 const size_t num_properties = mem_properties.memoryHeapCount;
1464 device_access_memory = 0; 982 device_access_memory = 0;
@@ -1474,7 +992,7 @@ void Device::CollectPhysicalMemoryInfo() {
1474 if (is_heap_local) { 992 if (is_heap_local) {
1475 local_memory += mem_properties.memoryHeaps[element].size; 993 local_memory += mem_properties.memoryHeaps[element].size;
1476 } 994 }
1477 if (ext_memory_budget) { 995 if (extensions.memory_budget) {
1478 device_initial_usage += budget.heapUsage[element]; 996 device_initial_usage += budget.heapUsage[element];
1479 device_access_memory += budget.heapBudget[element]; 997 device_access_memory += budget.heapBudget[element];
1480 continue; 998 continue;
@@ -1490,7 +1008,7 @@ void Device::CollectPhysicalMemoryInfo() {
1490} 1008}
1491 1009
1492void Device::CollectToolingInfo() { 1010void Device::CollectToolingInfo() {
1493 if (!ext_tooling_info) { 1011 if (!extensions.tooling_info) {
1494 return; 1012 return;
1495 } 1013 }
1496 auto tools{physical.GetPhysicalDeviceToolProperties()}; 1014 auto tools{physical.GetPhysicalDeviceToolProperties()};
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 6a26c4e6e..0662a2d9f 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <set>
6#include <span> 7#include <span>
7#include <string> 8#include <string>
8#include <unordered_map> 9#include <unordered_map>
@@ -11,6 +12,156 @@
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
13 14
15// Define all features which may be used by the implementation here.
16// Vulkan version in the macro describes the minimum version required for feature availability.
17// If the Vulkan version is lower than the required version, the named extension is required.
18#define FOR_EACH_VK_FEATURE_1_1(FEATURE) \
19 FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
20 FEATURE(KHR, 16BitStorage, 16BIT_STORAGE, bit16_storage) \
21 FEATURE(KHR, ShaderAtomicInt64, SHADER_ATOMIC_INT64, shader_atomic_int64) \
22 FEATURE(KHR, ShaderDrawParameters, SHADER_DRAW_PARAMETERS, shader_draw_parameters) \
23 FEATURE(KHR, ShaderFloat16Int8, SHADER_FLOAT16_INT8, shader_float16_int8) \
24 FEATURE(KHR, UniformBufferStandardLayout, UNIFORM_BUFFER_STANDARD_LAYOUT, \
25 uniform_buffer_standard_layout) \
26 FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer)
27
28#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \
29 FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \
30 FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \
31 FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
32
33#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
34 FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
35 shader_demote_to_helper_invocation)
36
37// Define all features which may be used by the implementation and require an extension here.
38#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \
39 FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \
40 FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
41 FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
42 FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
43 FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
44 FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
45 FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
46 FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \
47 primitive_topology_list_restart) \
48 FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
49 FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
50 FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
51 FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
52 FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
53 pipeline_executable_properties) \
54 FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
55 workgroup_memory_explicit_layout)
56
57// Define miscellaneous extensions which may be used by the implementation here.
58#define FOR_EACH_VK_EXTENSION(EXTENSION) \
59 EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
60 EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
61 EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
62 EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \
63 EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \
64 EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
65 EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
66 EXTENSION(EXT, TOOLING_INFO, tooling_info) \
67 EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
68 EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
69 EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
70 EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \
71 EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
72 EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \
73 EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \
74 EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \
75 EXTENSION(KHR, SWAPCHAIN, swapchain) \
76 EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \
77 EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \
78 EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \
79 EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
80 EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle)
81
82#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \
83 EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32)
84
85// Define extensions which must be supported.
86#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
87 EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
88 EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
89 EXTENSION_NAME(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) \
90 EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \
91 EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)
92
93#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \
94 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
95
96#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \
97 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
98
99// Define extensions where the absence of the extension may result in a degraded experience.
100#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \
101 EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \
102 EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \
103 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
104 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
105 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
106 EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
107 EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
108 EXTENSION_NAME(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME) \
109 EXTENSION_NAME(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME) \
110 EXTENSION_NAME(VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME)
111
112// Define features which must be supported.
113#define FOR_EACH_VK_MANDATORY_FEATURE(FEATURE_NAME) \
114 FEATURE_NAME(bit16_storage, storageBuffer16BitAccess) \
115 FEATURE_NAME(bit16_storage, uniformAndStorageBuffer16BitAccess) \
116 FEATURE_NAME(bit8_storage, storageBuffer8BitAccess) \
117 FEATURE_NAME(bit8_storage, uniformAndStorageBuffer8BitAccess) \
118 FEATURE_NAME(features, depthBiasClamp) \
119 FEATURE_NAME(features, depthClamp) \
120 FEATURE_NAME(features, drawIndirectFirstInstance) \
121 FEATURE_NAME(features, dualSrcBlend) \
122 FEATURE_NAME(features, fillModeNonSolid) \
123 FEATURE_NAME(features, fragmentStoresAndAtomics) \
124 FEATURE_NAME(features, geometryShader) \
125 FEATURE_NAME(features, imageCubeArray) \
126 FEATURE_NAME(features, independentBlend) \
127 FEATURE_NAME(features, largePoints) \
128 FEATURE_NAME(features, logicOp) \
129 FEATURE_NAME(features, multiDrawIndirect) \
130 FEATURE_NAME(features, multiViewport) \
131 FEATURE_NAME(features, occlusionQueryPrecise) \
132 FEATURE_NAME(features, robustBufferAccess) \
133 FEATURE_NAME(features, samplerAnisotropy) \
134 FEATURE_NAME(features, sampleRateShading) \
135 FEATURE_NAME(features, shaderClipDistance) \
136 FEATURE_NAME(features, shaderCullDistance) \
137 FEATURE_NAME(features, shaderImageGatherExtended) \
138 FEATURE_NAME(features, shaderStorageImageWriteWithoutFormat) \
139 FEATURE_NAME(features, tessellationShader) \
140 FEATURE_NAME(features, vertexPipelineStoresAndAtomics) \
141 FEATURE_NAME(features, wideLines) \
142 FEATURE_NAME(host_query_reset, hostQueryReset) \
143 FEATURE_NAME(robustness2, nullDescriptor) \
144 FEATURE_NAME(robustness2, robustBufferAccess2) \
145 FEATURE_NAME(robustness2, robustImageAccess2) \
146 FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \
147 FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \
148 FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
149 FEATURE_NAME(variable_pointer, variablePointers) \
150 FEATURE_NAME(variable_pointer, variablePointersStorageBuffer)
151
152// Define features where the absence of the feature may result in a degraded experience.
153#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \
154 FEATURE_NAME(custom_border_color, customBorderColors) \
155 FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
156 FEATURE_NAME(index_type_uint8, indexTypeUint8) \
157 FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
158 FEATURE_NAME(provoking_vertex, provokingVertexLast) \
159 FEATURE_NAME(shader_float16_int8, shaderFloat16) \
160 FEATURE_NAME(shader_float16_int8, shaderInt8) \
161 FEATURE_NAME(transform_feedback, transformFeedback) \
162 FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
163 FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
164
14namespace Vulkan { 165namespace Vulkan {
15 166
16class NsightAftermathTracker; 167class NsightAftermathTracker;
@@ -88,67 +239,69 @@ public:
88 239
89 /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers. 240 /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers.
90 u32 ApiVersion() const { 241 u32 ApiVersion() const {
91 return properties.apiVersion; 242 return properties.properties.apiVersion;
92 } 243 }
93 244
94 /// Returns the current driver version provided in Vulkan-formatted version numbers. 245 /// Returns the current driver version provided in Vulkan-formatted version numbers.
95 u32 GetDriverVersion() const { 246 u32 GetDriverVersion() const {
96 return properties.driverVersion; 247 return properties.properties.driverVersion;
97 } 248 }
98 249
99 /// Returns the device name. 250 /// Returns the device name.
100 std::string_view GetModelName() const { 251 std::string_view GetModelName() const {
101 return properties.deviceName; 252 return properties.properties.deviceName;
102 } 253 }
103 254
104 /// Returns the driver ID. 255 /// Returns the driver ID.
105 VkDriverIdKHR GetDriverID() const { 256 VkDriverIdKHR GetDriverID() const {
106 return driver_id; 257 return properties.driver.driverID;
107 } 258 }
108 259
260 bool ShouldBoostClocks() const;
261
109 /// Returns uniform buffer alignment requeriment. 262 /// Returns uniform buffer alignment requeriment.
110 VkDeviceSize GetUniformBufferAlignment() const { 263 VkDeviceSize GetUniformBufferAlignment() const {
111 return properties.limits.minUniformBufferOffsetAlignment; 264 return properties.properties.limits.minUniformBufferOffsetAlignment;
112 } 265 }
113 266
114 /// Returns storage alignment requeriment. 267 /// Returns storage alignment requeriment.
115 VkDeviceSize GetStorageBufferAlignment() const { 268 VkDeviceSize GetStorageBufferAlignment() const {
116 return properties.limits.minStorageBufferOffsetAlignment; 269 return properties.properties.limits.minStorageBufferOffsetAlignment;
117 } 270 }
118 271
119 /// Returns the maximum range for storage buffers. 272 /// Returns the maximum range for storage buffers.
120 VkDeviceSize GetMaxStorageBufferRange() const { 273 VkDeviceSize GetMaxStorageBufferRange() const {
121 return properties.limits.maxStorageBufferRange; 274 return properties.properties.limits.maxStorageBufferRange;
122 } 275 }
123 276
124 /// Returns the maximum size for push constants. 277 /// Returns the maximum size for push constants.
125 VkDeviceSize GetMaxPushConstantsSize() const { 278 VkDeviceSize GetMaxPushConstantsSize() const {
126 return properties.limits.maxPushConstantsSize; 279 return properties.properties.limits.maxPushConstantsSize;
127 } 280 }
128 281
129 /// Returns the maximum size for shared memory. 282 /// Returns the maximum size for shared memory.
130 u32 GetMaxComputeSharedMemorySize() const { 283 u32 GetMaxComputeSharedMemorySize() const {
131 return properties.limits.maxComputeSharedMemorySize; 284 return properties.properties.limits.maxComputeSharedMemorySize;
132 } 285 }
133 286
134 /// Returns float control properties of the device. 287 /// Returns float control properties of the device.
135 const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const { 288 const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const {
136 return float_controls; 289 return properties.float_controls;
137 } 290 }
138 291
139 /// Returns true if ASTC is natively supported. 292 /// Returns true if ASTC is natively supported.
140 bool IsOptimalAstcSupported() const { 293 bool IsOptimalAstcSupported() const {
141 return is_optimal_astc_supported; 294 return features.features.textureCompressionASTC_LDR;
142 } 295 }
143 296
144 /// Returns true if the device supports float16 natively. 297 /// Returns true if the device supports float16 natively.
145 bool IsFloat16Supported() const { 298 bool IsFloat16Supported() const {
146 return is_float16_supported; 299 return features.shader_float16_int8.shaderFloat16;
147 } 300 }
148 301
149 /// Returns true if the device supports int8 natively. 302 /// Returns true if the device supports int8 natively.
150 bool IsInt8Supported() const { 303 bool IsInt8Supported() const {
151 return is_int8_supported; 304 return features.shader_float16_int8.shaderInt8;
152 } 305 }
153 306
154 /// Returns true if the device warp size can potentially be bigger than guest's warp size. 307 /// Returns true if the device warp size can potentially be bigger than guest's warp size.
@@ -158,32 +311,32 @@ public:
158 311
159 /// Returns true if the device can be forced to use the guest warp size. 312 /// Returns true if the device can be forced to use the guest warp size.
160 bool IsGuestWarpSizeSupported(VkShaderStageFlagBits stage) const { 313 bool IsGuestWarpSizeSupported(VkShaderStageFlagBits stage) const {
161 return guest_warp_stages & stage; 314 return properties.subgroup_size_control.requiredSubgroupSizeStages & stage;
162 } 315 }
163 316
164 /// Returns the maximum number of push descriptors. 317 /// Returns the maximum number of push descriptors.
165 u32 MaxPushDescriptors() const { 318 u32 MaxPushDescriptors() const {
166 return max_push_descriptors; 319 return properties.push_descriptor.maxPushDescriptors;
167 } 320 }
168 321
169 /// Returns true if formatless image load is supported. 322 /// Returns true if formatless image load is supported.
170 bool IsFormatlessImageLoadSupported() const { 323 bool IsFormatlessImageLoadSupported() const {
171 return is_formatless_image_load_supported; 324 return features.features.shaderStorageImageReadWithoutFormat;
172 } 325 }
173 326
174 /// Returns true if shader int64 is supported. 327 /// Returns true if shader int64 is supported.
175 bool IsShaderInt64Supported() const { 328 bool IsShaderInt64Supported() const {
176 return is_shader_int64_supported; 329 return features.features.shaderInt64;
177 } 330 }
178 331
179 /// Returns true if shader int16 is supported. 332 /// Returns true if shader int16 is supported.
180 bool IsShaderInt16Supported() const { 333 bool IsShaderInt16Supported() const {
181 return is_shader_int16_supported; 334 return features.features.shaderInt16;
182 } 335 }
183 336
184 // Returns true if depth bounds is supported. 337 // Returns true if depth bounds is supported.
185 bool IsDepthBoundsSupported() const { 338 bool IsDepthBoundsSupported() const {
186 return is_depth_bounds_supported; 339 return features.features.depthBounds;
187 } 340 }
188 341
189 /// Returns true when blitting from and to depth stencil images is supported. 342 /// Returns true when blitting from and to depth stencil images is supported.
@@ -193,127 +346,151 @@ public:
193 346
194 /// Returns true if the device supports VK_NV_viewport_swizzle. 347 /// Returns true if the device supports VK_NV_viewport_swizzle.
195 bool IsNvViewportSwizzleSupported() const { 348 bool IsNvViewportSwizzleSupported() const {
196 return nv_viewport_swizzle; 349 return extensions.viewport_swizzle;
197 } 350 }
198 351
199 /// Returns true if the device supports VK_NV_viewport_array2. 352 /// Returns true if the device supports VK_NV_viewport_array2.
200 bool IsNvViewportArray2Supported() const { 353 bool IsNvViewportArray2Supported() const {
201 return nv_viewport_array2; 354 return extensions.viewport_array2;
202 } 355 }
203 356
204 /// Returns true if the device supports VK_NV_geometry_shader_passthrough. 357 /// Returns true if the device supports VK_NV_geometry_shader_passthrough.
205 bool IsNvGeometryShaderPassthroughSupported() const { 358 bool IsNvGeometryShaderPassthroughSupported() const {
206 return nv_geometry_shader_passthrough; 359 return extensions.geometry_shader_passthrough;
207 } 360 }
208 361
209 /// Returns true if the device supports VK_KHR_uniform_buffer_standard_layout. 362 /// Returns true if the device supports VK_KHR_uniform_buffer_standard_layout.
210 bool IsKhrUniformBufferStandardLayoutSupported() const { 363 bool IsKhrUniformBufferStandardLayoutSupported() const {
211 return khr_uniform_buffer_standard_layout; 364 return extensions.uniform_buffer_standard_layout;
212 } 365 }
213 366
214 /// Returns true if the device supports VK_KHR_push_descriptor. 367 /// Returns true if the device supports VK_KHR_push_descriptor.
215 bool IsKhrPushDescriptorSupported() const { 368 bool IsKhrPushDescriptorSupported() const {
216 return khr_push_descriptor; 369 return extensions.push_descriptor;
217 } 370 }
218 371
219 /// Returns true if VK_KHR_pipeline_executable_properties is enabled. 372 /// Returns true if VK_KHR_pipeline_executable_properties is enabled.
220 bool IsKhrPipelineExecutablePropertiesEnabled() const { 373 bool IsKhrPipelineExecutablePropertiesEnabled() const {
221 return khr_pipeline_executable_properties; 374 return extensions.pipeline_executable_properties;
222 } 375 }
223 376
224 /// Returns true if VK_KHR_swapchain_mutable_format is enabled. 377 /// Returns true if VK_KHR_swapchain_mutable_format is enabled.
225 bool IsKhrSwapchainMutableFormatEnabled() const { 378 bool IsKhrSwapchainMutableFormatEnabled() const {
226 return khr_swapchain_mutable_format; 379 return extensions.swapchain_mutable_format;
227 } 380 }
228 381
229 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 382 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
230 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 383 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
231 return khr_workgroup_memory_explicit_layout; 384 return extensions.workgroup_memory_explicit_layout;
232 } 385 }
233 386
234 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. 387 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
235 bool IsTopologyListPrimitiveRestartSupported() const { 388 bool IsTopologyListPrimitiveRestartSupported() const {
236 return is_topology_list_restart_supported; 389 return features.primitive_topology_list_restart.primitiveTopologyListRestart;
237 } 390 }
238 391
239 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. 392 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
240 bool IsPatchListPrimitiveRestartSupported() const { 393 bool IsPatchListPrimitiveRestartSupported() const {
241 return is_patch_list_restart_supported; 394 return features.primitive_topology_list_restart.primitiveTopologyPatchListRestart;
242 } 395 }
243 396
244 /// Returns true if the device supports VK_EXT_index_type_uint8. 397 /// Returns true if the device supports VK_EXT_index_type_uint8.
245 bool IsExtIndexTypeUint8Supported() const { 398 bool IsExtIndexTypeUint8Supported() const {
246 return ext_index_type_uint8; 399 return extensions.index_type_uint8;
247 } 400 }
248 401
249 /// Returns true if the device supports VK_EXT_sampler_filter_minmax. 402 /// Returns true if the device supports VK_EXT_sampler_filter_minmax.
250 bool IsExtSamplerFilterMinmaxSupported() const { 403 bool IsExtSamplerFilterMinmaxSupported() const {
251 return ext_sampler_filter_minmax; 404 return extensions.sampler_filter_minmax;
252 } 405 }
253 406
254 /// Returns true if the device supports VK_EXT_depth_range_unrestricted. 407 /// Returns true if the device supports VK_EXT_depth_range_unrestricted.
255 bool IsExtDepthRangeUnrestrictedSupported() const { 408 bool IsExtDepthRangeUnrestrictedSupported() const {
256 return ext_depth_range_unrestricted; 409 return extensions.depth_range_unrestricted;
257 } 410 }
258 411
259 /// Returns true if the device supports VK_EXT_depth_clip_control. 412 /// Returns true if the device supports VK_EXT_depth_clip_control.
260 bool IsExtDepthClipControlSupported() const { 413 bool IsExtDepthClipControlSupported() const {
261 return ext_depth_clip_control; 414 return extensions.depth_clip_control;
262 } 415 }
263 416
264 /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. 417 /// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
265 bool IsExtShaderViewportIndexLayerSupported() const { 418 bool IsExtShaderViewportIndexLayerSupported() const {
266 return ext_shader_viewport_index_layer; 419 return extensions.shader_viewport_index_layer;
267 } 420 }
268 421
269 /// Returns true if the device supports VK_EXT_subgroup_size_control. 422 /// Returns true if the device supports VK_EXT_subgroup_size_control.
270 bool IsExtSubgroupSizeControlSupported() const { 423 bool IsExtSubgroupSizeControlSupported() const {
271 return ext_subgroup_size_control; 424 return extensions.subgroup_size_control;
272 } 425 }
273 426
274 /// Returns true if the device supports VK_EXT_transform_feedback. 427 /// Returns true if the device supports VK_EXT_transform_feedback.
275 bool IsExtTransformFeedbackSupported() const { 428 bool IsExtTransformFeedbackSupported() const {
276 return ext_transform_feedback; 429 return extensions.transform_feedback;
277 } 430 }
278 431
279 /// Returns true if the device supports VK_EXT_custom_border_color. 432 /// Returns true if the device supports VK_EXT_custom_border_color.
280 bool IsExtCustomBorderColorSupported() const { 433 bool IsExtCustomBorderColorSupported() const {
281 return ext_custom_border_color; 434 return extensions.custom_border_color;
282 } 435 }
283 436
284 /// Returns true if the device supports VK_EXT_extended_dynamic_state. 437 /// Returns true if the device supports VK_EXT_extended_dynamic_state.
285 bool IsExtExtendedDynamicStateSupported() const { 438 bool IsExtExtendedDynamicStateSupported() const {
286 return ext_extended_dynamic_state; 439 return extensions.extended_dynamic_state;
440 }
441
442 /// Returns true if the device supports VK_EXT_extended_dynamic_state2.
443 bool IsExtExtendedDynamicState2Supported() const {
444 return extensions.extended_dynamic_state2;
445 }
446
447 bool IsExtExtendedDynamicState2ExtrasSupported() const {
448 return features.extended_dynamic_state2.extendedDynamicState2LogicOp;
449 }
450
451 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
452 bool IsExtExtendedDynamicState3Supported() const {
453 return extensions.extended_dynamic_state3;
454 }
455
456 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
457 bool IsExtExtendedDynamicState3BlendingSupported() const {
458 return dynamic_state3_blending;
459 }
460
461 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
462 bool IsExtExtendedDynamicState3EnablesSupported() const {
463 return dynamic_state3_enables;
287 } 464 }
288 465
289 /// Returns true if the device supports VK_EXT_line_rasterization. 466 /// Returns true if the device supports VK_EXT_line_rasterization.
290 bool IsExtLineRasterizationSupported() const { 467 bool IsExtLineRasterizationSupported() const {
291 return ext_line_rasterization; 468 return extensions.line_rasterization;
292 } 469 }
293 470
294 /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state. 471 /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
295 bool IsExtVertexInputDynamicStateSupported() const { 472 bool IsExtVertexInputDynamicStateSupported() const {
296 return ext_vertex_input_dynamic_state; 473 return extensions.vertex_input_dynamic_state;
297 } 474 }
298 475
299 /// Returns true if the device supports VK_EXT_shader_stencil_export. 476 /// Returns true if the device supports VK_EXT_shader_stencil_export.
300 bool IsExtShaderStencilExportSupported() const { 477 bool IsExtShaderStencilExportSupported() const {
301 return ext_shader_stencil_export; 478 return extensions.shader_stencil_export;
302 } 479 }
303 480
304 /// Returns true if the device supports VK_EXT_conservative_rasterization. 481 /// Returns true if the device supports VK_EXT_conservative_rasterization.
305 bool IsExtConservativeRasterizationSupported() const { 482 bool IsExtConservativeRasterizationSupported() const {
306 return ext_conservative_rasterization; 483 return extensions.conservative_rasterization;
307 } 484 }
308 485
309 /// Returns true if the device supports VK_EXT_provoking_vertex. 486 /// Returns true if the device supports VK_EXT_provoking_vertex.
310 bool IsExtProvokingVertexSupported() const { 487 bool IsExtProvokingVertexSupported() const {
311 return ext_provoking_vertex; 488 return extensions.provoking_vertex;
312 } 489 }
313 490
314 /// Returns true if the device supports VK_KHR_shader_atomic_int64. 491 /// Returns true if the device supports VK_KHR_shader_atomic_int64.
315 bool IsExtShaderAtomicInt64Supported() const { 492 bool IsExtShaderAtomicInt64Supported() const {
316 return ext_shader_atomic_int64; 493 return extensions.shader_atomic_int64;
317 } 494 }
318 495
319 /// Returns the minimum supported version of SPIR-V. 496 /// Returns the minimum supported version of SPIR-V.
@@ -321,7 +498,7 @@ public:
321 if (instance_version >= VK_API_VERSION_1_3) { 498 if (instance_version >= VK_API_VERSION_1_3) {
322 return 0x00010600U; 499 return 0x00010600U;
323 } 500 }
324 if (khr_spirv_1_4) { 501 if (extensions.spirv_1_4) {
325 return 0x00010400U; 502 return 0x00010400U;
326 } 503 }
327 return 0x00010000U; 504 return 0x00010000U;
@@ -339,11 +516,11 @@ public:
339 516
340 /// Returns the vendor name reported from Vulkan. 517 /// Returns the vendor name reported from Vulkan.
341 std::string_view GetVendorName() const { 518 std::string_view GetVendorName() const {
342 return vendor_name; 519 return properties.driver.driverName;
343 } 520 }
344 521
345 /// Returns the list of available extensions. 522 /// Returns the list of available extensions.
346 const std::vector<std::string>& GetAvailableExtensions() const { 523 const std::set<std::string, std::less<>>& GetAvailableExtensions() const {
347 return supported_extensions; 524 return supported_extensions;
348 } 525 }
349 526
@@ -352,7 +529,7 @@ public:
352 } 529 }
353 530
354 bool CanReportMemoryUsage() const { 531 bool CanReportMemoryUsage() const {
355 return ext_memory_budget; 532 return extensions.memory_budget;
356 } 533 }
357 534
358 u64 GetDeviceMemoryUsage() const; 535 u64 GetDeviceMemoryUsage() const;
@@ -373,33 +550,30 @@ public:
373 return must_emulate_bgr565; 550 return must_emulate_bgr565;
374 } 551 }
375 552
553 bool HasNullDescriptor() const {
554 return features.robustness2.nullDescriptor;
555 }
556
376 u32 GetMaxVertexInputAttributes() const { 557 u32 GetMaxVertexInputAttributes() const {
377 return max_vertex_input_attributes; 558 return properties.properties.limits.maxVertexInputAttributes;
378 } 559 }
379 560
380 u32 GetMaxVertexInputBindings() const { 561 u32 GetMaxVertexInputBindings() const {
381 return max_vertex_input_bindings; 562 return properties.properties.limits.maxVertexInputBindings;
382 } 563 }
383 564
384private: 565private:
385 /// Checks if the physical device is suitable. 566 /// Checks if the physical device is suitable and configures the object state
386 void CheckSuitability(bool requires_swapchain) const; 567 /// with all necessary info about its properties.
568 bool GetSuitability(bool requires_swapchain);
387 569
388 /// Loads extensions into a vector and stores available ones in this object. 570 // Remove extensions which have incomplete feature support.
389 std::vector<const char*> LoadExtensions(bool requires_surface); 571 void RemoveUnsuitableExtensions();
572 void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
390 573
391 /// Sets up queue families. 574 /// Sets up queue families.
392 void SetupFamilies(VkSurfaceKHR surface); 575 void SetupFamilies(VkSurfaceKHR surface);
393 576
394 /// Sets up device features.
395 void SetupFeatures();
396
397 /// Sets up device properties.
398 void SetupProperties();
399
400 /// Collects telemetry information from the device.
401 void CollectTelemetryParameters();
402
403 /// Collects information about attached tools. 577 /// Collects information about attached tools.
404 void CollectToolingInfo(); 578 void CollectToolingInfo();
405 579
@@ -410,84 +584,93 @@ private:
410 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; 584 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
411 585
412 /// Returns true if ASTC textures are natively supported. 586 /// Returns true if ASTC textures are natively supported.
413 bool IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const; 587 bool ComputeIsOptimalAstcSupported() const;
414 588
415 /// Returns true if the device natively supports blitting depth stencil images. 589 /// Returns true if the device natively supports blitting depth stencil images.
416 bool TestDepthStencilBlits() const; 590 bool TestDepthStencilBlits() const;
417 591
418 VkInstance instance; ///< Vulkan instance. 592private:
419 vk::DeviceDispatch dld; ///< Device function pointers. 593 VkInstance instance; ///< Vulkan instance.
420 vk::PhysicalDevice physical; ///< Physical device. 594 vk::DeviceDispatch dld; ///< Device function pointers.
421 VkPhysicalDeviceProperties properties; ///< Device properties. 595 vk::PhysicalDevice physical; ///< Physical device.
422 VkPhysicalDeviceFloatControlsPropertiesKHR float_controls{}; ///< Float control properties. 596 vk::Device logical; ///< Logical device.
423 vk::Device logical; ///< Logical device. 597 vk::Queue graphics_queue; ///< Main graphics queue.
424 vk::Queue graphics_queue; ///< Main graphics queue. 598 vk::Queue present_queue; ///< Main present queue.
425 vk::Queue present_queue; ///< Main present queue. 599 u32 instance_version{}; ///< Vulkan instance version.
426 u32 instance_version{}; ///< Vulkan onstance version. 600 u32 graphics_family{}; ///< Main graphics queue family index.
427 u32 graphics_family{}; ///< Main graphics queue family index. 601 u32 present_family{}; ///< Main present queue family index.
428 u32 present_family{}; ///< Main present queue family index. 602
429 VkDriverIdKHR driver_id{}; ///< Driver ID. 603 struct Extensions {
430 VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. 604#define EXTENSION(prefix, macro_name, var_name) bool var_name{};
431 u64 device_access_memory{}; ///< Total size of device local memory in bytes. 605#define FEATURE(prefix, struct_name, macro_name, var_name) bool var_name{};
432 u32 max_push_descriptors{}; ///< Maximum number of push descriptors 606
433 u32 sets_per_pool{}; ///< Sets per Description Pool 607 FOR_EACH_VK_FEATURE_1_1(FEATURE);
434 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 608 FOR_EACH_VK_FEATURE_1_2(FEATURE);
435 bool is_float16_supported{}; ///< Support for float16 arithmetic. 609 FOR_EACH_VK_FEATURE_1_3(FEATURE);
436 bool is_int8_supported{}; ///< Support for int8 arithmetic. 610 FOR_EACH_VK_FEATURE_EXT(FEATURE);
437 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. 611 FOR_EACH_VK_EXTENSION(EXTENSION);
438 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. 612 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
439 bool is_depth_bounds_supported{}; ///< Support for depth bounds. 613
440 bool is_shader_float64_supported{}; ///< Support for float64. 614#undef EXTENSION
441 bool is_shader_int64_supported{}; ///< Support for int64. 615#undef FEATURE
442 bool is_shader_int16_supported{}; ///< Support for int16. 616 };
443 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. 617
444 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. 618 struct Features {
445 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list 619#define FEATURE_CORE(prefix, struct_name, macro_name, var_name) \
446 ///< topologies. 620 VkPhysicalDevice##struct_name##Features var_name{};
447 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. 621#define FEATURE_EXT(prefix, struct_name, macro_name, var_name) \
448 bool is_integrated{}; ///< Is GPU an iGPU. 622 VkPhysicalDevice##struct_name##Features##prefix var_name{};
449 bool is_virtual{}; ///< Is GPU a virtual GPU. 623
450 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 624 FOR_EACH_VK_FEATURE_1_1(FEATURE_CORE);
451 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 625 FOR_EACH_VK_FEATURE_1_2(FEATURE_CORE);
452 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 626 FOR_EACH_VK_FEATURE_1_3(FEATURE_CORE);
453 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 627 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXT);
454 bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts. 628
455 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. 629#undef FEATURE_CORE
456 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. 630#undef FEATURE_EXT
457 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. 631
458 bool khr_pipeline_executable_properties{}; ///< Support for executable properties. 632 VkPhysicalDeviceFeatures features{};
459 bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. 633 };
460 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 634
461 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 635 struct Properties {
462 bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control 636 VkPhysicalDeviceDriverProperties driver{};
463 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 637 VkPhysicalDeviceFloatControlsProperties float_controls{};
464 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 638 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{};
465 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. 639 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
466 bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control. 640 VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
467 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 641
468 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 642 VkPhysicalDeviceProperties properties{};
469 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 643 };
470 bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization. 644
471 bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state. 645 Extensions extensions{};
472 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. 646 Features features{};
473 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. 647 Properties properties{};
474 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. 648
475 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. 649 VkPhysicalDeviceFeatures2 features2{};
476 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget. 650 VkPhysicalDeviceProperties2 properties2{};
477 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 651
652 // Misc features
653 bool is_optimal_astc_supported{}; ///< Support for all guest ASTC formats.
654 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
655 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
656 bool is_integrated{}; ///< Is GPU an iGPU.
657 bool is_virtual{}; ///< Is GPU a virtual GPU.
658 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
478 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit 659 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
479 bool has_renderdoc{}; ///< Has RenderDoc attached 660 bool has_renderdoc{}; ///< Has RenderDoc attached
480 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 661 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
481 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 662 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
482 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 663 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
483 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 664 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
484 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline 665 bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
485 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline 666 bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
667 u64 device_access_memory{}; ///< Total size of device local memory in bytes.
668 u32 sets_per_pool{}; ///< Sets per Description Pool
486 669
487 // Telemetry parameters 670 // Telemetry parameters
488 std::string vendor_name; ///< Device's driver name. 671 std::set<std::string, std::less<>> supported_extensions; ///< Reported Vulkan extensions.
489 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. 672 std::set<std::string, std::less<>> loaded_extensions; ///< Loaded Vulkan extensions.
490 std::vector<size_t> valid_heap_memory; ///< Heaps used. 673 std::vector<size_t> valid_heap_memory; ///< Heaps used.
491 674
492 /// Format properties dictionary. 675 /// Format properties dictionary.
493 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 676 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index 562039b56..b6d83e446 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -32,7 +32,7 @@
32namespace Vulkan { 32namespace Vulkan {
33namespace { 33namespace {
34[[nodiscard]] std::vector<const char*> RequiredExtensions( 34[[nodiscard]] std::vector<const char*> RequiredExtensions(
35 Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) { 35 Core::Frontend::WindowSystemType window_type, bool enable_validation) {
36 std::vector<const char*> extensions; 36 std::vector<const char*> extensions;
37 extensions.reserve(6); 37 extensions.reserve(6);
38 switch (window_type) { 38 switch (window_type) {
@@ -65,7 +65,7 @@ namespace {
65 if (window_type != Core::Frontend::WindowSystemType::Headless) { 65 if (window_type != Core::Frontend::WindowSystemType::Headless) {
66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67 } 67 }
68 if (enable_debug_utils) { 68 if (enable_validation) {
69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
70 } 70 }
71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
@@ -95,9 +95,9 @@ namespace {
95 return true; 95 return true;
96} 96}
97 97
98[[nodiscard]] std::vector<const char*> Layers(bool enable_layers) { 98[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
99 std::vector<const char*> layers; 99 std::vector<const char*> layers;
100 if (enable_layers) { 100 if (enable_validation) {
101 layers.push_back("VK_LAYER_KHRONOS_validation"); 101 layers.push_back("VK_LAYER_KHRONOS_validation");
102 } 102 }
103 return layers; 103 return layers;
@@ -125,7 +125,7 @@ void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const
125 125
126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, 126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
127 u32 required_version, Core::Frontend::WindowSystemType window_type, 127 u32 required_version, Core::Frontend::WindowSystemType window_type,
128 bool enable_debug_utils, bool enable_layers) { 128 bool enable_validation) {
129 if (!library.IsOpen()) { 129 if (!library.IsOpen()) {
130 LOG_ERROR(Render_Vulkan, "Vulkan library not available"); 130 LOG_ERROR(Render_Vulkan, "Vulkan library not available");
131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
@@ -138,11 +138,11 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); 138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
140 } 140 }
141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); 141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation);
142 if (!AreExtensionsSupported(dld, extensions)) { 142 if (!AreExtensionsSupported(dld, extensions)) {
143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
144 } 144 }
145 std::vector<const char*> layers = Layers(enable_layers); 145 std::vector<const char*> layers = Layers(enable_validation);
146 RemoveUnavailableLayers(dld, layers); 146 RemoveUnavailableLayers(dld, layers);
147 147
148 const u32 available_version = vk::AvailableVersion(dld); 148 const u32 available_version = vk::AvailableVersion(dld);
diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h
index 40419d802..b59b92f83 100644
--- a/src/video_core/vulkan_common/vulkan_instance.h
+++ b/src/video_core/vulkan_common/vulkan_instance.h
@@ -17,8 +17,7 @@ namespace Vulkan {
17 * @param dld Dispatch table to load function pointers into 17 * @param dld Dispatch table to load function pointers into
18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1) 18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1)
19 * @param window_type Window system type's enabled extension 19 * @param window_type Window system type's enabled extension
20 * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not 20 * @param enable_validation Whether to enable Vulkan validation layers or not
21 * @param enable_layers Whether to enable Vulkan validation layers or not
22 * 21 *
23 * @return A new Vulkan instance 22 * @return A new Vulkan instance
24 * @throw vk::Exception on failure 23 * @throw vk::Exception on failure
@@ -26,6 +25,6 @@ namespace Vulkan {
26[[nodiscard]] vk::Instance CreateInstance( 25[[nodiscard]] vk::Instance CreateInstance(
27 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, 26 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version,
28 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless, 27 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless,
29 bool enable_debug_utils = false, bool enable_layers = false); 28 bool enable_validation = false);
30 29
31} // namespace Vulkan 30} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 7dca7341c..486d4dfaf 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -94,6 +94,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
94 X(vkCmdDispatch); 94 X(vkCmdDispatch);
95 X(vkCmdDraw); 95 X(vkCmdDraw);
96 X(vkCmdDrawIndexed); 96 X(vkCmdDrawIndexed);
97 X(vkCmdDrawIndirect);
98 X(vkCmdDrawIndexedIndirect);
99 X(vkCmdDrawIndirectCount);
100 X(vkCmdDrawIndexedIndirectCount);
97 X(vkCmdEndQuery); 101 X(vkCmdEndQuery);
98 X(vkCmdEndRenderPass); 102 X(vkCmdEndRenderPass);
99 X(vkCmdEndTransformFeedbackEXT); 103 X(vkCmdEndTransformFeedbackEXT);
@@ -118,12 +122,22 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
118 X(vkCmdSetDepthCompareOpEXT); 122 X(vkCmdSetDepthCompareOpEXT);
119 X(vkCmdSetDepthTestEnableEXT); 123 X(vkCmdSetDepthTestEnableEXT);
120 X(vkCmdSetDepthWriteEnableEXT); 124 X(vkCmdSetDepthWriteEnableEXT);
125 X(vkCmdSetPrimitiveRestartEnableEXT);
126 X(vkCmdSetRasterizerDiscardEnableEXT);
127 X(vkCmdSetDepthBiasEnableEXT);
128 X(vkCmdSetLogicOpEnableEXT);
129 X(vkCmdSetDepthClampEnableEXT);
121 X(vkCmdSetFrontFaceEXT); 130 X(vkCmdSetFrontFaceEXT);
131 X(vkCmdSetLogicOpEXT);
132 X(vkCmdSetPatchControlPointsEXT);
122 X(vkCmdSetLineWidth); 133 X(vkCmdSetLineWidth);
123 X(vkCmdSetPrimitiveTopologyEXT); 134 X(vkCmdSetPrimitiveTopologyEXT);
124 X(vkCmdSetStencilOpEXT); 135 X(vkCmdSetStencilOpEXT);
125 X(vkCmdSetStencilTestEnableEXT); 136 X(vkCmdSetStencilTestEnableEXT);
126 X(vkCmdSetVertexInputEXT); 137 X(vkCmdSetVertexInputEXT);
138 X(vkCmdSetColorWriteMaskEXT);
139 X(vkCmdSetColorBlendEnableEXT);
140 X(vkCmdSetColorBlendEquationEXT);
127 X(vkCmdResolveImage); 141 X(vkCmdResolveImage);
128 X(vkCreateBuffer); 142 X(vkCreateBuffer);
129 X(vkCreateBufferView); 143 X(vkCreateBufferView);
@@ -138,6 +152,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
138 X(vkCreateGraphicsPipelines); 152 X(vkCreateGraphicsPipelines);
139 X(vkCreateImage); 153 X(vkCreateImage);
140 X(vkCreateImageView); 154 X(vkCreateImageView);
155 X(vkCreatePipelineCache);
141 X(vkCreatePipelineLayout); 156 X(vkCreatePipelineLayout);
142 X(vkCreateQueryPool); 157 X(vkCreateQueryPool);
143 X(vkCreateRenderPass); 158 X(vkCreateRenderPass);
@@ -157,6 +172,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
157 X(vkDestroyImage); 172 X(vkDestroyImage);
158 X(vkDestroyImageView); 173 X(vkDestroyImageView);
159 X(vkDestroyPipeline); 174 X(vkDestroyPipeline);
175 X(vkDestroyPipelineCache);
160 X(vkDestroyPipelineLayout); 176 X(vkDestroyPipelineLayout);
161 X(vkDestroyQueryPool); 177 X(vkDestroyQueryPool);
162 X(vkDestroyRenderPass); 178 X(vkDestroyRenderPass);
@@ -174,6 +190,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
174 X(vkGetEventStatus); 190 X(vkGetEventStatus);
175 X(vkGetFenceStatus); 191 X(vkGetFenceStatus);
176 X(vkGetImageMemoryRequirements); 192 X(vkGetImageMemoryRequirements);
193 X(vkGetPipelineCacheData);
177 X(vkGetMemoryFdKHR); 194 X(vkGetMemoryFdKHR);
178#ifdef _WIN32 195#ifdef _WIN32
179 X(vkGetMemoryWin32HandleKHR); 196 X(vkGetMemoryWin32HandleKHR);
@@ -204,6 +221,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
204 if (!dld.vkResetQueryPool) { 221 if (!dld.vkResetQueryPool) {
205 Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device); 222 Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device);
206 } 223 }
224
225 // Support for draw indirect with count is optional in Vulkan 1.2
226 if (!dld.vkCmdDrawIndirectCount) {
227 Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device);
228 Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device);
229 }
207#undef X 230#undef X
208} 231}
209 232
@@ -417,6 +440,10 @@ void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noex
417 dld.vkDestroyPipeline(device, handle, nullptr); 440 dld.vkDestroyPipeline(device, handle, nullptr);
418} 441}
419 442
443void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept {
444 dld.vkDestroyPipelineCache(device, handle, nullptr);
445}
446
420void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { 447void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept {
421 dld.vkDestroyPipelineLayout(device, handle, nullptr); 448 dld.vkDestroyPipelineLayout(device, handle, nullptr);
422} 449}
@@ -637,6 +664,10 @@ void ShaderModule::SetObjectNameEXT(const char* name) const {
637 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); 664 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name);
638} 665}
639 666
667void PipelineCache::SetObjectNameEXT(const char* name) const {
668 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name);
669}
670
640void Semaphore::SetObjectNameEXT(const char* name) const { 671void Semaphore::SetObjectNameEXT(const char* name) const {
641 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name); 672 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name);
642} 673}
@@ -732,21 +763,29 @@ DescriptorSetLayout Device::CreateDescriptorSetLayout(
732 return DescriptorSetLayout(object, handle, *dld); 763 return DescriptorSetLayout(object, handle, *dld);
733} 764}
734 765
766PipelineCache Device::CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const {
767 VkPipelineCache cache;
768 Check(dld->vkCreatePipelineCache(handle, &ci, nullptr, &cache));
769 return PipelineCache(cache, handle, *dld);
770}
771
735PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { 772PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const {
736 VkPipelineLayout object; 773 VkPipelineLayout object;
737 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); 774 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object));
738 return PipelineLayout(object, handle, *dld); 775 return PipelineLayout(object, handle, *dld);
739} 776}
740 777
741Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { 778Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
779 VkPipelineCache cache) const {
742 VkPipeline object; 780 VkPipeline object;
743 Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); 781 Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object));
744 return Pipeline(object, handle, *dld); 782 return Pipeline(object, handle, *dld);
745} 783}
746 784
747Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { 785Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
786 VkPipelineCache cache) const {
748 VkPipeline object; 787 VkPipeline object;
749 Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); 788 Check(dld->vkCreateComputePipelines(handle, cache, 1, &ci, nullptr, &object));
750 return Pipeline(object, handle, *dld); 789 return Pipeline(object, handle, *dld);
751} 790}
752 791
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 8bd4fd4d9..e86f661cb 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -213,6 +213,10 @@ struct DeviceDispatch : InstanceDispatch {
213 PFN_vkCmdDispatch vkCmdDispatch{}; 213 PFN_vkCmdDispatch vkCmdDispatch{};
214 PFN_vkCmdDraw vkCmdDraw{}; 214 PFN_vkCmdDraw vkCmdDraw{};
215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; 215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
216 PFN_vkCmdDrawIndirect vkCmdDrawIndirect{};
217 PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{};
218 PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{};
219 PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{};
216 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; 220 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
217 PFN_vkCmdEndQuery vkCmdEndQuery{}; 221 PFN_vkCmdEndQuery vkCmdEndQuery{};
218 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; 222 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@@ -230,8 +234,15 @@ struct DeviceDispatch : InstanceDispatch {
230 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{}; 234 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
231 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{}; 235 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
232 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; 236 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
237 PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
238 PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
239 PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
240 PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{};
241 PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{};
233 PFN_vkCmdSetEvent vkCmdSetEvent{}; 242 PFN_vkCmdSetEvent vkCmdSetEvent{};
234 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; 243 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
244 PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{};
245 PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{};
235 PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; 246 PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
236 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; 247 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
237 PFN_vkCmdSetScissor vkCmdSetScissor{}; 248 PFN_vkCmdSetScissor vkCmdSetScissor{};
@@ -242,6 +253,9 @@ struct DeviceDispatch : InstanceDispatch {
242 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; 253 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
243 PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; 254 PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
244 PFN_vkCmdSetViewport vkCmdSetViewport{}; 255 PFN_vkCmdSetViewport vkCmdSetViewport{};
256 PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{};
257 PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{};
258 PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{};
245 PFN_vkCmdWaitEvents vkCmdWaitEvents{}; 259 PFN_vkCmdWaitEvents vkCmdWaitEvents{};
246 PFN_vkCreateBuffer vkCreateBuffer{}; 260 PFN_vkCreateBuffer vkCreateBuffer{};
247 PFN_vkCreateBufferView vkCreateBufferView{}; 261 PFN_vkCreateBufferView vkCreateBufferView{};
@@ -256,6 +270,7 @@ struct DeviceDispatch : InstanceDispatch {
256 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; 270 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{};
257 PFN_vkCreateImage vkCreateImage{}; 271 PFN_vkCreateImage vkCreateImage{};
258 PFN_vkCreateImageView vkCreateImageView{}; 272 PFN_vkCreateImageView vkCreateImageView{};
273 PFN_vkCreatePipelineCache vkCreatePipelineCache{};
259 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; 274 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
260 PFN_vkCreateQueryPool vkCreateQueryPool{}; 275 PFN_vkCreateQueryPool vkCreateQueryPool{};
261 PFN_vkCreateRenderPass vkCreateRenderPass{}; 276 PFN_vkCreateRenderPass vkCreateRenderPass{};
@@ -275,6 +290,7 @@ struct DeviceDispatch : InstanceDispatch {
275 PFN_vkDestroyImage vkDestroyImage{}; 290 PFN_vkDestroyImage vkDestroyImage{};
276 PFN_vkDestroyImageView vkDestroyImageView{}; 291 PFN_vkDestroyImageView vkDestroyImageView{};
277 PFN_vkDestroyPipeline vkDestroyPipeline{}; 292 PFN_vkDestroyPipeline vkDestroyPipeline{};
293 PFN_vkDestroyPipelineCache vkDestroyPipelineCache{};
278 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; 294 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{};
279 PFN_vkDestroyQueryPool vkDestroyQueryPool{}; 295 PFN_vkDestroyQueryPool vkDestroyQueryPool{};
280 PFN_vkDestroyRenderPass vkDestroyRenderPass{}; 296 PFN_vkDestroyRenderPass vkDestroyRenderPass{};
@@ -292,6 +308,7 @@ struct DeviceDispatch : InstanceDispatch {
292 PFN_vkGetEventStatus vkGetEventStatus{}; 308 PFN_vkGetEventStatus vkGetEventStatus{};
293 PFN_vkGetFenceStatus vkGetFenceStatus{}; 309 PFN_vkGetFenceStatus vkGetFenceStatus{};
294 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; 310 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
311 PFN_vkGetPipelineCacheData vkGetPipelineCacheData{};
295 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; 312 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
296#ifdef _WIN32 313#ifdef _WIN32
297 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; 314 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
@@ -337,6 +354,7 @@ void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
337void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; 354void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
338void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; 355void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept;
339void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; 356void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept;
357void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept;
340void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; 358void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept;
341void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; 359void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept;
342void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; 360void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept;
@@ -759,6 +777,18 @@ public:
759 void SetObjectNameEXT(const char* name) const; 777 void SetObjectNameEXT(const char* name) const;
760}; 778};
761 779
780class PipelineCache : public Handle<VkPipelineCache, VkDevice, DeviceDispatch> {
781 using Handle<VkPipelineCache, VkDevice, DeviceDispatch>::Handle;
782
783public:
784 /// Set object name.
785 void SetObjectNameEXT(const char* name) const;
786
787 VkResult Read(size_t* size, void* data) const noexcept {
788 return dld->vkGetPipelineCacheData(owner, handle, size, data);
789 }
790};
791
762class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { 792class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
763 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle; 793 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
764 794
@@ -830,11 +860,15 @@ public:
830 860
831 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; 861 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
832 862
863 PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
864
833 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; 865 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
834 866
835 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; 867 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
868 VkPipelineCache cache = nullptr) const;
836 869
837 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; 870 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
871 VkPipelineCache cache = nullptr) const;
838 872
839 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; 873 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
840 874
@@ -1019,6 +1053,29 @@ public:
1019 first_instance); 1053 first_instance);
1020 } 1054 }
1021 1055
1056 void DrawIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
1057 u32 stride) const noexcept {
1058 dld->vkCmdDrawIndirect(handle, src_buffer, src_offset, draw_count, stride);
1059 }
1060
1061 void DrawIndexedIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
1062 u32 stride) const noexcept {
1063 dld->vkCmdDrawIndexedIndirect(handle, src_buffer, src_offset, draw_count, stride);
1064 }
1065
1066 void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer,
1067 VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept {
1068 dld->vkCmdDrawIndirectCount(handle, src_buffer, src_offset, count_buffer, count_offset,
1069 draw_count, stride);
1070 }
1071
1072 void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset,
1073 VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count,
1074 u32 stride) const noexcept {
1075 dld->vkCmdDrawIndexedIndirectCount(handle, src_buffer, src_offset, count_buffer,
1076 count_offset, draw_count, stride);
1077 }
1078
1022 void ClearAttachments(Span<VkClearAttachment> attachments, 1079 void ClearAttachments(Span<VkClearAttachment> attachments,
1023 Span<VkClearRect> rects) const noexcept { 1080 Span<VkClearRect> rects) const noexcept {
1024 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), 1081 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
@@ -1192,10 +1249,51 @@ public:
1192 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); 1249 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1193 } 1250 }
1194 1251
1252 void SetPrimitiveRestartEnableEXT(bool enable) const noexcept {
1253 dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1254 }
1255
1256 void SetRasterizerDiscardEnableEXT(bool enable) const noexcept {
1257 dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1258 }
1259
1260 void SetDepthBiasEnableEXT(bool enable) const noexcept {
1261 dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1262 }
1263
1264 void SetLogicOpEnableEXT(bool enable) const noexcept {
1265 dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1266 }
1267
1268 void SetDepthClampEnableEXT(bool enable) const noexcept {
1269 dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1270 }
1271
1195 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { 1272 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
1196 dld->vkCmdSetFrontFaceEXT(handle, front_face); 1273 dld->vkCmdSetFrontFaceEXT(handle, front_face);
1197 } 1274 }
1198 1275
1276 void SetLogicOpEXT(VkLogicOp logic_op) const noexcept {
1277 dld->vkCmdSetLogicOpEXT(handle, logic_op);
1278 }
1279
1280 void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept {
1281 dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
1282 }
1283
1284 void SetColorWriteMaskEXT(u32 first, Span<VkColorComponentFlags> masks) const noexcept {
1285 dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data());
1286 }
1287
1288 void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept {
1289 dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data());
1290 }
1291
1292 void SetColorBlendEquationEXT(u32 first,
1293 Span<VkColorBlendEquationEXT> equations) const noexcept {
1294 dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data());
1295 }
1296
1199 void SetLineWidth(float line_width) const noexcept { 1297 void SetLineWidth(float line_width) const noexcept {
1200 dld->vkCmdSetLineWidth(handle, line_width); 1298 dld->vkCmdSetLineWidth(handle, line_width);
1201 } 1299 }
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4a7d35617..dfc675cc8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -5,7 +5,6 @@ set(CMAKE_AUTOMOC ON)
5set(CMAKE_AUTORCC ON) 5set(CMAKE_AUTORCC ON)
6set(CMAKE_AUTOUIC ON) 6set(CMAKE_AUTOUIC ON)
7set(CMAKE_INCLUDE_CURRENT_DIR ON) 7set(CMAKE_INCLUDE_CURRENT_DIR ON)
8set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
9 8
10# Set the RPATH for Qt Libraries 9# Set the RPATH for Qt Libraries
11# This must be done before the `yuzu` target is created 10# This must be done before the `yuzu` target is created
diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist
index 0eb377926..f05f3186c 100644
--- a/src/yuzu/Info.plist
+++ b/src/yuzu/Info.plist
@@ -34,6 +34,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
34 <string></string> 34 <string></string>
35 <key>CSResourcesFileMapped</key> 35 <key>CSResourcesFileMapped</key>
36 <true/> 36 <true/>
37 <key>LSApplicationCategoryType</key>
38 <string>public.app-category.games</string>
37 <key>LSRequiresCarbon</key> 39 <key>LSRequiresCarbon</key>
38 <true/> 40 <true/>
39 <key>NSHumanReadableCopyright</key> 41 <key>NSHumanReadableCopyright</key>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 3d560f303..d65991734 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -96,9 +96,9 @@ void EmuThread::run() {
96 m_is_running.store(false); 96 m_is_running.store(false);
97 m_is_running.notify_all(); 97 m_is_running.notify_all();
98 98
99 emit DebugModeEntered(); 99 EmulationPaused(lk);
100 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); 100 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
101 emit DebugModeLeft(); 101 EmulationResumed(lk);
102 } 102 }
103 } 103 }
104 104
@@ -111,6 +111,21 @@ void EmuThread::run() {
111#endif 111#endif
112} 112}
113 113
114// Unlock while emitting signals so that the main thread can
115// continue pumping events.
116
117void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
118 lk.unlock();
119 emit DebugModeEntered();
120 lk.lock();
121}
122
123void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
124 lk.unlock();
125 emit DebugModeLeft();
126 lk.lock();
127}
128
114#ifdef HAS_OPENGL 129#ifdef HAS_OPENGL
115class OpenGLSharedContext : public Core::Frontend::GraphicsContext { 130class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
116public: 131public:
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index eca16b313..092c6206f 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -92,6 +92,10 @@ public:
92 } 92 }
93 93
94private: 94private:
95 void EmulationPaused(std::unique_lock<std::mutex>& lk);
96 void EmulationResumed(std::unique_lock<std::mutex>& lk);
97
98private:
95 Core::System& m_system; 99 Core::System& m_system;
96 100
97 std::stop_source m_stop_source; 101 std::stop_source m_stop_source;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2ea4f367b..35fef506a 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -70,28 +70,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
70// UISetting::values.shortcuts, which is alphabetically ordered. 70// UISetting::values.shortcuts, which is alphabetically ordered.
71// clang-format off 71// clang-format off
72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}},
74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
95}}; 95}};
96// clang-format on 96// clang-format on
97 97
@@ -440,6 +440,7 @@ void Config::ReadControlValues() {
440 ReadBasicSetting(Settings::values.emulate_analog_keyboard); 440 ReadBasicSetting(Settings::values.emulate_analog_keyboard);
441 Settings::values.mouse_panning = false; 441 Settings::values.mouse_panning = false;
442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity); 442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
443 ReadBasicSetting(Settings::values.enable_joycon_driver);
443 444
444 ReadBasicSetting(Settings::values.tas_enable); 445 ReadBasicSetting(Settings::values.tas_enable);
445 ReadBasicSetting(Settings::values.tas_loop); 446 ReadBasicSetting(Settings::values.tas_loop);
@@ -562,6 +563,7 @@ void Config::ReadDebuggingValues() {
562 ReadBasicSetting(Settings::values.reporting_services); 563 ReadBasicSetting(Settings::values.reporting_services);
563 ReadBasicSetting(Settings::values.quest_flag); 564 ReadBasicSetting(Settings::values.quest_flag);
564 ReadBasicSetting(Settings::values.disable_macro_jit); 565 ReadBasicSetting(Settings::values.disable_macro_jit);
566 ReadBasicSetting(Settings::values.disable_macro_hle);
565 ReadBasicSetting(Settings::values.extended_logging); 567 ReadBasicSetting(Settings::values.extended_logging);
566 ReadBasicSetting(Settings::values.use_debug_asserts); 568 ReadBasicSetting(Settings::values.use_debug_asserts);
567 ReadBasicSetting(Settings::values.use_auto_stub); 569 ReadBasicSetting(Settings::values.use_auto_stub);
@@ -689,6 +691,7 @@ void Config::ReadRendererValues() {
689 qt_config->beginGroup(QStringLiteral("Renderer")); 691 qt_config->beginGroup(QStringLiteral("Renderer"));
690 692
691 ReadGlobalSetting(Settings::values.renderer_backend); 693 ReadGlobalSetting(Settings::values.renderer_backend);
694 ReadGlobalSetting(Settings::values.renderer_force_max_clock);
692 ReadGlobalSetting(Settings::values.vulkan_device); 695 ReadGlobalSetting(Settings::values.vulkan_device);
693 ReadGlobalSetting(Settings::values.fullscreen_mode); 696 ReadGlobalSetting(Settings::values.fullscreen_mode);
694 ReadGlobalSetting(Settings::values.aspect_ratio); 697 ReadGlobalSetting(Settings::values.aspect_ratio);
@@ -708,6 +711,7 @@ void Config::ReadRendererValues() {
708 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 711 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
709 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 712 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
710 ReadGlobalSetting(Settings::values.use_pessimistic_flushes); 713 ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
714 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
711 ReadGlobalSetting(Settings::values.bg_red); 715 ReadGlobalSetting(Settings::values.bg_red);
712 ReadGlobalSetting(Settings::values.bg_green); 716 ReadGlobalSetting(Settings::values.bg_green);
713 ReadGlobalSetting(Settings::values.bg_blue); 717 ReadGlobalSetting(Settings::values.bg_blue);
@@ -744,7 +748,7 @@ void Config::ReadShortcutValues() {
744 for (const auto& [name, group, shortcut] : default_hotkeys) { 748 for (const auto& [name, group, shortcut] : default_hotkeys) {
745 qt_config->beginGroup(group); 749 qt_config->beginGroup(group);
746 qt_config->beginGroup(name); 750 qt_config->beginGroup(name);
747 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 751 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
748 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open 752 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
749 // a file dialog in windowed mode 753 // a file dialog in windowed mode
750 UISettings::values.shortcuts.push_back( 754 UISettings::values.shortcuts.push_back(
@@ -753,7 +757,7 @@ void Config::ReadShortcutValues() {
753 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), 757 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
754 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) 758 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
755 .toString(), 759 .toString(),
756 shortcut.context}}); 760 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
757 qt_config->endGroup(); 761 qt_config->endGroup();
758 qt_config->endGroup(); 762 qt_config->endGroup();
759 } 763 }
@@ -941,7 +945,6 @@ void Config::ReadValues() {
941 ReadRendererValues(); 945 ReadRendererValues();
942 ReadAudioValues(); 946 ReadAudioValues();
943 ReadSystemValues(); 947 ReadSystemValues();
944 ReadMultiplayerValues();
945} 948}
946 949
947void Config::SavePlayerValue(std::size_t player_index) { 950void Config::SavePlayerValue(std::size_t player_index) {
@@ -1099,7 +1102,6 @@ void Config::SaveValues() {
1099 SaveRendererValues(); 1102 SaveRendererValues();
1100 SaveAudioValues(); 1103 SaveAudioValues();
1101 SaveSystemValues(); 1104 SaveSystemValues();
1102 SaveMultiplayerValues();
1103} 1105}
1104 1106
1105void Config::SaveAudioValues() { 1107void Config::SaveAudioValues() {
@@ -1138,6 +1140,7 @@ void Config::SaveControlValues() {
1138 WriteGlobalSetting(Settings::values.enable_accurate_vibrations); 1140 WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
1139 WriteGlobalSetting(Settings::values.motion_enabled); 1141 WriteGlobalSetting(Settings::values.motion_enabled);
1140 WriteBasicSetting(Settings::values.enable_raw_input); 1142 WriteBasicSetting(Settings::values.enable_raw_input);
1143 WriteBasicSetting(Settings::values.enable_joycon_driver);
1141 WriteBasicSetting(Settings::values.keyboard_enabled); 1144 WriteBasicSetting(Settings::values.keyboard_enabled);
1142 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1145 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1143 WriteBasicSetting(Settings::values.mouse_panning_sensitivity); 1146 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
@@ -1200,6 +1203,7 @@ void Config::SaveDebuggingValues() {
1200 WriteBasicSetting(Settings::values.quest_flag); 1203 WriteBasicSetting(Settings::values.quest_flag);
1201 WriteBasicSetting(Settings::values.use_debug_asserts); 1204 WriteBasicSetting(Settings::values.use_debug_asserts);
1202 WriteBasicSetting(Settings::values.disable_macro_jit); 1205 WriteBasicSetting(Settings::values.disable_macro_jit);
1206 WriteBasicSetting(Settings::values.disable_macro_hle);
1203 WriteBasicSetting(Settings::values.enable_all_controllers); 1207 WriteBasicSetting(Settings::values.enable_all_controllers);
1204 WriteBasicSetting(Settings::values.create_crash_dumps); 1208 WriteBasicSetting(Settings::values.create_crash_dumps);
1205 WriteBasicSetting(Settings::values.perform_vulkan_check); 1209 WriteBasicSetting(Settings::values.perform_vulkan_check);
@@ -1305,6 +1309,9 @@ void Config::SaveRendererValues() {
1305 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1309 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1306 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1310 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1307 Settings::values.renderer_backend.UsingGlobal()); 1311 Settings::values.renderer_backend.UsingGlobal());
1312 WriteSetting(QString::fromStdString(Settings::values.renderer_force_max_clock.GetLabel()),
1313 static_cast<u32>(Settings::values.renderer_force_max_clock.GetValue(global)),
1314 static_cast<u32>(Settings::values.renderer_force_max_clock.GetDefault()));
1308 WriteGlobalSetting(Settings::values.vulkan_device); 1315 WriteGlobalSetting(Settings::values.vulkan_device);
1309 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), 1316 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
1310 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)), 1317 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
@@ -1348,6 +1355,7 @@ void Config::SaveRendererValues() {
1348 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1355 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1349 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1356 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1350 WriteGlobalSetting(Settings::values.use_pessimistic_flushes); 1357 WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
1358 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1351 WriteGlobalSetting(Settings::values.bg_red); 1359 WriteGlobalSetting(Settings::values.bg_red);
1352 WriteGlobalSetting(Settings::values.bg_green); 1360 WriteGlobalSetting(Settings::values.bg_green);
1353 WriteGlobalSetting(Settings::values.bg_blue); 1361 WriteGlobalSetting(Settings::values.bg_blue);
@@ -1387,6 +1395,7 @@ void Config::SaveShortcutValues() {
1387 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, 1395 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1388 default_hotkey.controller_keyseq); 1396 default_hotkey.controller_keyseq);
1389 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); 1397 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1398 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1390 qt_config->endGroup(); 1399 qt_config->endGroup();
1391 qt_config->endGroup(); 1400 qt_config->endGroup();
1392 } 1401 }
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 97fb664bf..ac42cc7fc 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -92,3 +92,13 @@ void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index
92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); 92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); 93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
94} 94}
95
96int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
97 if (Settings::IsConfiguringGlobal()) {
98 return combobox->currentIndex();
99 }
100 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
101 return global_setting_index;
102 }
103 return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
104}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index e597dcdb5..04c88758c 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -69,4 +69,7 @@ void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox 69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
70void InsertGlobalItem(QComboBox* combobox, int global_index); 70void InsertGlobalItem(QComboBox* combobox, int global_index);
71 71
72// Returns the correct index of a QComboBox taking into account global configuration
73int GetComboboxIndex(int global_setting_index, const QComboBox* combobox);
74
72} // namespace ConfigurationShared 75} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index dacc75a20..cbeb8f168 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -73,6 +73,8 @@ void ConfigureDebug::SetConfiguration() {
73 ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue()); 73 ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
74 ui->disable_macro_jit->setEnabled(runtime_lock); 74 ui->disable_macro_jit->setEnabled(runtime_lock);
75 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 75 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
76 ui->disable_macro_hle->setEnabled(runtime_lock);
77 ui->disable_macro_hle->setChecked(Settings::values.disable_macro_hle.GetValue());
76 ui->disable_loop_safety_checks->setEnabled(runtime_lock); 78 ui->disable_loop_safety_checks->setEnabled(runtime_lock);
77 ui->disable_loop_safety_checks->setChecked( 79 ui->disable_loop_safety_checks->setChecked(
78 Settings::values.disable_shader_loop_safety_checks.GetValue()); 80 Settings::values.disable_shader_loop_safety_checks.GetValue());
@@ -117,6 +119,7 @@ void ConfigureDebug::ApplyConfiguration() {
117 Settings::values.disable_shader_loop_safety_checks = 119 Settings::values.disable_shader_loop_safety_checks =
118 ui->disable_loop_safety_checks->isChecked(); 120 ui->disable_loop_safety_checks->isChecked();
119 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 121 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
122 Settings::values.disable_macro_hle = ui->disable_macro_hle->isChecked();
120 Settings::values.extended_logging = ui->extended_logging->isChecked(); 123 Settings::values.extended_logging = ui->extended_logging->isChecked();
121 Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked(); 124 Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked();
122 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked(); 125 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 102c8c66c..15acefe33 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -176,7 +176,7 @@
176 </property> 176 </property>
177 </widget> 177 </widget>
178 </item> 178 </item>
179 <item row="0" column="2"> 179 <item row="1" column="2">
180 <widget class="QCheckBox" name="dump_macros"> 180 <widget class="QCheckBox" name="dump_macros">
181 <property name="enabled"> 181 <property name="enabled">
182 <bool>true</bool> 182 <bool>true</bool>
@@ -202,6 +202,19 @@
202 </property> 202 </property>
203 </widget> 203 </widget>
204 </item> 204 </item>
205 <item row="0" column="2">
206 <widget class="QCheckBox" name="disable_macro_hle">
207 <property name="enabled">
208 <bool>true</bool>
209 </property>
210 <property name="toolTip">
211 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
212 </property>
213 <property name="text">
214 <string>Disable Macro HLE</string>
215 </property>
216 </widget>
217 </item>
205 <item row="1" column="0"> 218 <item row="1" column="0">
206 <widget class="QCheckBox" name="enable_shader_feedback"> 219 <widget class="QCheckBox" name="enable_shader_feedback">
207 <property name="toolTip"> 220 <property name="toolTip">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4301313cf..2aaefcc05 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
66 66
67 web_tab->SetWebServiceConfigEnabled(enable_web_config); 67 web_tab->SetWebServiceConfigEnabled(enable_web_config);
68 hotkeys_tab->Populate(registry); 68 hotkeys_tab->Populate(registry);
69 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
70 69
71 input_tab->Initialize(input_subsystem); 70 input_tab->Initialize(input_subsystem);
72 71
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index aa02cc63c..a45ec69ec 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -366,6 +366,11 @@
366 </item> 366 </item>
367 <item> 367 <item>
368 <property name="text"> 368 <property name="text">
369 <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
370 </property>
371 </item>
372 <item>
373 <property name="text">
369 <string>2X (1440p/2160p)</string> 374 <string>2X (1440p/2160p)</string>
370 </property> 375 </property>
371 </item> 376 </item>
@@ -389,6 +394,16 @@
389 <string>6X (4320p/6480p)</string> 394 <string>6X (4320p/6480p)</string>
390 </property> 395 </property>
391 </item> 396 </item>
397 <item>
398 <property name="text">
399 <string>7X (5040p/7560p)</string>
400 </property>
401 </item>
402 <item>
403 <property name="text">
404 <string>8X (5760p/8640p)</string>
405 </property>
406 </item>
392 </widget> 407 </widget>
393 </item> 408 </item>
394 </layout> 409 </layout>
@@ -445,7 +460,7 @@
445 </item> 460 </item>
446 <item> 461 <item>
447 <property name="text"> 462 <property name="text">
448 <string>AMD FidelityFX™️ Super Resolution (Vulkan Only)</string> 463 <string>AMD FidelityFX™️ Super Resolution</string>
449 </property> 464 </property>
450 </item> 465 </item>
451 </widget> 466 </widget>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 01f074699..cc0155a2c 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -22,13 +22,17 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
22void ConfigureGraphicsAdvanced::SetConfiguration() { 22void ConfigureGraphicsAdvanced::SetConfiguration() {
23 const bool runtime_lock = !system.IsPoweredOn(); 23 const bool runtime_lock = !system.IsPoweredOn();
24 ui->use_vsync->setEnabled(runtime_lock); 24 ui->use_vsync->setEnabled(runtime_lock);
25 ui->renderer_force_max_clock->setEnabled(runtime_lock);
25 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 26 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
26 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 27 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
27 28
29 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
28 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
29 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 31 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
30 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 32 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
31 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); 33 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
34 ui->use_vulkan_driver_pipeline_cache->setChecked(
35 Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
32 36
33 if (Settings::IsConfiguringGlobal()) { 37 if (Settings::IsConfiguringGlobal()) {
34 ui->gpu_accuracy->setCurrentIndex( 38 ui->gpu_accuracy->setCurrentIndex(
@@ -41,6 +45,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
41 &Settings::values.max_anisotropy); 45 &Settings::values.max_anisotropy);
42 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, 46 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
43 !Settings::values.gpu_accuracy.UsingGlobal()); 47 !Settings::values.gpu_accuracy.UsingGlobal());
48 ConfigurationShared::SetHighlight(ui->renderer_force_max_clock,
49 !Settings::values.renderer_force_max_clock.UsingGlobal());
44 ConfigurationShared::SetHighlight(ui->af_label, 50 ConfigurationShared::SetHighlight(ui->af_label,
45 !Settings::values.max_anisotropy.UsingGlobal()); 51 !Settings::values.max_anisotropy.UsingGlobal());
46 } 52 }
@@ -48,6 +54,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
48 54
49void ConfigureGraphicsAdvanced::ApplyConfiguration() { 55void ConfigureGraphicsAdvanced::ApplyConfiguration() {
50 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); 56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock,
58 ui->renderer_force_max_clock,
59 renderer_force_max_clock);
51 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
52 ui->anisotropic_filtering_combobox); 61 ui->anisotropic_filtering_combobox);
53 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
@@ -58,6 +67,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
58 ui->use_fast_gpu_time, use_fast_gpu_time); 67 ui->use_fast_gpu_time, use_fast_gpu_time);
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, 68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
60 ui->use_pessimistic_flushes, use_pessimistic_flushes); 69 ui->use_pessimistic_flushes, use_pessimistic_flushes);
70 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
71 ui->use_vulkan_driver_pipeline_cache,
72 use_vulkan_driver_pipeline_cache);
61} 73}
62 74
63void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 75void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -76,18 +88,25 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
76 // Disable if not global (only happens during game) 88 // Disable if not global (only happens during game)
77 if (Settings::IsConfiguringGlobal()) { 89 if (Settings::IsConfiguringGlobal()) {
78 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 90 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
91 ui->renderer_force_max_clock->setEnabled(
92 Settings::values.renderer_force_max_clock.UsingGlobal());
79 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 93 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
80 ui->use_asynchronous_shaders->setEnabled( 94 ui->use_asynchronous_shaders->setEnabled(
81 Settings::values.use_asynchronous_shaders.UsingGlobal()); 95 Settings::values.use_asynchronous_shaders.UsingGlobal());
82 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 96 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
83 ui->use_pessimistic_flushes->setEnabled( 97 ui->use_pessimistic_flushes->setEnabled(
84 Settings::values.use_pessimistic_flushes.UsingGlobal()); 98 Settings::values.use_pessimistic_flushes.UsingGlobal());
99 ui->use_vulkan_driver_pipeline_cache->setEnabled(
100 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
85 ui->anisotropic_filtering_combobox->setEnabled( 101 ui->anisotropic_filtering_combobox->setEnabled(
86 Settings::values.max_anisotropy.UsingGlobal()); 102 Settings::values.max_anisotropy.UsingGlobal());
87 103
88 return; 104 return;
89 } 105 }
90 106
107 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
108 Settings::values.renderer_force_max_clock,
109 renderer_force_max_clock);
91 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 110 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
92 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 111 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
93 Settings::values.use_asynchronous_shaders, 112 Settings::values.use_asynchronous_shaders,
@@ -97,6 +116,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
97 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, 116 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
98 Settings::values.use_pessimistic_flushes, 117 Settings::values.use_pessimistic_flushes,
99 use_pessimistic_flushes); 118 use_pessimistic_flushes);
119 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
120 Settings::values.use_vulkan_driver_pipeline_cache,
121 use_vulkan_driver_pipeline_cache);
100 ConfigurationShared::SetColoredComboBox( 122 ConfigurationShared::SetColoredComboBox(
101 ui->gpu_accuracy, ui->label_gpu_accuracy, 123 ui->gpu_accuracy, ui->label_gpu_accuracy,
102 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 124 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 12e816905..df557d585 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -36,10 +36,12 @@ private:
36 36
37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
38 38
39 ConfigurationShared::CheckState renderer_force_max_clock;
39 ConfigurationShared::CheckState use_vsync; 40 ConfigurationShared::CheckState use_vsync;
40 ConfigurationShared::CheckState use_asynchronous_shaders; 41 ConfigurationShared::CheckState use_asynchronous_shaders;
41 ConfigurationShared::CheckState use_fast_gpu_time; 42 ConfigurationShared::CheckState use_fast_gpu_time;
42 ConfigurationShared::CheckState use_pessimistic_flushes; 43 ConfigurationShared::CheckState use_pessimistic_flushes;
44 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
43 45
44 const Core::System& system; 46 const Core::System& system;
45}; 47};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 87a121471..061885e30 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -70,6 +70,16 @@
70 </widget> 70 </widget>
71 </item> 71 </item>
72 <item> 72 <item>
73 <widget class="QCheckBox" name="renderer_force_max_clock">
74 <property name="toolTip">
75 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
76 </property>
77 <property name="text">
78 <string>Force maximum clocks (Vulkan only)</string>
79 </property>
80 </widget>
81 </item>
82 <item>
73 <widget class="QCheckBox" name="use_vsync"> 83 <widget class="QCheckBox" name="use_vsync">
74 <property name="toolTip"> 84 <property name="toolTip">
75 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> 85 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
@@ -110,6 +120,16 @@
110 </widget> 120 </widget>
111 </item> 121 </item>
112 <item> 122 <item>
123 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
124 <property name="toolTip">
125 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
126 </property>
127 <property name="text">
128 <string>Use Vulkan pipeline cache</string>
129 </property>
130 </widget>
131 </item>
132 <item>
113 <widget class="QWidget" name="af_layout" native="true"> 133 <widget class="QWidget" name="af_layout" native="true">
114 <layout class="QHBoxLayout" name="horizontalLayout_1"> 134 <layout class="QHBoxLayout" name="horizontalLayout_1">
115 <property name="leftMargin"> 135 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 235b813d9..77b976e74 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -138,6 +138,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
138 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); 139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); 140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
141 Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
141} 142}
142 143
143void ConfigureInputAdvanced::LoadConfiguration() { 144void ConfigureInputAdvanced::LoadConfiguration() {
@@ -172,6 +173,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
172 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 173 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
173 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); 174 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
174 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); 175 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
176 ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
175 177
176 UpdateUIEnabled(); 178 UpdateUIEnabled();
177} 179}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index fac8cf827..75d96d3ab 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2696,6 +2696,22 @@
2696 </widget> 2696 </widget>
2697 </item> 2697 </item>
2698 <item row="5" column="0"> 2698 <item row="5" column="0">
2699 <widget class="QCheckBox" name="enable_joycon_driver">
2700 <property name="toolTip">
2701 <string>Requires restarting yuzu</string>
2702 </property>
2703 <property name="minimumSize">
2704 <size>
2705 <width>0</width>
2706 <height>23</height>
2707 </size>
2708 </property>
2709 <property name="text">
2710 <string>Enable direct JoyCon driver</string>
2711 </property>
2712 </widget>
2713 </item>
2714 <item row="6" column="0">
2699 <widget class="QCheckBox" name="mouse_panning"> 2715 <widget class="QCheckBox" name="mouse_panning">
2700 <property name="minimumSize"> 2716 <property name="minimumSize">
2701 <size> 2717 <size>
@@ -2708,7 +2724,7 @@
2708 </property> 2724 </property>
2709 </widget> 2725 </widget>
2710 </item> 2726 </item>
2711 <item row="5" column="2"> 2727 <item row="6" column="2">
2712 <widget class="QSpinBox" name="mouse_panning_sensitivity"> 2728 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2713 <property name="toolTip"> 2729 <property name="toolTip">
2714 <string>Mouse sensitivity</string> 2730 <string>Mouse sensitivity</string>
@@ -2730,14 +2746,14 @@
2730 </property> 2746 </property>
2731 </widget> 2747 </widget>
2732 </item> 2748 </item>
2733 <item row="6" column="0"> 2749 <item row="7" column="0">
2734 <widget class="QLabel" name="motion_touch"> 2750 <widget class="QLabel" name="motion_touch">
2735 <property name="text"> 2751 <property name="text">
2736 <string>Motion / Touch</string> 2752 <string>Motion / Touch</string>
2737 </property> 2753 </property>
2738 </widget> 2754 </widget>
2739 </item> 2755 </item>
2740 <item row="6" column="2"> 2756 <item row="7" column="2">
2741 <widget class="QPushButton" name="buttonMotionTouch"> 2757 <widget class="QPushButton" name="buttonMotionTouch">
2742 <property name="text"> 2758 <property name="text">
2743 <string>Configure</string> 2759 <string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index b1575b0d3..4b7e3b01b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -66,6 +66,18 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
66 return QObject::tr("R"); 66 return QObject::tr("R");
67 case Common::Input::ButtonNames::TriggerL: 67 case Common::Input::ButtonNames::TriggerL:
68 return QObject::tr("L"); 68 return QObject::tr("L");
69 case Common::Input::ButtonNames::TriggerZR:
70 return QObject::tr("ZR");
71 case Common::Input::ButtonNames::TriggerZL:
72 return QObject::tr("ZL");
73 case Common::Input::ButtonNames::TriggerSR:
74 return QObject::tr("SR");
75 case Common::Input::ButtonNames::TriggerSL:
76 return QObject::tr("SL");
77 case Common::Input::ButtonNames::ButtonStickL:
78 return QObject::tr("Stick L");
79 case Common::Input::ButtonNames::ButtonStickR:
80 return QObject::tr("Stick R");
69 case Common::Input::ButtonNames::ButtonA: 81 case Common::Input::ButtonNames::ButtonA:
70 return QObject::tr("A"); 82 return QObject::tr("A");
71 case Common::Input::ButtonNames::ButtonB: 83 case Common::Input::ButtonNames::ButtonB:
@@ -76,6 +88,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
76 return QObject::tr("Y"); 88 return QObject::tr("Y");
77 case Common::Input::ButtonNames::ButtonStart: 89 case Common::Input::ButtonNames::ButtonStart:
78 return QObject::tr("Start"); 90 return QObject::tr("Start");
91 case Common::Input::ButtonNames::ButtonPlus:
92 return QObject::tr("Plus");
93 case Common::Input::ButtonNames::ButtonMinus:
94 return QObject::tr("Minus");
95 case Common::Input::ButtonNames::ButtonHome:
96 return QObject::tr("Home");
97 case Common::Input::ButtonNames::ButtonCapture:
98 return QObject::tr("Capture");
79 case Common::Input::ButtonNames::L1: 99 case Common::Input::ButtonNames::L1:
80 return QObject::tr("L1"); 100 return QObject::tr("L1");
81 case Common::Input::ButtonNames::L2: 101 case Common::Input::ButtonNames::L2:
@@ -738,13 +758,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
738 758
739 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, 759 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
740 &ConfigureInputPlayer::UpdateMappingWithDefaults); 760 &ConfigureInputPlayer::UpdateMappingWithDefaults);
761 ui->comboDevices->installEventFilter(this);
741 762
742 ui->comboDevices->setCurrentIndex(-1); 763 ui->comboDevices->setCurrentIndex(-1);
743 764
744 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
745 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
746 [this] { emit RefreshInputDevices(); });
747
748 timeout_timer->setSingleShot(true); 765 timeout_timer->setSingleShot(true);
749 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 766 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
750 767
@@ -1469,6 +1486,12 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1469 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button); 1486 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
1470} 1487}
1471 1488
1489void ConfigureInputPlayer::wheelEvent(QWheelEvent* event) {
1490 const int x = event->angleDelta().x();
1491 const int y = event->angleDelta().y();
1492 input_subsystem->GetMouse()->MouseWheelChange(x, y);
1493}
1494
1472void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1495void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1473 if (!input_setter || !event) { 1496 if (!input_setter || !event) {
1474 return; 1497 return;
@@ -1479,6 +1502,13 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1479 } 1502 }
1480} 1503}
1481 1504
1505bool ConfigureInputPlayer::eventFilter(QObject* object, QEvent* event) {
1506 if (object == ui->comboDevices && event->type() == QEvent::MouseButtonPress) {
1507 RefreshInputDevices();
1508 }
1509 return object->eventFilter(object, event);
1510}
1511
1482void ConfigureInputPlayer::CreateProfile() { 1512void ConfigureInputPlayer::CreateProfile() {
1483 const auto profile_name = 1513 const auto profile_name =
1484 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30, 1514 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30,
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 26f60d121..99a9c875d 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -116,9 +116,15 @@ private:
116 /// Handle mouse button press events. 116 /// Handle mouse button press events.
117 void mousePressEvent(QMouseEvent* event) override; 117 void mousePressEvent(QMouseEvent* event) override;
118 118
119 /// Handle mouse wheel move events.
120 void wheelEvent(QWheelEvent* event) override;
121
119 /// Handle key press events. 122 /// Handle key press events.
120 void keyPressEvent(QKeyEvent* event) override; 123 void keyPressEvent(QKeyEvent* event) override;
121 124
125 /// Handle combobox list refresh
126 bool eventFilter(QObject* object, QEvent* event) override;
127
122 /// Update UI to reflect current configuration. 128 /// Update UI to reflect current configuration.
123 void UpdateUI(); 129 void UpdateUI();
124 130
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index a62b57501..a9567c6ee 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -122,25 +122,6 @@
122 </property> 122 </property>
123 </widget> 123 </widget>
124 </item> 124 </item>
125 <item>
126 <widget class="QPushButton" name="buttonRefreshDevices">
127 <property name="minimumSize">
128 <size>
129 <width>21</width>
130 <height>21</height>
131 </size>
132 </property>
133 <property name="maximumSize">
134 <size>
135 <width>21</width>
136 <height>21</height>
137 </size>
138 </property>
139 <property name="styleSheet">
140 <string notr="true"/>
141 </property>
142 </widget>
143 </item>
144 </layout> 125 </layout>
145 </widget> 126 </widget>
146 </item> 127 </item>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 11390fec0..68af6c20c 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -103,9 +103,13 @@ void PlayerControlPreview::UpdateColors() {
103 103
104 colors.left = colors.primary; 104 colors.left = colors.primary;
105 colors.right = colors.primary; 105 colors.right = colors.primary;
106 // Possible alternative to set colors from settings 106
107 // colors.left = QColor(controller->GetColors().left.body); 107 const auto color_left = controller->GetColorsValues()[0].body;
108 // colors.right = QColor(controller->GetColors().right.body); 108 const auto color_right = controller->GetColorsValues()[1].body;
109 if (color_left != 0 && color_right != 0) {
110 colors.left = QColor(color_left);
111 colors.right = QColor(color_right);
112 }
109} 113}
110 114
111void PlayerControlPreview::ResetInputs() { 115void PlayerControlPreview::ResetInputs() {
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index d1b870c72..fb1292f07 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
91 91
92 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
93 SetConfiguration(); 92 SetConfiguration();
94 UpdateUiDisplay(); 93 UpdateUiDisplay();
95 ConnectEvents(); 94 ConnectEvents();
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 93db47cfd..7e757eafd 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
66 66
67 setFocusPolicy(Qt::ClickFocus); 67 setFocusPolicy(Qt::ClickFocus);
68 setWindowTitle(tr("Properties")); 68 setWindowTitle(tr("Properties"));
69 // remove Help question mark button from the title bar
70 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
71 69
72 addons_tab->SetTitleId(title_id); 70 addons_tab->SetTitleId(title_id);
73 71
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 688c2dd38..1275f10c8 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -4,9 +4,11 @@
4#include <memory> 4#include <memory>
5#include <QKeyEvent> 5#include <QKeyEvent>
6#include <QMenu> 6#include <QMenu>
7#include <QMessageBox>
7#include <QTimer> 8#include <QTimer>
9#include <fmt/format.h>
8 10
9#include "core/hid/emulated_devices.h" 11#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h" 12#include "core/hid/hid_core.h"
11#include "input_common/drivers/keyboard.h" 13#include "input_common/drivers/keyboard.h"
12#include "input_common/drivers/mouse.h" 14#include "input_common/drivers/mouse.h"
@@ -126,9 +128,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
126 ui->buttonRingAnalogPush, 128 ui->buttonRingAnalogPush,
127 }; 129 };
128 130
129 emulated_device = hid_core_.GetEmulatedDevices(); 131 emulated_controller = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
130 emulated_device->SaveCurrentConfig(); 132 emulated_controller->SaveCurrentConfig();
131 emulated_device->EnableConfiguration(); 133 emulated_controller->EnableConfiguration();
134
135 Core::HID::ControllerUpdateCallback engine_callback{
136 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
137 .is_npad_service = false,
138 };
139 callback_key = emulated_controller->SetCallback(engine_callback);
140 is_controller_set = true;
132 141
133 LoadConfiguration(); 142 LoadConfiguration();
134 143
@@ -143,9 +152,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
143 HandleClick( 152 HandleClick(
144 analog_map_buttons[sub_button_id], 153 analog_map_buttons[sub_button_id],
145 [=, this](const Common::ParamPackage& params) { 154 [=, this](const Common::ParamPackage& params) {
146 Common::ParamPackage param = emulated_device->GetRingParam(); 155 Common::ParamPackage param = emulated_controller->GetRingParam();
147 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); 156 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
148 emulated_device->SetRingParam(param); 157 emulated_controller->SetRingParam(param);
149 }, 158 },
150 InputCommon::Polling::InputType::Stick); 159 InputCommon::Polling::InputType::Stick);
151 }); 160 });
@@ -155,16 +164,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
155 connect(analog_button, &QPushButton::customContextMenuRequested, 164 connect(analog_button, &QPushButton::customContextMenuRequested,
156 [=, this](const QPoint& menu_location) { 165 [=, this](const QPoint& menu_location) {
157 QMenu context_menu; 166 QMenu context_menu;
158 Common::ParamPackage param = emulated_device->GetRingParam(); 167 Common::ParamPackage param = emulated_controller->GetRingParam();
159 context_menu.addAction(tr("Clear"), [&] { 168 context_menu.addAction(tr("Clear"), [&] {
160 emulated_device->SetRingParam({}); 169 emulated_controller->SetRingParam(param);
161 analog_map_buttons[sub_button_id]->setText(tr("[not set]")); 170 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
162 }); 171 });
163 context_menu.addAction(tr("Invert axis"), [&] { 172 context_menu.addAction(tr("Invert axis"), [&] {
164 const bool invert_value = param.Get("invert_x", "+") == "-"; 173 const bool invert_value = param.Get("invert_x", "+") == "-";
165 const std::string invert_str = invert_value ? "+" : "-"; 174 const std::string invert_str = invert_value ? "+" : "-";
166 param.Set("invert_x", invert_str); 175 param.Set("invert_x", invert_str);
167 emulated_device->SetRingParam(param); 176 emulated_controller->SetRingParam(param);
168 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM; 177 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
169 ++sub_button_id2) { 178 ++sub_button_id2) {
170 analog_map_buttons[sub_button_id2]->setText( 179 analog_map_buttons[sub_button_id2]->setText(
@@ -177,16 +186,19 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
177 } 186 }
178 187
179 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { 188 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
180 Common::ParamPackage param = emulated_device->GetRingParam(); 189 Common::ParamPackage param = emulated_controller->GetRingParam();
181 const auto slider_value = ui->sliderRingAnalogDeadzone->value(); 190 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
182 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); 191 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
183 param.Set("deadzone", slider_value / 100.0f); 192 param.Set("deadzone", slider_value / 100.0f);
184 emulated_device->SetRingParam(param); 193 emulated_controller->SetRingParam(param);
185 }); 194 });
186 195
187 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 196 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
188 &ConfigureRingController::RestoreDefaults); 197 &ConfigureRingController::RestoreDefaults);
189 198
199 connect(ui->enable_ring_controller_button, &QPushButton::clicked, this,
200 &ConfigureRingController::EnableRingController);
201
190 timeout_timer->setSingleShot(true); 202 timeout_timer->setSingleShot(true);
191 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 203 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
192 204
@@ -202,7 +214,14 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
202} 214}
203 215
204ConfigureRingController::~ConfigureRingController() { 216ConfigureRingController::~ConfigureRingController() {
205 emulated_device->DisableConfiguration(); 217 emulated_controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
218 Common::Input::PollingMode::Active);
219 emulated_controller->DisableConfiguration();
220
221 if (is_controller_set) {
222 emulated_controller->DeleteCallback(callback_key);
223 is_controller_set = false;
224 }
206}; 225};
207 226
208void ConfigureRingController::changeEvent(QEvent* event) { 227void ConfigureRingController::changeEvent(QEvent* event) {
@@ -219,7 +238,7 @@ void ConfigureRingController::RetranslateUI() {
219 238
220void ConfigureRingController::UpdateUI() { 239void ConfigureRingController::UpdateUI() {
221 RetranslateUI(); 240 RetranslateUI();
222 const Common::ParamPackage param = emulated_device->GetRingParam(); 241 const Common::ParamPackage param = emulated_controller->GetRingParam();
223 242
224 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 243 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
225 auto* const analog_button = analog_map_buttons[sub_button_id]; 244 auto* const analog_button = analog_map_buttons[sub_button_id];
@@ -240,9 +259,9 @@ void ConfigureRingController::UpdateUI() {
240} 259}
241 260
242void ConfigureRingController::ApplyConfiguration() { 261void ConfigureRingController::ApplyConfiguration() {
243 emulated_device->DisableConfiguration(); 262 emulated_controller->DisableConfiguration();
244 emulated_device->SaveCurrentConfig(); 263 emulated_controller->SaveCurrentConfig();
245 emulated_device->EnableConfiguration(); 264 emulated_controller->EnableConfiguration();
246} 265}
247 266
248void ConfigureRingController::LoadConfiguration() { 267void ConfigureRingController::LoadConfiguration() {
@@ -252,10 +271,62 @@ void ConfigureRingController::LoadConfiguration() {
252void ConfigureRingController::RestoreDefaults() { 271void ConfigureRingController::RestoreDefaults() {
253 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( 272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
254 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); 273 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
255 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string)); 274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
256 UpdateUI(); 275 UpdateUI();
257} 276}
258 277
278void ConfigureRingController::EnableRingController() {
279 const auto dialog_title = tr("Error enabling ring input");
280
281 is_ring_enabled = false;
282 ui->ring_controller_sensor_value->setText(tr("Not connected"));
283
284 if (!Settings::values.enable_joycon_driver) {
285 QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled"));
286 return;
287 }
288
289 ui->enable_ring_controller_button->setEnabled(false);
290 ui->enable_ring_controller_button->setText(tr("Configuring"));
291 // SetPollingMode is blocking. Allow to update the button status before calling the command
292 repaint();
293
294 const auto result = emulated_controller->SetPollingMode(
295 Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Ring);
296 switch (result) {
297 case Common::Input::DriverResult::Success:
298 is_ring_enabled = true;
299 break;
300 case Common::Input::DriverResult::NotSupported:
301 QMessageBox::warning(this, dialog_title,
302 tr("The current mapped device doesn't support the ring controller"));
303 break;
304 case Common::Input::DriverResult::NoDeviceDetected:
305 QMessageBox::warning(this, dialog_title,
306 tr("The current mapped device doesn't have a ring attached"));
307 break;
308 default:
309 QMessageBox::warning(this, dialog_title,
310 tr("Unexpected driver result %1").arg(static_cast<int>(result)));
311 break;
312 }
313 ui->enable_ring_controller_button->setEnabled(true);
314 ui->enable_ring_controller_button->setText(tr("Enable"));
315}
316
317void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) {
318 if (!is_ring_enabled) {
319 return;
320 }
321 if (type != Core::HID::ControllerTriggerType::RingController) {
322 return;
323 }
324
325 const auto value = emulated_controller->GetRingSensorValues();
326 const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value));
327 ui->ring_controller_sensor_value->setText(tex_value);
328}
329
259void ConfigureRingController::HandleClick( 330void ConfigureRingController::HandleClick(
260 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 331 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
261 InputCommon::Polling::InputType type) { 332 InputCommon::Polling::InputType type) {
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index 38a9cb716..b23c27906 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -13,7 +13,7 @@ class InputSubsystem;
13 13
14namespace Core::HID { 14namespace Core::HID {
15class HIDCore; 15class HIDCore;
16class EmulatedDevices; 16class EmulatedController;
17} // namespace Core::HID 17} // namespace Core::HID
18 18
19namespace Ui { 19namespace Ui {
@@ -42,6 +42,12 @@ private:
42 /// Restore all buttons to their default values. 42 /// Restore all buttons to their default values.
43 void RestoreDefaults(); 43 void RestoreDefaults();
44 44
45 /// Sets current polling mode to ring input
46 void EnableRingController();
47
48 // Handles emulated controller events
49 void ControllerUpdate(Core::HID::ControllerTriggerType type);
50
45 /// Called when the button was pressed. 51 /// Called when the button was pressed.
46 void HandleClick(QPushButton* button, 52 void HandleClick(QPushButton* button,
47 std::function<void(const Common::ParamPackage&)> new_input_setter, 53 std::function<void(const Common::ParamPackage&)> new_input_setter,
@@ -78,7 +84,11 @@ private:
78 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 84 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
79 85
80 InputCommon::InputSubsystem* input_subsystem; 86 InputCommon::InputSubsystem* input_subsystem;
81 Core::HID::EmulatedDevices* emulated_device; 87 Core::HID::EmulatedController* emulated_controller;
88
89 bool is_ring_enabled{};
90 bool is_controller_set{};
91 int callback_key;
82 92
83 std::unique_ptr<Ui::ConfigureRingController> ui; 93 std::unique_ptr<Ui::ConfigureRingController> ui;
84}; 94};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 9ec634dd4..514dff372 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>298</width> 9 <width>315</width>
10 <height>339</height> 10 <height>400</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -46,187 +46,283 @@
46 </property> 46 </property>
47 </spacer> 47 </spacer>
48 </item> 48 </item>
49 <item> 49 <item>
50 <widget class="QGroupBox" name="RingAnalog"> 50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title"> 51 <property name="title">
52 <string>Ring Sensor Parameters</string> 52 <string>Virtual Ring Sensor Parameters</string>
53 </property> 53 </property>
54 <property name="alignment"> 54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> 55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property> 56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3"> 57 <layout class="QVBoxLayout" name="verticalLayout_1">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
63 </property>
64 <property name="leftMargin">
65 <number>3</number>
66 </property>
67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing"> 58 <property name="spacing">
79 <number>3</number> 59 <number>0</number>
80 </property> 60 </property>
81 <item alignment="Qt::AlignHCenter"> 61 <property name="sizeConstraint">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> 62 <enum>QLayout::SetDefaultConstraint</enum>
83 <property name="title"> 63 </property>
84 <string>Pull</string> 64 <property name="leftMargin">
85 </property> 65 <number>3</number>
86 <property name="alignment"> 66 </property>
87 <set>Qt::AlignCenter</set> 67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing">
79 <number>3</number>
88 </property> 80 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> 81 <item alignment="Qt::AlignHCenter">
90 <property name="spacing"> 82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
91 <number>3</number> 83 <property name="title">
84 <string>Pull</string>
92 </property> 85 </property>
93 <property name="leftMargin"> 86 <property name="alignment">
94 <number>3</number> 87 <set>Qt::AlignCenter</set>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property> 88 </property>
99 <property name="rightMargin"> 89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
100 <number>3</number> 90 <property name="spacing">
91 <number>3</number>
92 </property>
93 <property name="leftMargin">
94 <number>3</number>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property>
99 <property name="rightMargin">
100 <number>3</number>
101 </property>
102 <property name="bottomMargin">
103 <number>3</number>
104 </property>
105 <item>
106 <widget class="QPushButton" name="buttonRingAnalogPull">
107 <property name="minimumSize">
108 <size>
109 <width>70</width>
110 <height>0</height>
111 </size>
112 </property>
113 <property name="maximumSize">
114 <size>
115 <width>68</width>
116 <height>16777215</height>
117 </size>
118 </property>
119 <property name="styleSheet">
120 <string notr="true">min-width: 68px;</string>
121 </property>
122 <property name="text">
123 <string>Pull</string>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </widget>
129 </item>
130 <item alignment="Qt::AlignHCenter">
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
132 <property name="title">
133 <string>Push</string>
101 </property> 134 </property>
102 <property name="bottomMargin"> 135 <property name="alignment">
103 <number>3</number> 136 <set>Qt::AlignCenter</set>
104 </property> 137 </property>
105 <item> 138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
106 <widget class="QPushButton" name="buttonRingAnalogPull"> 139 <property name="spacing">
107 <property name="minimumSize"> 140 <number>3</number>
108 <size> 141 </property>
109 <width>68</width> 142 <property name="leftMargin">
110 <height>0</height> 143 <number>3</number>
111 </size> 144 </property>
112 </property> 145 <property name="topMargin">
113 <property name="maximumSize"> 146 <number>3</number>
114 <size> 147 </property>
115 <width>68</width> 148 <property name="rightMargin">
116 <height>16777215</height> 149 <number>3</number>
117 </size> 150 </property>
118 </property> 151 <property name="bottomMargin">
119 <property name="styleSheet"> 152 <number>3</number>
120 <string notr="true">min-width: 68px;</string> 153 </property>
121 </property> 154 <item>
122 <property name="text"> 155 <widget class="QPushButton" name="buttonRingAnalogPush">
123 <string>Pull</string> 156 <property name="minimumSize">
124 </property> 157 <size>
125 </widget> 158 <width>70</width>
126 </item> 159 <height>0</height>
127 </layout> 160 </size>
128 </widget> 161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text">
172 <string>Push</string>
173 </property>
174 </widget>
175 </item>
176 </layout>
177 </widget>
178 </item>
179 </layout>
129 </item> 180 </item>
130 <item alignment="Qt::AlignHCenter"> 181 <item>
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> 182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
132 <property name="title"> 183 <property name="spacing">
133 <string>Push</string> 184 <number>3</number>
134 </property> 185 </property>
135 <property name="alignment"> 186 <property name="sizeConstraint">
136 <set>Qt::AlignCenter</set> 187 <enum>QLayout::SetDefaultConstraint</enum>
137 </property> 188 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> 189 <property name="leftMargin">
139 <property name="spacing"> 190 <number>0</number>
140 <number>3</number> 191 </property>
141 </property> 192 <property name="topMargin">
142 <property name="leftMargin"> 193 <number>10</number>
143 <number>3</number> 194 </property>
144 </property> 195 <property name="rightMargin">
145 <property name="topMargin"> 196 <number>0</number>
146 <number>3</number> 197 </property>
147 </property> 198 <property name="bottomMargin">
148 <property name="rightMargin"> 199 <number>3</number>
149 <number>3</number> 200 </property>
150 </property> 201 <item>
151 <property name="bottomMargin"> 202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
152 <number>3</number>
153 </property>
154 <item> 203 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush"> 204 <widget class="QLabel" name="labelRingAnalogDeadzone">
156 <property name="minimumSize">
157 <size>
158 <width>68</width>
159 <height>0</height>
160 </size>
161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text"> 205 <property name="text">
172 <string>Push</string> 206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
173 </property> 210 </property>
174 </widget> 211 </widget>
175 </item> 212 </item>
176 </layout> 213 </layout>
177 </widget> 214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item>
225 </layout>
178 </item> 226 </item>
179 </layout> 227 </layout>
180 </item> 228 </widget>
181 <item> 229 </item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> 230 <item>
231 <widget class="QGroupBox" name="RingDriver">
232 <property name="title">
233 <string>Direct Joycon Driver</string>
234 </property>
235 <property name="alignment">
236 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
237 </property>
238 <layout class="QVBoxLayout" name="verticalLayout_2">
183 <property name="spacing"> 239 <property name="spacing">
184 <number>3</number> 240 <number>0</number>
185 </property> 241 </property>
186 <property name="sizeConstraint"> 242 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum> 243 <enum>QLayout::SetDefaultConstraint</enum>
188 </property> 244 </property>
189 <property name="leftMargin"> 245 <property name="leftMargin">
190 <number>0</number> 246 <number>3</number>
191 </property> 247 </property>
192 <property name="topMargin"> 248 <property name="topMargin">
193 <number>10</number> 249 <number>6</number>
194 </property> 250 </property>
195 <property name="rightMargin"> 251 <property name="rightMargin">
196 <number>0</number> 252 <number>3</number>
197 </property> 253 </property>
198 <property name="bottomMargin"> 254 <property name="bottomMargin">
199 <number>3</number> 255 <number>10</number>
200 </property> 256 </property>
201 <item> 257 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> 258 <layout class="QGridLayout" name="gridLayout">
203 <item> 259 <property name="leftMargin">
204 <widget class="QLabel" name="labelRingAnalogDeadzone"> 260 <number>10</number>
261 </property>
262 <property name="topMargin">
263 <number>6</number>
264 </property>
265 <property name="rightMargin">
266 <number>10</number>
267 </property>
268 <property name="bottomMargin">
269 <number>10</number>
270 </property>
271 <property name="verticalSpacing">
272 <number>10</number>
273 </property>
274 <item row="0" column="1">
275 <spacer name="horizontalSpacer">
276 <property name="orientation">
277 <enum>Qt::Horizontal</enum>
278 </property>
279 <property name="sizeType">
280 <enum>QSizePolicy::Fixed</enum>
281 </property>
282 <property name="sizeHint" stdset="0">
283 <size>
284 <width>76</width>
285 <height>20</height>
286 </size>
287 </property>
288 </spacer>
289 </item>
290 <item row="0" column="0">
291 <widget class="QLabel" name="enable_ring_controller_label">
292 <property name="text">
293 <string>Enable Ring Input</string>
294 </property>
295 </widget>
296 </item>
297 <item row="0" column="2">
298 <widget class="QPushButton" name="enable_ring_controller_button">
205 <property name="text"> 299 <property name="text">
206 <string>Deadzone: 0%</string> 300 <string>Enable</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <widget class="QLabel" name="ring_controller_sensor_label">
306 <property name="text">
307 <string>Ring Sensor Value</string>
308 </property>
309 </widget>
310 </item>
311 <item row="1" column="2">
312 <widget class="QLabel" name="ring_controller_sensor_value">
313 <property name="text">
314 <string>Not connected</string>
207 </property> 315 </property>
208 <property name="alignment"> 316 <property name="alignment">
209 <set>Qt::AlignHCenter</set> 317 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
210 </property> 318 </property>
211 </widget> 319 </widget>
212 </item> 320 </item>
213 </layout> 321 </layout>
214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item> 322 </item>
225 </layout> 323 </layout>
226 </item> 324 </widget>
227 </layout> 325 </item>
228 </widget>
229 </item>
230 <item> 326 <item>
231 <spacer name="verticalSpacer"> 327 <spacer name="verticalSpacer">
232 <property name="orientation"> 328 <property name="orientation">
@@ -273,6 +369,6 @@
273 <signal>rejected()</signal> 369 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver> 370 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot> 371 <slot>reject()</slot>
276 </connection> 372 </connection>
277 </connections> 373 </connections>
278</ui> 374</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9b14e5903..9ea4c02da 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -14,6 +14,29 @@
14#include "yuzu/configuration/configuration_shared.h" 14#include "yuzu/configuration/configuration_shared.h"
15#include "yuzu/configuration/configure_system.h" 15#include "yuzu/configuration/configure_system.h"
16 16
17constexpr std::array<u32, 7> LOCALE_BLOCKLIST{
18 // pzzefezrpnkzeidfej
19 // thhsrnhutlohsternp
20 // BHH4CG U
21 // Raa1AB S
22 // nn9
23 // ts
24 0b0100011100001100000, // Japan
25 0b0000001101001100100, // Americas
26 0b0100110100001000010, // Europe
27 0b0100110100001000010, // Australia
28 0b0000000000000000000, // China
29 0b0100111100001000000, // Korea
30 0b0100111100001000000, // Taiwan
31};
32
33static bool IsValidLocale(u32 region_index, u32 language_index) {
34 if (region_index >= LOCALE_BLOCKLIST.size()) {
35 return false;
36 }
37 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
38}
39
17ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) 40ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
18 : QWidget(parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { 41 : QWidget(parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} {
19 ui->setupUi(this); 42 ui->setupUi(this);
@@ -34,6 +57,25 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
34 } 57 }
35 }); 58 });
36 59
60 const auto locale_check = [this](int index) {
61 const auto region_index = ConfigurationShared::GetComboboxIndex(
62 Settings::values.region_index.GetValue(true), ui->combo_region);
63 const auto language_index = ConfigurationShared::GetComboboxIndex(
64 Settings::values.language_index.GetValue(true), ui->combo_language);
65 const bool valid_locale = IsValidLocale(region_index, language_index);
66 ui->label_warn_invalid_locale->setVisible(!valid_locale);
67 if (!valid_locale) {
68 ui->label_warn_invalid_locale->setText(
69 tr("Warning: \"%1\" is not a valid language for region \"%2\"")
70 .arg(ui->combo_language->currentText())
71 .arg(ui->combo_region->currentText()));
72 }
73 };
74
75 connect(ui->combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this,
76 locale_check);
77 connect(ui->combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
78
37 ui->label_console_id->setVisible(Settings::IsConfiguringGlobal()); 79 ui->label_console_id->setVisible(Settings::IsConfiguringGlobal());
38 ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal()); 80 ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal());
39 81
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 8f02880a7..a7f086258 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -42,13 +42,7 @@ private:
42 std::unique_ptr<Ui::ConfigureSystem> ui; 42 std::unique_ptr<Ui::ConfigureSystem> ui;
43 bool enabled = false; 43 bool enabled = false;
44 44
45 int language_index = 0;
46 int region_index = 0;
47 int time_zone_index = 0;
48 int sound_index = 0;
49
50 ConfigurationShared::CheckState use_rng_seed; 45 ConfigurationShared::CheckState use_rng_seed;
51 ConfigurationShared::CheckState use_custom_rtc;
52 46
53 Core::System& system; 47 Core::System& system;
54}; 48};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 46892f5c1..0459cd924 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -326,7 +326,7 @@
326 </item> 326 </item>
327 <item> 327 <item>
328 <property name="text"> 328 <property name="text">
329 <string>English</string> 329 <string>American English</string>
330 </property> 330 </property>
331 </item> 331 </item>
332 <item> 332 <item>
@@ -546,6 +546,16 @@
546 </spacer> 546 </spacer>
547 </item> 547 </item>
548 <item> 548 <item>
549 <widget class="QLabel" name="label_warn_invalid_locale">
550 <property name="text">
551 <string></string>
552 </property>
553 <property name="wordWrap">
554 <bool>true</bool>
555 </property>
556 </widget>
557 </item>
558 <item>
549 <widget class="QLabel" name="label_disable_info"> 559 <widget class="QLabel" name="label_disable_info">
550 <property name="text"> 560 <property name="text">
551 <string>System settings are available only when game is not running.</string> 561 <string>System settings are available only when game is not running.</string>
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 1edc5f1f3..5a545aa70 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
17 17
18 setFocusPolicy(Qt::ClickFocus); 18 setFocusPolicy(Qt::ClickFocus);
19 setWindowTitle(tr("TAS Configuration")); 19 setWindowTitle(tr("TAS Configuration"));
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21 20
22 connect(ui->tas_path_button, &QToolButton::pressed, this, 21 connect(ui->tas_path_button, &QToolButton::pressed, this,
23 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); 22 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); });
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 9bb69cab1..41ef4250a 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -58,13 +58,16 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
58 std::vector<std::string> profile_names; 58 std::vector<std::string> profile_names;
59 profile_names.reserve(map_profiles.size()); 59 profile_names.reserve(map_profiles.size());
60 60
61 for (const auto& [profile_name, config] : map_profiles) { 61 auto it = map_profiles.cbegin();
62 while (it != map_profiles.cend()) {
63 const auto& [profile_name, config] = *it;
62 if (!ProfileExistsInFilesystem(profile_name)) { 64 if (!ProfileExistsInFilesystem(profile_name)) {
63 DeleteProfile(profile_name); 65 it = map_profiles.erase(it);
64 continue; 66 continue;
65 } 67 }
66 68
67 profile_names.push_back(profile_name); 69 profile_names.push_back(profile_name);
70 ++it;
68 } 71 }
69 72
70 std::stable_sort(profile_names.begin(), profile_names.end()); 73 std::stable_sort(profile_names.begin(), profile_names.end());
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index e4bf16a04..e2f55ebae 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
20 setWindowTitle(tr("Controller P1")); 20 setWindowTitle(tr("Controller P1"));
21 resize(500, 350); 21 resize(500, 350);
22 setMinimumSize(500, 350); 22 setMinimumSize(500, 350);
23 // Remove the "?" button from the titlebar and enable the maximize button 23 // Enable the maximize button
24 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 24 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
25 Qt::WindowMaximizeButtonHint);
26 25
27 widget = new PlayerControlPreview(this); 26 widget = new PlayerControlPreview(this);
28 refreshConfiguration(); 27 refreshConfiguration();
@@ -93,7 +92,7 @@ void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
93 case Core::HID::ControllerTriggerType::Button: 92 case Core::HID::ControllerTriggerType::Button:
94 case Core::HID::ControllerTriggerType::Stick: { 93 case Core::HID::ControllerTriggerType::Stick: {
95 const auto buttons_values = controller->GetButtonsValues(); 94 const auto buttons_values = controller->GetButtonsValues();
96 const auto stick_values = controller->GetSticksValues(); 95 const auto stick_values = controller->GetSticks();
97 u64 buttons = 0; 96 u64 buttons = 0;
98 std::size_t index = 0; 97 std::size_t index = 0;
99 for (const auto& button : buttons_values) { 98 for (const auto& button : buttons_values) {
@@ -101,12 +100,12 @@ void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
101 index++; 100 index++;
102 } 101 }
103 const InputCommon::TasInput::TasAnalog left_axis = { 102 const InputCommon::TasInput::TasAnalog left_axis = {
104 .x = stick_values[Settings::NativeAnalog::LStick].x.value, 103 .x = stick_values.left.x / 32767.f,
105 .y = stick_values[Settings::NativeAnalog::LStick].y.value, 104 .y = stick_values.left.y / 32767.f,
106 }; 105 };
107 const InputCommon::TasInput::TasAnalog right_axis = { 106 const InputCommon::TasInput::TasAnalog right_axis = {
108 .x = stick_values[Settings::NativeAnalog::RStick].x.value, 107 .x = stick_values.right.x / 32767.f,
109 .y = stick_values[Settings::NativeAnalog::RStick].y.value, 108 .y = stick_values.right.y / 32767.f,
110 }; 109 };
111 input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis); 110 input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
112 break; 111 break;
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index d3e2d3c12..493ee0b17 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di
49 setObjectName(QStringLiteral("MicroProfile")); 49 setObjectName(QStringLiteral("MicroProfile"));
50 setWindowTitle(tr("&MicroProfile")); 50 setWindowTitle(tr("&MicroProfile"));
51 resize(1000, 600); 51 resize(1000, 600);
52 // Remove the "?" button from the titlebar and enable the maximize button 52 // Enable the maximize button
53 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 53 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
54 Qt::WindowMaximizeButtonHint);
55 54
56#if MICROPROFILE_ENABLED 55#if MICROPROFILE_ENABLED
57 56
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 13723f6e5..6530186c1 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -21,7 +21,7 @@ void HotkeyRegistry::SaveHotkeys() {
21 {hotkey.first, group.first, 21 {hotkey.first, group.first,
22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), 22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
23 hotkey.second.controller_keyseq, 23 hotkey.second.controller_keyseq,
24 hotkey.second.context})}); 24 hotkey.second.context, hotkey.second.repeat})});
25 } 25 }
26 } 26 }
27} 27}
@@ -47,6 +47,7 @@ void HotkeyRegistry::LoadHotkeys() {
47 hk.controller_shortcut->disconnect(); 47 hk.controller_shortcut->disconnect();
48 hk.controller_shortcut->SetKey(hk.controller_keyseq); 48 hk.controller_shortcut->SetKey(hk.controller_keyseq);
49 } 49 }
50 hk.repeat = shortcut.shortcut.repeat;
50 } 51 }
51} 52}
52 53
@@ -57,8 +58,7 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
57 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); 58 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
58 } 59 }
59 60
60 hk.shortcut->setAutoRepeat(false); 61 hk.shortcut->setAutoRepeat(hk.repeat);
61
62 return hk.shortcut; 62 return hk.shortcut;
63} 63}
64 64
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index dc5b7f628..848239c35 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -115,6 +115,7 @@ private:
115 QShortcut* shortcut = nullptr; 115 QShortcut* shortcut = nullptr;
116 ControllerShortcut* controller_shortcut = nullptr; 116 ControllerShortcut* controller_shortcut = nullptr;
117 Qt::ShortcutContext context = Qt::WindowShortcut; 117 Qt::ShortcutContext context = Qt::WindowShortcut;
118 bool repeat;
118 }; 119 };
119 120
120 using HotkeyMap = std::map<QString, Hotkey>; 121 using HotkeyMap = std::map<QString, Hotkey>;
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 84ec4fe13..673bbaa83 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
46 vbox_layout->addLayout(hbox_layout); 46 vbox_layout->addLayout(hbox_layout);
47 47
48 setLayout(vbox_layout); 48 setLayout(vbox_layout);
49 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
50 setWindowTitle(tr("Install Files to NAND")); 49 setWindowTitle(tr("Install Files to NAND"));
51} 50}
52 51
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 524650144..42b7b64c8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -983,11 +983,6 @@ void GMainWindow::InitializeWidgets() {
983 filter_status_button->setFocusPolicy(Qt::NoFocus); 983 filter_status_button->setFocusPolicy(Qt::NoFocus);
984 connect(filter_status_button, &QPushButton::clicked, this, 984 connect(filter_status_button, &QPushButton::clicked, this,
985 &GMainWindow::OnToggleAdaptingFilter); 985 &GMainWindow::OnToggleAdaptingFilter);
986 auto filter = Settings::values.scaling_filter.GetValue();
987 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
988 filter == Settings::ScalingFilter::Fsr) {
989 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
990 }
991 UpdateFilterText(); 986 UpdateFilterText();
992 filter_status_button->setCheckable(true); 987 filter_status_button->setCheckable(true);
993 filter_status_button->setChecked(true); 988 filter_status_button->setChecked(true);
@@ -1839,9 +1834,11 @@ void GMainWindow::OnEmulationStopTimeExpired() {
1839 1834
1840void GMainWindow::OnEmulationStopped() { 1835void GMainWindow::OnEmulationStopped() {
1841 shutdown_timer.stop(); 1836 shutdown_timer.stop();
1842 emu_thread->disconnect(); 1837 if (emu_thread) {
1843 emu_thread->wait(); 1838 emu_thread->disconnect();
1844 emu_thread = nullptr; 1839 emu_thread->wait();
1840 emu_thread.reset();
1841 }
1845 1842
1846 if (shutdown_dialog) { 1843 if (shutdown_dialog) {
1847 shutdown_dialog->deleteLater(); 1844 shutdown_dialog->deleteLater();
@@ -2229,8 +2226,10 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
2229 } 2226 }
2230 2227
2231 switch (target) { 2228 switch (target) {
2232 case GameListRemoveTarget::GlShaderCache:
2233 case GameListRemoveTarget::VkShaderCache: 2229 case GameListRemoveTarget::VkShaderCache:
2230 RemoveVulkanDriverPipelineCache(program_id);
2231 [[fallthrough]];
2232 case GameListRemoveTarget::GlShaderCache:
2234 RemoveTransferableShaderCache(program_id, target); 2233 RemoveTransferableShaderCache(program_id, target);
2235 break; 2234 break;
2236 case GameListRemoveTarget::AllShaderCache: 2235 case GameListRemoveTarget::AllShaderCache:
@@ -2271,6 +2270,22 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa
2271 } 2270 }
2272} 2271}
2273 2272
2273void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
2274 static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
2275
2276 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2277 const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
2278 const auto target_file = shader_cache_folder_path / target_file_name;
2279
2280 if (!Common::FS::Exists(target_file)) {
2281 return;
2282 }
2283 if (!Common::FS::RemoveFile(target_file)) {
2284 QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"),
2285 tr("Failed to remove the driver pipeline cache."));
2286 }
2287}
2288
2274void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { 2289void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) {
2275 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); 2290 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2276 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); 2291 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
@@ -2738,8 +2753,7 @@ void GMainWindow::OnMenuInstallToNAND() {
2738 ui->action_Install_File_NAND->setEnabled(false); 2753 ui->action_Install_File_NAND->setEnabled(false);
2739 2754
2740 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); 2755 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
2741 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & 2756 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
2742 ~Qt::WindowMaximizeButtonHint);
2743 install_progress->setAttribute(Qt::WA_DeleteOnClose, true); 2757 install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
2744 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); 2758 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
2745 install_progress->show(); 2759 install_progress->show();
@@ -3011,6 +3025,8 @@ void GMainWindow::OnStopGame() {
3011 3025
3012 if (OnShutdownBegin()) { 3026 if (OnShutdownBegin()) {
3013 OnShutdownBeginDialog(); 3027 OnShutdownBeginDialog();
3028 } else {
3029 OnEmulationStopped();
3014 } 3030 }
3015} 3031}
3016 3032
@@ -3447,10 +3463,6 @@ void GMainWindow::OnToggleAdaptingFilter() {
3447 } else { 3463 } else {
3448 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); 3464 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3449 } 3465 }
3450 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
3451 filter == Settings::ScalingFilter::Fsr) {
3452 filter = Settings::ScalingFilter::NearestNeighbor;
3453 }
3454 Settings::values.scaling_filter.SetValue(filter); 3466 Settings::values.scaling_filter.SetValue(filter);
3455 filter_status_button->setChecked(true); 3467 filter_status_button->setChecked(true);
3456 UpdateFilterText(); 3468 UpdateFilterText();
@@ -3708,15 +3720,36 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
3708 } 3720 }
3709} 3721}
3710 3722
3723std::string GMainWindow::CreateTASFramesString(
3724 std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const {
3725 std::string string = "";
3726 size_t maxPlayerIndex = 0;
3727 for (size_t i = 0; i < frames.size(); i++) {
3728 if (frames[i] != 0) {
3729 if (maxPlayerIndex != 0)
3730 string += ", ";
3731 while (maxPlayerIndex++ != i)
3732 string += "0, ";
3733 string += std::to_string(frames[i]);
3734 }
3735 }
3736 return string;
3737}
3738
3711QString GMainWindow::GetTasStateDescription() const { 3739QString GMainWindow::GetTasStateDescription() const {
3712 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus(); 3740 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
3741 std::string tas_frames_string = CreateTASFramesString(total_tas_frames);
3713 switch (tas_status) { 3742 switch (tas_status) {
3714 case InputCommon::TasInput::TasState::Running: 3743 case InputCommon::TasInput::TasState::Running:
3715 return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames); 3744 return tr("TAS state: Running %1/%2")
3745 .arg(current_tas_frame)
3746 .arg(QString::fromStdString(tas_frames_string));
3716 case InputCommon::TasInput::TasState::Recording: 3747 case InputCommon::TasInput::TasState::Recording:
3717 return tr("TAS state: Recording %1").arg(total_tas_frames); 3748 return tr("TAS state: Recording %1").arg(total_tas_frames[0]);
3718 case InputCommon::TasInput::TasState::Stopped: 3749 case InputCommon::TasInput::TasState::Stopped:
3719 return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); 3750 return tr("TAS state: Idle %1/%2")
3751 .arg(current_tas_frame)
3752 .arg(QString::fromStdString(tas_frames_string));
3720 default: 3753 default:
3721 return tr("TAS State: Invalid"); 3754 return tr("TAS State: Invalid");
3722 } 3755 }
@@ -4413,6 +4446,11 @@ int main(int argc, char* argv[]) {
4413 } 4446 }
4414#endif 4447#endif
4415 4448
4449#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4450 // Disables the "?" button on all dialogs. Disabled by default on Qt6.
4451 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
4452#endif
4453
4416 // Enables the core to make the qt created contexts current on std::threads 4454 // Enables the core to make the qt created contexts current on std::threads
4417 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4455 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4418 QApplication app(argc, argv); 4456 QApplication app(argc, argv);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index db318485d..0f61abc7a 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -12,6 +12,7 @@
12 12
13#include "common/announce_multiplayer_room.h" 13#include "common/announce_multiplayer_room.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/drivers/tas_input.h"
15#include "yuzu/compatibility_list.h" 16#include "yuzu/compatibility_list.h"
16#include "yuzu/hotkeys.h" 17#include "yuzu/hotkeys.h"
17 18
@@ -266,6 +267,9 @@ private:
266 void changeEvent(QEvent* event) override; 267 void changeEvent(QEvent* event) override;
267 void closeEvent(QCloseEvent* event) override; 268 void closeEvent(QCloseEvent* event) override;
268 269
270 std::string CreateTASFramesString(
271 std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
272
269#ifdef __unix__ 273#ifdef __unix__
270 void SetupSigInterrupts(); 274 void SetupSigInterrupts();
271 static void HandleSigInterrupt(int); 275 static void HandleSigInterrupt(int);
@@ -347,6 +351,7 @@ private:
347 void RemoveUpdateContent(u64 program_id, InstalledEntryType type); 351 void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
348 void RemoveAddOnContent(u64 program_id, InstalledEntryType type); 352 void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
349 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); 353 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
354 void RemoveVulkanDriverPipelineCache(u64 program_id);
350 void RemoveAllTransferableShaderCaches(u64 program_id); 355 void RemoveAllTransferableShaderCaches(u64 program_id);
351 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 356 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
352 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 357 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index cbd52da85..d71cc23a7 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -81,20 +81,13 @@ void DirectConnectWindow::Connect() {
81 } 81 }
82 } 82 }
83 } 83 }
84 switch (static_cast<ConnectionType>(ui->connection_type->currentIndex())) { 84 if (!ui->ip->hasAcceptableInput()) {
85 case ConnectionType::TraversalServer: 85 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
86 break; 86 return;
87 case ConnectionType::IP: 87 }
88 if (!ui->ip->hasAcceptableInput()) { 88 if (!ui->port->hasAcceptableInput()) {
89 NetworkMessage::ErrorManager::ShowError( 89 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
90 NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); 90 return;
91 return;
92 }
93 if (!ui->port->hasAcceptableInput()) {
94 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
95 return;
96 }
97 break;
98 } 91 }
99 92
100 // Store settings 93 // Store settings
diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui
index 57d6ec25a..0dd4e6829 100644
--- a/src/yuzu/multiplayer/direct_connect.ui
+++ b/src/yuzu/multiplayer/direct_connect.ui
@@ -27,19 +27,10 @@
27 <number>0</number> 27 <number>0</number>
28 </property> 28 </property>
29 <item> 29 <item>
30 <widget class="QComboBox" name="connection_type">
31 <item>
32 <property name="text">
33 <string>IP Address</string>
34 </property>
35 </item>
36 </widget>
37 </item>
38 <item>
39 <widget class="QWidget" name="ip_container" native="true"> 30 <widget class="QWidget" name="ip_container" native="true">
40 <layout class="QHBoxLayout" name="ip_layout"> 31 <layout class="QHBoxLayout" name="ip_layout">
41 <property name="leftMargin"> 32 <property name="leftMargin">
42 <number>5</number> 33 <number>0</number>
43 </property> 34 </property>
44 <property name="topMargin"> 35 <property name="topMargin">
45 <number>0</number> 36 <number>0</number>
@@ -53,17 +44,17 @@
53 <item> 44 <item>
54 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="label_2">
55 <property name="text"> 46 <property name="text">
56 <string>IP</string> 47 <string>Server Address</string>
57 </property> 48 </property>
58 </widget> 49 </widget>
59 </item> 50 </item>
60 <item> 51 <item>
61 <widget class="QLineEdit" name="ip"> 52 <widget class="QLineEdit" name="ip">
62 <property name="toolTip"> 53 <property name="toolTip">
63 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IPv4 address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 54 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Server address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
64 </property> 55 </property>
65 <property name="maxLength"> 56 <property name="maxLength">
66 <number>16</number> 57 <number>253</number>
67 </property> 58 </property>
68 </widget> 59 </widget>
69 </item> 60 </item>
@@ -85,6 +76,12 @@
85 <property name="placeholderText"> 76 <property name="placeholderText">
86 <string notr="true" extracomment="placeholder string that tells user default port">24872</string> 77 <string notr="true" extracomment="placeholder string that tells user default port">24872</string>
87 </property> 78 </property>
79 <property name="maximumSize">
80 <size>
81 <width>65</width>
82 <height>50</height>
83 </size>
84 </property>
88 </widget> 85 </widget>
89 </item> 86 </item>
90 </layout> 87 </layout>
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h
index dd25af280..cbbe6757b 100644
--- a/src/yuzu/multiplayer/validation.h
+++ b/src/yuzu/multiplayer/validation.h
@@ -38,11 +38,28 @@ private:
38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}")); 38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
39 QRegularExpressionValidator nickname; 39 QRegularExpressionValidator nickname;
40 40
41 /// ipv4 address only 41 /// ipv4 / ipv6 / hostnames
42 // TODO remove this when we support hostnames in direct connect
43 QRegularExpression ip_regex = QRegularExpression(QStringLiteral( 42 QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
44 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" 43 // IPv4 regex
45 "2[0-4][0-9]|25[0-5])")); 44 "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|"
45 // IPv6 regex
46 "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|"
47 "(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-"
48 "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
49 "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)"
50 "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
51 "(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]"
52 "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
53 "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2["
54 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
55 "(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2["
56 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
57 "(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2["
58 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
59 "(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?"
60 "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|"
61 // Hostname regex
62 "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$"));
46 QRegularExpressionValidator ip; 63 QRegularExpressionValidator ip;
47 64
48 /// port must be between 0 and 65535 65 /// port must be between 0 and 65535
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2006b883e..db43b7033 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -22,6 +22,7 @@ struct ContextualShortcut {
22 QString keyseq; 22 QString keyseq;
23 QString controller_keyseq; 23 QString controller_keyseq;
24 int context; 24 int context;
25 bool repeat;
25}; 26};
26 27
27struct Shortcut { 28struct Shortcut {
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
index bbb370595..5f6a9c193 100644
--- a/src/yuzu/util/limitable_input_dialog.cpp
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
16LimitableInputDialog::~LimitableInputDialog() = default; 16LimitableInputDialog::~LimitableInputDialog() = default;
17 17
18void LimitableInputDialog::CreateUI() { 18void LimitableInputDialog::CreateUI() {
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
20
21 text_label = new QLabel(this); 19 text_label = new QLabel(this);
22 text_entry = new QLineEdit(this); 20 text_entry = new QLineEdit(this);
23 text_label_invalid = new QLabel(this); 21 text_label_invalid = new QLabel(this);
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
index 4b10fa517..1670aa596 100644
--- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -8,7 +8,6 @@
8 8
9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { 9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
10 setWindowTitle(tr("Enter a hotkey")); 10 setWindowTitle(tr("Enter a hotkey"));
11 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
12 11
13 key_sequence = new QKeySequenceEdit; 12 key_sequence = new QKeySequenceEdit;
14 13
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 61b6cc4e0..46eddf423 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -1,8 +1,6 @@
1# SPDX-FileCopyrightText: 2018 yuzu Emulator Project 1# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
5
6# Credits to Samantas5855 and others for this function. 4# Credits to Samantas5855 and others for this function.
7function(create_resource file output filename) 5function(create_resource file output filename)
8 # Read hex data from file 6 # Read hex data from file
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index de9b220da..527017282 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -296,6 +296,7 @@ void Config::ReadValues() {
296 296
297 // Renderer 297 // Renderer
298 ReadSetting("Renderer", Settings::values.renderer_backend); 298 ReadSetting("Renderer", Settings::values.renderer_backend);
299 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
299 ReadSetting("Renderer", Settings::values.renderer_debug); 300 ReadSetting("Renderer", Settings::values.renderer_debug);
300 ReadSetting("Renderer", Settings::values.renderer_shader_feedback); 301 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
301 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); 302 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
@@ -321,6 +322,7 @@ void Config::ReadValues() {
321 ReadSetting("Renderer", Settings::values.accelerate_astc); 322 ReadSetting("Renderer", Settings::values.accelerate_astc);
322 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 323 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
323 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); 324 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);
325 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
324 326
325 ReadSetting("Renderer", Settings::values.bg_red); 327 ReadSetting("Renderer", Settings::values.bg_red);
326 ReadSetting("Renderer", Settings::values.bg_green); 328 ReadSetting("Renderer", Settings::values.bg_green);
@@ -348,6 +350,7 @@ void Config::ReadValues() {
348 ReadSetting("Debugging", Settings::values.use_debug_asserts); 350 ReadSetting("Debugging", Settings::values.use_debug_asserts);
349 ReadSetting("Debugging", Settings::values.use_auto_stub); 351 ReadSetting("Debugging", Settings::values.use_auto_stub);
350 ReadSetting("Debugging", Settings::values.disable_macro_jit); 352 ReadSetting("Debugging", Settings::values.disable_macro_jit);
353 ReadSetting("Debugging", Settings::values.disable_macro_hle);
351 ReadSetting("Debugging", Settings::values.use_gdbstub); 354 ReadSetting("Debugging", Settings::values.use_gdbstub);
352 ReadSetting("Debugging", Settings::values.gdbstub_port); 355 ReadSetting("Debugging", Settings::values.gdbstub_port);
353 356
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6fcf04e1b..67d230462 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -5,8 +5,8 @@
5 5
6namespace DefaultINI { 6namespace DefaultINI {
7 7
8const char* sdl2_config_file = R"( 8const char* sdl2_config_file =
9 9 R"(
10[ControlsP0] 10[ControlsP0]
11# The input devices and parameters for each Switch native input 11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... 12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
@@ -143,6 +143,8 @@ mouse_enabled =
143# 0 (default): Disabled, 1: Enabled 143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled = 144keyboard_enabled =
145 145
146)"
147 R"(
146[Core] 148[Core]
147# Whether to use multi-core for CPU emulation 149# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled 150# 0: Disabled, 1 (default): Enabled
@@ -242,6 +244,8 @@ cpuopt_unsafe_fastmem_check =
242# 0: Disabled, 1 (default): Enabled 244# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor = 245cpuopt_unsafe_ignore_global_monitor =
244 246
247)"
248 R"(
245[Renderer] 249[Renderer]
246# Which backend API to use. 250# Which backend API to use.
247# 0: OpenGL, 1 (default): Vulkan 251# 0: OpenGL, 1 (default): Vulkan
@@ -360,6 +364,8 @@ bg_red =
360bg_blue = 364bg_blue =
361bg_green = 365bg_green =
362 366
367)"
368 R"(
363[Audio] 369[Audio]
364# Which audio output engine to use. 370# Which audio output engine to use.
365# auto (default): Auto-select 371# auto (default): Auto-select