summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts4
-rw-r--r--src/android/app/src/main/AndroidManifest.xml5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt65
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt172
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt122
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt59
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt1
-rw-r--r--src/android/app/src/main/jni/native.cpp246
-rw-r--r--src/android/app/src/main/jni/native.h165
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_mute.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_unmute.xml9
-rw-r--r--src/android/app/src/main/res/values/strings.xml26
-rw-r--r--src/android/gradle.properties3
-rw-r--r--src/audio_core/device/audio_buffers.h8
-rw-r--r--src/audio_core/device/device_session.cpp12
-rw-r--r--src/audio_core/device/device_session.h7
-rw-r--r--src/audio_core/in/audio_in_system.cpp5
-rw-r--r--src/audio_core/out/audio_out_system.cpp4
-rw-r--r--src/audio_core/renderer/adsp/adsp.cpp1
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp5
-rw-r--r--src/audio_core/renderer/adsp/command_list_processor.cpp1
-rw-r--r--src/audio_core/renderer/command/data_source/decode.cpp23
-rw-r--r--src/audio_core/renderer/command/effect/compressor.cpp8
-rw-r--r--src/audio_core/renderer/command/effect/delay.cpp14
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp4
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp12
-rw-r--r--src/audio_core/renderer/command/performance/performance.cpp15
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.cpp4
-rw-r--r--src/audio_core/renderer/command/sink/device.cpp5
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp6
-rw-r--r--src/audio_core/renderer/nodes/node_states.cpp4
-rw-r--r--src/audio_core/renderer/nodes/node_states.h2
-rw-r--r--src/audio_core/renderer/system.cpp1
-rw-r--r--src/audio_core/sink/null_sink.h2
-rw-r--r--src/audio_core/sink/sink_stream.cpp16
-rw-r--r--src/audio_core/sink/sink_stream.h5
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/fs/fs.cpp14
-rw-r--r--src/common/fs/fs_types.h2
-rw-r--r--src/common/input.h45
-rw-r--r--src/common/ring_buffer.h3
-rw-r--r--src/common/scratch_buffer.h9
-rw-r--r--src/common/settings.cpp19
-rw-r--r--src/common/settings.h12
-rw-r--r--src/common/steady_clock.cpp5
-rw-r--r--src/common/telemetry.cpp1
-rw-r--r--src/common/thread.h2
-rw-r--r--src/common/wall_clock.cpp77
-rw-r--r--src/common/wall_clock.h89
-rw-r--r--src/common/x64/cpu_detect.cpp4
-rw-r--r--src/common/x64/cpu_detect.h1
-rw-r--r--src/common/x64/cpu_wait.cpp70
-rw-r--r--src/common/x64/native_clock.cpp166
-rw-r--r--src/common/x64/native_clock.h59
-rw-r--r--src/common/x64/rdtsc.cpp39
-rw-r--r--src/common/x64/rdtsc.h37
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp5
-rw-r--r--src/core/core_timing.cpp52
-rw-r--r--src/core/core_timing.h14
-rw-r--r--src/core/core_timing_util.h58
-rw-r--r--src/core/file_sys/patch_manager.cpp9
-rw-r--r--src/core/file_sys/vfs_concat.cpp14
-rw-r--r--src/core/file_sys/vfs_real.cpp101
-rw-r--r--src/core/file_sys/vfs_real.h22
-rw-r--r--src/core/hid/emulated_controller.cpp93
-rw-r--r--src/core/hid/emulated_controller.h28
-rw-r--r--src/core/hid/input_converter.cpp6
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp5
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp8
-rw-r--r--src/core/hle/kernel/k_thread.h3
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp7
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp10
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp10
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp16
-rw-r--r--src/core/hle/service/audio/audout_u.cpp15
-rw-r--r--src/core/hle/service/audio/audren_u.cpp22
-rw-r--r--src/core/hle/service/audio/audren_u.h1
-rw-r--r--src/core/hle/service/audio/hwopus.cpp9
-rw-r--r--src/core/hle/service/hid/hidbus.cpp1
-rw-r--r--src/core/hle/service/nfc/common/device.cpp182
-rw-r--r--src/core/hle/service/nfc/common/device.h10
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp11
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h2
-rw-r--r--src/core/hle/service/nfc/mifare_types.h11
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h30
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp19
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h38
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp59
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h20
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp24
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h3
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp15
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h3
-rw-r--r--src/core/hle/service/nvnflinger/parcel.h7
-rw-r--r--src/core/hle/service/server_manager.cpp7
-rw-r--r--src/core/hle/service/server_manager.h4
-rw-r--r--src/core/hle/service/time/clock_types.h13
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp5
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp6
-rw-r--r--src/input_common/drivers/joycon.cpp158
-rw-r--r--src/input_common/drivers/joycon.h19
-rw-r--r--src/input_common/drivers/mouse.cpp99
-rw-r--r--src/input_common/drivers/mouse.h2
-rw-r--r--src/input_common/drivers/sdl_driver.cpp32
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp130
-rw-r--r--src/input_common/drivers/virtual_amiibo.h16
-rw-r--r--src/input_common/helpers/joycon_driver.cpp245
-rw-r--r--src/input_common/helpers/joycon_driver.h43
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp53
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h15
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp110
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h44
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp52
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h41
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp70
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.h22
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h76
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp531
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h74
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h4
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp45
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.h14
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp5
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.h8
-rw-r--r--src/input_common/input_engine.h34
-rw-r--r--src/input_common/input_poller.cpp24
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp4
-rw-r--r--src/shader_recompiler/runtime_info.h3
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h4
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h4
-rw-r--r--src/video_core/cdma_pusher.h1
-rw-r--r--src/video_core/dma_pusher.h8
-rw-r--r--src/video_core/engines/maxwell_dma.cpp40
-rw-r--r--src/video_core/gpu.cpp13
-rw-r--r--src/video_core/host1x/codecs/h264.cpp4
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/opengl_lmem_warmup.comp47
-rw-r--r--src/video_core/memory_manager.cpp17
-rw-r--r--src/video_core/memory_manager.h15
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h5
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp3
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp20
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp34
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp86
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.cpp39
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp115
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp54
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.cpp10
-rw-r--r--src/video_core/shader_cache.cpp4
-rw-r--r--src/video_core/surface.cpp22
-rw-r--r--src/video_core/surface.h2
-rw-r--r--src/video_core/texture_cache/decode_bc.cpp129
-rw-r--r--src/video_core/texture_cache/decode_bc.h (renamed from src/video_core/texture_cache/decode_bc4.h)6
-rw-r--r--src/video_core/texture_cache/decode_bc4.cpp96
-rw-r--r--src/video_core/texture_cache/image_base.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h53
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h14
-rw-r--r--src/video_core/texture_cache/util.cpp72
-rw-r--r--src/video_core/texture_cache/util.h31
-rw-r--r--src/video_core/textures/bcn.cpp1
-rw-r--r--src/video_core/textures/bcn.h9
-rw-r--r--src/video_core/transform_feedback.cpp8
-rw-r--r--src/video_core/transform_feedback.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.cpp40
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp44
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h66
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp58
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp173
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h28
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp64
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h170
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp37
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp10
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui10
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp7
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui37
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp16
-rw-r--r--src/yuzu/configuration/configure_input_player.ui96
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.cpp79
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.h35
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.ui238
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp3
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui2
-rw-r--r--src/yuzu/main.cpp82
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu/vk_device_info.cpp13
-rw-r--r--src/yuzu/vk_device_info.h4
-rw-r--r--src/yuzu_cmd/default_ini.h26
266 files changed, 4819 insertions, 2810 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index bab4f4d0f..9a47e2bd8 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -26,7 +26,7 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
26android { 26android {
27 namespace = "org.yuzu.yuzu_emu" 27 namespace = "org.yuzu.yuzu_emu"
28 28
29 compileSdkVersion = "android-33" 29 compileSdkVersion = "android-34"
30 ndkVersion = "25.2.9519653" 30 ndkVersion = "25.2.9519653"
31 31
32 buildFeatures { 32 buildFeatures {
@@ -51,7 +51,7 @@ android {
51 // TODO If this is ever modified, change application_id in strings.xml 51 // TODO If this is ever modified, change application_id in strings.xml
52 applicationId = "org.yuzu.yuzu_emu" 52 applicationId = "org.yuzu.yuzu_emu"
53 minSdk = 30 53 minSdk = 30
54 targetSdk = 33 54 targetSdk = 34
55 versionName = getGitVersion() 55 versionName = getGitVersion()
56 56
57 // If you want to use autoVersion for the versionCode, create a property in local.properties 57 // If you want to use autoVersion for the versionCode, create a property in local.properties
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index e31ad69e2..51d949d65 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
13 13
14 <uses-permission android:name="android.permission.INTERNET" /> 14 <uses-permission android:name="android.permission.INTERNET" />
15 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 15 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
16 <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
16 <uses-permission android:name="android.permission.NFC" /> 17 <uses-permission android:name="android.permission.NFC" />
17 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 18 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
18 19
@@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
69 android:resource="@xml/nfc_tech_filter" /> 70 android:resource="@xml/nfc_tech_filter" />
70 </activity> 71 </activity>
71 72
72 <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/> 73 <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
74 <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
75 </service>
73 76
74 <provider 77 <provider
75 android:name=".features.DocumentProvider" 78 android:name=".features.DocumentProvider"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index f860cdd4b..9c32e044c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -286,7 +286,7 @@ object NativeLibrary {
286 /** 286 /**
287 * Unpauses emulation from a paused state. 287 * Unpauses emulation from a paused state.
288 */ 288 */
289 external fun unPauseEmulation() 289 external fun unpauseEmulation()
290 290
291 /** 291 /**
292 * Pauses emulation. 292 * Pauses emulation.
@@ -314,6 +314,21 @@ object NativeLibrary {
314 external fun isPaused(): Boolean 314 external fun isPaused(): Boolean
315 315
316 /** 316 /**
317 * Mutes emulation sound
318 */
319 external fun muteAudio(): Boolean
320
321 /**
322 * Unmutes emulation sound
323 */
324 external fun unmuteAudio(): Boolean
325
326 /**
327 * Returns true if emulation audio is muted.
328 */
329 external fun isMuted(): Boolean
330
331 /**
317 * Returns the performance stats for the current game 332 * Returns the performance stats for the current game
318 */ 333 */
319 external fun getPerfStats(): DoubleArray 334 external fun getPerfStats(): DoubleArray
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index f0a6753a9..ae665ed2e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -27,13 +27,13 @@ import android.view.MotionEvent
27import android.view.Surface 27import android.view.Surface
28import android.view.View 28import android.view.View
29import android.view.inputmethod.InputMethodManager 29import android.view.inputmethod.InputMethodManager
30import android.widget.Toast
30import androidx.activity.viewModels 31import androidx.activity.viewModels
31import androidx.appcompat.app.AppCompatActivity 32import androidx.appcompat.app.AppCompatActivity
32import androidx.core.view.WindowCompat 33import androidx.core.view.WindowCompat
33import androidx.core.view.WindowInsetsCompat 34import androidx.core.view.WindowInsetsCompat
34import androidx.core.view.WindowInsetsControllerCompat 35import androidx.core.view.WindowInsetsControllerCompat
35import androidx.navigation.fragment.NavHostFragment 36import androidx.navigation.fragment.NavHostFragment
36import kotlin.math.roundToInt
37import org.yuzu.yuzu_emu.NativeLibrary 37import org.yuzu.yuzu_emu.NativeLibrary
38import org.yuzu.yuzu_emu.R 38import org.yuzu.yuzu_emu.R
39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding 39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
@@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game
44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
45import org.yuzu.yuzu_emu.utils.ForegroundService 45import org.yuzu.yuzu_emu.utils.ForegroundService
46import org.yuzu.yuzu_emu.utils.InputHandler 46import org.yuzu.yuzu_emu.utils.InputHandler
47import org.yuzu.yuzu_emu.utils.MemoryUtil
47import org.yuzu.yuzu_emu.utils.NfcReader 48import org.yuzu.yuzu_emu.utils.NfcReader
48import org.yuzu.yuzu_emu.utils.ThemeHelper 49import org.yuzu.yuzu_emu.utils.ThemeHelper
50import kotlin.math.roundToInt
49 51
50class EmulationActivity : AppCompatActivity(), SensorEventListener { 52class EmulationActivity : AppCompatActivity(), SensorEventListener {
51 private lateinit var binding: ActivityEmulationBinding 53 private lateinit var binding: ActivityEmulationBinding
@@ -63,6 +65,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
63 65
64 private val actionPause = "ACTION_EMULATOR_PAUSE" 66 private val actionPause = "ACTION_EMULATOR_PAUSE"
65 private val actionPlay = "ACTION_EMULATOR_PLAY" 67 private val actionPlay = "ACTION_EMULATOR_PLAY"
68 private val actionMute = "ACTION_EMULATOR_MUTE"
69 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
66 70
67 private val settingsViewModel: SettingsViewModel by viewModels() 71 private val settingsViewModel: SettingsViewModel by viewModels()
68 72
@@ -102,6 +106,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
102 inputHandler = InputHandler() 106 inputHandler = InputHandler()
103 inputHandler.initialize() 107 inputHandler.initialize()
104 108
109 val memoryUtil = MemoryUtil(this)
110 if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
111 Toast.makeText(
112 this,
113 getString(
114 R.string.device_memory_inadequate,
115 memoryUtil.getDeviceRAM(),
116 "8 ${getString(R.string.memory_gigabyte)}"
117 ),
118 Toast.LENGTH_LONG
119 ).show()
120 }
121
105 // Start a foreground service to prevent the app from getting killed in the background 122 // Start a foreground service to prevent the app from getting killed in the background
106 val startIntent = Intent(this, ForegroundService::class.java) 123 val startIntent = Intent(this, ForegroundService::class.java)
107 startForegroundService(startIntent) 124 startForegroundService(startIntent)
@@ -305,6 +322,41 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
305 pictureInPictureActions.add(pauseRemoteAction) 322 pictureInPictureActions.add(pauseRemoteAction)
306 } 323 }
307 324
325 if (NativeLibrary.isMuted()) {
326 val unmuteIcon = Icon.createWithResource(
327 this@EmulationActivity,
328 R.drawable.ic_pip_unmute
329 )
330 val unmutePendingIntent = PendingIntent.getBroadcast(
331 this@EmulationActivity,
332 R.drawable.ic_pip_unmute,
333 Intent(actionUnmute),
334 pendingFlags
335 )
336 val unmuteRemoteAction = RemoteAction(
337 unmuteIcon,
338 getString(R.string.unmute),
339 getString(R.string.unmute),
340 unmutePendingIntent
341 )
342 pictureInPictureActions.add(unmuteRemoteAction)
343 } else {
344 val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
345 val mutePendingIntent = PendingIntent.getBroadcast(
346 this@EmulationActivity,
347 R.drawable.ic_pip_mute,
348 Intent(actionMute),
349 pendingFlags
350 )
351 val muteRemoteAction = RemoteAction(
352 muteIcon,
353 getString(R.string.mute),
354 getString(R.string.mute),
355 mutePendingIntent
356 )
357 pictureInPictureActions.add(muteRemoteAction)
358 }
359
308 return this.apply { setActions(pictureInPictureActions) } 360 return this.apply { setActions(pictureInPictureActions) }
309 } 361 }
310 362
@@ -322,10 +374,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
322 private var pictureInPictureReceiver = object : BroadcastReceiver() { 374 private var pictureInPictureReceiver = object : BroadcastReceiver() {
323 override fun onReceive(context: Context?, intent: Intent) { 375 override fun onReceive(context: Context?, intent: Intent) {
324 if (intent.action == actionPlay) { 376 if (intent.action == actionPlay) {
325 if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation() 377 if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
326 } else if (intent.action == actionPause) { 378 } else if (intent.action == actionPause) {
327 if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation() 379 if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
328 } 380 }
381 if (intent.action == actionUnmute) {
382 if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
383 } else if (intent.action == actionMute) {
384 if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
385 }
329 buildPictureInPictureParams() 386 buildPictureInPictureParams()
330 } 387 }
331 } 388 }
@@ -339,6 +396,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
339 IntentFilter().apply { 396 IntentFilter().apply {
340 addAction(actionPause) 397 addAction(actionPause)
341 addAction(actionPlay) 398 addAction(actionPlay)
399 addAction(actionMute)
400 addAction(actionUnmute)
342 }.also { 401 }.also {
343 registerReceiver(pictureInPictureReceiver, it) 402 registerReceiver(pictureInPictureReceiver, it)
344 } 403 }
@@ -347,6 +406,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
347 unregisterReceiver(pictureInPictureReceiver) 406 unregisterReceiver(pictureInPictureReceiver)
348 } catch (ignored: Exception) { 407 } catch (ignored: Exception) {
349 } 408 }
409 // Always resume audio, since there is no UI button
410 if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
350 } 411 }
351 } 412 }
352 413
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 4643418c1..09976db62 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -714,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
714 State.PAUSED -> { 714 State.PAUSED -> {
715 Log.debug("[EmulationFragment] Resuming emulation.") 715 Log.debug("[EmulationFragment] Resuming emulation.")
716 NativeLibrary.surfaceChanged(surface) 716 NativeLibrary.surfaceChanged(surface)
717 NativeLibrary.unPauseEmulation() 717 NativeLibrary.unpauseEmulation()
718 } 718 }
719 719
720 else -> Log.debug("[EmulationFragment] Bug, run called while already running.") 720 else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 6f8adbba5..5a36ffad4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -68,79 +68,109 @@ class HomeSettingsFragment : Fragment() {
68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
69 mainActivity = requireActivity() as MainActivity 69 mainActivity = requireActivity() as MainActivity
70 70
71 val optionsList: MutableList<HomeSetting> = mutableListOf( 71 val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
72 HomeSetting( 72 add(
73 R.string.advanced_settings, 73 HomeSetting(
74 R.string.settings_description, 74 R.string.advanced_settings,
75 R.drawable.ic_settings 75 R.string.settings_description,
76 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, 76 R.drawable.ic_settings
77 HomeSetting( 77 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
78 R.string.open_user_folder, 78 )
79 R.string.open_user_folder_description, 79 add(
80 R.drawable.ic_folder_open 80 HomeSetting(
81 ) { openFileManager() }, 81 R.string.open_user_folder,
82 HomeSetting( 82 R.string.open_user_folder_description,
83 R.string.preferences_theme, 83 R.drawable.ic_folder_open
84 R.string.theme_and_color_description, 84 ) { openFileManager() }
85 R.drawable.ic_palette 85 )
86 ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }, 86 add(
87 HomeSetting( 87 HomeSetting(
88 R.string.install_gpu_driver, 88 R.string.preferences_theme,
89 R.string.install_gpu_driver_description, 89 R.string.theme_and_color_description,
90 R.drawable.ic_exit 90 R.drawable.ic_palette
91 ) { driverInstaller() }, 91 ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
92 HomeSetting( 92 )
93 R.string.install_amiibo_keys, 93
94 R.string.install_amiibo_keys_description, 94 if (GpuDriverHelper.supportsCustomDriverLoading()) {
95 R.drawable.ic_nfc 95 add(
96 ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }, 96 HomeSetting(
97 HomeSetting( 97 R.string.install_gpu_driver,
98 R.string.install_game_content, 98 R.string.install_gpu_driver_description,
99 R.string.install_game_content_description, 99 R.drawable.ic_exit
100 R.drawable.ic_system_update_alt 100 ) { driverInstaller() }
101 ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) },
102 HomeSetting(
103 R.string.select_games_folder,
104 R.string.select_games_folder_description,
105 R.drawable.ic_add
106 ) {
107 mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
108 },
109 HomeSetting(
110 R.string.manage_save_data,
111 R.string.import_export_saves_description,
112 R.drawable.ic_save
113 ) {
114 ImportExportSavesFragment().show(
115 parentFragmentManager,
116 ImportExportSavesFragment.TAG
117 ) 101 )
118 },
119 HomeSetting(
120 R.string.install_prod_keys,
121 R.string.install_prod_keys_description,
122 R.drawable.ic_unlock
123 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
124 HomeSetting(
125 R.string.install_firmware,
126 R.string.install_firmware_description,
127 R.drawable.ic_firmware
128 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
129 HomeSetting(
130 R.string.share_log,
131 R.string.share_log_description,
132 R.drawable.ic_log
133 ) { shareLog() },
134 HomeSetting(
135 R.string.about,
136 R.string.about_description,
137 R.drawable.ic_info_outline
138 ) {
139 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
140 parentFragmentManager.primaryNavigationFragment?.findNavController()
141 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
142 } 102 }
143 ) 103
104 add(
105 HomeSetting(
106 R.string.install_amiibo_keys,
107 R.string.install_amiibo_keys_description,
108 R.drawable.ic_nfc
109 ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
110 )
111 add(
112 HomeSetting(
113 R.string.install_game_content,
114 R.string.install_game_content_description,
115 R.drawable.ic_system_update_alt
116 ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
117 )
118 add(
119 HomeSetting(
120 R.string.select_games_folder,
121 R.string.select_games_folder_description,
122 R.drawable.ic_add
123 ) {
124 mainActivity.getGamesDirectory.launch(
125 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
126 )
127 }
128 )
129 add(
130 HomeSetting(
131 R.string.manage_save_data,
132 R.string.import_export_saves_description,
133 R.drawable.ic_save
134 ) {
135 ImportExportSavesFragment().show(
136 parentFragmentManager,
137 ImportExportSavesFragment.TAG
138 )
139 }
140 )
141 add(
142 HomeSetting(
143 R.string.install_prod_keys,
144 R.string.install_prod_keys_description,
145 R.drawable.ic_unlock
146 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
147 )
148 add(
149 HomeSetting(
150 R.string.install_firmware,
151 R.string.install_firmware_description,
152 R.drawable.ic_firmware
153 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
154 )
155 add(
156 HomeSetting(
157 R.string.share_log,
158 R.string.share_log_description,
159 R.drawable.ic_log
160 ) { shareLog() }
161 )
162 add(
163 HomeSetting(
164 R.string.about,
165 R.string.about_description,
166 R.drawable.ic_info_outline
167 ) {
168 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
169 parentFragmentManager.primaryNavigationFragment?.findNavController()
170 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
171 }
172 )
173 }
144 174
145 if (!BuildConfig.PREMIUM) { 175 if (!BuildConfig.PREMIUM) {
146 optionsList.add( 176 optionsList.add(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
new file mode 100644
index 000000000..b29b627e9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.Intent
8import android.net.Uri
9import android.os.Bundle
10import androidx.fragment.app.DialogFragment
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.R
13
14class LongMessageDialogFragment : DialogFragment() {
15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
16 val titleId = requireArguments().getInt(TITLE)
17 val description = requireArguments().getString(DESCRIPTION)
18 val helpLinkId = requireArguments().getInt(HELP_LINK)
19
20 val dialog = MaterialAlertDialogBuilder(requireContext())
21 .setPositiveButton(R.string.close, null)
22 .setTitle(titleId)
23 .setMessage(description)
24
25 if (helpLinkId != 0) {
26 dialog.setNeutralButton(R.string.learn_more) { _, _ ->
27 openLink(getString(helpLinkId))
28 }
29 }
30
31 return dialog.show()
32 }
33
34 private fun openLink(link: String) {
35 val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
36 startActivity(intent)
37 }
38
39 companion object {
40 const val TAG = "LongMessageDialogFragment"
41
42 private const val TITLE = "Title"
43 private const val DESCRIPTION = "Description"
44 private const val HELP_LINK = "Link"
45
46 fun newInstance(
47 titleId: Int,
48 description: String,
49 helpLinkId: Int = 0
50 ): LongMessageDialogFragment {
51 val dialog = LongMessageDialogFragment()
52 val bundle = Bundle()
53 bundle.apply {
54 putInt(TITLE, titleId)
55 putString(DESCRIPTION, description)
56 putInt(HELP_LINK, helpLinkId)
57 }
58 dialog.arguments = bundle
59 return dialog
60 }
61 }
62}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index dd6c895fd..f54dccc69 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
29import org.yuzu.yuzu_emu.model.Game 29import org.yuzu.yuzu_emu.model.Game
30import org.yuzu.yuzu_emu.model.GamesViewModel 30import org.yuzu.yuzu_emu.model.GamesViewModel
31import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.FileUtil
33 32
34class SearchFragment : Fragment() { 33class SearchFragment : Fragment() {
35 private var _binding: FragmentSearchBinding? = null 34 private var _binding: FragmentSearchBinding? = null
@@ -128,10 +127,7 @@ class SearchFragment : Fragment() {
128 127
129 R.id.chip_homebrew -> baseList.filter { it.isHomebrew } 128 R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
130 129
131 R.id.chip_retail -> baseList.filter { 130 R.id.chip_retail -> baseList.filter { !it.isHomebrew }
132 FileUtil.hasExtension(it.path, "xci") ||
133 FileUtil.hasExtension(it.path, "nsp")
134 }
135 131
136 else -> baseList 132 else -> baseList
137 } 133 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index 6a048e39f..6527c64ab 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -43,7 +43,7 @@ class Game(
43 43
44 companion object { 44 companion object {
45 val extensions: Set<String> = HashSet( 45 val extensions: Set<String> = HashSet(
46 listOf(".xci", ".nsp", ".nca", ".nro") 46 listOf("xci", "nsp", "nca", "nro")
47 ) 47 )
48 } 48 }
49} 49}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index cc1d87f1b..f7d7aed1e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.ui.main 4package org.yuzu.yuzu_emu.ui.main
5 5
6import android.content.Intent 6import android.content.Intent
7import android.net.Uri
7import android.os.Bundle 8import android.os.Bundle
8import android.view.View 9import android.view.View
9import android.view.ViewGroup.MarginLayoutParams 10import android.view.ViewGroup.MarginLayoutParams
@@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
42import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
46import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
46import org.yuzu.yuzu_emu.model.GamesViewModel 48import org.yuzu.yuzu_emu.model.GamesViewModel
47import org.yuzu.yuzu_emu.model.HomeViewModel 49import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -294,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
294 return@registerForActivityResult 296 return@registerForActivityResult
295 } 297 }
296 298
297 if (!FileUtil.hasExtension(result, "keys")) { 299 if (FileUtil.getExtension(result) != "keys") {
298 MessageDialogFragment.newInstance( 300 MessageDialogFragment.newInstance(
299 R.string.reading_keys_failure, 301 R.string.reading_keys_failure,
300 R.string.install_prod_keys_failure_extension_description 302 R.string.install_prod_keys_failure_extension_description
@@ -391,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
391 return@registerForActivityResult 393 return@registerForActivityResult
392 } 394 }
393 395
394 if (!FileUtil.hasExtension(result, "bin")) { 396 if (FileUtil.getExtension(result) != "bin") {
395 MessageDialogFragment.newInstance( 397 MessageDialogFragment.newInstance(
396 R.string.reading_keys_failure, 398 R.string.reading_keys_failure,
397 R.string.install_amiibo_keys_failure_extension_description 399 R.string.install_amiibo_keys_failure_extension_description
@@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
481 } 483 }
482 } 484 }
483 485
484 val installGameUpdate = 486 val installGameUpdate = registerForActivityResult(
485 registerForActivityResult(ActivityResultContracts.OpenDocument()) { 487 ActivityResultContracts.OpenMultipleDocuments()
486 if (it == null) { 488 ) { documents: List<Uri> ->
487 return@registerForActivityResult 489 if (documents.isNotEmpty()) {
488 }
489
490 IndeterminateProgressDialogFragment.newInstance( 490 IndeterminateProgressDialogFragment.newInstance(
491 this@MainActivity, 491 this@MainActivity,
492 R.string.install_game_content 492 R.string.install_game_content
493 ) { 493 ) {
494 val result = NativeLibrary.installFileToNand(it.toString()) 494 var installSuccess = 0
495 var installOverwrite = 0
496 var errorBaseGame = 0
497 var errorExtension = 0
498 var errorOther = 0
499 var errorTotal = 0
495 lifecycleScope.launch { 500 lifecycleScope.launch {
496 withContext(Dispatchers.Main) { 501 documents.forEach {
497 when (result) { 502 when (NativeLibrary.installFileToNand(it.toString())) {
498 NativeLibrary.InstallFileToNandResult.Success -> { 503 NativeLibrary.InstallFileToNandResult.Success -> {
499 Toast.makeText( 504 installSuccess += 1
500 applicationContext,
501 R.string.install_game_content_success,
502 Toast.LENGTH_SHORT
503 ).show()
504 } 505 }
505 506
506 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { 507 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
507 Toast.makeText( 508 installOverwrite += 1
508 applicationContext,
509 R.string.install_game_content_success_overwrite,
510 Toast.LENGTH_SHORT
511 ).show()
512 } 509 }
513 510
514 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { 511 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
515 MessageDialogFragment.newInstance( 512 errorBaseGame += 1
516 R.string.install_game_content_failure,
517 R.string.install_game_content_failure_base
518 ).show(supportFragmentManager, MessageDialogFragment.TAG)
519 } 513 }
520 514
521 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { 515 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
522 MessageDialogFragment.newInstance( 516 errorExtension += 1
523 R.string.install_game_content_failure,
524 R.string.install_game_content_failure_file_extension,
525 R.string.install_game_content_help_link
526 ).show(supportFragmentManager, MessageDialogFragment.TAG)
527 } 517 }
528 518
529 else -> { 519 else -> {
530 MessageDialogFragment.newInstance( 520 errorOther += 1
531 R.string.install_game_content_failure,
532 R.string.install_game_content_failure_description,
533 R.string.install_game_content_help_link
534 ).show(supportFragmentManager, MessageDialogFragment.TAG)
535 } 521 }
536 } 522 }
537 } 523 }
524 withContext(Dispatchers.Main) {
525 val separator = System.getProperty("line.separator") ?: "\n"
526 val installResult = StringBuilder()
527 if (installSuccess > 0) {
528 installResult.append(
529 getString(
530 R.string.install_game_content_success_install,
531 installSuccess
532 )
533 )
534 installResult.append(separator)
535 }
536 if (installOverwrite > 0) {
537 installResult.append(
538 getString(
539 R.string.install_game_content_success_overwrite,
540 installOverwrite
541 )
542 )
543 installResult.append(separator)
544 }
545 errorTotal = errorBaseGame + errorExtension + errorOther
546 if (errorTotal > 0) {
547 installResult.append(separator)
548 installResult.append(
549 getString(
550 R.string.install_game_content_failed_count,
551 errorTotal
552 )
553 )
554 installResult.append(separator)
555 if (errorBaseGame > 0) {
556 installResult.append(separator)
557 installResult.append(
558 getString(R.string.install_game_content_failure_base)
559 )
560 installResult.append(separator)
561 }
562 if (errorExtension > 0) {
563 installResult.append(separator)
564 installResult.append(
565 getString(R.string.install_game_content_failure_file_extension)
566 )
567 installResult.append(separator)
568 }
569 if (errorOther > 0) {
570 installResult.append(
571 getString(R.string.install_game_content_failure_description)
572 )
573 installResult.append(separator)
574 }
575 LongMessageDialogFragment.newInstance(
576 R.string.install_game_content_failure,
577 installResult.toString().trim(),
578 R.string.install_game_content_help_link
579 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
580 } else {
581 LongMessageDialogFragment.newInstance(
582 R.string.install_game_content_success,
583 installResult.toString().trim()
584 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
585 }
586 }
538 } 587 }
539 return@newInstance result 588 return@newInstance installSuccess + installOverwrite + errorTotal
540 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 589 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
541 } 590 }
591 }
542} 592}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 9f3bbe56f..142af5f26 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -7,7 +7,6 @@ import android.content.Context
7import android.database.Cursor 7import android.database.Cursor
8import android.net.Uri 8import android.net.Uri
9import android.provider.DocumentsContract 9import android.provider.DocumentsContract
10import android.provider.OpenableColumns
11import androidx.documentfile.provider.DocumentFile 10import androidx.documentfile.provider.DocumentFile
12import java.io.BufferedInputStream 11import java.io.BufferedInputStream
13import java.io.File 12import java.io.File
@@ -185,19 +184,18 @@ object FileUtil {
185 184
186 /** 185 /**
187 * Get file display name from given path 186 * Get file display name from given path
188 * @param path content uri path 187 * @param uri content uri
189 * @return String display name 188 * @return String display name
190 */ 189 */
191 fun getFilename(context: Context, path: String): String { 190 fun getFilename(uri: Uri): String {
192 val resolver = context.contentResolver 191 val resolver = YuzuApplication.appContext.contentResolver
193 val columns = arrayOf( 192 val columns = arrayOf(
194 DocumentsContract.Document.COLUMN_DISPLAY_NAME 193 DocumentsContract.Document.COLUMN_DISPLAY_NAME
195 ) 194 )
196 var filename = "" 195 var filename = ""
197 var c: Cursor? = null 196 var c: Cursor? = null
198 try { 197 try {
199 val mUri = Uri.parse(path) 198 c = resolver.query(uri, columns, null, null, null)
200 c = resolver.query(mUri, columns, null, null, null)
201 c!!.moveToNext() 199 c!!.moveToNext()
202 filename = c.getString(0) 200 filename = c.getString(0)
203 } catch (e: Exception) { 201 } catch (e: Exception) {
@@ -326,25 +324,9 @@ object FileUtil {
326 } 324 }
327 } 325 }
328 326
329 fun hasExtension(path: String, extension: String): Boolean = 327 fun getExtension(uri: Uri): String {
330 path.substring(path.lastIndexOf(".") + 1).contains(extension) 328 val fileName = getFilename(uri)
331 329 return fileName.substring(fileName.lastIndexOf(".") + 1)
332 fun hasExtension(uri: Uri, extension: String): Boolean { 330 .lowercase()
333 val fileName: String?
334 val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
335 val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
336 cursor?.moveToFirst()
337
338 if (nameIndex == null) {
339 return false
340 }
341
342 fileName = cursor.getString(nameIndex)
343 cursor.close()
344
345 if (fileName == null) {
346 return false
347 }
348 return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
349 } 331 }
350} 332}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index ee9f3e570..f8e7eeca7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
6import android.content.SharedPreferences 6import android.content.SharedPreferences
7import android.net.Uri 7import android.net.Uri
8import androidx.preference.PreferenceManager 8import androidx.preference.PreferenceManager
9import java.util.*
10import kotlinx.serialization.encodeToString 9import kotlinx.serialization.encodeToString
11import kotlinx.serialization.json.Json 10import kotlinx.serialization.json.Json
12import org.yuzu.yuzu_emu.NativeLibrary 11import org.yuzu.yuzu_emu.NativeLibrary
@@ -33,15 +32,9 @@ object GameHelper {
33 val children = FileUtil.listFiles(context, gamesUri) 32 val children = FileUtil.listFiles(context, gamesUri)
34 for (file in children) { 33 for (file in children) {
35 if (!file.isDirectory) { 34 if (!file.isDirectory) {
36 val filename = file.uri.toString() 35 // Check that the file has an extension we care about before trying to read out of it.
37 val extensionStart = filename.lastIndexOf('.') 36 if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
38 if (extensionStart > 0) { 37 games.add(getGame(file.uri))
39 val fileExtension = filename.substring(extensionStart)
40
41 // Check that the file has an extension we care about before trying to read out of it.
42 if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
43 games.add(getGame(filename))
44 }
45 } 38 }
46 } 39 }
47 } 40 }
@@ -59,21 +52,19 @@ object GameHelper {
59 return games.toList() 52 return games.toList()
60 } 53 }
61 54
62 private fun getGame(filePath: String): Game { 55 private fun getGame(uri: Uri): Game {
56 val filePath = uri.toString()
63 var name = NativeLibrary.getTitle(filePath) 57 var name = NativeLibrary.getTitle(filePath)
64 58
65 // If the game's title field is empty, use the filename. 59 // If the game's title field is empty, use the filename.
66 if (name.isEmpty()) { 60 if (name.isEmpty()) {
67 name = filePath.substring(filePath.lastIndexOf("/") + 1) 61 name = FileUtil.getFilename(uri)
68 } 62 }
69 var gameId = NativeLibrary.getGameId(filePath) 63 var gameId = NativeLibrary.getGameId(filePath)
70 64
71 // If the game's ID field is empty, use the filename without extension. 65 // If the game's ID field is empty, use the filename without extension.
72 if (gameId.isEmpty()) { 66 if (gameId.isEmpty()) {
73 gameId = filePath.substring( 67 gameId = name.substring(0, name.lastIndexOf("."))
74 filePath.lastIndexOf("/") + 1,
75 filePath.lastIndexOf(".")
76 )
77 } 68 }
78 69
79 val newGame = Game( 70 val newGame = Game(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index dad159481..1d4695a2a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -113,6 +113,8 @@ object GpuDriverHelper {
113 initializeDriverParameters(context) 113 initializeDriverParameters(context)
114 } 114 }
115 115
116 external fun supportsCustomDriverLoading(): Boolean
117
116 // Parse the custom driver metadata to retrieve the name. 118 // Parse the custom driver metadata to retrieve the name.
117 val customDriverName: String? 119 val customDriverName: String?
118 get() { 120 get() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
new file mode 100644
index 000000000..18e5fa0b0
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.app.ActivityManager
7import android.content.Context
8import org.yuzu.yuzu_emu.R
9import java.util.Locale
10
11class MemoryUtil(val context: Context) {
12
13 private val Long.floatForm: String
14 get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
15
16 private fun bytesToSizeUnit(size: Long): String {
17 return when {
18 size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
19 size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
20 size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
21 size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
22 size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
23 size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
24 else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
25 }
26 }
27
28 private val totalMemory =
29 with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
30 val memInfo = ActivityManager.MemoryInfo()
31 getMemoryInfo(memInfo)
32 memInfo.totalMem
33 }
34
35 fun isLessThan(minimum: Int, size: Long): Boolean {
36 return when (size) {
37 Kb -> totalMemory < Mb && totalMemory < minimum
38 Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
39 Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
40 Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
41 Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
42 Eb -> totalMemory / Eb < minimum
43 else -> totalMemory < Kb && totalMemory < minimum
44 }
45 }
46
47 fun getDeviceRAM(): String {
48 return bytesToSizeUnit(totalMemory)
49 }
50
51 companion object {
52 const val Kb: Long = 1024
53 const val Mb = Kb * 1024
54 const val Gb = Mb * 1024
55 const val Tb = Gb * 1024
56 const val Pb = Tb * 1024
57 const val Eb = Pb * 1024
58 }
59}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 041781577..e2ed08e9f 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,7 +14,6 @@ add_library(yuzu-android SHARED
14 id_cache.cpp 14 id_cache.cpp
15 id_cache.h 15 id_cache.h
16 native.cpp 16 native.cpp
17 native.h
18) 17)
19 18
20set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 19set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index f9617202b..8bc6a4a04 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -14,6 +14,7 @@
14#include <android/api-level.h> 14#include <android/api-level.h>
15#include <android/native_window_jni.h> 15#include <android/native_window_jni.h>
16#include <core/loader/nro.h> 16#include <core/loader/nro.h>
17#include <jni.h>
17 18
18#include "common/detached_tasks.h" 19#include "common/detached_tasks.h"
19#include "common/dynamic_library.h" 20#include "common/dynamic_library.h"
@@ -59,6 +60,9 @@
59#include "video_core/rasterizer_interface.h" 60#include "video_core/rasterizer_interface.h"
60#include "video_core/renderer_base.h" 61#include "video_core/renderer_base.h"
61 62
63#define jconst [[maybe_unused]] const auto
64#define jauto [[maybe_unused]] auto
65
62namespace { 66namespace {
63 67
64class EmulationSession final { 68class EmulationSession final {
@@ -98,8 +102,8 @@ public:
98 } 102 }
99 103
100 int InstallFileToNand(std::string filename) { 104 int InstallFileToNand(std::string filename) {
101 const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, 105 jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
102 std::size_t block_size) { 106 std::size_t block_size) {
103 if (src == nullptr || dest == nullptr) { 107 if (src == nullptr || dest == nullptr) {
104 return false; 108 return false;
105 } 109 }
@@ -108,10 +112,10 @@ public:
108 } 112 }
109 113
110 using namespace Common::Literals; 114 using namespace Common::Literals;
111 std::vector<u8> buffer(1_MiB); 115 [[maybe_unused]] std::vector<u8> buffer(1_MiB);
112 116
113 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { 117 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
114 const auto read = src->Read(buffer.data(), buffer.size(), i); 118 jconst read = src->Read(buffer.data(), buffer.size(), i);
115 dest->Write(buffer.data(), read, i); 119 dest->Write(buffer.data(), read, i);
116 } 120 }
117 return true; 121 return true;
@@ -128,14 +132,14 @@ public:
128 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 132 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
129 m_system.GetFileSystemController().CreateFactories(*m_vfs); 133 m_system.GetFileSystemController().CreateFactories(*m_vfs);
130 134
131 std::shared_ptr<FileSys::NSP> nsp; 135 [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
132 if (filename.ends_with("nsp")) { 136 if (filename.ends_with("nsp")) {
133 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); 137 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
134 if (nsp->IsExtractedType()) { 138 if (nsp->IsExtractedType()) {
135 return InstallError; 139 return InstallError;
136 } 140 }
137 } else if (filename.ends_with("xci")) { 141 } else if (filename.ends_with("xci")) {
138 const auto xci = 142 jconst xci =
139 std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); 143 std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
140 nsp = xci->GetSecurePartitionNSP(); 144 nsp = xci->GetSecurePartitionNSP();
141 } else { 145 } else {
@@ -150,7 +154,7 @@ public:
150 return InstallError; 154 return InstallError;
151 } 155 }
152 156
153 const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( 157 jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
154 *nsp, true, copy_func); 158 *nsp, true, copy_func);
155 159
156 switch (res) { 160 switch (res) {
@@ -233,10 +237,11 @@ public:
233 m_system.SetFilesystem(m_vfs); 237 m_system.SetFilesystem(m_vfs);
234 238
235 // Initialize system. 239 // Initialize system.
236 auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); 240 jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
237 m_software_keyboard = android_keyboard.get(); 241 m_software_keyboard = android_keyboard.get();
238 m_system.SetShuttingDown(false); 242 m_system.SetShuttingDown(false);
239 m_system.ApplySettings(); 243 m_system.ApplySettings();
244 Settings::LogSettings();
240 m_system.HIDCore().ReloadInputDevices(); 245 m_system.HIDCore().ReloadInputDevices();
241 m_system.SetAppletFrontendSet({ 246 m_system.SetAppletFrontendSet({
242 nullptr, // Amiibo Settings 247 nullptr, // Amiibo Settings
@@ -330,7 +335,7 @@ public:
330 335
331 while (true) { 336 while (true) {
332 { 337 {
333 std::unique_lock lock(m_mutex); 338 [[maybe_unused]] std::unique_lock lock(m_mutex);
334 if (m_cv.wait_for(lock, std::chrono::milliseconds(800), 339 if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
335 [&]() { return !m_is_running; })) { 340 [&]() { return !m_is_running; })) {
336 // Emulation halted. 341 // Emulation halted.
@@ -362,7 +367,7 @@ public:
362 } 367 }
363 368
364 bool IsHandheldOnly() { 369 bool IsHandheldOnly() {
365 const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); 370 jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
366 371
367 if (npad_style_set.fullkey == 1) { 372 if (npad_style_set.fullkey == 1) {
368 return false; 373 return false;
@@ -375,17 +380,17 @@ public:
375 return !Settings::values.use_docked_mode.GetValue(); 380 return !Settings::values.use_docked_mode.GetValue();
376 } 381 }
377 382
378 void SetDeviceType(int index, int type) { 383 void SetDeviceType([[maybe_unused]] int index, int type) {
379 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 384 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
380 controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); 385 controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
381 } 386 }
382 387
383 void OnGamepadConnectEvent(int index) { 388 void OnGamepadConnectEvent([[maybe_unused]] int index) {
384 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 389 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
385 390
386 // Ensure that player1 is configured correctly and handheld disconnected 391 // Ensure that player1 is configured correctly and handheld disconnected
387 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { 392 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
388 auto handheld = 393 jauto handheld =
389 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); 394 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
390 395
391 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { 396 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
@@ -397,7 +402,8 @@ public:
397 402
398 // Ensure that handheld is configured correctly and player 1 disconnected 403 // Ensure that handheld is configured correctly and player 1 disconnected
399 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { 404 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
400 auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 405 jauto player1 =
406 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
401 407
402 if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { 408 if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
403 player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 409 player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
@@ -411,8 +417,8 @@ public:
411 } 417 }
412 } 418 }
413 419
414 void OnGamepadDisconnectEvent(int index) { 420 void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
415 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 421 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
416 controller->Disconnect(); 422 controller->Disconnect();
417 } 423 }
418 424
@@ -428,7 +434,7 @@ private:
428 }; 434 };
429 435
430 RomMetadata GetRomMetadata(const std::string& path) { 436 RomMetadata GetRomMetadata(const std::string& path) {
431 if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { 437 if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
432 return search->second; 438 return search->second;
433 } 439 }
434 440
@@ -436,14 +442,14 @@ private:
436 } 442 }
437 443
438 RomMetadata CacheRomMetadata(const std::string& path) { 444 RomMetadata CacheRomMetadata(const std::string& path) {
439 const auto file = Core::GetGameFileFromPath(m_vfs, path); 445 jconst file = Core::GetGameFileFromPath(m_vfs, path);
440 auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); 446 jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
441 447
442 RomMetadata entry; 448 RomMetadata entry;
443 loader->ReadTitle(entry.title); 449 loader->ReadTitle(entry.title);
444 loader->ReadIcon(entry.icon); 450 loader->ReadIcon(entry.icon);
445 if (loader->GetFileType() == Loader::FileType::NRO) { 451 if (loader->GetFileType() == Loader::FileType::NRO) {
446 auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); 452 jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
447 entry.isHomebrew = loader_nro->IsHomebrew(); 453 entry.isHomebrew = loader_nro->IsHomebrew();
448 } else { 454 } else {
449 entry.isHomebrew = false; 455 entry.isHomebrew = false;
@@ -514,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
514 520
515 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 521 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
516 522
517 const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); 523 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
518 if (result != Core::SystemResultStatus::Success) { 524 if (result != Core::SystemResultStatus::Success) {
519 return result; 525 return result;
520 } 526 }
@@ -526,83 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
526 532
527extern "C" { 533extern "C" {
528 534
529void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, 535void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
530 [[maybe_unused]] jclass clazz, 536 [[maybe_unused]] jobject surf) {
531 jobject surf) {
532 EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); 537 EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
533 EmulationSession::GetInstance().SurfaceChanged(); 538 EmulationSession::GetInstance().SurfaceChanged();
534} 539}
535 540
536void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, 541void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
537 [[maybe_unused]] jclass clazz) {
538 ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); 542 ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
539 EmulationSession::GetInstance().SetNativeWindow(nullptr); 543 EmulationSession::GetInstance().SetNativeWindow(nullptr);
540 EmulationSession::GetInstance().SurfaceChanged(); 544 EmulationSession::GetInstance().SurfaceChanged();
541} 545}
542 546
543void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, 547void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
544 [[maybe_unused]] jclass clazz, 548 [[maybe_unused]] jstring j_directory) {
545 jstring j_directory) {
546 Common::FS::SetAppDirectory(GetJString(env, j_directory)); 549 Common::FS::SetAppDirectory(GetJString(env, j_directory));
547} 550}
548 551
549int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, 552int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
550 [[maybe_unused]] jclass clazz, 553 [[maybe_unused]] jstring j_file) {
551 jstring j_file) {
552 return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); 554 return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
553} 555}
554 556
555void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( 557void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
556 JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, 558 jstring hook_lib_dir,
557 jstring custom_driver_name, jstring file_redirect_dir) { 559 jstring custom_driver_dir,
560 jstring custom_driver_name,
561 jstring file_redirect_dir) {
558 EmulationSession::GetInstance().InitializeGpuDriver( 562 EmulationSession::GetInstance().InitializeGpuDriver(
559 GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), 563 GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
560 GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); 564 GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
561} 565}
562 566
563jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, 567[[maybe_unused]] static bool CheckKgslPresent() {
564 [[maybe_unused]] jclass clazz) { 568 constexpr auto KgslPath{"/dev/kgsl-3d0"};
569
570 return access(KgslPath, F_OK) == 0;
571}
572
573[[maybe_unused]] bool SupportsCustomDriver() {
574 return android_get_device_api_level() >= 28 && CheckKgslPresent();
575}
576
577jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
578 JNIEnv* env, jobject instance) {
579#ifdef ARCHITECTURE_arm64
580 // If the KGSL device exists custom drivers can be loaded using adrenotools
581 return SupportsCustomDriver();
582#else
583 return false;
584#endif
585}
586
587jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
565 Core::Crypto::KeyManager::Instance().ReloadKeys(); 588 Core::Crypto::KeyManager::Instance().ReloadKeys();
566 return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); 589 return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
567} 590}
568 591
569void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, 592void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
570 [[maybe_unused]] jclass clazz) {
571 EmulationSession::GetInstance().UnPauseEmulation(); 593 EmulationSession::GetInstance().UnPauseEmulation();
572} 594}
573 595
574void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, 596void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
575 [[maybe_unused]] jclass clazz) {
576 EmulationSession::GetInstance().PauseEmulation(); 597 EmulationSession::GetInstance().PauseEmulation();
577} 598}
578 599
579void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, 600void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
580 [[maybe_unused]] jclass clazz) {
581 EmulationSession::GetInstance().HaltEmulation(); 601 EmulationSession::GetInstance().HaltEmulation();
582} 602}
583 603
584void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, 604void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
585 [[maybe_unused]] jclass clazz) {
586 EmulationSession::GetInstance().ResetRomMetadata(); 605 EmulationSession::GetInstance().ResetRomMetadata();
587} 606}
588 607
589jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, 608jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
590 [[maybe_unused]] jclass clazz) {
591 return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); 609 return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
592} 610}
593 611
594jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env, 612jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
595 [[maybe_unused]] jclass clazz) {
596 return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); 613 return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
597} 614}
598 615
599jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, 616void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
600 [[maybe_unused]] jclass clazz) { 617 Settings::values.audio_muted = true;
618}
619
620void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
621 Settings::values.audio_muted = false;
622}
623
624jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
625 return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
626}
627
628jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
601 return EmulationSession::GetInstance().IsHandheldOnly(); 629 return EmulationSession::GetInstance().IsHandheldOnly();
602} 630}
603 631
604jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, 632jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
605 [[maybe_unused]] jclass clazz,
606 jint j_device, jint j_type) { 633 jint j_device, jint j_type) {
607 if (EmulationSession::GetInstance().IsRunning()) { 634 if (EmulationSession::GetInstance().IsRunning()) {
608 EmulationSession::GetInstance().SetDeviceType(j_device, j_type); 635 EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
@@ -610,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
610 return static_cast<jboolean>(true); 637 return static_cast<jboolean>(true);
611} 638}
612 639
613jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, 640jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
614 [[maybe_unused]] jclass clazz,
615 jint j_device) { 641 jint j_device) {
616 if (EmulationSession::GetInstance().IsRunning()) { 642 if (EmulationSession::GetInstance().IsRunning()) {
617 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); 643 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -619,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
619 return static_cast<jboolean>(true); 645 return static_cast<jboolean>(true);
620} 646}
621 647
622jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( 648jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
623 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { 649 jint j_device) {
624 if (EmulationSession::GetInstance().IsRunning()) { 650 if (EmulationSession::GetInstance().IsRunning()) {
625 EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); 651 EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
626 } 652 }
627 return static_cast<jboolean>(true); 653 return static_cast<jboolean>(true);
628} 654}
629jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, 655jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
630 [[maybe_unused]] jclass clazz, 656 jint j_device, jint j_button,
631 [[maybe_unused]] jint j_device, 657 jint action) {
632 jint j_button, jint action) {
633 if (EmulationSession::GetInstance().IsRunning()) { 658 if (EmulationSession::GetInstance().IsRunning()) {
634 // Ensure gamepad is connected 659 // Ensure gamepad is connected
635 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); 660 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -639,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
639 return static_cast<jboolean>(true); 664 return static_cast<jboolean>(true);
640} 665}
641 666
642jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, 667jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
643 [[maybe_unused]] jclass clazz,
644 jint j_device, jint stick_id, 668 jint j_device, jint stick_id,
645 jfloat x, jfloat y) { 669 jfloat x, jfloat y) {
646 if (EmulationSession::GetInstance().IsRunning()) { 670 if (EmulationSession::GetInstance().IsRunning()) {
@@ -650,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
650} 674}
651 675
652jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( 676jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
653 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, 677 JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
654 jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, 678 jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
655 jfloat accel_y, jfloat accel_z) {
656 if (EmulationSession::GetInstance().IsRunning()) { 679 if (EmulationSession::GetInstance().IsRunning()) {
657 EmulationSession::GetInstance().Window().OnGamepadMotionEvent( 680 EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
658 j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); 681 j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
@@ -660,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
660 return static_cast<jboolean>(true); 683 return static_cast<jboolean>(true);
661} 684}
662 685
663jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, 686jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
664 [[maybe_unused]] jclass clazz,
665 jbyteArray j_data) { 687 jbyteArray j_data) {
666 jboolean isCopy{false}; 688 jboolean isCopy{false};
667 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), 689 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
@@ -673,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI
673 return static_cast<jboolean>(true); 695 return static_cast<jboolean>(true);
674} 696}
675 697
676jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, 698jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
677 [[maybe_unused]] jclass clazz) {
678 if (EmulationSession::GetInstance().IsRunning()) { 699 if (EmulationSession::GetInstance().IsRunning()) {
679 EmulationSession::GetInstance().Window().OnRemoveNfcTag(); 700 EmulationSession::GetInstance().Window().OnRemoveNfcTag();
680 } 701 }
681 return static_cast<jboolean>(true); 702 return static_cast<jboolean>(true);
682} 703}
683 704
684void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, 705void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
685 [[maybe_unused]] jclass clazz, jint id,
686 jfloat x, jfloat y) { 706 jfloat x, jfloat y) {
687 if (EmulationSession::GetInstance().IsRunning()) { 707 if (EmulationSession::GetInstance().IsRunning()) {
688 EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); 708 EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
689 } 709 }
690} 710}
691 711
692void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, 712void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
693 [[maybe_unused]] jclass clazz, jint id,
694 jfloat x, jfloat y) { 713 jfloat x, jfloat y) {
695 if (EmulationSession::GetInstance().IsRunning()) { 714 if (EmulationSession::GetInstance().IsRunning()) {
696 EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); 715 EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
697 } 716 }
698} 717}
699 718
700void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, 719void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
701 [[maybe_unused]] jclass clazz, jint id) {
702 if (EmulationSession::GetInstance().IsRunning()) { 720 if (EmulationSession::GetInstance().IsRunning()) {
703 EmulationSession::GetInstance().Window().OnTouchReleased(id); 721 EmulationSession::GetInstance().Window().OnTouchReleased(id);
704 } 722 }
705} 723}
706 724
707jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, 725jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
708 [[maybe_unused]] jclass clazz, 726 jstring j_filename) {
709 [[maybe_unused]] jstring j_filename) { 727 jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
710 auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
711 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); 728 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
712 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), 729 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
713 reinterpret_cast<jbyte*>(icon_data.data())); 730 reinterpret_cast<jbyte*>(icon_data.data()));
714 return icon; 731 return icon;
715} 732}
716 733
717jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, 734jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
718 [[maybe_unused]] jclass clazz, 735 jstring j_filename) {
719 [[maybe_unused]] jstring j_filename) { 736 jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
720 auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
721 return env->NewStringUTF(title.c_str()); 737 return env->NewStringUTF(title.c_str());
722} 738}
723 739
724jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, 740jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
725 [[maybe_unused]] jclass clazz,
726 jstring j_filename) { 741 jstring j_filename) {
727 return j_filename; 742 return j_filename;
728} 743}
729 744
730jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, 745jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
731 [[maybe_unused]] jclass clazz,
732 jstring j_filename) { 746 jstring j_filename) {
733 return j_filename; 747 return j_filename;
734} 748}
735 749
736jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, 750jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
737 [[maybe_unused]] jclass clazz, 751 jstring j_filename) {
738 [[maybe_unused]] jstring j_filename) {
739 return env->NewStringUTF(""); 752 return env->NewStringUTF("");
740} 753}
741 754
742jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, 755jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
743 [[maybe_unused]] jclass clazz, 756 jstring j_filename) {
744 [[maybe_unused]] jstring j_filename) {
745 return env->NewStringUTF(""); 757 return env->NewStringUTF("");
746} 758}
747 759
748jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, 760jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
749 [[maybe_unused]] jclass clazz, 761 jstring j_filename) {
750 [[maybe_unused]] jstring j_filename) {
751 return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); 762 return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
752} 763}
753 764
754void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation 765void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
755 [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
756 // Create the default config.ini. 766 // Create the default config.ini.
757 Config{}; 767 Config{};
758 // Initialize the emulated system. 768 // Initialize the emulated system.
759 EmulationSession::GetInstance().System().Initialize(); 769 EmulationSession::GetInstance().System().Initialize();
760} 770}
761 771
762jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, 772jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
763 [[maybe_unused]] jclass clazz) {
764 return {}; 773 return {};
765} 774}
766 775
767void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( 776void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
768 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, 777 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
769 [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
770 778
771void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, 779void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
772 [[maybe_unused]] jclass clazz) {
773 Config{}; 780 Config{};
774} 781}
775 782
776jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env, 783jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
777 [[maybe_unused]] jclass clazz,
778 jstring j_game_id, jstring j_section, 784 jstring j_game_id, jstring j_section,
779 jstring j_key) { 785 jstring j_key) {
780 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 786 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -788,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN
788 return env->NewStringUTF(""); 794 return env->NewStringUTF("");
789} 795}
790 796
791void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env, 797void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
792 [[maybe_unused]] jclass clazz,
793 jstring j_game_id, jstring j_section, 798 jstring j_game_id, jstring j_section,
794 jstring j_key, jstring j_value) { 799 jstring j_key, jstring j_value) {
795 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 800 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -803,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn
803 env->ReleaseStringUTFChars(j_value, value.data()); 808 env->ReleaseStringUTFChars(j_value, value.data());
804} 809}
805 810
806void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env, 811void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
807 [[maybe_unused]] jclass clazz,
808 jstring j_game_id) { 812 jstring j_game_id) {
809 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 813 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
810 814
811 env->ReleaseStringUTFChars(j_game_id, game_id.data()); 815 env->ReleaseStringUTFChars(j_game_id, game_id.data());
812} 816}
813 817
814jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, 818jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
815 [[maybe_unused]] jclass clazz) {
816 jdoubleArray j_stats = env->NewDoubleArray(4); 819 jdoubleArray j_stats = env->NewDoubleArray(4);
817 820
818 if (EmulationSession::GetInstance().IsRunning()) { 821 if (EmulationSession::GetInstance().IsRunning()) {
819 const auto results = EmulationSession::GetInstance().PerfStats(); 822 jconst results = EmulationSession::GetInstance().PerfStats();
820 823
821 // Converting the structure into an array makes it easier to pass it to the frontend 824 // Converting the structure into an array makes it easier to pass it to the frontend
822 double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, 825 double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
@@ -828,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]]
828 return j_stats; 831 return j_stats;
829} 832}
830 833
831void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( 834void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
832 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} 835 jclass clazz,
836 jstring j_path) {}
833 837
834void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, 838void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
835 [[maybe_unused]] jclass clazz,
836 jstring j_path) { 839 jstring j_path) {
837 const std::string path = GetJString(env, j_path); 840 const std::string path = GetJString(env, j_path);
838 841
@@ -843,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
843 } 846 }
844} 847}
845 848
846void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, 849void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
847 [[maybe_unused]] jclass clazz) {
848 LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); 850 LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
849 LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); 851 LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
850} 852}
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
deleted file mode 100644
index 24dcbbcb8..000000000
--- a/src/android/app/src/main/jni/native.h
+++ /dev/null
@@ -1,165 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <jni.h>
7
8// Function calls from the Java side
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
14 jclass clazz);
15
16JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
17 jclass clazz);
18
19JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
20 jclass clazz);
21
22JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
23 jclass clazz);
24
25JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
26 jclass clazz);
27
28JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
29 jclass clazz);
30
31JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
32 jclass clazz,
33 jstring j_device,
34 jstring j_type);
35
36JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
37 JNIEnv* env, jclass clazz, jstring j_device);
38
39JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
40 JNIEnv* env, jclass clazz, jstring j_device);
41
42JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
43 JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
44
45JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
46 JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
47
48JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
49 JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
50
51JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
52 jclass clazz,
53 jbyteArray j_data);
54
55JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
56 jclass clazz);
57
58JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
59 jclass clazz,
60 jfloat x, jfloat y,
61 jboolean pressed);
62
63JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
64 jfloat x, jfloat y);
65
66JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
67 jclass clazz,
68 jstring j_file);
69
70JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
71 jstring j_filename);
72
73JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
74 jclass clazz,
75 jstring j_filename);
76
77JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
78 jstring j_filename);
79
80JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
81 jclass clazz,
82 jstring j_filename);
83
84JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
85 jclass clazz,
86 jstring j_filename);
87
88JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
89 jclass clazz);
90
91JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
92 jclass clazz,
93 jstring j_directory);
94
95JNIEXPORT void JNICALL
96Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
97 JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
98 jstring custom_driver_name, jstring file_redirect_dir);
99
100JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
101 jclass clazz);
102
103JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
104 JNIEnv* env, jclass clazz, jstring path_);
105
106JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
107 jclass clazz,
108 jstring path);
109
110JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
111 jclass clazz);
112
113JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
114 jclass clazz);
115JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
116 jboolean enable);
117
118JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
119 jclass clazz);
120
121JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
122 JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
123
124JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
125 JNIEnv* env, jclass clazz, jstring j_path);
126
127JNIEXPORT void JNICALL
128Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
129 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
130
131JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
132 jclass clazz,
133 jobject surf);
134
135JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
136 jclass clazz);
137
138JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
139 jstring j_game_id);
140
141JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
142 jclass clazz);
143
144JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
145 JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
146 jstring j_value);
147
148JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
149 JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
150
151JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
152 jclass clazz);
153
154JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
155 jclass clazz);
156
157JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
158 JNIEnv* env, jclass clazz, jstring j_text);
159
160JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
161 JNIEnv* env, jclass clazz, jint j_key_code);
162
163#ifdef __cplusplus
164}
165#endif
diff --git a/src/android/app/src/main/res/drawable/ic_pip_mute.xml b/src/android/app/src/main/res/drawable/ic_pip_mute.xml
new file mode 100644
index 000000000..a271c5fe8
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_mute.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="@android:color/white"
8 android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pip_unmute.xml b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml
new file mode 100644
index 000000000..f7ed0862e
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="@android:color/white"
8 android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
9</vector>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index cc1d8c39d..af7450619 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -104,12 +104,14 @@
104 <string name="share_log_missing">No log file found</string> 104 <string name="share_log_missing">No log file found</string>
105 <string name="install_game_content">Install game content</string> 105 <string name="install_game_content">Install game content</string>
106 <string name="install_game_content_description">Install game updates or DLC</string> 106 <string name="install_game_content_description">Install game updates or DLC</string>
107 <string name="install_game_content_failure">Error installing file to NAND</string> 107 <string name="install_game_content_failure">Error installing file(s) to NAND</string>
108 <string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string> 108 <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
109 <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string> 109 <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
110 <string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string> 110 <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
111 <string name="install_game_content_success">Game content installed successfully</string> 111 <string name="install_game_content_failed_count">%1$d installation error(s)</string>
112 <string name="install_game_content_success_overwrite">Game content was overwritten successfully</string> 112 <string name="install_game_content_success">Game content(s) installed successfully</string>
113 <string name="install_game_content_success_install">%1$d installed successfully</string>
114 <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
113 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> 115 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
114 116
115 <!-- About screen strings --> 117 <!-- About screen strings -->
@@ -270,6 +272,7 @@
270 <string name="fatal_error">Fatal Error</string> 272 <string name="fatal_error">Fatal Error</string>
271 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> 273 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
272 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> 274 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
275 <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
273 276
274 <!-- Region Names --> 277 <!-- Region Names -->
275 <string name="region_japan">Japan</string> 278 <string name="region_japan">Japan</string>
@@ -300,6 +303,15 @@
300 <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string> 303 <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
301 <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> 304 <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
302 305
306 <!-- Memory Sizes -->
307 <string name="memory_byte">Byte</string>
308 <string name="memory_kilobyte">KB</string>
309 <string name="memory_megabyte">MB</string>
310 <string name="memory_gigabyte">GB</string>
311 <string name="memory_terabyte">TB</string>
312 <string name="memory_petabyte">PB</string>
313 <string name="memory_exabyte">EB</string>
314
303 <!-- Renderer APIs --> 315 <!-- Renderer APIs -->
304 <string name="renderer_vulkan">Vulkan</string> 316 <string name="renderer_vulkan">Vulkan</string>
305 <string name="renderer_none">None</string> 317 <string name="renderer_none">None</string>
@@ -387,6 +399,8 @@
387 <string name="picture_in_picture_description">Minimize window when placed in the background</string> 399 <string name="picture_in_picture_description">Minimize window when placed in the background</string>
388 <string name="pause">Pause</string> 400 <string name="pause">Pause</string>
389 <string name="play">Play</string> 401 <string name="play">Play</string>
402 <string name="mute">Mute</string>
403 <string name="unmute">Unmute</string>
390 404
391 <!-- Licenses screen strings --> 405 <!-- Licenses screen strings -->
392 <string name="licenses">Licenses</string> 406 <string name="licenses">Licenses</string>
diff --git a/src/android/gradle.properties b/src/android/gradle.properties
index e2f278f33..4fca1b576 100644
--- a/src/android/gradle.properties
+++ b/src/android/gradle.properties
@@ -15,3 +15,6 @@ android.useAndroidX=true
15kotlin.code.style=official 15kotlin.code.style=official
16kotlin.parallel.tasks.in.project=true 16kotlin.parallel.tasks.in.project=true
17android.defaults.buildfeatures.buildconfig=true 17android.defaults.buildfeatures.buildconfig=true
18
19# Android Gradle plugin 8.0.2
20android.suppressUnsupportedCompileSdk=34
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h
index 15082f6c6..5d8ed0ef7 100644
--- a/src/audio_core/device/audio_buffers.h
+++ b/src/audio_core/device/audio_buffers.h
@@ -7,6 +7,7 @@
7#include <mutex> 7#include <mutex>
8#include <span> 8#include <span>
9#include <vector> 9#include <vector>
10#include <boost/container/static_vector.hpp>
10 11
11#include "audio_buffer.h" 12#include "audio_buffer.h"
12#include "audio_core/device/device_session.h" 13#include "audio_core/device/device_session.h"
@@ -48,7 +49,7 @@ public:
48 * 49 *
49 * @param out_buffers - The buffers which were registered. 50 * @param out_buffers - The buffers which were registered.
50 */ 51 */
51 void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) { 52 void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) {
52 std::scoped_lock l{lock}; 53 std::scoped_lock l{lock};
53 const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit), 54 const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
54 BufferAppendLimit - registered_count)}; 55 BufferAppendLimit - registered_count)};
@@ -162,7 +163,8 @@ public:
162 * @param max_buffers - Maximum number of buffers to released. 163 * @param max_buffers - Maximum number of buffers to released.
163 * @return The number of buffers released. 164 * @return The number of buffers released.
164 */ 165 */
165 u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) { 166 u32 GetRegisteredAppendedBuffers(
167 boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) {
166 std::scoped_lock l{lock}; 168 std::scoped_lock l{lock};
167 if (registered_count + appended_count == 0) { 169 if (registered_count + appended_count == 0) {
168 return 0; 170 return 0;
@@ -270,7 +272,7 @@ public:
270 */ 272 */
271 bool FlushBuffers(u32& buffers_released) { 273 bool FlushBuffers(u32& buffers_released) {
272 std::scoped_lock l{lock}; 274 std::scoped_lock l{lock};
273 std::vector<AudioBuffer> buffers_flushed{}; 275 boost::container::static_vector<AudioBuffer, N> buffers_flushed{};
274 276
275 buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit); 277 buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
276 278
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index b5c0ef0e6..86811fcb8 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() {
79 } 79 }
80} 80}
81 81
82void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { 82void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
83 for (const auto& buffer : buffers) { 83 for (const auto& buffer : buffers) {
84 Sink::SinkBuffer new_buffer{ 84 Sink::SinkBuffer new_buffer{
85 .frames = buffer.size / (channel_count * sizeof(s16)), 85 .frames = buffer.size / (channel_count * sizeof(s16)),
@@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
88 .consumed = false, 88 .consumed = false,
89 }; 89 };
90 90
91 tmp_samples.resize_destructive(buffer.size / sizeof(s16));
91 if (type == Sink::StreamType::In) { 92 if (type == Sink::StreamType::In) {
92 std::vector<s16> samples{}; 93 stream->AppendBuffer(new_buffer, tmp_samples);
93 stream->AppendBuffer(new_buffer, samples);
94 } else { 94 } else {
95 std::vector<s16> samples(buffer.size / sizeof(s16)); 95 system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(),
96 system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size); 96 buffer.size);
97 stream->AppendBuffer(new_buffer, samples); 97 stream->AppendBuffer(new_buffer, tmp_samples);
98 } 98 }
99 } 99 }
100} 100}
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h
index 75f766c68..7d52f362d 100644
--- a/src/audio_core/device/device_session.h
+++ b/src/audio_core/device/device_session.h
@@ -10,6 +10,7 @@
10 10
11#include "audio_core/common/common.h" 11#include "audio_core/common/common.h"
12#include "audio_core/sink/sink.h" 12#include "audio_core/sink/sink.h"
13#include "common/scratch_buffer.h"
13#include "core/hle/service/audio/errors.h" 14#include "core/hle/service/audio/errors.h"
14 15
15namespace Core { 16namespace Core {
@@ -62,7 +63,7 @@ public:
62 * 63 *
63 * @param buffers - The buffers to play. 64 * @param buffers - The buffers to play.
64 */ 65 */
65 void AppendBuffers(std::span<const AudioBuffer> buffers) const; 66 void AppendBuffers(std::span<const AudioBuffer> buffers);
66 67
67 /** 68 /**
68 * (Audio In only) Pop samples from the backend, and write them back to this buffer's address. 69 * (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
@@ -146,8 +147,8 @@ private:
146 std::shared_ptr<Core::Timing::EventType> thread_event; 147 std::shared_ptr<Core::Timing::EventType> thread_event;
147 /// Is this session initialised? 148 /// Is this session initialised?
148 bool initialized{}; 149 bool initialized{};
149 /// Buffer queue 150 /// Temporary sample buffer
150 std::vector<AudioBuffer> buffer_queue{}; 151 Common::ScratchBuffer<s16> tmp_samples{};
151}; 152};
152 153
153} // namespace AudioCore 154} // namespace AudioCore
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index e23e51758..579129121 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.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 <mutex> 4#include <mutex>
5
5#include "audio_core/audio_event.h" 6#include "audio_core/audio_event.h"
6#include "audio_core/audio_manager.h" 7#include "audio_core/audio_manager.h"
7#include "audio_core/in/audio_in_system.h" 8#include "audio_core/in/audio_in_system.h"
@@ -89,7 +90,7 @@ Result System::Start() {
89 session->Start(); 90 session->Start();
90 state = State::Started; 91 state = State::Started;
91 92
92 std::vector<AudioBuffer> buffers_to_flush{}; 93 boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
93 buffers.RegisterBuffers(buffers_to_flush); 94 buffers.RegisterBuffers(buffers_to_flush);
94 session->AppendBuffers(buffers_to_flush); 95 session->AppendBuffers(buffers_to_flush);
95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); 96 session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
134 135
135void System::RegisterBuffers() { 136void System::RegisterBuffers() {
136 if (state == State::Started) { 137 if (state == State::Started) {
137 std::vector<AudioBuffer> registered_buffers{}; 138 boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
138 buffers.RegisterBuffers(registered_buffers); 139 buffers.RegisterBuffers(registered_buffers);
139 session->AppendBuffers(registered_buffers); 140 session->AppendBuffers(registered_buffers);
140 } 141 }
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index bd13f7219..0adf64bd3 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -89,7 +89,7 @@ Result System::Start() {
89 session->Start(); 89 session->Start();
90 state = State::Started; 90 state = State::Started;
91 91
92 std::vector<AudioBuffer> buffers_to_flush{}; 92 boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
93 buffers.RegisterBuffers(buffers_to_flush); 93 buffers.RegisterBuffers(buffers_to_flush);
94 session->AppendBuffers(buffers_to_flush); 94 session->AppendBuffers(buffers_to_flush);
95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); 95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
134 134
135void System::RegisterBuffers() { 135void System::RegisterBuffers() {
136 if (state == State::Started) { 136 if (state == State::Started) {
137 std::vector<AudioBuffer> registered_buffers{}; 137 boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
138 buffers.RegisterBuffers(registered_buffers); 138 buffers.RegisterBuffers(registered_buffers);
139 session->AppendBuffers(registered_buffers); 139 session->AppendBuffers(registered_buffers);
140 } 140 }
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp
index 74772fc50..b1db31e93 100644
--- a/src/audio_core/renderer/adsp/adsp.cpp
+++ b/src/audio_core/renderer/adsp/adsp.cpp
@@ -7,7 +7,6 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/core_timing_util.h"
11#include "core/memory.h" 10#include "core/memory.h"
12 11
13namespace AudioCore::AudioRenderer::ADSP { 12namespace AudioCore::AudioRenderer::ADSP {
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
index 8bc39f9f9..9ca716b60 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -13,7 +13,6 @@
13#include "common/thread.h" 13#include "common/thread.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/core_timing_util.h"
17 16
18MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); 17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
19 18
@@ -144,6 +143,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
144 143
145 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); 144 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
146 145
146 // 0.12 seconds (2304000 / 19200000)
147 constexpr u64 max_process_time{2'304'000ULL}; 147 constexpr u64 max_process_time{2'304'000ULL};
148 148
149 while (!stop_token.stop_requested()) { 149 while (!stop_token.stop_requested()) {
@@ -184,8 +184,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
184 u64 max_time{max_process_time}; 184 u64 max_time{max_process_time};
185 if (index == 1 && command_buffer.applet_resource_user_id == 185 if (index == 1 && command_buffer.applet_resource_user_id ==
186 mailbox->GetCommandBuffer(0).applet_resource_user_id) { 186 mailbox->GetCommandBuffer(0).applet_resource_user_id) {
187 max_time = max_process_time - 187 max_time = max_process_time - render_times_taken[0];
188 Core::Timing::CyclesToNs(render_times_taken[0]).count();
189 if (render_times_taken[0] > max_process_time) { 188 if (render_times_taken[0] > max_process_time) {
190 max_time = 0; 189 max_time = 0;
191 } 190 }
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/renderer/adsp/command_list_processor.cpp
index 7a300d216..3a0f1ae38 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.cpp
+++ b/src/audio_core/renderer/adsp/command_list_processor.cpp
@@ -9,7 +9,6 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/core_timing_util.h"
13#include "core/memory.h" 12#include "core/memory.h"
14 13
15namespace AudioCore::AudioRenderer::ADSP { 14namespace AudioCore::AudioRenderer::ADSP {
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp
index ff5d31bd6..f45933203 100644
--- a/src/audio_core/renderer/command/data_source/decode.cpp
+++ b/src/audio_core/renderer/command/data_source/decode.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/renderer/command/resample/resample.h" 8#include "audio_core/renderer/command/resample/resample.h"
9#include "common/fixed_point.h" 9#include "common/fixed_point.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scratch_buffer.h"
11#include "core/memory.h" 12#include "core/memory.h"
12 13
13namespace AudioCore::AudioRenderer { 14namespace AudioCore::AudioRenderer {
@@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
27template <typename T> 28template <typename T>
28static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 29static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
29 const DecodeArg& req) { 30 const DecodeArg& req) {
31 std::array<T, TempBufferSize> tmp_samples{};
30 constexpr s32 min{std::numeric_limits<s16>::min()}; 32 constexpr s32 min{std::numeric_limits<s16>::min()};
31 constexpr s32 max{std::numeric_limits<s16>::max()}; 33 constexpr s32 max{std::numeric_limits<s16>::max()};
32 34
@@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
49 const u64 size{channel_count * samples_to_decode}; 51 const u64 size{channel_count * samples_to_decode};
50 const u64 size_bytes{size * sizeof(T)}; 52 const u64 size_bytes{size * sizeof(T)};
51 53
52 std::vector<T> samples(size); 54 memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
53 memory.ReadBlockUnsafe(source, samples.data(), size_bytes);
54 55
55 if constexpr (std::is_floating_point_v<T>) { 56 if constexpr (std::is_floating_point_v<T>) {
56 for (u32 i = 0; i < samples_to_decode; i++) { 57 for (u32 i = 0; i < samples_to_decode; i++) {
57 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * 58 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
58 std::numeric_limits<s16>::max())}; 59 std::numeric_limits<s16>::max())};
59 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 60 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
60 } 61 }
61 } else { 62 } else {
62 for (u32 i = 0; i < samples_to_decode; i++) { 63 for (u32 i = 0; i < samples_to_decode; i++) {
63 out_buffer[i] = samples[i * channel_count + req.target_channel]; 64 out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];
64 } 65 }
65 } 66 }
66 } break; 67 } break;
@@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
73 } 74 }
74 75
75 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; 76 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
76 std::vector<T> samples(samples_to_decode); 77 memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));
77 memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T));
78 78
79 if constexpr (std::is_floating_point_v<T>) { 79 if constexpr (std::is_floating_point_v<T>) {
80 for (u32 i = 0; i < samples_to_decode; i++) { 80 for (u32 i = 0; i < samples_to_decode; i++) {
81 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * 81 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
82 std::numeric_limits<s16>::max())}; 82 std::numeric_limits<s16>::max())};
83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
84 } 84 }
85 } else { 85 } else {
86 std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16)); 86 std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));
87 } 87 }
88 break; 88 break;
89 } 89 }
@@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
101 */ 101 */
102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
103 const DecodeArg& req) { 103 const DecodeArg& req) {
104 std::array<u8, TempBufferSize> wavebuffer{};
104 constexpr u32 SamplesPerFrame{14}; 105 constexpr u32 SamplesPerFrame{14};
105 constexpr u32 NibblesPerFrame{16}; 106 constexpr u32 NibblesPerFrame{16};
106 107
@@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
138 } 139 }
139 140
140 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; 141 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
141 std::vector<u8> wavebuffer(size); 142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);
142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(),
143 wavebuffer.size());
144 143
145 auto context{req.adpcm_context}; 144 auto context{req.adpcm_context};
146 auto header{context->header}; 145 auto header{context->header};
@@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
258 u32 offset{voice_state.offset}; 257 u32 offset{voice_state.offset};
259 258
260 auto output_buffer{args.output}; 259 auto output_buffer{args.output};
261 std::vector<s16> temp_buffer(TempBufferSize, 0); 260 std::array<s16, TempBufferSize> temp_buffer{};
262 261
263 while (remaining_sample_count > 0) { 262 while (remaining_sample_count > 0) {
264 const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)}; 263 const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp
index 7229618e8..ee9b68d5b 100644
--- a/src/audio_core/renderer/command/effect/compressor.cpp
+++ b/src/audio_core/renderer/command/effect/compressor.cpp
@@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2&
44 44
45static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params, 45static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,
46 CompressorInfo::State& state, bool enabled, 46 CompressorInfo::State& state, bool enabled,
47 std::vector<std::span<const s32>> input_buffers, 47 std::span<std::span<const s32>> input_buffers,
48 std::vector<std::span<s32>> output_buffers, u32 sample_count) { 48 std::span<std::span<s32>> output_buffers, u32 sample_count) {
49 if (enabled) { 49 if (enabled) {
50 auto state_00{state.unk_00}; 50 auto state_00{state.unk_00};
51 auto state_04{state.unk_04}; 51 auto state_04{state.unk_04};
@@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
124} 124}
125 125
126void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { 126void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
127 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 127 std::array<std::span<const s32>, MaxChannels> input_buffers{};
128 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 128 std::array<std::span<s32>, MaxChannels> output_buffers{};
129 129
130 for (s16 i = 0; i < parameter.channel_count; i++) { 130 for (s16 i = 0; i < parameter.channel_count; i++) {
131 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 131 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp
index a4e408d40..e536cbb1e 100644
--- a/src/audio_core/renderer/command/effect/delay.cpp
+++ b/src/audio_core/renderer/command/effect/delay.cpp
@@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
51 state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor(); 51 state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
52 state.delay_lines[channel].sample_count = sample_count.to_int_floor(); 52 state.delay_lines[channel].sample_count = sample_count.to_int_floor();
53 state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0); 53 state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
54 if (state.delay_lines[channel].buffer.size() == 0) { 54 if (state.delay_lines[channel].sample_count == 0) {
55 state.delay_lines[channel].buffer.push_back(0); 55 state.delay_lines[channel].buffer.push_back(0);
56 } 56 }
57 state.delay_lines[channel].buffer_pos = 0; 57 state.delay_lines[channel].buffer_pos = 0;
@@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
74 */ 74 */
75template <size_t NumChannels> 75template <size_t NumChannels>
76static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, 76static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
77 std::vector<std::span<const s32>>& inputs, 77 std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs,
78 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 78 const u32 sample_count) {
79 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { 79 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
80 std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{}; 80 std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
81 for (u32 channel = 0; channel < NumChannels; channel++) { 81 for (u32 channel = 0; channel < NumChannels; channel++) {
@@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St
153 * @param sample_count - Number of samples to process. 153 * @param sample_count - Number of samples to process.
154 */ 154 */
155static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, 155static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
156 const bool enabled, std::vector<std::span<const s32>>& inputs, 156 const bool enabled, std::span<std::span<const s32>> inputs,
157 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 157 std::span<std::span<s32>> outputs, const u32 sample_count) {
158 158
159 if (!IsChannelCountValid(params.channel_count)) { 159 if (!IsChannelCountValid(params.channel_count)) {
160 LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count); 160 LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
@@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce
208} 208}
209 209
210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { 210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
211 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 211 std::array<std::span<const s32>, MaxChannels> input_buffers{};
212 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 212 std::array<std::span<s32>, MaxChannels> output_buffers{};
213 213
214 for (s16 i = 0; i < parameter.channel_count; i++) { 214 for (s16 i = 0; i < parameter.channel_count; i++) {
215 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 215 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index 27d8b9844..d2bfb67cc 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
408} 408}
409 409
410void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 410void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
411 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 411 std::array<std::span<const s32>, MaxChannels> input_buffers{};
412 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 412 std::array<std::span<s32>, MaxChannels> output_buffers{};
413 413
414 for (u32 i = 0; i < parameter.channel_count; i++) { 414 for (u32 i = 0; i < parameter.channel_count; i++) {
415 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 415 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp
index e8fb0e2fc..4161a9821 100644
--- a/src/audio_core/renderer/command/effect/light_limiter.cpp
+++ b/src/audio_core/renderer/command/effect/light_limiter.cpp
@@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio
47 */ 47 */
48static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params, 48static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
49 LightLimiterInfo::State& state, const bool enabled, 49 LightLimiterInfo::State& state, const bool enabled,
50 std::vector<std::span<const s32>>& inputs, 50 std::span<std::span<const s32>> inputs,
51 std::vector<std::span<s32>>& outputs, const u32 sample_count, 51 std::span<std::span<s32>> outputs, const u32 sample_count,
52 LightLimiterInfo::StatisticsInternal* statistics) { 52 LightLimiterInfo::StatisticsInternal* statistics) {
53 constexpr s64 min{std::numeric_limits<s32>::min()}; 53 constexpr s64 min{std::numeric_limits<s32>::min()};
54 constexpr s64 max{std::numeric_limits<s32>::max()}; 54 constexpr s64 max{std::numeric_limits<s32>::max()};
@@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP
147} 147}
148 148
149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
150 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 150 std::array<std::span<const s32>, MaxChannels> input_buffers{};
151 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 151 std::array<std::span<s32>, MaxChannels> output_buffers{};
152 152
153 for (u32 i = 0; i < parameter.channel_count; i++) { 153 for (u32 i = 0; i < parameter.channel_count; i++) {
154 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 154 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
190} 190}
191 191
192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
193 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 193 std::array<std::span<const s32>, MaxChannels> input_buffers{};
194 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 194 std::array<std::span<s32>, MaxChannels> output_buffers{};
195 195
196 for (u32 i = 0; i < parameter.channel_count; i++) { 196 for (u32 i = 0; i < parameter.channel_count; i++) {
197 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 197 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index 8b9b65214..fc2f15a5e 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
250 */ 250 */
251template <size_t NumChannels> 251template <size_t NumChannels>
252static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, 252static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
253 std::vector<std::span<const s32>>& inputs, 253 std::span<std::span<const s32>> inputs,
254 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 254 std::span<std::span<s32>> outputs, const u32 sample_count) {
255 static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{ 255 static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
256 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 256 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
257 }; 257 };
@@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
369 * @param sample_count - Number of samples to process. 369 * @param sample_count - Number of samples to process.
370 */ 370 */
371static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, 371static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
372 const bool enabled, std::vector<std::span<const s32>>& inputs, 372 const bool enabled, std::span<std::span<const s32>> inputs,
373 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 373 std::span<std::span<s32>> outputs, const u32 sample_count) {
374 if (enabled) { 374 if (enabled) {
375 switch (params.channel_count) { 375 switch (params.channel_count) {
376 case 0: 376 case 0:
@@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
412} 412}
413 413
414void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 414void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
415 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 415 std::array<std::span<const s32>, MaxChannels> input_buffers{};
416 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 416 std::array<std::span<s32>, MaxChannels> output_buffers{};
417 417
418 for (u32 i = 0; i < parameter.channel_count; i++) { 418 for (u32 i = 0; i < parameter.channel_count; i++) {
419 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 419 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp
index 985958b03..4a881547f 100644
--- a/src/audio_core/renderer/command/performance/performance.cpp
+++ b/src/audio_core/renderer/command/performance/performance.cpp
@@ -5,7 +5,6 @@
5#include "audio_core/renderer/command/performance/performance.h" 5#include "audio_core/renderer/command/performance/performance.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9 8
10namespace AudioCore::AudioRenderer { 9namespace AudioCore::AudioRenderer {
11 10
@@ -18,20 +17,18 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
18 auto base{entry_address.translated_address}; 17 auto base{entry_address.translated_address};
19 if (state == PerformanceState::Start) { 18 if (state == PerformanceState::Start) {
20 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; 19 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
21 *start_time_ptr = static_cast<u32>( 20 *start_time_ptr =
22 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - 21 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
23 processor.start_time - processor.current_processing_time) 22 processor.current_processing_time);
24 .count());
25 } else if (state == PerformanceState::Stop) { 23 } else if (state == PerformanceState::Stop) {
26 auto processed_time_ptr{ 24 auto processed_time_ptr{
27 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; 25 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
28 auto entry_count_ptr{ 26 auto entry_count_ptr{
29 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; 27 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
30 28
31 *processed_time_ptr = static_cast<u32>( 29 *processed_time_ptr =
32 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - 30 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
33 processor.start_time - processor.current_processing_time) 31 processor.current_processing_time);
34 .count());
35 (*entry_count_ptr)++; 32 (*entry_count_ptr)++;
36 } 33 }
37} 34}
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp
index ded5afc94..e2ce59792 100644
--- a/src/audio_core/renderer/command/sink/circular_buffer.cpp
+++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp
@@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
24 constexpr s32 min{std::numeric_limits<s16>::min()}; 24 constexpr s32 min{std::numeric_limits<s16>::min()};
25 constexpr s32 max{std::numeric_limits<s16>::max()}; 25 constexpr s32 max{std::numeric_limits<s16>::max()};
26 26
27 std::vector<s16> output(processor.sample_count); 27 std::array<s16, TargetSampleCount * MaxChannels> output{};
28 for (u32 channel = 0; channel < input_count; channel++) { 28 for (u32 channel = 0; channel < input_count; channel++) {
29 auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count, 29 auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
30 processor.sample_count)}; 30 processor.sample_count)};
@@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
33 } 33 }
34 34
35 processor.memory->WriteBlockUnsafe(address + pos, output.data(), 35 processor.memory->WriteBlockUnsafe(address + pos, output.data(),
36 output.size() * sizeof(s16)); 36 processor.sample_count * sizeof(s16));
37 pos += static_cast<u32>(processor.sample_count * sizeof(s16)); 37 pos += static_cast<u32>(processor.sample_count * sizeof(s16));
38 if (pos >= size) { 38 if (pos >= size) {
39 pos = 0; 39 pos = 0;
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp
index e88372a75..5f74dd7ad 100644
--- a/src/audio_core/renderer/command/sink/device.cpp
+++ b/src/audio_core/renderer/command/sink/device.cpp
@@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
33 .consumed{false}, 33 .consumed{false},
34 }; 34 };
35 35
36 std::vector<s16> samples(out_buffer.frames * input_count); 36 std::array<s16, TargetSampleCount * MaxChannels> samples{};
37
38 for (u32 channel = 0; channel < input_count; channel++) { 37 for (u32 channel = 0; channel < input_count; channel++) {
39 const auto offset{inputs[channel] * out_buffer.frames}; 38 const auto offset{inputs[channel] * out_buffer.frames};
40 39
@@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
45 } 44 }
46 45
47 out_buffer.tag = reinterpret_cast<u64>(samples.data()); 46 out_buffer.tag = reinterpret_cast<u64>(samples.data());
48 stream->AppendBuffer(out_buffer, samples); 47 stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count});
49 48
50 if (stream->IsPaused()) { 49 if (stream->IsPaused()) {
51 stream->Start(); 50 stream->Start();
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
index 35b748ede..3a18ae7c2 100644
--- a/src/audio_core/renderer/mix/mix_context.cpp
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) {
125 return false; 125 return false;
126 } 126 }
127 127
128 std::vector<s32> sorted_results{node_states.GetSortedResuls()}; 128 auto sorted_results{node_states.GetSortedResuls()};
129 const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))}; 129 const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))};
130 for (s32 i = 0; i < result_size; i++) { 130 for (s32 i = 0; i < result_size; i++) {
131 sorted_mix_infos[i] = &mix_infos[sorted_results[i]]; 131 sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]];
132 } 132 }
133 133
134 CalcMixBufferOffset(); 134 CalcMixBufferOffset();
diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp
index 1821a51e6..b7a44a54c 100644
--- a/src/audio_core/renderer/nodes/node_states.cpp
+++ b/src/audio_core/renderer/nodes/node_states.cpp
@@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const {
134 return node_count; 134 return node_count;
135} 135}
136 136
137std::vector<s32> NodeStates::GetSortedResuls() const { 137std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const {
138 return {results.rbegin(), results.rbegin() + result_pos}; 138 return {results.rbegin(), result_pos};
139} 139}
140 140
141} // namespace AudioCore::AudioRenderer 141} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h
index 94b1d1254..e768cd4b5 100644
--- a/src/audio_core/renderer/nodes/node_states.h
+++ b/src/audio_core/renderer/nodes/node_states.h
@@ -175,7 +175,7 @@ public:
175 * 175 *
176 * @return Vector of nodes in reverse order. 176 * @return Vector of nodes in reverse order.
177 */ 177 */
178 std::vector<s32> GetSortedResuls() const; 178 std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const;
179 179
180private: 180private:
181 /// Number of nodes in the graph 181 /// Number of nodes in the graph
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index 53b258c4f..a23627472 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
444 std::scoped_lock l{lock}; 444 std::scoped_lock l{lock};
445 445
446 const auto start_time{core.CoreTiming().GetClockTicks()}; 446 const auto start_time{core.CoreTiming().GetClockTicks()};
447 std::memset(output.data(), 0, output.size());
447 448
448 InfoUpdater info_updater(input, output, process_handle, behavior); 449 InfoUpdater info_updater(input, output, process_handle, behavior);
449 450
diff --git a/src/audio_core/sink/null_sink.h b/src/audio_core/sink/null_sink.h
index 1215d3cd2..b6b43c93e 100644
--- a/src/audio_core/sink/null_sink.h
+++ b/src/audio_core/sink/null_sink.h
@@ -20,7 +20,7 @@ public:
20 explicit NullSinkStreamImpl(Core::System& system_, StreamType type_) 20 explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)
21 : SinkStream{system_, type_} {} 21 : SinkStream{system_, type_} {}
22 ~NullSinkStreamImpl() override {} 22 ~NullSinkStreamImpl() override {}
23 void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {} 23 void AppendBuffer(SinkBuffer&, std::span<s16>) override {}
24 std::vector<s16> ReleaseBuffer(u64) override { 24 std::vector<s16> ReleaseBuffer(u64) override {
25 return {}; 25 return {};
26 } 26 }
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index f44fedfd5..404dcd0e9 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -15,11 +15,10 @@
15#include "common/settings.h" 15#include "common/settings.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/core_timing_util.h"
19 18
20namespace AudioCore::Sink { 19namespace AudioCore::Sink {
21 20
22void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { 21void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
23 if (type == StreamType::In) { 22 if (type == StreamType::In) {
24 queue.enqueue(buffer); 23 queue.enqueue(buffer);
25 queued_buffers++; 24 queued_buffers++;
@@ -67,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
67 static_cast<s16>(std::clamp(right_sample, min, max)); 66 static_cast<s16>(std::clamp(right_sample, min, max));
68 } 67 }
69 68
70 samples.resize(samples.size() / system_channels * device_channels); 69 samples = samples.subspan(0, samples.size() / system_channels * device_channels);
71 70
72 } else if (system_channels == 2 && device_channels == 6) { 71 } else if (system_channels == 2 && device_channels == 6) {
73 // We need moar samples! Not all games will provide 6 channel audio. 72 // We need moar samples! Not all games will provide 6 channel audio.
74 // TODO: Implement some upmixing here. Currently just passthrough, with other 73 // TODO: Implement some upmixing here. Currently just passthrough, with other
75 // channels left as silence. 74 // channels left as silence.
76 std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); 75 auto new_size = samples.size() / system_channels * device_channels;
76 tmp_samples.resize_destructive(new_size);
77 77
78 for (u32 read_index = 0, write_index = 0; read_index < samples.size(); 78 for (u32 read_index = 0, write_index = 0; read_index < new_size;
79 read_index += system_channels, write_index += device_channels) { 79 read_index += system_channels, write_index += device_channels) {
80 const auto left_sample{static_cast<s16>(std::clamp( 80 const auto left_sample{static_cast<s16>(std::clamp(
81 static_cast<s32>( 81 static_cast<s32>(
@@ -83,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
83 volume), 83 volume),
84 min, max))}; 84 min, max))};
85 85
86 new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; 86 tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
87 87
88 const auto right_sample{static_cast<s16>(std::clamp( 88 const auto right_sample{static_cast<s16>(std::clamp(
89 static_cast<s32>( 89 static_cast<s32>(
@@ -91,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
91 volume), 91 volume),
92 min, max))}; 92 min, max))};
93 93
94 new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; 94 tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
95 } 95 }
96 samples = std::move(new_samples); 96 samples = std::span<s16>(tmp_samples);
97 97
98 } else if (volume != 1.0f) { 98 } else if (volume != 1.0f) {
99 for (u32 i = 0; i < samples.size(); i++) { 99 for (u32 i = 0; i < samples.size(); i++) {
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 41cbadc9c..98d72ace1 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -16,6 +16,7 @@
16#include "common/polyfill_thread.h" 16#include "common/polyfill_thread.h"
17#include "common/reader_writer_queue.h" 17#include "common/reader_writer_queue.h"
18#include "common/ring_buffer.h" 18#include "common/ring_buffer.h"
19#include "common/scratch_buffer.h"
19#include "common/thread.h" 20#include "common/thread.h"
20 21
21namespace Core { 22namespace Core {
@@ -170,7 +171,7 @@ public:
170 * @param buffer - Audio buffer information to be queued. 171 * @param buffer - Audio buffer information to be queued.
171 * @param samples - The s16 samples to be queue for playback. 172 * @param samples - The s16 samples to be queue for playback.
172 */ 173 */
173 virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples); 174 virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);
174 175
175 /** 176 /**
176 * Release a buffer. Audio In only, will fill a buffer with recorded samples. 177 * Release a buffer. Audio In only, will fill a buffer with recorded samples.
@@ -255,6 +256,8 @@ private:
255 /// Signalled when ring buffer entries are consumed 256 /// Signalled when ring buffer entries are consumed
256 std::condition_variable_any release_cv; 257 std::condition_variable_any release_cv;
257 std::mutex release_mutex; 258 std::mutex release_mutex;
259 /// Temporary buffer for appending samples when upmixing
260 Common::ScratchBuffer<s16> tmp_samples{};
258}; 261};
259 262
260using SinkStreamPtr = std::unique_ptr<SinkStream>; 263using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index efc4a9fe9..3adf13a3f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,6 +172,8 @@ if(ARCHITECTURE_x86_64)
172 x64/cpu_wait.h 172 x64/cpu_wait.h
173 x64/native_clock.cpp 173 x64/native_clock.cpp
174 x64/native_clock.h 174 x64/native_clock.h
175 x64/rdtsc.cpp
176 x64/rdtsc.h
175 x64/xbyak_abi.h 177 x64/xbyak_abi.h
176 x64/xbyak_util.h 178 x64/xbyak_util.h
177 ) 179 )
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index 6d66c926d..36e67c145 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -436,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
436 436
437 if (True(filter & DirEntryFilter::File) && 437 if (True(filter & DirEntryFilter::File) &&
438 entry.status().type() == fs::file_type::regular) { 438 entry.status().type() == fs::file_type::regular) {
439 if (!callback(entry.path())) { 439 if (!callback(entry)) {
440 callback_error = true; 440 callback_error = true;
441 break; 441 break;
442 } 442 }
@@ -444,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
444 444
445 if (True(filter & DirEntryFilter::Directory) && 445 if (True(filter & DirEntryFilter::Directory) &&
446 entry.status().type() == fs::file_type::directory) { 446 entry.status().type() == fs::file_type::directory) {
447 if (!callback(entry.path())) { 447 if (!callback(entry)) {
448 callback_error = true; 448 callback_error = true;
449 break; 449 break;
450 } 450 }
@@ -493,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
493 493
494 if (True(filter & DirEntryFilter::File) && 494 if (True(filter & DirEntryFilter::File) &&
495 entry.status().type() == fs::file_type::regular) { 495 entry.status().type() == fs::file_type::regular) {
496 if (!callback(entry.path())) { 496 if (!callback(entry)) {
497 callback_error = true; 497 callback_error = true;
498 break; 498 break;
499 } 499 }
@@ -501,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
501 501
502 if (True(filter & DirEntryFilter::Directory) && 502 if (True(filter & DirEntryFilter::Directory) &&
503 entry.status().type() == fs::file_type::directory) { 503 entry.status().type() == fs::file_type::directory) {
504 if (!callback(entry.path())) { 504 if (!callback(entry)) {
505 callback_error = true; 505 callback_error = true;
506 break; 506 break;
507 } 507 }
@@ -605,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
605} 605}
606 606
607u64 GetSize(const fs::path& path) { 607u64 GetSize(const fs::path& path) {
608#ifdef ANDROID
609 if (Android::IsContentUri(path)) {
610 return Android::GetSize(path);
611 }
612#endif
613
608 std::error_code ec; 614 std::error_code ec;
609 615
610 const auto file_size = fs::file_size(path, ec); 616 const auto file_size = fs::file_size(path, ec);
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h
index 5a4090c19..900f85d24 100644
--- a/src/common/fs/fs_types.h
+++ b/src/common/fs/fs_types.h
@@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
66 * @returns A boolean value. 66 * @returns A boolean value.
67 * Return true to indicate whether the callback is successful, false otherwise. 67 * Return true to indicate whether the callback is successful, false otherwise.
68 */ 68 */
69using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; 69using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;
70 70
71} // namespace Common::FS 71} // namespace Common::FS
diff --git a/src/common/input.h b/src/common/input.h
index 66fb15f0a..2c4ccea22 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -75,8 +75,10 @@ enum class DriverResult {
75 ErrorWritingData, 75 ErrorWritingData,
76 NoDeviceDetected, 76 NoDeviceDetected,
77 InvalidHandle, 77 InvalidHandle,
78 InvalidParameters,
78 NotSupported, 79 NotSupported,
79 Disabled, 80 Disabled,
81 Delayed,
80 Unknown, 82 Unknown,
81}; 83};
82 84
@@ -86,7 +88,7 @@ enum class NfcState {
86 NewAmiibo, 88 NewAmiibo,
87 WaitingForAmiibo, 89 WaitingForAmiibo,
88 AmiiboRemoved, 90 AmiiboRemoved,
89 NotAnAmiibo, 91 InvalidTagType,
90 NotSupported, 92 NotSupported,
91 WrongDeviceState, 93 WrongDeviceState,
92 WriteFailed, 94 WriteFailed,
@@ -218,8 +220,22 @@ struct CameraStatus {
218}; 220};
219 221
220struct NfcStatus { 222struct NfcStatus {
221 NfcState state{}; 223 NfcState state{NfcState::Unknown};
222 std::vector<u8> data{}; 224 u8 uuid_length;
225 u8 protocol;
226 u8 tag_type;
227 std::array<u8, 10> uuid;
228};
229
230struct MifareData {
231 u8 command;
232 u8 sector;
233 std::array<u8, 0x6> key;
234 std::array<u8, 0x10> data;
235};
236
237struct MifareRequest {
238 std::array<MifareData, 0x10> data;
223}; 239};
224 240
225// List of buttons to be passed to Qt that can be translated 241// List of buttons to be passed to Qt that can be translated
@@ -294,7 +310,7 @@ struct CallbackStatus {
294 BatteryStatus battery_status{}; 310 BatteryStatus battery_status{};
295 VibrationStatus vibration_status{}; 311 VibrationStatus vibration_status{};
296 CameraFormat camera_status{CameraFormat::None}; 312 CameraFormat camera_status{CameraFormat::None};
297 NfcState nfc_status{NfcState::Unknown}; 313 NfcStatus nfc_status{};
298 std::vector<u8> raw_data{}; 314 std::vector<u8> raw_data{};
299}; 315};
300 316
@@ -356,9 +372,30 @@ public:
356 return NfcState::NotSupported; 372 return NfcState::NotSupported;
357 } 373 }
358 374
375 virtual NfcState StartNfcPolling() {
376 return NfcState::NotSupported;
377 }
378
379 virtual NfcState StopNfcPolling() {
380 return NfcState::NotSupported;
381 }
382
383 virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
384 return NfcState::NotSupported;
385 }
386
359 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { 387 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
360 return NfcState::NotSupported; 388 return NfcState::NotSupported;
361 } 389 }
390
391 virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
392 [[maybe_unused]] MifareRequest& out_data) {
393 return NfcState::NotSupported;
394 }
395
396 virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
397 return NfcState::NotSupported;
398 }
362}; 399};
363 400
364/// An abstract class template for a factory that can create input devices. 401/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 4c328ab44..416680d44 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -9,6 +9,7 @@
9#include <cstddef> 9#include <cstddef>
10#include <cstring> 10#include <cstring>
11#include <new> 11#include <new>
12#include <span>
12#include <type_traits> 13#include <type_traits>
13#include <vector> 14#include <vector>
14 15
@@ -53,7 +54,7 @@ public:
53 return push_count; 54 return push_count;
54 } 55 }
55 56
56 std::size_t Push(const std::vector<T>& input) { 57 std::size_t Push(const std::span<T> input) {
57 return Push(input.data(), input.size()); 58 return Push(input.data(), input.size());
58 } 59 }
59 60
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
index a69a5a7af..6fe907953 100644
--- a/src/common/scratch_buffer.h
+++ b/src/common/scratch_buffer.h
@@ -3,6 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <iterator>
7
8#include "common/concepts.h"
6#include "common/make_unique_for_overwrite.h" 9#include "common/make_unique_for_overwrite.h"
7 10
8namespace Common { 11namespace Common {
@@ -16,6 +19,12 @@ namespace Common {
16template <typename T> 19template <typename T>
17class ScratchBuffer { 20class ScratchBuffer {
18public: 21public:
22 using iterator = T*;
23 using const_iterator = const T*;
24 using value_type = T;
25 using element_type = T;
26 using iterator_category = std::contiguous_iterator_tag;
27
19 ScratchBuffer() = default; 28 ScratchBuffer() = default;
20 29
21 explicit ScratchBuffer(size_t initial_capacity) 30 explicit ScratchBuffer(size_t initial_capacity)
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 66dffc9bf..6cbbea1b2 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -1,8 +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 <version>
4#if __cpp_lib_chrono >= 201907L 5#if __cpp_lib_chrono >= 201907L
5#include <chrono> 6#include <chrono>
7#include <exception>
8#include <stdexcept>
6#endif 9#endif
7#include <string_view> 10#include <string_view>
8 11
@@ -25,9 +28,19 @@ std::string GetTimeZoneString() {
25 if (time_zone_index == 0) { // Auto 28 if (time_zone_index == 0) { // Auto
26#if __cpp_lib_chrono >= 201907L 29#if __cpp_lib_chrono >= 201907L
27 const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); 30 const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
28 const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); 31 try {
29 std::string_view current_zone_name = current_zone->name(); 32 const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
30 location_name = current_zone_name; 33 std::string_view current_zone_name = current_zone->name();
34 location_name = current_zone_name;
35 } catch (std::runtime_error& runtime_error) {
36 // VCRUNTIME will throw a runtime_error if the operating system's selected time zone
37 // cannot be found
38 location_name = Common::TimeZone::FindSystemTimeZone();
39 LOG_WARNING(Common,
40 "Error occurred when trying to determine system time zone:\n{}\nFalling "
41 "back to hour offset \"{}\"",
42 runtime_error.what(), location_name);
43 }
31#else 44#else
32 location_name = Common::TimeZone::FindSystemTimeZone(); 45 location_name = Common::TimeZone::FindSystemTimeZone();
33#endif 46#endif
diff --git a/src/common/settings.h b/src/common/settings.h
index 9682281b0..ae5ed93d8 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -483,6 +483,7 @@ struct Values {
483 AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, 483 AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
484 "astc_recompression"}; 484 "astc_recompression"};
485 SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; 485 SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
486 SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
486 487
487 SwitchableSetting<u8> bg_red{0, "bg_red"}; 488 SwitchableSetting<u8> bg_red{0, "bg_red"};
488 SwitchableSetting<u8> bg_green{0, "bg_green"}; 489 SwitchableSetting<u8> bg_green{0, "bg_green"};
@@ -524,9 +525,16 @@ struct Values {
524 Setting<bool> tas_loop{false, "tas_loop"}; 525 Setting<bool> tas_loop{false, "tas_loop"};
525 526
526 Setting<bool> mouse_panning{false, "mouse_panning"}; 527 Setting<bool> mouse_panning{false, "mouse_panning"};
527 Setting<u8, true> mouse_panning_sensitivity{50, 1, 100, "mouse_panning_sensitivity"}; 528 Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"};
528 Setting<bool> mouse_enabled{false, "mouse_enabled"}; 529 Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"};
530 Setting<u8, true> mouse_panning_deadzone_x_counterweight{
531 0, 0, 100, "mouse_panning_deadzone_x_counterweight"};
532 Setting<u8, true> mouse_panning_deadzone_y_counterweight{
533 0, 0, 100, "mouse_panning_deadzone_y_counterweight"};
534 Setting<u8, true> mouse_panning_decay_strength{22, 0, 100, "mouse_panning_decay_strength"};
535 Setting<u8, true> mouse_panning_min_decay{5, 0, 100, "mouse_panning_min_decay"};
529 536
537 Setting<bool> mouse_enabled{false, "mouse_enabled"};
530 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 538 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
531 Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; 539 Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
532 540
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
index 782859196..9415eed29 100644
--- a/src/common/steady_clock.cpp
+++ b/src/common/steady_clock.cpp
@@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() {
28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. 28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
29 static constexpr s64 Multiplier = 100; 29 static constexpr s64 Multiplier = 100;
30 // Convert Windows epoch to Unix epoch. 30 // Convert Windows epoch to Unix epoch.
31 static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL; 31 static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
32 32
33 FILETIME filetime; 33 FILETIME filetime;
34 GetSystemTimePreciseAsFileTime(&filetime); 34 GetSystemTimePreciseAsFileTime(&filetime);
35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) + 35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
36 static_cast<s64>(filetime.dwLowDateTime)) - 36 static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
37 WindowsEpochToUnixEpochNS;
38} 37}
39#endif 38#endif
40 39
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 91352912d..929ed67e4 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -93,6 +93,7 @@ void AppendCPUInfo(FieldCollection& fc) {
93 add_field("CPU_Extension_x64_GFNI", caps.gfni); 93 add_field("CPU_Extension_x64_GFNI", caps.gfni);
94 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); 94 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
95 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); 95 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
96 add_field("CPU_Extension_x64_MONITORX", caps.monitorx);
96 add_field("CPU_Extension_x64_MOVBE", caps.movbe); 97 add_field("CPU_Extension_x64_MOVBE", caps.movbe);
97 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); 98 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
98 add_field("CPU_Extension_x64_POPCNT", caps.popcnt); 99 add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
diff --git a/src/common/thread.h b/src/common/thread.h
index 8ae169b4e..c6976fb6c 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -55,7 +55,7 @@ public:
55 is_set = false; 55 is_set = false;
56 } 56 }
57 57
58 [[nodiscard]] bool IsSet() { 58 [[nodiscard]] bool IsSet() const {
59 return is_set; 59 return is_set;
60 } 60 }
61 61
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 817e71d52..dc0dcbd68 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -2,88 +2,75 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/steady_clock.h" 4#include "common/steady_clock.h"
5#include "common/uint128.h"
6#include "common/wall_clock.h" 5#include "common/wall_clock.h"
7 6
8#ifdef ARCHITECTURE_x86_64 7#ifdef ARCHITECTURE_x86_64
9#include "common/x64/cpu_detect.h" 8#include "common/x64/cpu_detect.h"
10#include "common/x64/native_clock.h" 9#include "common/x64/native_clock.h"
10#include "common/x64/rdtsc.h"
11#endif 11#endif
12 12
13namespace Common { 13namespace Common {
14 14
15class StandardWallClock final : public WallClock { 15class StandardWallClock final : public WallClock {
16public: 16public:
17 explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) 17 explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
18 : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
19 start_time{SteadyClock::Now()} {}
20 18
21 std::chrono::nanoseconds GetTimeNS() override { 19 std::chrono::nanoseconds GetTimeNS() const override {
22 return SteadyClock::Now() - start_time; 20 return SteadyClock::Now() - start_time;
23 } 21 }
24 22
25 std::chrono::microseconds GetTimeUS() override { 23 std::chrono::microseconds GetTimeUS() const override {
26 return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS()); 24 return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
27 } 25 }
28 26
29 std::chrono::milliseconds GetTimeMS() override { 27 std::chrono::milliseconds GetTimeMS() const override {
30 return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS()); 28 return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
31 } 29 }
32 30
33 u64 GetClockCycles() override { 31 u64 GetCNTPCT() const override {
34 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency); 32 return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
35 return Common::Divide128On32(temp, NS_RATIO).first;
36 } 33 }
37 34
38 u64 GetCPUCycles() override { 35 u64 GetGPUTick() const override {
39 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency); 36 return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
40 return Common::Divide128On32(temp, NS_RATIO).first;
41 } 37 }
42 38
43 void Pause([[maybe_unused]] bool is_paused) override { 39 u64 GetHostTicksNow() const override {
44 // Do nothing in this clock type. 40 return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
41 }
42
43 u64 GetHostTicksElapsed() const override {
44 return static_cast<u64>(GetTimeNS().count());
45 }
46
47 bool IsNative() const override {
48 return false;
45 } 49 }
46 50
47private: 51private:
48 SteadyClock::time_point start_time; 52 SteadyClock::time_point start_time;
49}; 53};
50 54
55std::unique_ptr<WallClock> CreateOptimalClock() {
51#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
52
53std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
54 u64 emulated_clock_frequency) {
55 const auto& caps = GetCPUCaps(); 57 const auto& caps = GetCPUCaps();
56 u64 rtsc_frequency = 0;
57 if (caps.invariant_tsc) {
58 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
59 }
60 58
61 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 59 if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) {
62 // - A nanosecond 60 return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
63 // - The emulated CPU frequency
64 // - The emulated clock counter frequency (CNTFRQ)
65 if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
66 rtsc_frequency <= emulated_clock_frequency) {
67 return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
68 emulated_clock_frequency);
69 } else { 61 } else {
70 return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, 62 // Fallback to StandardWallClock if the hardware TSC
71 rtsc_frequency); 63 // - Is not invariant
64 // - Is not more precise than GPUTickFreq
65 return std::make_unique<StandardWallClock>();
72 } 66 }
73}
74
75#else 67#else
76 68 return std::make_unique<StandardWallClock>();
77std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
78 u64 emulated_clock_frequency) {
79 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
80}
81
82#endif 69#endif
70}
83 71
84std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 72std::unique_ptr<WallClock> CreateStandardWallClock() {
85 u64 emulated_clock_frequency) { 73 return std::make_unique<StandardWallClock>();
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87} 74}
88 75
89} // namespace Common 76} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 157ec5eae..f45d3d8c5 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -5,6 +5,7 @@
5 5
6#include <chrono> 6#include <chrono>
7#include <memory> 7#include <memory>
8#include <ratio>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -12,50 +13,82 @@ namespace Common {
12 13
13class WallClock { 14class WallClock {
14public: 15public:
15 static constexpr u64 NS_RATIO = 1'000'000'000; 16 static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
16 static constexpr u64 US_RATIO = 1'000'000; 17 static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
17 static constexpr u64 MS_RATIO = 1'000; 18 static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
18 19
19 virtual ~WallClock() = default; 20 virtual ~WallClock() = default;
20 21
21 /// Returns current wall time in nanoseconds 22 /// @returns The time in nanoseconds since the construction of this clock.
22 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0; 23 virtual std::chrono::nanoseconds GetTimeNS() const = 0;
23 24
24 /// Returns current wall time in microseconds 25 /// @returns The time in microseconds since the construction of this clock.
25 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0; 26 virtual std::chrono::microseconds GetTimeUS() const = 0;
26 27
27 /// Returns current wall time in milliseconds 28 /// @returns The time in milliseconds since the construction of this clock.
28 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0; 29 virtual std::chrono::milliseconds GetTimeMS() const = 0;
29 30
30 /// Returns current wall time in emulated clock cycles 31 /// @returns The guest CNTPCT ticks since the construction of this clock.
31 [[nodiscard]] virtual u64 GetClockCycles() = 0; 32 virtual u64 GetCNTPCT() const = 0;
32 33
33 /// Returns current wall time in emulated cpu cycles 34 /// @returns The guest GPU ticks since the construction of this clock.
34 [[nodiscard]] virtual u64 GetCPUCycles() = 0; 35 virtual u64 GetGPUTick() const = 0;
35 36
36 virtual void Pause(bool is_paused) = 0; 37 /// @returns The raw host timer ticks since an indeterminate epoch.
38 virtual u64 GetHostTicksNow() const = 0;
37 39
38 /// Tells if the wall clock, uses the host CPU's hardware clock 40 /// @returns The raw host timer ticks since the construction of this clock.
39 [[nodiscard]] bool IsNative() const { 41 virtual u64 GetHostTicksElapsed() const = 0;
40 return is_native; 42
43 /// @returns Whether the clock directly uses the host's hardware clock.
44 virtual bool IsNative() const = 0;
45
46 static inline u64 NSToCNTPCT(u64 ns) {
47 return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
48 }
49
50 static inline u64 NSToGPUTick(u64 ns) {
51 return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
52 }
53
54 // Cycle Timing
55
56 static inline u64 CPUTickToNS(u64 cpu_tick) {
57 return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
58 }
59
60 static inline u64 CPUTickToUS(u64 cpu_tick) {
61 return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
62 }
63
64 static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
65 return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
66 }
67
68 static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
69 return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
41 } 70 }
42 71
43protected: 72protected:
44 explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_) 73 using NsRatio = std::nano;
45 : emulated_cpu_frequency{emulated_cpu_frequency_}, 74 using UsRatio = std::micro;
46 emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {} 75 using MsRatio = std::milli;
76
77 using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
78 using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
79 using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
80 using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
47 81
48 u64 emulated_cpu_frequency; 82 // Cycle Timing
49 u64 emulated_clock_frequency;
50 83
51private: 84 using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
52 bool is_native; 85 using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
86 using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
87 using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
53}; 88};
54 89
55[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, 90std::unique_ptr<WallClock> CreateOptimalClock();
56 u64 emulated_clock_frequency);
57 91
58[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 92std::unique_ptr<WallClock> CreateStandardWallClock();
59 u64 emulated_clock_frequency);
60 93
61} // namespace Common 94} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 72ed6e96c..780120a5b 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -14,6 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/x64/cpu_detect.h" 16#include "common/x64/cpu_detect.h"
17#include "common/x64/rdtsc.h"
17 18
18#ifdef _WIN32 19#ifdef _WIN32
19#include <windows.h> 20#include <windows.h>
@@ -167,6 +168,7 @@ static CPUCaps Detect() {
167 __cpuid(cpu_id, 0x80000001); 168 __cpuid(cpu_id, 0x80000001);
168 caps.lzcnt = Common::Bit<5>(cpu_id[2]); 169 caps.lzcnt = Common::Bit<5>(cpu_id[2]);
169 caps.fma4 = Common::Bit<16>(cpu_id[2]); 170 caps.fma4 = Common::Bit<16>(cpu_id[2]);
171 caps.monitorx = Common::Bit<29>(cpu_id[2]);
170 } 172 }
171 173
172 if (max_ex_fn >= 0x80000007) { 174 if (max_ex_fn >= 0x80000007) {
@@ -187,6 +189,8 @@ static CPUCaps Detect() {
187 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * 189 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
188 caps.tsc_crystal_ratio_numerator / 190 caps.tsc_crystal_ratio_numerator /
189 caps.tsc_crystal_ratio_denominator; 191 caps.tsc_crystal_ratio_denominator;
192 } else {
193 caps.tsc_frequency = X64::EstimateRDTSCFrequency();
190 } 194 }
191 } 195 }
192 196
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 8253944d6..756459417 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -63,6 +63,7 @@ struct CPUCaps {
63 bool gfni : 1; 63 bool gfni : 1;
64 bool invariant_tsc : 1; 64 bool invariant_tsc : 1;
65 bool lzcnt : 1; 65 bool lzcnt : 1;
66 bool monitorx : 1;
66 bool movbe : 1; 67 bool movbe : 1;
67 bool pclmulqdq : 1; 68 bool pclmulqdq : 1;
68 bool popcnt : 1; 69 bool popcnt : 1;
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp
index cfeef6a3d..41d385f59 100644
--- a/src/common/x64/cpu_wait.cpp
+++ b/src/common/x64/cpu_wait.cpp
@@ -9,58 +9,64 @@
9 9
10#include "common/x64/cpu_detect.h" 10#include "common/x64/cpu_detect.h"
11#include "common/x64/cpu_wait.h" 11#include "common/x64/cpu_wait.h"
12#include "common/x64/rdtsc.h"
12 13
13namespace Common::X64 { 14namespace Common::X64 {
14 15
16namespace {
17
18// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
19// For reference:
20// At 1 GHz, 100K cycles is 100us
21// At 2 GHz, 100K cycles is 50us
22// At 4 GHz, 100K cycles is 25us
23constexpr auto PauseCycles = 100'000U;
24
25} // Anonymous namespace
26
15#ifdef _MSC_VER 27#ifdef _MSC_VER
16__forceinline static u64 FencedRDTSC() { 28__forceinline static void TPAUSE() {
17 _mm_lfence(); 29 static constexpr auto RequestC02State = 0U;
18 _ReadWriteBarrier(); 30 _tpause(RequestC02State, FencedRDTSC() + PauseCycles);
19 const u64 result = __rdtsc();
20 _mm_lfence();
21 _ReadWriteBarrier();
22 return result;
23} 31}
24 32
25__forceinline static void TPAUSE() { 33__forceinline static void MWAITX() {
26 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 34 static constexpr auto EnableWaitTimeFlag = 1U << 1;
27 // For reference: 35 static constexpr auto RequestC1State = 0U;
28 // At 1 GHz, 100K cycles is 100us 36
29 // At 2 GHz, 100K cycles is 50us 37 // monitor_var should be aligned to a cache line.
30 // At 4 GHz, 100K cycles is 25us 38 alignas(64) u64 monitor_var{};
31 static constexpr auto PauseCycles = 100'000; 39 _mm_monitorx(&monitor_var, 0, 0);
32 _tpause(0, FencedRDTSC() + PauseCycles); 40 _mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
33} 41}
34#else 42#else
35static u64 FencedRDTSC() {
36 u64 eax;
37 u64 edx;
38 asm volatile("lfence\n\t"
39 "rdtsc\n\t"
40 "lfence\n\t"
41 : "=a"(eax), "=d"(edx));
42 return (edx << 32) | eax;
43}
44
45static void TPAUSE() { 43static void TPAUSE() {
46 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 44 static constexpr auto RequestC02State = 0U;
47 // For reference:
48 // At 1 GHz, 100K cycles is 100us
49 // At 2 GHz, 100K cycles is 50us
50 // At 4 GHz, 100K cycles is 25us
51 static constexpr auto PauseCycles = 100'000;
52 const auto tsc = FencedRDTSC() + PauseCycles; 45 const auto tsc = FencedRDTSC() + PauseCycles;
53 const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF); 46 const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
54 const auto edx = static_cast<u32>(tsc >> 32); 47 const auto edx = static_cast<u32>(tsc >> 32);
55 asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax)); 48 asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
49}
50
51static void MWAITX() {
52 static constexpr auto EnableWaitTimeFlag = 1U << 1;
53 static constexpr auto RequestC1State = 0U;
54
55 // monitor_var should be aligned to a cache line.
56 alignas(64) u64 monitor_var{};
57 asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
58 asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
56} 59}
57#endif 60#endif
58 61
59void MicroSleep() { 62void MicroSleep() {
60 static const bool has_waitpkg = GetCPUCaps().waitpkg; 63 static const bool has_waitpkg = GetCPUCaps().waitpkg;
64 static const bool has_monitorx = GetCPUCaps().monitorx;
61 65
62 if (has_waitpkg) { 66 if (has_waitpkg) {
63 TPAUSE(); 67 TPAUSE();
68 } else if (has_monitorx) {
69 MWAITX();
64 } else { 70 } else {
65 std::this_thread::yield(); 71 std::this_thread::yield();
66 } 72 }
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 277b00662..7d2a26bd9 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -1,164 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include <chrono>
6#include <thread>
7
8#include "common/atomic_ops.h"
9#include "common/steady_clock.h"
10#include "common/uint128.h" 4#include "common/uint128.h"
11#include "common/x64/native_clock.h" 5#include "common/x64/native_clock.h"
6#include "common/x64/rdtsc.h"
12 7
13#ifdef _MSC_VER 8namespace Common::X64 {
14#include <intrin.h>
15#endif
16
17namespace Common {
18 9
19#ifdef _MSC_VER 10NativeClock::NativeClock(u64 rdtsc_frequency_)
20__forceinline static u64 FencedRDTSC() { 11 : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
21 _mm_lfence(); 12 ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
22 _ReadWriteBarrier(); 13 us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
23 const u64 result = __rdtsc(); 14 ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
24 _mm_lfence(); 15 cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
25 _ReadWriteBarrier(); 16 gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 eax;
31 u64 edx;
32 asm volatile("lfence\n\t"
33 "rdtsc\n\t"
34 "lfence\n\t"
35 : "=a"(eax), "=d"(edx));
36 return (edx << 32) | eax;
37}
38#endif
39 17
40template <u64 Nearest> 18std::chrono::nanoseconds NativeClock::GetTimeNS() const {
41static u64 RoundToNearest(u64 value) { 19 return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
42 const auto mod = value % Nearest;
43 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
44} 20}
45 21
46u64 EstimateRDTSCFrequency() { 22std::chrono::microseconds NativeClock::GetTimeUS() const {
47 // Discard the first result measuring the rdtsc. 23 return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
48 FencedRDTSC();
49 std::this_thread::sleep_for(std::chrono::milliseconds{1});
50 FencedRDTSC();
51
52 // Get the current time.
53 const auto start_time = Common::RealTimeClock::Now();
54 const u64 tsc_start = FencedRDTSC();
55 // Wait for 250 milliseconds.
56 std::this_thread::sleep_for(std::chrono::milliseconds{250});
57 const auto end_time = Common::RealTimeClock::Now();
58 const u64 tsc_end = FencedRDTSC();
59 // Calculate differences.
60 const u64 timer_diff = static_cast<u64>(
61 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
62 const u64 tsc_diff = tsc_end - tsc_start;
63 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
64 return RoundToNearest<1000>(tsc_freq);
65} 24}
66 25
67namespace X64 { 26std::chrono::milliseconds NativeClock::GetTimeMS() const {
68NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 27 return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
69 u64 rtsc_frequency_)
70 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
71 rtsc_frequency_} {
72 // Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed.
73 time_sync_thread = std::jthread{[this](std::stop_token token) {
74 // Get the current time.
75 const auto start_time = Common::RealTimeClock::Now();
76 const u64 tsc_start = FencedRDTSC();
77 // Wait for 10 seconds.
78 if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) {
79 return;
80 }
81 const auto end_time = Common::RealTimeClock::Now();
82 const u64 tsc_end = FencedRDTSC();
83 // Calculate differences.
84 const u64 timer_diff = static_cast<u64>(
85 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
86 const u64 tsc_diff = tsc_end - tsc_start;
87 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
88 rtsc_frequency = tsc_freq;
89 CalculateAndSetFactors();
90 }};
91
92 time_point.inner.last_measure = FencedRDTSC();
93 time_point.inner.accumulated_ticks = 0U;
94 CalculateAndSetFactors();
95} 28}
96 29
97u64 NativeClock::GetRTSC() { 30u64 NativeClock::GetCNTPCT() const {
98 TimePoint new_time_point{}; 31 return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
99 TimePoint current_time_point{};
100
101 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
102 do {
103 const u64 current_measure = FencedRDTSC();
104 u64 diff = current_measure - current_time_point.inner.last_measure;
105 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
106 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
107 ? current_measure
108 : current_time_point.inner.last_measure;
109 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
110 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
111 current_time_point.pack, current_time_point.pack));
112 return new_time_point.inner.accumulated_ticks;
113} 32}
114 33
115void NativeClock::Pause(bool is_paused) { 34u64 NativeClock::GetGPUTick() const {
116 if (!is_paused) { 35 return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
117 TimePoint current_time_point{};
118 TimePoint new_time_point{};
119
120 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
121 do {
122 new_time_point.pack = current_time_point.pack;
123 new_time_point.inner.last_measure = FencedRDTSC();
124 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
125 current_time_point.pack, current_time_point.pack));
126 }
127} 36}
128 37
129std::chrono::nanoseconds NativeClock::GetTimeNS() { 38u64 NativeClock::GetHostTicksNow() const {
130 const u64 rtsc_value = GetRTSC(); 39 return FencedRDTSC();
131 return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
132} 40}
133 41
134std::chrono::microseconds NativeClock::GetTimeUS() { 42u64 NativeClock::GetHostTicksElapsed() const {
135 const u64 rtsc_value = GetRTSC(); 43 return FencedRDTSC() - start_ticks;
136 return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
137} 44}
138 45
139std::chrono::milliseconds NativeClock::GetTimeMS() { 46bool NativeClock::IsNative() const {
140 const u64 rtsc_value = GetRTSC(); 47 return true;
141 return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
142} 48}
143 49
144u64 NativeClock::GetClockCycles() { 50} // namespace Common::X64
145 const u64 rtsc_value = GetRTSC();
146 return MultiplyHigh(rtsc_value, clock_rtsc_factor);
147}
148
149u64 NativeClock::GetCPUCycles() {
150 const u64 rtsc_value = GetRTSC();
151 return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
152}
153
154void NativeClock::CalculateAndSetFactors() {
155 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
156 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
157 ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
158 clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
159 cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
160}
161
162} // namespace X64
163
164} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 03ca291d8..334415eff 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -3,58 +3,39 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/polyfill_thread.h"
7#include "common/wall_clock.h" 6#include "common/wall_clock.h"
8 7
9namespace Common { 8namespace Common::X64 {
10 9
11namespace X64 {
12class NativeClock final : public WallClock { 10class NativeClock final : public WallClock {
13public: 11public:
14 explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 12 explicit NativeClock(u64 rdtsc_frequency_);
15 u64 rtsc_frequency_);
16 13
17 std::chrono::nanoseconds GetTimeNS() override; 14 std::chrono::nanoseconds GetTimeNS() const override;
18 15
19 std::chrono::microseconds GetTimeUS() override; 16 std::chrono::microseconds GetTimeUS() const override;
20 17
21 std::chrono::milliseconds GetTimeMS() override; 18 std::chrono::milliseconds GetTimeMS() const override;
22 19
23 u64 GetClockCycles() override; 20 u64 GetCNTPCT() const override;
24 21
25 u64 GetCPUCycles() override; 22 u64 GetGPUTick() const override;
26 23
27 void Pause(bool is_paused) override; 24 u64 GetHostTicksNow() const override;
28 25
29private: 26 u64 GetHostTicksElapsed() const override;
30 u64 GetRTSC();
31
32 void CalculateAndSetFactors();
33
34 union alignas(16) TimePoint {
35 TimePoint() : pack{} {}
36 u128 pack{};
37 struct Inner {
38 u64 last_measure{};
39 u64 accumulated_ticks{};
40 } inner;
41 };
42
43 TimePoint time_point;
44 27
45 // factors 28 bool IsNative() const override;
46 u64 clock_rtsc_factor{};
47 u64 cpu_rtsc_factor{};
48 u64 ns_rtsc_factor{};
49 u64 us_rtsc_factor{};
50 u64 ms_rtsc_factor{};
51 29
52 u64 rtsc_frequency; 30private:
53 31 u64 start_ticks;
54 std::jthread time_sync_thread; 32 u64 rdtsc_frequency;
33
34 u64 ns_rdtsc_factor;
35 u64 us_rdtsc_factor;
36 u64 ms_rdtsc_factor;
37 u64 cntpct_rdtsc_factor;
38 u64 gputick_rdtsc_factor;
55}; 39};
56} // namespace X64
57
58u64 EstimateRDTSCFrequency();
59 40
60} // namespace Common 41} // namespace Common::X64
diff --git a/src/common/x64/rdtsc.cpp b/src/common/x64/rdtsc.cpp
new file mode 100644
index 000000000..9273274a3
--- /dev/null
+++ b/src/common/x64/rdtsc.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5
6#include "common/steady_clock.h"
7#include "common/uint128.h"
8#include "common/x64/rdtsc.h"
9
10namespace Common::X64 {
11
12template <u64 Nearest>
13static u64 RoundToNearest(u64 value) {
14 const auto mod = value % Nearest;
15 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
16}
17
18u64 EstimateRDTSCFrequency() {
19 // Discard the first result measuring the rdtsc.
20 FencedRDTSC();
21 std::this_thread::sleep_for(std::chrono::milliseconds{1});
22 FencedRDTSC();
23
24 // Get the current time.
25 const auto start_time = RealTimeClock::Now();
26 const u64 tsc_start = FencedRDTSC();
27 // Wait for 100 milliseconds.
28 std::this_thread::sleep_for(std::chrono::milliseconds{100});
29 const auto end_time = RealTimeClock::Now();
30 const u64 tsc_end = FencedRDTSC();
31 // Calculate differences.
32 const u64 timer_diff = static_cast<u64>(
33 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
34 const u64 tsc_diff = tsc_end - tsc_start;
35 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
36 return RoundToNearest<100'000>(tsc_freq);
37}
38
39} // namespace Common::X64
diff --git a/src/common/x64/rdtsc.h b/src/common/x64/rdtsc.h
new file mode 100644
index 000000000..0ec4f52f9
--- /dev/null
+++ b/src/common/x64/rdtsc.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#ifdef _MSC_VER
7#include <intrin.h>
8#endif
9
10#include "common/common_types.h"
11
12namespace Common::X64 {
13
14#ifdef _MSC_VER
15__forceinline static u64 FencedRDTSC() {
16 _mm_lfence();
17 _ReadWriteBarrier();
18 const u64 result = __rdtsc();
19 _mm_lfence();
20 _ReadWriteBarrier();
21 return result;
22}
23#else
24static inline u64 FencedRDTSC() {
25 u64 eax;
26 u64 edx;
27 asm volatile("lfence\n\t"
28 "rdtsc\n\t"
29 "lfence\n\t"
30 : "=a"(eax), "=d"(edx));
31 return (edx << 32) | eax;
32}
33#endif
34
35u64 EstimateRDTSCFrequency();
36
37} // namespace Common::X64
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4c53aed72..dd2c053d0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -14,7 +14,6 @@ add_library(core STATIC
14 core.h 14 core.h
15 core_timing.cpp 15 core_timing.cpp
16 core_timing.h 16 core_timing.h
17 core_timing_util.h
18 cpu_manager.cpp 17 cpu_manager.cpp
19 cpu_manager.h 18 cpu_manager.h
20 crypto/aes_util.cpp 19 crypto/aes_util.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 5acf9008d..3b82fb73c 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -322,11 +322,6 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
322 } 322 }
323 } 323 }
324 324
325#ifdef ARCHITECTURE_arm64
326 // TODO: remove when fixed in dynarmic
327 config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
328#endif
329
330 return std::make_unique<Dynarmic::A32::Jit>(config); 325 return std::make_unique<Dynarmic::A32::Jit>(config);
331} 326}
332 327
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 4f2692b05..4f0a3f8ea 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -16,12 +16,11 @@
16 16
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/core_timing_util.h"
20#include "core/hardware_properties.h" 19#include "core/hardware_properties.h"
21 20
22namespace Core::Timing { 21namespace Core::Timing {
23 22
24constexpr s64 MAX_SLICE_LENGTH = 4000; 23constexpr s64 MAX_SLICE_LENGTH = 10000;
25 24
26std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 25std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
27 return std::make_shared<EventType>(std::move(callback), std::move(name)); 26 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -45,9 +44,7 @@ struct CoreTiming::Event {
45 } 44 }
46}; 45};
47 46
48CoreTiming::CoreTiming() 47CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {}
49 : cpu_clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)},
50 event_clock{Common::CreateStandardWallClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
51 48
52CoreTiming::~CoreTiming() { 49CoreTiming::~CoreTiming() {
53 Reset(); 50 Reset();
@@ -68,7 +65,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
68 on_thread_init = std::move(on_thread_init_); 65 on_thread_init = std::move(on_thread_init_);
69 event_fifo_id = 0; 66 event_fifo_id = 0;
70 shutting_down = false; 67 shutting_down = false;
71 ticks = 0; 68 cpu_ticks = 0;
72 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) 69 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
73 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; 70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
74 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 71 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
@@ -173,38 +170,30 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
173} 170}
174 171
175void CoreTiming::AddTicks(u64 ticks_to_add) { 172void CoreTiming::AddTicks(u64 ticks_to_add) {
176 ticks += ticks_to_add; 173 cpu_ticks += ticks_to_add;
177 downcount -= static_cast<s64>(ticks); 174 downcount -= static_cast<s64>(cpu_ticks);
178} 175}
179 176
180void CoreTiming::Idle() { 177void CoreTiming::Idle() {
181 if (!event_queue.empty()) { 178 cpu_ticks += 1000U;
182 const u64 next_event_time = event_queue.front().time;
183 const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U;
184 if (next_ticks > ticks) {
185 ticks = next_ticks;
186 }
187 return;
188 }
189 ticks += 1000U;
190} 179}
191 180
192void CoreTiming::ResetTicks() { 181void CoreTiming::ResetTicks() {
193 downcount = MAX_SLICE_LENGTH; 182 downcount = MAX_SLICE_LENGTH;
194} 183}
195 184
196u64 CoreTiming::GetCPUTicks() const { 185u64 CoreTiming::GetClockTicks() const {
197 if (is_multicore) [[likely]] { 186 if (is_multicore) [[likely]] {
198 return cpu_clock->GetCPUCycles(); 187 return clock->GetCNTPCT();
199 } 188 }
200 return ticks; 189 return Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
201} 190}
202 191
203u64 CoreTiming::GetClockTicks() const { 192u64 CoreTiming::GetGPUTicks() const {
204 if (is_multicore) [[likely]] { 193 if (is_multicore) [[likely]] {
205 return cpu_clock->GetClockCycles(); 194 return clock->GetGPUTick();
206 } 195 }
207 return CpuCyclesToClockCycles(ticks); 196 return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
208} 197}
209 198
210std::optional<s64> CoreTiming::Advance() { 199std::optional<s64> CoreTiming::Advance() {
@@ -297,9 +286,7 @@ void CoreTiming::ThreadLoop() {
297 } 286 }
298 287
299 paused_set = true; 288 paused_set = true;
300 event_clock->Pause(true);
301 pause_event.Wait(); 289 pause_event.Wait();
302 event_clock->Pause(false);
303 } 290 }
304} 291}
305 292
@@ -315,25 +302,18 @@ void CoreTiming::Reset() {
315 has_started = false; 302 has_started = false;
316} 303}
317 304
318std::chrono::nanoseconds CoreTiming::GetCPUTimeNs() const {
319 if (is_multicore) [[likely]] {
320 return cpu_clock->GetTimeNS();
321 }
322 return CyclesToNs(ticks);
323}
324
325std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { 305std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
326 if (is_multicore) [[likely]] { 306 if (is_multicore) [[likely]] {
327 return event_clock->GetTimeNS(); 307 return clock->GetTimeNS();
328 } 308 }
329 return CyclesToNs(ticks); 309 return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
330} 310}
331 311
332std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { 312std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
333 if (is_multicore) [[likely]] { 313 if (is_multicore) [[likely]] {
334 return event_clock->GetTimeUS(); 314 return clock->GetTimeUS();
335 } 315 }
336 return CyclesToUs(ticks); 316 return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
337} 317}
338 318
339} // namespace Core::Timing 319} // namespace Core::Timing
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index e7c4a949f..10db1de55 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -116,14 +116,11 @@ public:
116 return downcount; 116 return downcount;
117 } 117 }
118 118
119 /// Returns current time in emulated CPU cycles 119 /// Returns the current CNTPCT tick value.
120 u64 GetCPUTicks() const;
121
122 /// Returns current time in emulated in Clock cycles
123 u64 GetClockTicks() const; 120 u64 GetClockTicks() const;
124 121
125 /// Returns current time in nanoseconds. 122 /// Returns the current GPU tick value.
126 std::chrono::nanoseconds GetCPUTimeNs() const; 123 u64 GetGPUTicks() const;
127 124
128 /// Returns current time in microseconds. 125 /// Returns current time in microseconds.
129 std::chrono::microseconds GetGlobalTimeUs() const; 126 std::chrono::microseconds GetGlobalTimeUs() const;
@@ -142,8 +139,7 @@ private:
142 139
143 void Reset(); 140 void Reset();
144 141
145 std::unique_ptr<Common::WallClock> cpu_clock; 142 std::unique_ptr<Common::WallClock> clock;
146 std::unique_ptr<Common::WallClock> event_clock;
147 143
148 s64 global_timer = 0; 144 s64 global_timer = 0;
149 145
@@ -171,7 +167,7 @@ private:
171 s64 pause_end_time{}; 167 s64 pause_end_time{};
172 168
173 /// Cycle timing 169 /// Cycle timing
174 u64 ticks{}; 170 u64 cpu_ticks{};
175 s64 downcount{}; 171 s64 downcount{};
176}; 172};
177 173
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
deleted file mode 100644
index fe5aaefc7..000000000
--- a/src/core/core_timing_util.h
+++ /dev/null
@@ -1,58 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10
11namespace Core::Timing {
12
13namespace detail {
14constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
15constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
16} // namespace detail
17
18[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
19 return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
20}
21
22[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
23 return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
24}
25
26[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
27 return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
28}
29
30[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
31 return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
32}
33
34[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
35 return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
36}
37
38[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
39 return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
40}
41
42[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
43 return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
44}
45
46[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
47 return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
48}
49
50[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
51 return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
52}
53
54[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
55 return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
56}
57
58} // namespace Core::Timing
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 4e61d4335..d3286b352 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -153,7 +153,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
153 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 153 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
154 154
155 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; 155 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
156 if (load_dir != nullptr && load_dir->GetSize() > 0) { 156 if (load_dir != nullptr) {
157 const auto load_patch_dirs = load_dir->GetSubdirectories(); 157 const auto load_patch_dirs = load_dir->GetSubdirectories();
158 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); 158 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
159 } 159 }
@@ -354,8 +354,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
354 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 354 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
355 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 355 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
356 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 356 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
357 ((load_dir == nullptr || load_dir->GetSize() <= 0) && 357 (load_dir == nullptr && sdmc_load_dir == nullptr)) {
358 (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) {
359 return; 358 return;
360 } 359 }
361 360
@@ -496,7 +495,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
496 495
497 // General Mods (LayeredFS and IPS) 496 // General Mods (LayeredFS and IPS)
498 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); 497 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
499 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 498 if (mod_dir != nullptr) {
500 for (const auto& mod : mod_dir->GetSubdirectories()) { 499 for (const auto& mod : mod_dir->GetSubdirectories()) {
501 std::string types; 500 std::string types;
502 501
@@ -540,7 +539,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
540 539
541 // SDMC mod directory (RomFS LayeredFS) 540 // SDMC mod directory (RomFS LayeredFS)
542 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 541 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
543 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { 542 if (sdmc_mod_dir != nullptr) {
544 std::string types; 543 std::string types;
545 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { 544 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
546 AppendCommaIfNotEmpty(types, "LayeredExeFS"); 545 AppendCommaIfNotEmpty(types, "LayeredExeFS");
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 853b893a1..311a59e5f 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -150,23 +150,29 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
150 while (cur_length > 0 && it != concatenation_map.end()) { 150 while (cur_length > 0 && it != concatenation_map.end()) {
151 // Check if we can read the file at this position. 151 // Check if we can read the file at this position.
152 const auto& file = it->file; 152 const auto& file = it->file;
153 const u64 file_offset = it->offset; 153 const u64 map_offset = it->offset;
154 const u64 file_size = file->GetSize(); 154 const u64 file_size = file->GetSize();
155 155
156 if (cur_offset >= file_offset + file_size) { 156 if (cur_offset > map_offset + file_size) {
157 // Entirely out of bounds read. 157 // Entirely out of bounds read.
158 break; 158 break;
159 } 159 }
160 160
161 // Read the file at this position. 161 // Read the file at this position.
162 const u64 intended_read_size = std::min<u64>(cur_length, file_size); 162 const u64 file_seek = cur_offset - map_offset;
163 const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek);
163 const u64 actual_read_size = 164 const u64 actual_read_size =
164 file->Read(data + (cur_offset - offset), intended_read_size, cur_offset - file_offset); 165 file->Read(data + (cur_offset - offset), intended_read_size, file_seek);
165 166
166 // Update tracking. 167 // Update tracking.
167 cur_offset += actual_read_size; 168 cur_offset += actual_read_size;
168 cur_length -= actual_read_size; 169 cur_length -= actual_read_size;
169 it++; 170 it++;
171
172 // If we encountered a short read, we're done.
173 if (actual_read_size < intended_read_size) {
174 break;
175 }
170 } 176 }
171 177
172 return cur_offset - offset; 178 return cur_offset - offset;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 7a15d8438..b0515ec05 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -10,6 +10,7 @@
10#include "common/fs/fs.h" 10#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/file_sys/vfs.h"
13#include "core/file_sys/vfs_real.h" 14#include "core/file_sys/vfs_real.h"
14 15
15// For FileTimeStampRaw 16// For FileTimeStampRaw
@@ -72,8 +73,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
72 return VfsEntryType::File; 73 return VfsEntryType::File;
73} 74}
74 75
75VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 76VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size,
77 Mode perms) {
76 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 78 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
79 std::scoped_lock lk{list_lock};
77 80
78 if (auto it = cache.find(path); it != cache.end()) { 81 if (auto it = cache.find(path); it != cache.end()) {
79 if (auto file = it->second.lock(); file) { 82 if (auto file = it->second.lock(); file) {
@@ -81,23 +84,30 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
81 } 84 }
82 } 85 }
83 86
84 if (!FS::Exists(path) || !FS::IsFile(path)) { 87 if (!size && !FS::IsFile(path)) {
85 return nullptr; 88 return nullptr;
86 } 89 }
87 90
88 auto reference = std::make_unique<FileReference>(); 91 auto reference = std::make_unique<FileReference>();
89 this->InsertReferenceIntoList(*reference); 92 this->InsertReferenceIntoListLocked(*reference);
90 93
91 auto file = 94 auto file = std::shared_ptr<RealVfsFile>(
92 std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, std::move(reference), path, perms)); 95 new RealVfsFile(*this, std::move(reference), path, perms, size));
93 cache[path] = file; 96 cache[path] = file;
94 97
95 return file; 98 return file;
96} 99}
97 100
101VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
102 return OpenFileFromEntry(path_, {}, perms);
103}
104
98VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 105VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
99 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 106 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
100 cache.erase(path); 107 {
108 std::scoped_lock lk{list_lock};
109 cache.erase(path);
110 }
101 111
102 // Current usages of CreateFile expect to delete the contents of an existing file. 112 // Current usages of CreateFile expect to delete the contents of an existing file.
103 if (FS::IsFile(path)) { 113 if (FS::IsFile(path)) {
@@ -127,8 +137,11 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
127VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 137VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
128 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); 138 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
129 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); 139 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
130 cache.erase(old_path); 140 {
131 cache.erase(new_path); 141 std::scoped_lock lk{list_lock};
142 cache.erase(old_path);
143 cache.erase(new_path);
144 }
132 if (!FS::RenameFile(old_path, new_path)) { 145 if (!FS::RenameFile(old_path, new_path)) {
133 return nullptr; 146 return nullptr;
134 } 147 }
@@ -137,7 +150,10 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
137 150
138bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 151bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
139 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 152 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
140 cache.erase(path); 153 {
154 std::scoped_lock lk{list_lock};
155 cache.erase(path);
156 }
141 return FS::RemoveFile(path); 157 return FS::RemoveFile(path);
142} 158}
143 159
@@ -176,14 +192,17 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
176 return FS::RemoveDirRecursively(path); 192 return FS::RemoveDirRecursively(path);
177} 193}
178 194
179void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms, 195std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path,
180 FileReference& reference) { 196 Mode perms,
197 FileReference& reference) {
198 std::unique_lock lk{list_lock};
199
181 // Temporarily remove from list. 200 // Temporarily remove from list.
182 this->RemoveReferenceFromList(reference); 201 this->RemoveReferenceFromListLocked(reference);
183 202
184 // Restore file if needed. 203 // Restore file if needed.
185 if (!reference.file) { 204 if (!reference.file) {
186 this->EvictSingleReference(); 205 this->EvictSingleReferenceLocked();
187 206
188 reference.file = 207 reference.file =
189 FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); 208 FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
@@ -193,12 +212,16 @@ void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms,
193 } 212 }
194 213
195 // Reinsert into list. 214 // Reinsert into list.
196 this->InsertReferenceIntoList(reference); 215 this->InsertReferenceIntoListLocked(reference);
216
217 return lk;
197} 218}
198 219
199void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { 220void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
221 std::scoped_lock lk{list_lock};
222
200 // Remove from list. 223 // Remove from list.
201 this->RemoveReferenceFromList(*reference); 224 this->RemoveReferenceFromListLocked(*reference);
202 225
203 // Close the file. 226 // Close the file.
204 if (reference->file) { 227 if (reference->file) {
@@ -207,14 +230,14 @@ void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference
207 } 230 }
208} 231}
209 232
210void RealVfsFilesystem::EvictSingleReference() { 233void RealVfsFilesystem::EvictSingleReferenceLocked() {
211 if (num_open_files < MaxOpenFiles || open_references.empty()) { 234 if (num_open_files < MaxOpenFiles || open_references.empty()) {
212 return; 235 return;
213 } 236 }
214 237
215 // Get and remove from list. 238 // Get and remove from list.
216 auto& reference = open_references.back(); 239 auto& reference = open_references.back();
217 this->RemoveReferenceFromList(reference); 240 this->RemoveReferenceFromListLocked(reference);
218 241
219 // Close the file. 242 // Close the file.
220 if (reference.file) { 243 if (reference.file) {
@@ -223,10 +246,10 @@ void RealVfsFilesystem::EvictSingleReference() {
223 } 246 }
224 247
225 // Reinsert into closed list. 248 // Reinsert into closed list.
226 this->InsertReferenceIntoList(reference); 249 this->InsertReferenceIntoListLocked(reference);
227} 250}
228 251
229void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) { 252void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) {
230 if (reference.file) { 253 if (reference.file) {
231 open_references.push_front(reference); 254 open_references.push_front(reference);
232 } else { 255 } else {
@@ -234,7 +257,7 @@ void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) {
234 } 257 }
235} 258}
236 259
237void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) { 260void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) {
238 if (reference.file) { 261 if (reference.file) {
239 open_references.erase(open_references.iterator_to(reference)); 262 open_references.erase(open_references.iterator_to(reference));
240 } else { 263 } else {
@@ -243,10 +266,10 @@ void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) {
243} 266}
244 267
245RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, 268RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
246 const std::string& path_, Mode perms_) 269 const std::string& path_, Mode perms_, std::optional<u64> size_)
247 : base(base_), reference(std::move(reference_)), path(path_), 270 : base(base_), reference(std::move(reference_)), path(path_),
248 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)), 271 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
249 perms(perms_) {} 272 size(size_), perms(perms_) {}
250 273
251RealVfsFile::~RealVfsFile() { 274RealVfsFile::~RealVfsFile() {
252 base.DropReference(std::move(reference)); 275 base.DropReference(std::move(reference));
@@ -257,12 +280,15 @@ std::string RealVfsFile::GetName() const {
257} 280}
258 281
259std::size_t RealVfsFile::GetSize() const { 282std::size_t RealVfsFile::GetSize() const {
260 base.RefreshReference(path, perms, *reference); 283 if (size) {
261 return reference->file ? reference->file->GetSize() : 0; 284 return *size;
285 }
286 return FS::GetSize(path);
262} 287}
263 288
264bool RealVfsFile::Resize(std::size_t new_size) { 289bool RealVfsFile::Resize(std::size_t new_size) {
265 base.RefreshReference(path, perms, *reference); 290 size.reset();
291 auto lk = base.RefreshReference(path, perms, *reference);
266 return reference->file ? reference->file->SetSize(new_size) : false; 292 return reference->file ? reference->file->SetSize(new_size) : false;
267} 293}
268 294
@@ -279,7 +305,7 @@ bool RealVfsFile::IsReadable() const {
279} 305}
280 306
281std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 307std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
282 base.RefreshReference(path, perms, *reference); 308 auto lk = base.RefreshReference(path, perms, *reference);
283 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { 309 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
284 return 0; 310 return 0;
285 } 311 }
@@ -287,7 +313,8 @@ std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset)
287} 313}
288 314
289std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 315std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
290 base.RefreshReference(path, perms, *reference); 316 size.reset();
317 auto lk = base.RefreshReference(path, perms, *reference);
291 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { 318 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
292 return 0; 319 return 0;
293 } 320 }
@@ -309,10 +336,11 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
309 336
310 std::vector<VirtualFile> out; 337 std::vector<VirtualFile> out;
311 338
312 const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { 339 const FS::DirEntryCallable callback = [this,
313 const auto full_path_string = FS::PathToUTF8String(full_path); 340 &out](const std::filesystem::directory_entry& entry) {
341 const auto full_path_string = FS::PathToUTF8String(entry.path());
314 342
315 out.emplace_back(base.OpenFile(full_path_string, perms)); 343 out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms));
316 344
317 return true; 345 return true;
318 }; 346 };
@@ -330,8 +358,9 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
330 358
331 std::vector<VirtualDir> out; 359 std::vector<VirtualDir> out;
332 360
333 const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { 361 const FS::DirEntryCallable callback = [this,
334 const auto full_path_string = FS::PathToUTF8String(full_path); 362 &out](const std::filesystem::directory_entry& entry) {
363 const auto full_path_string = FS::PathToUTF8String(entry.path());
335 364
336 out.emplace_back(base.OpenDirectory(full_path_string, perms)); 365 out.emplace_back(base.OpenDirectory(full_path_string, perms));
337 366
@@ -483,12 +512,10 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
483 512
484 std::map<std::string, VfsEntryType, std::less<>> out; 513 std::map<std::string, VfsEntryType, std::less<>> out;
485 514
486 const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) { 515 const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) {
487 const auto filename = FS::PathToUTF8String(full_path.filename()); 516 const auto filename = FS::PathToUTF8String(entry.path().filename());
488
489 out.insert_or_assign(filename, 517 out.insert_or_assign(filename,
490 FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File); 518 entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File);
491
492 return true; 519 return true;
493 }; 520 };
494 521
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index d8c900e33..26ea7df62 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -4,6 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include <map> 6#include <map>
7#include <mutex>
8#include <optional>
7#include <string_view> 9#include <string_view>
8#include "common/intrusive_list.h" 10#include "common/intrusive_list.h"
9#include "core/file_sys/mode.h" 11#include "core/file_sys/mode.h"
@@ -20,6 +22,8 @@ struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {
20}; 22};
21 23
22class RealVfsFile; 24class RealVfsFile;
25class RealVfsDirectory;
26
23class RealVfsFilesystem : public VfsFilesystem { 27class RealVfsFilesystem : public VfsFilesystem {
24public: 28public:
25 RealVfsFilesystem(); 29 RealVfsFilesystem();
@@ -45,17 +49,24 @@ private:
45 std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache; 49 std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
46 ReferenceListType open_references; 50 ReferenceListType open_references;
47 ReferenceListType closed_references; 51 ReferenceListType closed_references;
52 std::mutex list_lock;
48 size_t num_open_files{}; 53 size_t num_open_files{};
49 54
50private: 55private:
51 friend class RealVfsFile; 56 friend class RealVfsFile;
52 void RefreshReference(const std::string& path, Mode perms, FileReference& reference); 57 std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms,
58 FileReference& reference);
53 void DropReference(std::unique_ptr<FileReference>&& reference); 59 void DropReference(std::unique_ptr<FileReference>&& reference);
54 void EvictSingleReference();
55 60
56private: 61private:
57 void InsertReferenceIntoList(FileReference& reference); 62 friend class RealVfsDirectory;
58 void RemoveReferenceFromList(FileReference& reference); 63 VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size,
64 Mode perms = Mode::Read);
65
66private:
67 void EvictSingleReferenceLocked();
68 void InsertReferenceIntoListLocked(FileReference& reference);
69 void RemoveReferenceFromListLocked(FileReference& reference);
59}; 70};
60 71
61// An implementation of VfsFile that represents a file on the user's computer. 72// An implementation of VfsFile that represents a file on the user's computer.
@@ -78,13 +89,14 @@ public:
78 89
79private: 90private:
80 RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, 91 RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
81 const std::string& path, Mode perms = Mode::Read); 92 const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {});
82 93
83 RealVfsFilesystem& base; 94 RealVfsFilesystem& base;
84 std::unique_ptr<FileReference> reference; 95 std::unique_ptr<FileReference> reference;
85 std::string path; 96 std::string path;
86 std::string parent_path; 97 std::string parent_path;
87 std::vector<std::string> path_components; 98 std::vector<std::string> path_components;
99 std::optional<u64> size;
88 Mode perms; 100 Mode perms;
89}; 101};
90 102
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 0a7777732..1ebc32c1e 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() {
149 149
150 camera_params[0] = right_joycon; 150 camera_params[0] = right_joycon;
151 camera_params[0].Set("camera", true); 151 camera_params[0].Set("camera", true);
152 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
153 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
154 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
155 nfc_params[1] = right_joycon; 152 nfc_params[1] = right_joycon;
156 nfc_params[1].Set("nfc", true); 153 nfc_params[1].Set("nfc", true);
157 154
155 // Only map virtual devices to the first controller
156 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
157 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
158 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
159 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
160 }
161
158 output_params[LeftIndex] = left_joycon; 162 output_params[LeftIndex] = left_joycon;
159 output_params[RightIndex] = right_joycon; 163 output_params[RightIndex] = right_joycon;
160 output_params[2] = camera_params[1]; 164 output_params[2] = camera_params[1];
@@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1176 return; 1180 return;
1177 } 1181 }
1178 1182
1179 controller.nfc_state = { 1183 controller.nfc_state = controller.nfc_values;
1180 controller.nfc_values.state,
1181 controller.nfc_values.data,
1182 };
1183} 1184}
1184 1185
1185bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1186bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1249,6 +1250,11 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1249 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); 1250 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1250 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); 1251 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1251 1252
1253 // Restore previous state
1254 if (mapped_nfc_result != Common::Input::DriverResult::Success) {
1255 right_output_device->SetPollingMode(Common::Input::PollingMode::Active);
1256 }
1257
1252 if (virtual_nfc_result == Common::Input::DriverResult::Success) { 1258 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1253 return virtual_nfc_result; 1259 return virtual_nfc_result;
1254 } 1260 }
@@ -1308,6 +1314,79 @@ bool EmulatedController::HasNfc() const {
1308 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); 1314 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1309} 1315}
1310 1316
1317bool EmulatedController::AddNfcHandle() {
1318 nfc_handles++;
1319 return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) ==
1320 Common::Input::DriverResult::Success;
1321}
1322
1323bool EmulatedController::RemoveNfcHandle() {
1324 nfc_handles--;
1325 if (nfc_handles <= 0) {
1326 return SetPollingMode(EmulatedDeviceIndex::RightIndex,
1327 Common::Input::PollingMode::Active) ==
1328 Common::Input::DriverResult::Success;
1329 }
1330 return true;
1331}
1332
1333bool EmulatedController::StartNfcPolling() {
1334 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1335 auto& nfc_virtual_output_device = output_devices[3];
1336
1337 const auto device_result = nfc_output_device->StartNfcPolling();
1338 const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling();
1339
1340 return device_result == Common::Input::NfcState::Success ||
1341 virtual_device_result == Common::Input::NfcState::Success;
1342}
1343
1344bool EmulatedController::StopNfcPolling() {
1345 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1346 auto& nfc_virtual_output_device = output_devices[3];
1347
1348 const auto device_result = nfc_output_device->StopNfcPolling();
1349 const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling();
1350
1351 return device_result == Common::Input::NfcState::Success ||
1352 virtual_device_result == Common::Input::NfcState::Success;
1353}
1354
1355bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1356 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1357 auto& nfc_virtual_output_device = output_devices[3];
1358
1359 if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) {
1360 return true;
1361 }
1362
1363 return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success;
1364}
1365
1366bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1367 Common::Input::MifareRequest& out_data) {
1368 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1369 auto& nfc_virtual_output_device = output_devices[3];
1370
1371 if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) {
1372 return true;
1373 }
1374
1375 return nfc_virtual_output_device->ReadMifareData(request, out_data) ==
1376 Common::Input::NfcState::Success;
1377}
1378
1379bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1380 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1381 auto& nfc_virtual_output_device = output_devices[3];
1382
1383 if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) {
1384 return true;
1385 }
1386
1387 return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success;
1388}
1389
1311bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1390bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1312 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1391 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1313 auto& nfc_virtual_output_device = output_devices[3]; 1392 auto& nfc_virtual_output_device = output_devices[3];
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 09fe1a0ab..d511e5fac 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -97,10 +97,7 @@ struct RingSensorForce {
97 f32 force; 97 f32 force;
98}; 98};
99 99
100struct NfcState { 100using NfcState = Common::Input::NfcStatus;
101 Common::Input::NfcState state{};
102 std::vector<u8> data{};
103};
104 101
105struct ControllerMotion { 102struct ControllerMotion {
106 Common::Vec3f accel{}; 103 Common::Vec3f accel{};
@@ -393,9 +390,31 @@ public:
393 /// Returns true if the device has nfc support 390 /// Returns true if the device has nfc support
394 bool HasNfc() const; 391 bool HasNfc() const;
395 392
393 /// Sets the joycon in nfc mode and increments the handle count
394 bool AddNfcHandle();
395
396 /// Decrements the handle count if zero sets the joycon in active mode
397 bool RemoveNfcHandle();
398
399 /// Start searching for nfc tags
400 bool StartNfcPolling();
401
402 /// Stop searching for nfc tags
403 bool StopNfcPolling();
404
405 /// Returns true if the nfc tag was readable
406 bool ReadAmiiboData(std::vector<u8>& data);
407
396 /// Returns true if the nfc tag was written 408 /// Returns true if the nfc tag was written
397 bool WriteNfc(const std::vector<u8>& data); 409 bool WriteNfc(const std::vector<u8>& data);
398 410
411 /// Returns true if the nfc tag was readable
412 bool ReadMifareData(const Common::Input::MifareRequest& request,
413 Common::Input::MifareRequest& out_data);
414
415 /// Returns true if the nfc tag was written
416 bool WriteMifareData(const Common::Input::MifareRequest& request);
417
399 /// Returns the led pattern corresponding to this emulated controller 418 /// Returns the led pattern corresponding to this emulated controller
400 LedPattern GetLedPattern() const; 419 LedPattern GetLedPattern() const;
401 420
@@ -532,6 +551,7 @@ private:
532 bool system_buttons_enabled{true}; 551 bool system_buttons_enabled{true};
533 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 552 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
534 u32 turbo_button_state{0}; 553 u32 turbo_button_state{0};
554 std::size_t nfc_handles{0};
535 555
536 // Temporary values to avoid doing changes while the controller is in configuring mode 556 // Temporary values to avoid doing changes while the controller is in configuring mode
537 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 557 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 4ccb1c596..a05716fd8 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
299 Common::Input::NfcStatus nfc{}; 299 Common::Input::NfcStatus nfc{};
300 switch (callback.type) { 300 switch (callback.type) {
301 case Common::Input::InputType::Nfc: 301 case Common::Input::InputType::Nfc:
302 nfc = { 302 return callback.nfc_status;
303 .state = callback.nfc_status,
304 .data = callback.raw_data,
305 };
306 break;
307 default: 303 default:
308 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); 304 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
309 break; 305 break;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index faa12b4f0..75ce5a23c 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -184,7 +184,8 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
184 prev_highest_thread != highest_thread) [[likely]] { 184 prev_highest_thread != highest_thread) [[likely]] {
185 if (prev_highest_thread != nullptr) [[likely]] { 185 if (prev_highest_thread != nullptr) [[likely]] {
186 IncrementScheduledCount(prev_highest_thread); 186 IncrementScheduledCount(prev_highest_thread);
187 prev_highest_thread->SetLastScheduledTick(m_kernel.System().CoreTiming().GetCPUTicks()); 187 prev_highest_thread->SetLastScheduledTick(
188 m_kernel.System().CoreTiming().GetClockTicks());
188 } 189 }
189 if (m_state.should_count_idle) { 190 if (m_state.should_count_idle) {
190 if (highest_thread != nullptr) [[likely]] { 191 if (highest_thread != nullptr) [[likely]] {
@@ -351,7 +352,7 @@ void KScheduler::SwitchThread(KThread* next_thread) {
351 352
352 // Update the CPU time tracking variables. 353 // Update the CPU time tracking variables.
353 const s64 prev_tick = m_last_context_switch_time; 354 const s64 prev_tick = m_last_context_switch_time;
354 const s64 cur_tick = m_kernel.System().CoreTiming().GetCPUTicks(); 355 const s64 cur_tick = m_kernel.System().CoreTiming().GetClockTicks();
355 const s64 tick_diff = cur_tick - prev_tick; 356 const s64 tick_diff = cur_tick - prev_tick;
356 cur_thread->AddCpuTime(m_core_id, tick_diff); 357 cur_thread->AddCpuTime(m_core_id, tick_diff);
357 if (cur_process != nullptr) { 358 if (cur_process != nullptr) {
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index b7da3eee7..3e5b735b1 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/scratch_buffer.h"
6#include "core/hle/kernel/k_scheduler.h" 7#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
8#include "core/hle/kernel/k_synchronization_object.h" 9#include "core/hle/kernel/k_synchronization_object.h"
@@ -75,7 +76,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
75 KSynchronizationObject** objects, const s32 num_objects, 76 KSynchronizationObject** objects, const s32 num_objects,
76 s64 timeout) { 77 s64 timeout) {
77 // Allocate space on stack for thread nodes. 78 // Allocate space on stack for thread nodes.
78 std::vector<ThreadListNode> thread_nodes(num_objects); 79 std::array<ThreadListNode, Svc::ArgumentHandleCountMax> thread_nodes;
79 80
80 // Prepare for wait. 81 // Prepare for wait.
81 KThread* thread = GetCurrentThreadPointer(kernel); 82 KThread* thread = GetCurrentThreadPointer(kernel);
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 908811e2c..adb6ec581 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -909,7 +909,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) {
909 R_SUCCEED(); 909 R_SUCCEED();
910} 910}
911 911
912Result KThread::GetThreadContext3(std::vector<u8>& out) { 912Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {
913 // Lock ourselves. 913 // Lock ourselves.
914 KScopedLightLock lk{m_activity_pause_lock}; 914 KScopedLightLock lk{m_activity_pause_lock};
915 915
@@ -927,15 +927,13 @@ Result KThread::GetThreadContext3(std::vector<u8>& out) {
927 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. 927 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
928 auto context = GetContext64(); 928 auto context = GetContext64();
929 context.pstate &= 0xFF0FFE20; 929 context.pstate &= 0xFF0FFE20;
930 930 out.resize_destructive(sizeof(context));
931 out.resize(sizeof(context));
932 std::memcpy(out.data(), std::addressof(context), sizeof(context)); 931 std::memcpy(out.data(), std::addressof(context), sizeof(context));
933 } else { 932 } else {
934 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. 933 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
935 auto context = GetContext32(); 934 auto context = GetContext32();
936 context.cpsr &= 0xFF0FFE20; 935 context.cpsr &= 0xFF0FFE20;
937 936 out.resize_destructive(sizeof(context));
938 out.resize(sizeof(context));
939 std::memcpy(out.data(), std::addressof(context), sizeof(context)); 937 std::memcpy(out.data(), std::addressof(context), sizeof(context));
940 } 938 }
941 } 939 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 37fe5db77..dd662b3f8 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -15,6 +15,7 @@
15#include "common/intrusive_list.h" 15#include "common/intrusive_list.h"
16 16
17#include "common/intrusive_red_black_tree.h" 17#include "common/intrusive_red_black_tree.h"
18#include "common/scratch_buffer.h"
18#include "common/spin_lock.h" 19#include "common/spin_lock.h"
19#include "core/arm/arm_interface.h" 20#include "core/arm/arm_interface.h"
20#include "core/hle/kernel/k_affinity_mask.h" 21#include "core/hle/kernel/k_affinity_mask.h"
@@ -567,7 +568,7 @@ public:
567 568
568 void RemoveWaiter(KThread* thread); 569 void RemoveWaiter(KThread* thread);
569 570
570 Result GetThreadContext3(std::vector<u8>& out); 571 Result GetThreadContext3(Common::ScratchBuffer<u8>& out);
571 572
572 KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) { 573 KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) {
573 return this->RemoveWaiterByKey(out_has_waiters, key, false); 574 return this->RemoveWaiterByKey(out_has_waiters, key, false);
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index 2b2c878b5..445cdd87b 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -199,9 +199,9 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
199 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 199 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
200 const u64 thread_ticks = current_thread->GetCpuTime(); 200 const u64 thread_ticks = current_thread->GetCpuTime();
201 201
202 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 202 out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
203 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { 203 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
204 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 204 out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
205 } 205 }
206 206
207 *result = out_ticks; 207 *result = out_ticks;
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index ea03068aa..60247df2e 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.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 "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "common/scratch_buffer.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
7#include "core/hle/kernel/k_process.h" 8#include "core/hle/kernel/k_process.h"
@@ -45,11 +46,11 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
45 handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), 46 handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)),
46 ResultInvalidPointer); 47 ResultInvalidPointer);
47 48
48 std::vector<Handle> handles(num_handles); 49 std::array<Handle, Svc::ArgumentHandleCountMax> handles;
49 GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles); 50 GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles);
50 51
51 // Convert handle list to object table. 52 // Convert handle list to object table.
52 std::vector<KSynchronizationObject*> objs(num_handles); 53 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;
53 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(), 54 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(),
54 num_handles), 55 num_handles),
55 ResultInvalidHandle); 56 ResultInvalidHandle);
@@ -80,7 +81,7 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
80 // Wait for an object. 81 // Wait for an object.
81 s32 index; 82 s32 index;
82 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), 83 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
83 static_cast<s32>(objs.size()), timeout_ns); 84 num_handles, timeout_ns);
84 if (result == ResultTimedOut) { 85 if (result == ResultTimedOut) {
85 R_RETURN(result); 86 R_RETURN(result);
86 } 87 }
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
index 04d65f0bd..53df5bcd8 100644
--- a/src/core/hle/kernel/svc/svc_synchronization.cpp
+++ b/src/core/hle/kernel/svc/svc_synchronization.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 "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "common/scratch_buffer.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/hle/kernel/k_process.h" 7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_readable_event.h" 8#include "core/hle/kernel/k_readable_event.h"
@@ -54,7 +55,7 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons
54 // Get the synchronization context. 55 // Get the synchronization context.
55 auto& kernel = system.Kernel(); 56 auto& kernel = system.Kernel();
56 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); 57 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
57 std::vector<KSynchronizationObject*> objs(num_handles); 58 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;
58 59
59 // Copy user handles. 60 // Copy user handles.
60 if (num_handles > 0) { 61 if (num_handles > 0) {
@@ -72,8 +73,8 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons
72 }); 73 });
73 74
74 // Wait on the objects. 75 // Wait on the objects.
75 Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), 76 Result res =
76 static_cast<s32>(objs.size()), timeout_ns); 77 KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns);
77 78
78 R_SUCCEED_IF(res == ResultSessionClosed); 79 R_SUCCEED_IF(res == ResultSessionClosed);
79 R_RETURN(res); 80 R_RETURN(res);
@@ -87,8 +88,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
87 88
88 // Ensure number of handles is valid. 89 // Ensure number of handles is valid.
89 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); 90 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
90 91 std::array<Handle, Svc::ArgumentHandleCountMax> handles;
91 std::vector<Handle> handles(num_handles);
92 if (num_handles > 0) { 92 if (num_handles > 0) {
93 GetCurrentMemory(system.Kernel()) 93 GetCurrentMemory(system.Kernel())
94 .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); 94 .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle));
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 37b54079c..36b94e6bf 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -174,7 +174,7 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha
174 } 174 }
175 175
176 // Get the thread context. 176 // Get the thread context.
177 std::vector<u8> context; 177 static thread_local Common::ScratchBuffer<u8> context;
178 R_TRY(thread->GetThreadContext3(context)); 178 R_TRY(thread->GetThreadContext3(context));
179 179
180 // Copy the thread context to user space. 180 // Copy the thread context to user space.
diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp
index 561336482..7dd7c6e51 100644
--- a/src/core/hle/kernel/svc/svc_tick.cpp
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -12,16 +12,8 @@ namespace Kernel::Svc {
12int64_t GetSystemTick(Core::System& system) { 12int64_t GetSystemTick(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called"); 13 LOG_TRACE(Kernel_SVC, "called");
14 14
15 auto& core_timing = system.CoreTiming();
16
17 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 15 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
18 const u64 result{core_timing.GetClockTicks()}; 16 return static_cast<int64_t>(system.CoreTiming().GetClockTicks());
19
20 if (!system.Kernel().IsMulticore()) {
21 core_timing.AddTicks(400U);
22 }
23
24 return static_cast<int64_t>(result);
25} 17}
26 18
27int64_t GetSystemTick64(Core::System& system) { 19int64_t GetSystemTick64(Core::System& system) {
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index 8b754e9d4..19ed184e8 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -141,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
141 applet_output.device_handle = applet_input_common.device_handle; 141 applet_output.device_handle = applet_input_common.device_handle;
142 applet_output.result = CabinetResult::Cancel; 142 applet_output.result = CabinetResult::Cancel;
143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); 143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); 144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
145 nfp_device->Finalize(); 145 nfp_device->Finalize();
146 146
147 if (reg_result.IsSuccess()) { 147 if (reg_result.IsSuccess()) {
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index f0640c64f..c8d574993 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -5,6 +5,7 @@
5#include "audio_core/renderer/audio_device.h" 5#include "audio_core/renderer/audio_device.h"
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/settings.h"
8#include "common/string_util.h" 9#include "common/string_util.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
@@ -123,19 +124,13 @@ private:
123 124
124 void GetReleasedAudioInBuffer(HLERequestContext& ctx) { 125 void GetReleasedAudioInBuffer(HLERequestContext& ctx) {
125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 126 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 std::vector<u64> released_buffers(write_buffer_size); 127 tmp_buffer.resize_destructive(write_buffer_size);
128 tmp_buffer[0] = 0;
127 129
128 const auto count = impl->GetReleasedBuffers(released_buffers); 130 const auto count = impl->GetReleasedBuffers(tmp_buffer);
129 131
130 [[maybe_unused]] std::string tags{}; 132 ctx.WriteBuffer(tmp_buffer);
131 for (u32 i = 0; i < count; i++) {
132 tags += fmt::format("{:08X}, ", released_buffers[i]);
133 }
134 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
135 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
136 tags);
137 133
138 ctx.WriteBuffer(released_buffers);
139 IPC::ResponseBuilder rb{ctx, 3}; 134 IPC::ResponseBuilder rb{ctx, 3};
140 rb.Push(ResultSuccess); 135 rb.Push(ResultSuccess);
141 rb.Push(count); 136 rb.Push(count);
@@ -200,6 +195,7 @@ private:
200 KernelHelpers::ServiceContext service_context; 195 KernelHelpers::ServiceContext service_context;
201 Kernel::KEvent* event; 196 Kernel::KEvent* event;
202 std::shared_ptr<AudioCore::AudioIn::In> impl; 197 std::shared_ptr<AudioCore::AudioIn::In> impl;
198 Common::ScratchBuffer<u64> tmp_buffer;
203}; 199};
204 200
205AudInU::AudInU(Core::System& system_) 201AudInU::AudInU(Core::System& system_)
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 3e62fa4fc..032c8c11f 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -123,19 +123,13 @@ private:
123 123
124 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { 124 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) {
125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 std::vector<u64> released_buffers(write_buffer_size); 126 tmp_buffer.resize_destructive(write_buffer_size);
127 tmp_buffer[0] = 0;
127 128
128 const auto count = impl->GetReleasedBuffers(released_buffers); 129 const auto count = impl->GetReleasedBuffers(tmp_buffer);
129 130
130 [[maybe_unused]] std::string tags{}; 131 ctx.WriteBuffer(tmp_buffer);
131 for (u32 i = 0; i < count; i++) {
132 tags += fmt::format("{:08X}, ", released_buffers[i]);
133 }
134 [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()};
135 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
136 tags);
137 132
138 ctx.WriteBuffer(released_buffers);
139 IPC::ResponseBuilder rb{ctx, 3}; 133 IPC::ResponseBuilder rb{ctx, 3};
140 rb.Push(ResultSuccess); 134 rb.Push(ResultSuccess);
141 rb.Push(count); 135 rb.Push(count);
@@ -211,6 +205,7 @@ private:
211 KernelHelpers::ServiceContext service_context; 205 KernelHelpers::ServiceContext service_context;
212 Kernel::KEvent* event; 206 Kernel::KEvent* event;
213 std::shared_ptr<AudioCore::AudioOut::Out> impl; 207 std::shared_ptr<AudioCore::AudioOut::Out> impl;
208 Common::ScratchBuffer<u64> tmp_buffer;
214}; 209};
215 210
216AudOutU::AudOutU(Core::System& system_) 211AudOutU::AudOutU(Core::System& system_)
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 7086d4750..12845c23a 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -116,28 +116,26 @@ private:
116 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for 116 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
117 // checking size 0. Performance size is 0 for most games. 117 // checking size 0. Performance size is 0 for most games.
118 118
119 std::vector<u8> output{};
120 std::vector<u8> performance{};
121 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; 119 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
122 if (is_buffer_b) { 120 if (is_buffer_b) {
123 const auto buffersB{ctx.BufferDescriptorB()}; 121 const auto buffersB{ctx.BufferDescriptorB()};
124 output.resize(buffersB[0].Size(), 0); 122 tmp_output.resize_destructive(buffersB[0].Size());
125 performance.resize(buffersB[1].Size(), 0); 123 tmp_performance.resize_destructive(buffersB[1].Size());
126 } else { 124 } else {
127 const auto buffersC{ctx.BufferDescriptorC()}; 125 const auto buffersC{ctx.BufferDescriptorC()};
128 output.resize(buffersC[0].Size(), 0); 126 tmp_output.resize_destructive(buffersC[0].Size());
129 performance.resize(buffersC[1].Size(), 0); 127 tmp_performance.resize_destructive(buffersC[1].Size());
130 } 128 }
131 129
132 auto result = impl->RequestUpdate(input, performance, output); 130 auto result = impl->RequestUpdate(input, tmp_performance, tmp_output);
133 131
134 if (result.IsSuccess()) { 132 if (result.IsSuccess()) {
135 if (is_buffer_b) { 133 if (is_buffer_b) {
136 ctx.WriteBufferB(output.data(), output.size(), 0); 134 ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0);
137 ctx.WriteBufferB(performance.data(), performance.size(), 1); 135 ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1);
138 } else { 136 } else {
139 ctx.WriteBufferC(output.data(), output.size(), 0); 137 ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0);
140 ctx.WriteBufferC(performance.data(), performance.size(), 1); 138 ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1);
141 } 139 }
142 } else { 140 } else {
143 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); 141 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
@@ -235,6 +233,8 @@ private:
235 Kernel::KEvent* rendered_event; 233 Kernel::KEvent* rendered_event;
236 Manager& manager; 234 Manager& manager;
237 std::unique_ptr<Renderer> impl; 235 std::unique_ptr<Renderer> impl;
236 Common::ScratchBuffer<u8> tmp_output;
237 Common::ScratchBuffer<u8> tmp_performance;
238}; 238};
239 239
240class IAudioDevice final : public ServiceFramework<IAudioDevice> { 240class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 24ce37e87..d8e9c8719 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "audio_core/audio_render_manager.h" 6#include "audio_core/audio_render_manager.h"
7#include "common/scratch_buffer.h"
7#include "core/hle/service/kernel_helpers.h" 8#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 451ac224a..c835f6cb7 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -68,13 +68,13 @@ private:
68 ExtraBehavior extra_behavior) { 68 ExtraBehavior extra_behavior) {
69 u32 consumed = 0; 69 u32 consumed = 0;
70 u32 sample_count = 0; 70 u32 sample_count = 0;
71 std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>()); 71 tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>());
72 72
73 if (extra_behavior == ExtraBehavior::ResetContext) { 73 if (extra_behavior == ExtraBehavior::ResetContext) {
74 ResetDecoderContext(); 74 ResetDecoderContext();
75 } 75 }
76 76
77 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { 77 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) {
78 LOG_ERROR(Audio, "Failed to decode opus data"); 78 LOG_ERROR(Audio, "Failed to decode opus data");
79 IPC::ResponseBuilder rb{ctx, 2}; 79 IPC::ResponseBuilder rb{ctx, 2};
80 // TODO(ogniK): Use correct error code 80 // TODO(ogniK): Use correct error code
@@ -90,11 +90,11 @@ private:
90 if (performance) { 90 if (performance) {
91 rb.Push<u64>(*performance); 91 rb.Push<u64>(*performance);
92 } 92 }
93 ctx.WriteBuffer(samples); 93 ctx.WriteBuffer(tmp_samples);
94 } 94 }
95 95
96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, 96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
97 std::vector<opus_int16>& output, u64* out_performance_time) const { 97 std::span<opus_int16> output, u64* out_performance_time) const {
98 const auto start_time = std::chrono::steady_clock::now(); 98 const auto start_time = std::chrono::steady_clock::now();
99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
100 if (sizeof(OpusPacketHeader) > input.size()) { 100 if (sizeof(OpusPacketHeader) > input.size()) {
@@ -154,6 +154,7 @@ private:
154 OpusDecoderPtr decoder; 154 OpusDecoderPtr decoder;
155 u32 sample_rate; 155 u32 sample_rate;
156 u32 channel_count; 156 u32 channel_count;
157 Common::ScratchBuffer<opus_int16> tmp_samples;
157}; 158};
158 159
159class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 160class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 5604a6fda..80aac221b 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -5,7 +5,6 @@
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
10#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
11#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index f4b180b06..5bf289818 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
93 const auto nfc_status = npad_device->GetNfc(); 93 const auto nfc_status = npad_device->GetNfc();
94 switch (nfc_status.state) { 94 switch (nfc_status.state) {
95 case Common::Input::NfcState::NewAmiibo: 95 case Common::Input::NfcState::NewAmiibo:
96 LoadNfcTag(nfc_status.data); 96 LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length,
97 nfc_status.uuid);
97 break; 98 break;
98 case Common::Input::NfcState::AmiiboRemoved: 99 case Common::Input::NfcState::AmiiboRemoved:
99 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 100 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
@@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
108 } 109 }
109} 110}
110 111
111bool NfcDevice::LoadNfcTag(std::span<const u8> data) { 112bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {
112 if (device_state != DeviceState::SearchingForTag) { 113 if (device_state != DeviceState::SearchingForTag) {
113 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); 114 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
114 return false; 115 return false;
115 } 116 }
116 117
118 if ((protocol & static_cast<u8>(allowed_protocols)) == 0) {
119 LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol);
120 return false;
121 }
122
123 real_tag_info = {
124 .uuid = uuid,
125 .uuid_length = uuid_length,
126 .protocol = static_cast<NfcProtocol>(protocol),
127 .tag_type = static_cast<TagType>(tag_type),
128 };
129
130 device_state = DeviceState::TagFound;
131 deactivate_event->GetReadableEvent().Clear();
132 activate_event->Signal();
133 return true;
134}
135
136bool NfcDevice::LoadAmiiboData() {
137 std::vector<u8> data{};
138
139 if (!npad_device->ReadAmiiboData(data)) {
140 return false;
141 }
142
117 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { 143 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
118 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); 144 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
119 return false; 145 return false;
120 } 146 }
121 147
122 mifare_data.resize(data.size());
123 memcpy(mifare_data.data(), data.data(), data.size());
124
125 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 148 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
126 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data); 149 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);
127 is_write_protected = false; 150 is_write_protected = false;
128 151
129 device_state = DeviceState::TagFound;
130 deactivate_event->GetReadableEvent().Clear();
131 activate_event->Signal();
132
133 // Fallback for plain amiibos 152 // Fallback for plain amiibos
134 if (is_plain_amiibo) { 153 if (is_plain_amiibo) {
135 LOG_INFO(Service_NFP, "Using plain amiibo"); 154 LOG_INFO(Service_NFP, "Using plain amiibo");
@@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
147 return true; 166 return true;
148 } 167 }
149 168
169 LOG_INFO(Service_NFP, "Using encrypted amiibo");
150 tag_data = {}; 170 tag_data = {};
151 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 171 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
152 return true; 172 return true;
@@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() {
162 device_state = DeviceState::TagRemoved; 182 device_state = DeviceState::TagRemoved;
163 encrypted_tag_data = {}; 183 encrypted_tag_data = {};
164 tag_data = {}; 184 tag_data = {};
165 mifare_data = {};
166 activate_event->GetReadableEvent().Clear(); 185 activate_event->GetReadableEvent().Clear();
167 deactivate_event->Signal(); 186 deactivate_event->Signal();
168} 187}
@@ -179,8 +198,12 @@ void NfcDevice::Initialize() {
179 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; 198 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
180 encrypted_tag_data = {}; 199 encrypted_tag_data = {};
181 tag_data = {}; 200 tag_data = {};
182 mifare_data = {}; 201
183 is_initalized = true; 202 if (device_state != DeviceState::Initialized) {
203 return;
204 }
205
206 is_initalized = npad_device->AddNfcHandle();
184} 207}
185 208
186void NfcDevice::Finalize() { 209void NfcDevice::Finalize() {
@@ -190,6 +213,11 @@ void NfcDevice::Finalize() {
190 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 213 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
191 StopDetection(); 214 StopDetection();
192 } 215 }
216
217 if (device_state != DeviceState::Unavailable) {
218 npad_device->RemoveNfcHandle();
219 }
220
193 device_state = DeviceState::Unavailable; 221 device_state = DeviceState::Unavailable;
194 is_initalized = false; 222 is_initalized = false;
195} 223}
@@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
200 return ResultWrongDeviceState; 228 return ResultWrongDeviceState;
201 } 229 }
202 230
203 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 231 if (!npad_device->StartNfcPolling()) {
204 Common::Input::PollingMode::NFC) != 232 LOG_ERROR(Service_NFC, "Nfc polling not supported");
205 Common::Input::DriverResult::Success) {
206 LOG_ERROR(Service_NFC, "Nfc not supported");
207 return ResultNfcDisabled; 233 return ResultNfcDisabled;
208 } 234 }
209 235
@@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
213} 239}
214 240
215Result NfcDevice::StopDetection() { 241Result NfcDevice::StopDetection() {
216 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
217 Common::Input::PollingMode::Active);
218
219 if (device_state == DeviceState::Initialized) { 242 if (device_state == DeviceState::Initialized) {
220 return ResultSuccess; 243 return ResultSuccess;
221 } 244 }
@@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() {
225 } 248 }
226 249
227 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 250 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
251 npad_device->StopNfcPolling();
228 device_state = DeviceState::Initialized; 252 device_state = DeviceState::Initialized;
229 return ResultSuccess; 253 return ResultSuccess;
230 } 254 }
@@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() {
233 return ResultWrongDeviceState; 257 return ResultWrongDeviceState;
234} 258}
235 259
236Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { 260Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
237 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 261 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
238 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 262 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
239 if (device_state == DeviceState::TagRemoved) { 263 if (device_state == DeviceState::TagRemoved) {
@@ -242,41 +266,15 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
242 return ResultWrongDeviceState; 266 return ResultWrongDeviceState;
243 } 267 }
244 268
245 UniqueSerialNumber uuid{}; 269 tag_info = real_tag_info;
246 u8 uuid_length{};
247 NfcProtocol protocol{NfcProtocol::TypeA};
248 TagType tag_type{TagType::Type2};
249
250 if (is_mifare) {
251 tag_type = TagType::Mifare;
252 uuid_length = sizeof(NFP::NtagTagUuid);
253 memcpy(uuid.data(), mifare_data.data(), uuid_length);
254 } else {
255 tag_type = TagType::Type2;
256 uuid_length = sizeof(NFP::NtagTagUuid);
257 NFP::NtagTagUuid nUuid{
258 .part1 = encrypted_tag_data.uuid.part1,
259 .part2 = encrypted_tag_data.uuid.part2,
260 .nintendo_id = encrypted_tag_data.uuid.nintendo_id,
261 };
262 memcpy(uuid.data(), &nUuid, uuid_length);
263 270
264 // Generate random UUID to bypass amiibo load limits 271 // Generate random UUID to bypass amiibo load limits
265 if (Settings::values.random_amiibo_id) { 272 if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) {
266 Common::TinyMT rng{}; 273 Common::TinyMT rng{};
267 rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); 274 rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
268 rng.GenerateRandomBytes(uuid.data(), uuid_length); 275 rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);
269 }
270 } 276 }
271 277
272 // Protocol and tag type may change here
273 tag_info = {
274 .uuid = uuid,
275 .uuid_length = uuid_length,
276 .protocol = protocol,
277 .tag_type = tag_type,
278 };
279
280 return ResultSuccess; 278 return ResultSuccess;
281} 279}
282 280
@@ -293,7 +291,7 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
293 Result result = ResultSuccess; 291 Result result = ResultSuccess;
294 292
295 TagInfo tag_info{}; 293 TagInfo tag_info{};
296 result = GetTagInfo(tag_info, true); 294 result = GetTagInfo(tag_info);
297 295
298 if (result.IsError()) { 296 if (result.IsError()) {
299 return result; 297 return result;
@@ -307,6 +305,8 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
307 return ResultInvalidArgument; 305 return ResultInvalidArgument;
308 } 306 }
309 307
308 Common::Input::MifareRequest request{};
309 Common::Input::MifareRequest out_data{};
310 const auto unknown = parameters[0].sector_key.unknown; 310 const auto unknown = parameters[0].sector_key.unknown;
311 for (std::size_t i = 0; i < parameters.size(); i++) { 311 for (std::size_t i = 0; i < parameters.size(); i++) {
312 if (unknown != parameters[i].sector_key.unknown) { 312 if (unknown != parameters[i].sector_key.unknown) {
@@ -315,25 +315,29 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
315 } 315 }
316 316
317 for (std::size_t i = 0; i < parameters.size(); i++) { 317 for (std::size_t i = 0; i < parameters.size(); i++) {
318 result = ReadMifare(parameters[i], read_block_data[i]); 318 if (parameters[i].sector_key.command == MifareCmd::None) {
319 if (result.IsError()) { 319 continue;
320 break;
321 } 320 }
321 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
322 request.data[i].sector = parameters[i].sector_number;
323 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
324 sizeof(KeyData));
322 } 325 }
323 326
324 return result; 327 if (!npad_device->ReadMifareData(request, out_data)) {
325}
326
327Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
328 MifareReadBlockData& read_block_data) const {
329 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
330 read_block_data.sector_number = parameter.sector_number;
331 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
332 return ResultMifareError288; 328 return ResultMifareError288;
333 } 329 }
334 330
335 // TODO: Use parameter.sector_key to read encrypted data 331 for (std::size_t i = 0; i < read_block_data.size(); i++) {
336 memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); 332 if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) {
333 continue;
334 }
335
336 read_block_data[i] = {
337 .data = out_data.data[i].data,
338 .sector_number = out_data.data[i].sector,
339 };
340 }
337 341
338 return ResultSuccess; 342 return ResultSuccess;
339} 343}
@@ -342,7 +346,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
342 Result result = ResultSuccess; 346 Result result = ResultSuccess;
343 347
344 TagInfo tag_info{}; 348 TagInfo tag_info{};
345 result = GetTagInfo(tag_info, true); 349 result = GetTagInfo(tag_info);
346 350
347 if (result.IsError()) { 351 if (result.IsError()) {
348 return result; 352 return result;
@@ -363,42 +367,25 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
363 } 367 }
364 } 368 }
365 369
370 Common::Input::MifareRequest request{};
366 for (std::size_t i = 0; i < parameters.size(); i++) { 371 for (std::size_t i = 0; i < parameters.size(); i++) {
367 result = WriteMifare(parameters[i]); 372 if (parameters[i].sector_key.command == MifareCmd::None) {
368 if (result.IsError()) { 373 continue;
369 break;
370 } 374 }
375 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
376 request.data[i].sector = parameters[i].sector_number;
377 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
378 sizeof(KeyData));
379 memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));
371 } 380 }
372 381
373 if (!npad_device->WriteNfc(mifare_data)) { 382 if (!npad_device->WriteMifareData(request)) {
374 LOG_ERROR(Service_NFP, "Error writing to file");
375 return ResultMifareError288; 383 return ResultMifareError288;
376 } 384 }
377 385
378 return result; 386 return result;
379} 387}
380 388
381Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
382 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
383
384 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
385 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
386 if (device_state == DeviceState::TagRemoved) {
387 return ResultTagRemoved;
388 }
389 return ResultWrongDeviceState;
390 }
391
392 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
393 return ResultMifareError288;
394 }
395
396 // TODO: Use parameter.sector_key to encrypt the data
397 memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock));
398
399 return ResultSuccess;
400}
401
402Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 389Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
403 std::span<const u8> command_data, 390 std::span<const u8> command_data,
404 std::span<u8> out_data) { 391 std::span<u8> out_data) {
@@ -412,6 +399,11 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
412 return ResultWrongDeviceState; 399 return ResultWrongDeviceState;
413 } 400 }
414 401
402 if (!LoadAmiiboData()) {
403 LOG_ERROR(Service_NFP, "Not an amiibo");
404 return ResultInvalidTagType;
405 }
406
415 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { 407 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
416 LOG_ERROR(Service_NFP, "Not an amiibo"); 408 LOG_ERROR(Service_NFP, "Not an amiibo");
417 return ResultInvalidTagType; 409 return ResultInvalidTagType;
@@ -562,7 +554,7 @@ Result NfcDevice::Restore() {
562 554
563 NFC::TagInfo tag_info{}; 555 NFC::TagInfo tag_info{};
564 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; 556 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
565 Result result = GetTagInfo(tag_info, false); 557 Result result = GetTagInfo(tag_info);
566 558
567 if (result.IsError()) { 559 if (result.IsError()) {
568 return result; 560 return result;
@@ -635,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {
635 // TODO: Validate this data 627 // TODO: Validate this data
636 common_info = { 628 common_info = {
637 .last_write_date = settings.write_date.GetWriteDate(), 629 .last_write_date = settings.write_date.GetWriteDate(),
638 .write_counter = tag_data.write_counter, 630 .write_counter = tag_data.application_write_counter,
639 .version = tag_data.amiibo_version, 631 .version = tag_data.amiibo_version,
640 .application_area_size = sizeof(NFP::ApplicationArea), 632 .application_area_size = sizeof(NFP::ApplicationArea),
641 }; 633 };
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 7560210d6..0ed1ff34c 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -42,15 +42,12 @@ public:
42 Result StartDetection(NfcProtocol allowed_protocol); 42 Result StartDetection(NfcProtocol allowed_protocol);
43 Result StopDetection(); 43 Result StopDetection();
44 44
45 Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; 45 Result GetTagInfo(TagInfo& tag_info) const;
46 46
47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters, 47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
48 std::span<MifareReadBlockData> read_block_data) const; 48 std::span<MifareReadBlockData> read_block_data) const;
49 Result ReadMifare(const MifareReadBlockParameter& parameter,
50 MifareReadBlockData& read_block_data) const;
51 49
52 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); 50 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
53 Result WriteMifare(const MifareWriteBlockParameter& parameter);
54 51
55 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 52 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
56 std::span<const u8> command_data, std::span<u8> out_data); 53 std::span<const u8> command_data, std::span<u8> out_data);
@@ -105,7 +102,8 @@ public:
105 102
106private: 103private:
107 void NpadUpdate(Core::HID::ControllerTriggerType type); 104 void NpadUpdate(Core::HID::ControllerTriggerType type);
108 bool LoadNfcTag(std::span<const u8> data); 105 bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid);
106 bool LoadAmiiboData();
109 void CloseNfcTag(); 107 void CloseNfcTag();
110 108
111 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; 109 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
@@ -140,8 +138,8 @@ private:
140 bool is_write_protected{}; 138 bool is_write_protected{};
141 NFP::MountTarget mount_target{NFP::MountTarget::None}; 139 NFP::MountTarget mount_target{NFP::MountTarget::None};
142 140
141 TagInfo real_tag_info{};
143 NFP::NTAG215File tag_data{}; 142 NFP::NTAG215File tag_data{};
144 std::vector<u8> mifare_data{};
145 NFP::EncryptedNTAG215File encrypted_tag_data{}; 143 NFP::EncryptedNTAG215File encrypted_tag_data{};
146}; 144};
147 145
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index b0456508e..562f3a28e 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
29} 29}
30 30
31DeviceManager ::~DeviceManager() { 31DeviceManager ::~DeviceManager() {
32 if (is_initialized) {
33 Finalize();
34 }
32 service_context.CloseEvent(availability_change_event); 35 service_context.CloseEvent(availability_change_event);
33} 36}
34 37
@@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) {
125 return result; 128 return result;
126} 129}
127 130
128Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { 131Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {
129 std::scoped_lock lock{mutex}; 132 std::scoped_lock lock{mutex};
130 133
131 std::shared_ptr<NfcDevice> device = nullptr; 134 std::shared_ptr<NfcDevice> device = nullptr;
132 auto result = GetDeviceHandle(device_handle, device); 135 auto result = GetDeviceHandle(device_handle, device);
133 136
134 if (result.IsSuccess()) { 137 if (result.IsSuccess()) {
135 result = device->GetTagInfo(tag_info, is_mifare); 138 result = device->GetTagInfo(tag_info);
136 result = VerifyDeviceResult(device, result); 139 result = VerifyDeviceResult(device, result);
137 } 140 }
138 141
@@ -546,7 +549,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
546 NFC::TagInfo tag_info{}; 549 NFC::TagInfo tag_info{};
547 550
548 if (result.IsSuccess()) { 551 if (result.IsSuccess()) {
549 result = device->GetTagInfo(tag_info, false); 552 result = device->GetTagInfo(tag_info);
550 } 553 }
551 554
552 if (result.IsSuccess()) { 555 if (result.IsSuccess()) {
@@ -565,7 +568,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
565 NFC::TagInfo tag_info{}; 568 NFC::TagInfo tag_info{};
566 569
567 if (result.IsSuccess()) { 570 if (result.IsSuccess()) {
568 result = device->GetTagInfo(tag_info, false); 571 result = device->GetTagInfo(tag_info);
569 } 572 }
570 573
571 if (result.IsSuccess()) { 574 if (result.IsSuccess()) {
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index 2971e280f..c61ba0cf3 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -33,7 +33,7 @@ public:
33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; 33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); 34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
35 Result StopDetection(u64 device_handle); 35 Result StopDetection(u64 device_handle);
36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; 36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const;
37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; 37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; 38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
39 Result ReadMifare(u64 device_handle, 39 Result ReadMifare(u64 device_handle,
diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h
index 75b59f021..467937399 100644
--- a/src/core/hle/service/nfc/mifare_types.h
+++ b/src/core/hle/service/nfc/mifare_types.h
@@ -11,9 +11,10 @@
11namespace Service::NFC { 11namespace Service::NFC {
12 12
13enum class MifareCmd : u8 { 13enum class MifareCmd : u8 {
14 None = 0x00,
15 Read = 0x30,
14 AuthA = 0x60, 16 AuthA = 0x60,
15 AuthB = 0x61, 17 AuthB = 0x61,
16 Read = 0x30,
17 Write = 0xA0, 18 Write = 0xA0,
18 Transfer = 0xB0, 19 Transfer = 0xB0,
19 Decrement = 0xC0, 20 Decrement = 0xC0,
@@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
35 36
36// This is nn::nfc::MifareReadBlockParameter 37// This is nn::nfc::MifareReadBlockParameter
37struct MifareReadBlockParameter { 38struct MifareReadBlockParameter {
38 u8 sector_number; 39 u8 sector_number{};
39 INSERT_PADDING_BYTES(0x7); 40 INSERT_PADDING_BYTES(0x7);
40 SectorKey sector_key; 41 SectorKey sector_key{};
41}; 42};
42static_assert(sizeof(MifareReadBlockParameter) == 0x18, 43static_assert(sizeof(MifareReadBlockParameter) == 0x18,
43 "MifareReadBlockParameter is an invalid size"); 44 "MifareReadBlockParameter is an invalid size");
44 45
45// This is nn::nfc::MifareReadBlockData 46// This is nn::nfc::MifareReadBlockData
46struct MifareReadBlockData { 47struct MifareReadBlockData {
47 DataBlock data; 48 DataBlock data{};
48 u8 sector_number; 49 u8 sector_number{};
49 INSERT_PADDING_BYTES(0x7); 50 INSERT_PADDING_BYTES(0x7);
50}; 51};
51static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); 52static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 130fb7f78..e7ca7582e 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -174,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
174 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); 174 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
175 175
176 TagInfo tag_info{}; 176 TagInfo tag_info{};
177 auto result = 177 auto result = GetManager()->GetTagInfo(device_handle, tag_info);
178 GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
179 result = TranslateResultToServiceError(result); 178 result = TranslateResultToServiceError(result);
180 179
181 if (result.IsSuccess()) { 180 if (result.IsSuccess()) {
@@ -216,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) {
216 memcpy(read_commands.data(), buffer.data(), 215 memcpy(read_commands.data(), buffer.data(),
217 number_of_commands * sizeof(MifareReadBlockParameter)); 216 number_of_commands * sizeof(MifareReadBlockParameter));
218 217
219 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", 218 LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle,
220 device_handle, number_of_commands); 219 number_of_commands);
221 220
222 std::vector<MifareReadBlockData> out_data(number_of_commands); 221 std::vector<MifareReadBlockData> out_data(number_of_commands);
223 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); 222 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index ab1f30f9e..a04538d5d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -34,7 +34,7 @@ public:
34 * @returns The result code of the ioctl. 34 * @returns The result code of the ioctl.
35 */ 35 */
36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
37 std::vector<u8>& output) = 0; 37 std::span<u8> output) = 0;
38 38
39 /** 39 /**
40 * Handles an ioctl2 request. 40 * Handles an ioctl2 request.
@@ -45,7 +45,7 @@ public:
45 * @returns The result code of the ioctl. 45 * @returns The result code of the ioctl.
46 */ 46 */
47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
48 std::span<const u8> inline_input, std::vector<u8>& output) = 0; 48 std::span<const u8> inline_input, std::span<u8> output) = 0;
49 49
50 /** 50 /**
51 * Handles an ioctl3 request. 51 * Handles an ioctl3 request.
@@ -56,7 +56,7 @@ public:
56 * @returns The result code of the ioctl. 56 * @returns The result code of the ioctl.
57 */ 57 */
58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::vector<u8>& output, std::vector<u8>& inline_output) = 0; 59 std::span<u8> output, std::span<u8> inline_output) = 0;
60 60
61 /** 61 /**
62 * Called once a device is opened 62 * Called once a device is opened
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 5a5b2e305..05a43d8dc 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,19 +18,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
18nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
19 19
20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::vector<u8>& output) { 21 std::span<u8> output) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
23 return NvResult::NotImplemented; 23 return NvResult::NotImplemented;
24} 24}
25 25
26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 std::span<const u8> inline_input, std::vector<u8>& output) { 27 std::span<const u8> inline_input, std::span<u8> output) {
28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
29 return NvResult::NotImplemented; 29 return NvResult::NotImplemented;
30} 30}
31 31
32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
33 std::vector<u8>& output, std::vector<u8>& inline_output) { 33 std::span<u8> output, std::span<u8> inline_output) {
34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
35 return NvResult::NotImplemented; 35 return NvResult::NotImplemented;
36} 36}
@@ -51,8 +51,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form
51 stride, format, transform, crop_rect}; 51 stride, format, transform, crop_rect};
52 52
53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); 53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
54 system.GetPerfStats().EndSystemFrame();
55 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); 54 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
55 system.GetPerfStats().EndSystemFrame();
56 system.GetPerfStats().BeginSystemFrame(); 56 system.GetPerfStats().BeginSystemFrame();
57} 57}
58 58
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index bcd0e3ed5..daee05fe8 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -26,11 +26,11 @@ public:
26 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::span<u8> output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::span<const u8> inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::span<u8> output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
33 std::vector<u8>& inline_output) override; 33 std::span<u8> inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 681bd0867..07e570a9f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -28,7 +28,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con
28nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
29 29
30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::vector<u8>& output) { 31 std::span<u8> output) {
32 switch (command.group) { 32 switch (command.group) {
33 case 'A': 33 case 'A':
34 switch (command.cmd) { 34 switch (command.cmd) {
@@ -61,13 +61,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
61} 61}
62 62
63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 std::span<const u8> inline_input, std::vector<u8>& output) { 64 std::span<const u8> inline_input, std::span<u8> output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
68 68
69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
70 std::vector<u8>& output, std::vector<u8>& inline_output) { 70 std::span<u8> output, std::span<u8> inline_output) {
71 switch (command.group) { 71 switch (command.group) {
72 case 'A': 72 case 'A':
73 switch (command.cmd) { 73 switch (command.cmd) {
@@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 87void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 88void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 89
90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) {
91 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
93 93
@@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& ou
141 return NvResult::Success; 141 return NvResult::Success;
142} 142}
143 143
144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) { 144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) {
145 IoctlAllocSpace params{}; 145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size()); 146 std::memcpy(&params, input.data(), input.size());
147 147
@@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
220 mapping_map.erase(offset); 220 mapping_map.erase(offset);
221} 221}
222 222
223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) {
224 IoctlFreeSpace params{}; 224 IoctlFreeSpace params{};
225 std::memcpy(&params, input.data(), input.size()); 225 std::memcpy(&params, input.data(), input.size());
226 226
@@ -266,15 +266,14 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& ou
266 return NvResult::Success; 266 return NvResult::Success;
267} 267}
268 268
269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) { 269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
271 271
272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
273 273
274 std::vector<IoctlRemapEntry> entries(num_entries);
275 std::memcpy(entries.data(), input.data(), input.size());
276
277 std::scoped_lock lock(mutex); 274 std::scoped_lock lock(mutex);
275 entries.resize_destructive(num_entries);
276 std::memcpy(entries.data(), input.data(), input.size());
278 277
279 if (!vm.initialised) { 278 if (!vm.initialised) {
280 return NvResult::BadValue; 279 return NvResult::BadValue;
@@ -320,7 +319,7 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output
320 return NvResult::Success; 319 return NvResult::Success;
321} 320}
322 321
323NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) { 322NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) {
324 IoctlMapBufferEx params{}; 323 IoctlMapBufferEx params{};
325 std::memcpy(&params, input.data(), input.size()); 324 std::memcpy(&params, input.data(), input.size());
326 325
@@ -424,7 +423,7 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>&
424 return NvResult::Success; 423 return NvResult::Success;
425} 424}
426 425
427NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { 426NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
428 IoctlUnmapBuffer params{}; 427 IoctlUnmapBuffer params{};
429 std::memcpy(&params, input.data(), input.size()); 428 std::memcpy(&params, input.data(), input.size());
430 429
@@ -463,7 +462,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>&
463 return NvResult::Success; 462 return NvResult::Success;
464} 463}
465 464
466NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) { 465NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) {
467 IoctlBindChannel params{}; 466 IoctlBindChannel params{};
468 std::memcpy(&params, input.data(), input.size()); 467 std::memcpy(&params, input.data(), input.size());
469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 468 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
@@ -492,7 +491,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
492 }; 491 };
493} 492}
494 493
495NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) { 494NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) {
496 IoctlGetVaRegions params{}; 495 IoctlGetVaRegions params{};
497 std::memcpy(&params, input.data(), input.size()); 496 std::memcpy(&params, input.data(), input.size());
498 497
@@ -511,8 +510,8 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>&
511 return NvResult::Success; 510 return NvResult::Success;
512} 511}
513 512
514NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output, 513NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output,
515 std::vector<u8>& inline_output) { 514 std::span<u8> inline_output) {
516 IoctlGetVaRegions params{}; 515 IoctlGetVaRegions params{};
517 std::memcpy(&params, input.data(), input.size()); 516 std::memcpy(&params, input.data(), input.size());
518 517
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 1aba8d579..2af3e1260 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -15,6 +15,7 @@
15#include "common/address_space.h" 15#include "common/address_space.h"
16#include "common/common_funcs.h" 16#include "common/common_funcs.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/scratch_buffer.h"
18#include "common/swap.h" 19#include "common/swap.h"
19#include "core/hle/service/nvdrv/core/nvmap.h" 20#include "core/hle/service/nvdrv/core/nvmap.h"
20#include "core/hle/service/nvdrv/devices/nvdevice.h" 21#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -48,11 +49,11 @@ public:
48 ~nvhost_as_gpu() override; 49 ~nvhost_as_gpu() override;
49 50
50 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 51 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
51 std::vector<u8>& output) override; 52 std::span<u8> output) override;
52 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 53 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
53 std::span<const u8> inline_input, std::vector<u8>& output) override; 54 std::span<const u8> inline_input, std::span<u8> output) override;
54 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 55 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
55 std::vector<u8>& inline_output) override; 56 std::span<u8> inline_output) override;
56 57
57 void OnOpen(DeviceFD fd) override; 58 void OnOpen(DeviceFD fd) override;
58 void OnClose(DeviceFD fd) override; 59 void OnClose(DeviceFD fd) override;
@@ -138,18 +139,18 @@ private:
138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
139 "IoctlGetVaRegions is incorrect size"); 140 "IoctlGetVaRegions is incorrect size");
140 141
141 NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output); 142 NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output);
142 NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output); 143 NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output);
143 NvResult Remap(std::span<const u8> input, std::vector<u8>& output); 144 NvResult Remap(std::span<const u8> input, std::span<u8> output);
144 NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output); 145 NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output);
145 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); 146 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
146 NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output); 147 NvResult FreeSpace(std::span<const u8> input, std::span<u8> output);
147 NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output); 148 NvResult BindChannel(std::span<const u8> input, std::span<u8> output);
148 149
149 void GetVARegionsImpl(IoctlGetVaRegions& params); 150 void GetVARegionsImpl(IoctlGetVaRegions& params);
150 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output); 151 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output);
151 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output, 152 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output,
152 std::vector<u8>& inline_output); 153 std::span<u8> inline_output);
153 154
154 void FreeMappingLocked(u64 offset); 155 void FreeMappingLocked(u64 offset);
155 156
@@ -212,6 +213,7 @@ private:
212 bool initialised{}; 213 bool initialised{};
213 } vm; 214 } vm;
214 std::shared_ptr<Tegra::MemoryManager> gmmu; 215 std::shared_ptr<Tegra::MemoryManager> gmmu;
216 Common::ScratchBuffer<IoctlRemapEntry> entries;
215 217
216 // s32 channel{}; 218 // s32 channel{};
217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; 219 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index e12025560..4d55554b4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -35,7 +35,7 @@ nvhost_ctrl::~nvhost_ctrl() {
35} 35}
36 36
37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
38 std::vector<u8>& output) { 38 std::span<u8> output) {
39 switch (command.group) { 39 switch (command.group) {
40 case 0x0: 40 case 0x0:
41 switch (command.cmd) { 41 switch (command.cmd) {
@@ -64,13 +64,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp
64} 64}
65 65
66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
67 std::span<const u8> inline_input, std::vector<u8>& output) { 67 std::span<const u8> inline_input, std::span<u8> output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented; 69 return NvResult::NotImplemented;
70} 70}
71 71
72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
73 std::vector<u8>& output, std::vector<u8>& inline_outpu) { 73 std::span<u8> output, std::span<u8> inline_outpu) {
74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
75 return NvResult::NotImplemented; 75 return NvResult::NotImplemented;
76} 76}
@@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 79
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 80void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 81
82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) { 82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) {
83 IocGetConfigParams params{}; 83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params)); 84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
@@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 87 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 88}
89 89
90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, 90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output,
91 bool is_allocation) { 91 bool is_allocation) {
92 IocCtrlEventWaitParams params{}; 92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params)); 93 std::memcpy(&params, input.data(), sizeof(params));
@@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 231 return NvResult::Success;
232} 232}
233 233
234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) { 234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) {
235 IocCtrlEventRegisterParams params{}; 235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params)); 236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 237 const u32 event_id = params.user_event_id;
@@ -252,7 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vecto
252 return NvResult::Success; 252 return NvResult::Success;
253} 253}
254 254
255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) { 255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) {
256 IocCtrlEventUnregisterParams params{}; 256 IocCtrlEventUnregisterParams params{};
257 std::memcpy(&params, input.data(), sizeof(params)); 257 std::memcpy(&params, input.data(), sizeof(params));
258 const u32 event_id = params.user_event_id & 0x00FF; 258 const u32 event_id = params.user_event_id & 0x00FF;
@@ -262,8 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vec
262 return FreeEvent(event_id); 262 return FreeEvent(event_id);
263} 263}
264 264
265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, 265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) {
266 std::vector<u8>& output) {
267 IocCtrlEventUnregisterBatchParams params{}; 266 IocCtrlEventUnregisterBatchParams params{};
268 std::memcpy(&params, input.data(), sizeof(params)); 267 std::memcpy(&params, input.data(), sizeof(params));
269 u64 event_mask = params.user_events; 268 u64 event_mask = params.user_events;
@@ -281,7 +280,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,
281 return NvResult::Success; 280 return NvResult::Success;
282} 281}
283 282
284NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) { 283NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) {
285 IocCtrlEventClearParams params{}; 284 IocCtrlEventClearParams params{};
286 std::memcpy(&params, input.data(), sizeof(params)); 285 std::memcpy(&params, input.data(), sizeof(params));
287 286
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index dd2e7888a..2efed4862 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -26,11 +26,11 @@ public:
26 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::span<u8> output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::span<const u8> inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::span<u8> output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
33 std::vector<u8>& inline_output) override; 33 std::span<u8> inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
@@ -186,13 +186,12 @@ private:
186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size"); 187 "IocCtrlEventKill is incorrect size");
188 188
189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output); 189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output);
190 NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, 190 NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation);
191 bool is_allocation); 191 NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output);
192 NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output); 192 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output);
193 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output);
194 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output); 194 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output);
195 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output);
196 195
197 NvResult FreeEvent(u32 slot); 196 NvResult FreeEvent(u32 slot);
198 197
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index be3c083db..6081d92e9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -22,7 +22,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
22} 22}
23 23
24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) { 25 std::span<u8> output) {
26 switch (command.group) { 26 switch (command.group) {
27 case 'G': 27 case 'G':
28 switch (command.cmd) { 28 switch (command.cmd) {
@@ -54,13 +54,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
54} 54}
55 55
56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 std::span<const u8> inline_input, std::vector<u8>& output) { 57 std::span<const u8> inline_input, std::span<u8> output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented; 59 return NvResult::NotImplemented;
60} 60}
61 61
62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
63 std::vector<u8>& output, std::vector<u8>& inline_output) { 63 std::span<u8> output, std::span<u8> inline_output) {
64 switch (command.group) { 64 switch (command.group) {
65 case 'G': 65 case 'G':
66 switch (command.cmd) { 66 switch (command.cmd) {
@@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 84
85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) { 85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) {
86 LOG_DEBUG(Service_NVDRV, "called"); 86 LOG_DEBUG(Service_NVDRV, "called");
87 IoctlCharacteristics params{}; 87 IoctlCharacteristics params{};
88 std::memcpy(&params, input.data(), input.size()); 88 std::memcpy(&params, input.data(), input.size());
@@ -127,8 +127,8 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec
127 return NvResult::Success; 127 return NvResult::Success;
128} 128}
129 129
130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output,
131 std::vector<u8>& inline_output) { 131 std::span<u8> inline_output) {
132 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
133 IoctlCharacteristics params{}; 133 IoctlCharacteristics params{};
134 std::memcpy(&params, input.data(), input.size()); 134 std::memcpy(&params, input.data(), input.size());
@@ -175,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) { 178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) {
179 IoctlGpuGetTpcMasksArgs params{}; 179 IoctlGpuGetTpcMasksArgs params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -186,8 +186,8 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>
186 return NvResult::Success; 186 return NvResult::Success;
187} 187}
188 188
189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, 189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output,
190 std::vector<u8>& inline_output) { 190 std::span<u8> inline_output) {
191 IoctlGpuGetTpcMasksArgs params{}; 191 IoctlGpuGetTpcMasksArgs params{};
192 std::memcpy(&params, input.data(), input.size()); 192 std::memcpy(&params, input.data(), input.size());
193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -199,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>
199 return NvResult::Success; 199 return NvResult::Success;
200} 200}
201 201
202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) { 202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) {
203 LOG_DEBUG(Service_NVDRV, "called"); 203 LOG_DEBUG(Service_NVDRV, "called");
204 204
205 IoctlActiveSlotMask params{}; 205 IoctlActiveSlotMask params{};
@@ -212,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vect
212 return NvResult::Success; 212 return NvResult::Success;
213} 213}
214 214
215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) { 215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) {
216 LOG_DEBUG(Service_NVDRV, "called"); 216 LOG_DEBUG(Service_NVDRV, "called");
217 217
218 IoctlZcullGetCtxSize params{}; 218 IoctlZcullGetCtxSize params{};
@@ -224,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector
224 return NvResult::Success; 224 return NvResult::Success;
225} 225}
226 226
227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) { 227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) {
228 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
229 229
230 IoctlNvgpuGpuZcullGetInfoArgs params{}; 230 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -247,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8
247 return NvResult::Success; 247 return NvResult::Success;
248} 248}
249 249
250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) { 250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) {
251 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 251 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
252 252
253 IoctlZbcSetTable params{}; 253 IoctlZbcSetTable params{};
@@ -263,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>
263 return NvResult::Success; 263 return NvResult::Success;
264} 264}
265 265
266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) { 266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) {
267 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 267 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
268 268
269 IoctlZbcQueryTable params{}; 269 IoctlZbcQueryTable params{};
@@ -273,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u
273 return NvResult::Success; 273 return NvResult::Success;
274} 274}
275 275
276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) { 276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) {
277 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 277 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
278 278
279 IoctlFlushL2 params{}; 279 IoctlFlushL2 params{};
@@ -283,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& ou
283 return NvResult::Success; 283 return NvResult::Success;
284} 284}
285 285
286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) { 286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) {
287 LOG_DEBUG(Service_NVDRV, "called"); 287 LOG_DEBUG(Service_NVDRV, "called");
288 288
289 IoctlGetGpuTime params{}; 289 IoctlGetGpuTime params{};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index b9333d9d3..97995551c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -22,11 +22,11 @@ public:
22 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
23 23
24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) override; 25 std::span<u8> output) override;
26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 std::span<const u8> inline_input, std::vector<u8>& output) override; 27 std::span<const u8> inline_input, std::span<u8> output) override;
28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
29 std::vector<u8>& inline_output) override; 29 std::span<u8> inline_output) override;
30 30
31 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
32 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
@@ -151,21 +151,21 @@ private:
151 }; 151 };
152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
153 153
154 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output); 154 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output);
155 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, 155 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output,
156 std::vector<u8>& inline_output); 156 std::span<u8> inline_output);
157 157
158 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output); 158 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output);
159 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, 159 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output,
160 std::vector<u8>& inline_output); 160 std::span<u8> inline_output);
161 161
162 NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output); 162 NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output);
163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output); 163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output);
164 NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output); 164 NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output);
165 NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output); 165 NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output);
166 NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output);
167 NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output); 167 NvResult FlushL2(std::span<const u8> input, std::span<u8> output);
168 NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output); 168 NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);
169 169
170 EventInterface& events_interface; 170 EventInterface& events_interface;
171 171
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 453a965dc..46a25fcab 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -47,7 +47,7 @@ nvhost_gpu::~nvhost_gpu() {
47} 47}
48 48
49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
50 std::vector<u8>& output) { 50 std::span<u8> output) {
51 switch (command.group) { 51 switch (command.group) {
52 case 0x0: 52 case 0x0:
53 switch (command.cmd) { 53 switch (command.cmd) {
@@ -99,7 +99,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
99}; 99};
100 100
101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
102 std::span<const u8> inline_input, std::vector<u8>& output) { 102 std::span<const u8> inline_input, std::span<u8> output) {
103 switch (command.group) { 103 switch (command.group) {
104 case 'H': 104 case 'H':
105 switch (command.cmd) { 105 switch (command.cmd) {
@@ -113,7 +113,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu
113} 113}
114 114
115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
116 std::vector<u8>& output, std::vector<u8>& inline_output) { 116 std::span<u8> output, std::span<u8> inline_output) {
117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
118 return NvResult::NotImplemented; 118 return NvResult::NotImplemented;
119} 119}
@@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 121void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 122void nvhost_gpu::OnClose(DeviceFD fd) {}
123 123
124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { 124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
125 IoctlSetNvmapFD params{}; 125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size()); 126 std::memcpy(&params, input.data(), input.size());
127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& outp
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) { 133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135 135
136 IoctlClientData params{}; 136 IoctlClientData params{};
@@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& o
139 return NvResult::Success; 139 return NvResult::Success;
140} 140}
141 141
142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) { 142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) {
143 LOG_DEBUG(Service_NVDRV, "called"); 143 LOG_DEBUG(Service_NVDRV, "called");
144 144
145 IoctlClientData params{}; 145 IoctlClientData params{};
@@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& o
149 return NvResult::Success; 149 return NvResult::Success;
150} 150}
151 151
152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) { 152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 153 std::memcpy(&zcull_params, input.data(), input.size());
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 155 zcull_params.mode);
@@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& outpu
158 return NvResult::Success; 158 return NvResult::Success;
159} 159}
160 160
161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) { 161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) {
162 IoctlSetErrorNotifier params{}; 162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size()); 163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
@@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>
168 return NvResult::Success; 168 return NvResult::Success;
169} 169}
170 170
171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) { 171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 172 std::memcpy(&channel_priority, input.data(), input.size());
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174 174
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) { 178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) {
179 IoctlAllocGpfifoEx2 params{}; 179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 181 LOG_WARNING(Service_NVDRV,
@@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>&
197 return NvResult::Success; 197 return NvResult::Success;
198} 198}
199 199
200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) { 200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) {
201 IoctlAllocObjCtx params{}; 201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -208,7 +208,8 @@ NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vecto
208 return NvResult::Success; 208 return NvResult::Success;
209} 209}
210 210
211static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { 211static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList(
212 NvFence fence) {
212 return { 213 return {
213 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, 214 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
214 Tegra::SubmissionMode::Increasing), 215 Tegra::SubmissionMode::Increasing),
@@ -219,35 +220,35 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
219 }; 220 };
220} 221}
221 222
222static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { 223static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList(
223 std::vector<Tegra::CommandHeader> result{ 224 NvFence fence) {
225 boost::container::small_vector<Tegra::CommandHeader, 512> result{
224 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, 226 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
225 Tegra::SubmissionMode::Increasing), 227 Tegra::SubmissionMode::Increasing),
226 {}}; 228 {}};
227 229
228 for (u32 count = 0; count < 2; ++count) { 230 for (u32 count = 0; count < 2; ++count) {
229 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, 231 result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
230 Tegra::SubmissionMode::Increasing)); 232 Tegra::SubmissionMode::Increasing));
231 result.emplace_back( 233 result.push_back(
232 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); 234 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));
233 } 235 }
234 236
235 return result; 237 return result;
236} 238}
237 239
238static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { 240static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList(
239 std::vector<Tegra::CommandHeader> result{ 241 NvFence fence) {
242 boost::container::small_vector<Tegra::CommandHeader, 512> result{
240 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, 243 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
241 Tegra::SubmissionMode::Increasing), 244 Tegra::SubmissionMode::Increasing),
242 {}}; 245 {}};
243 const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; 246 auto increment_list{BuildIncrementCommandList(fence)};
244 247 result.insert(result.end(), increment_list.begin(), increment_list.end());
245 result.insert(result.end(), increment.begin(), increment.end());
246
247 return result; 248 return result;
248} 249}
249 250
250NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 251NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
251 Tegra::CommandList&& entries) { 252 Tegra::CommandList&& entries) {
252 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 253 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
253 params.num_entries, params.flags.raw); 254 params.num_entries, params.flags.raw);
@@ -293,7 +294,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
293 return NvResult::Success; 294 return NvResult::Success;
294} 295}
295 296
296NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, 297NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
297 bool kickoff) { 298 bool kickoff) {
298 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 299 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
299 UNIMPLEMENTED(); 300 UNIMPLEMENTED();
@@ -315,7 +316,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>
315} 316}
316 317
317NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 318NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
318 std::vector<u8>& output) { 319 std::span<u8> output) {
319 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 320 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
320 UNIMPLEMENTED(); 321 UNIMPLEMENTED();
321 return NvResult::InvalidSize; 322 return NvResult::InvalidSize;
@@ -327,7 +328,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const
327 return SubmitGPFIFOImpl(params, output, std::move(entries)); 328 return SubmitGPFIFOImpl(params, output, std::move(entries));
328} 329}
329 330
330NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { 331NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
331 IoctlGetWaitbase params{}; 332 IoctlGetWaitbase params{};
332 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
333 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
@@ -337,7 +338,7 @@ NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& out
337 return NvResult::Success; 338 return NvResult::Success;
338} 339}
339 340
340NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) { 341NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) {
341 IoctlChannelSetTimeout params{}; 342 IoctlChannelSetTimeout params{};
342 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
343 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
@@ -345,7 +346,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8
345 return NvResult::Success; 346 return NvResult::Success;
346} 347}
347 348
348NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) { 349NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) {
349 IoctlSetTimeslice params{}; 350 IoctlSetTimeslice params{};
350 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
351 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 3ca58202d..529c20526 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -41,11 +41,11 @@ public:
41 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
42 42
43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
44 std::vector<u8>& output) override; 44 std::span<u8> output) override;
45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 std::span<const u8> inline_input, std::vector<u8>& output) override; 46 std::span<const u8> inline_input, std::span<u8> output) override;
47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
48 std::vector<u8>& inline_output) override; 48 std::span<u8> inline_output) override;
49 49
50 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
51 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
@@ -186,23 +186,23 @@ private:
186 u32_le channel_priority{}; 186 u32_le channel_priority{};
187 u32_le channel_timeslice{}; 187 u32_le channel_timeslice{};
188 188
189 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); 189 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
190 NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output); 190 NvResult SetClientData(std::span<const u8> input, std::span<u8> output);
191 NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output); 191 NvResult GetClientData(std::span<const u8> input, std::span<u8> output);
192 NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output); 192 NvResult ZCullBind(std::span<const u8> input, std::span<u8> output);
193 NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output); 193 NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output);
194 NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output); 194 NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output);
195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output); 195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output);
196 NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output); 196 NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
198 Tegra::CommandList&& entries); 198 Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, 199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
200 bool kickoff = false); 200 bool kickoff = false);
201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
202 std::vector<u8>& output); 202 std::span<u8> output);
203 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); 203 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
204 NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output);
205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output);
206 206
207 EventInterface& events_interface; 207 EventInterface& events_interface;
208 NvCore::Container& core; 208 NvCore::Container& core;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index dc45169ad..a174442a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -16,7 +16,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::span<u8> output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -56,13 +56,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
56} 56}
57 57
58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::span<const u8> inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::span<u8> output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::span<u8> output, std::span<u8> inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
68} 68}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0d615bbcb..ad2233c49 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,11 +14,11 @@ public:
14 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
15 15
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
17 std::vector<u8>& output) override; 17 std::span<u8> output) override;
18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::span<const u8> inline_input, std::vector<u8>& output) override; 19 std::span<const u8> inline_input, std::span<u8> output) override;
20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
21 std::vector<u8>& inline_output) override; 21 std::span<u8> inline_output) override;
22 22
23 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 1ab51f10b..61649aa4a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -36,7 +36,7 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si
36// Writes the data in src to an offset into the dst vector. The offset is specified in bytes 36// Writes the data in src to an offset into the dst vector. The offset is specified in bytes
37// Returns the number of bytes written into dst. 37// Returns the number of bytes written into dst.
38template <typename T> 38template <typename T>
39std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { 39std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size_t offset) {
40 if (src.empty()) { 40 if (src.empty()) {
41 return 0; 41 return 0;
42 } 42 }
@@ -72,8 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
72 return NvResult::Success; 72 return NvResult::Success;
73} 73}
74 74
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, 75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) {
76 std::vector<u8>& output) {
77 IoctlSubmit params{}; 76 IoctlSubmit params{};
78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 77 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
79 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 78 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -121,7 +120,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,
121 return NvResult::Success; 120 return NvResult::Success;
122} 121}
123 122
124NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) { 123NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) {
125 IoctlGetSyncpoint params{}; 124 IoctlGetSyncpoint params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 125 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 126 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -133,7 +132,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vecto
133 return NvResult::Success; 132 return NvResult::Success;
134} 133}
135 134
136NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { 135NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
137 IoctlGetWaitbase params{}; 136 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 137 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 138 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
@@ -142,7 +141,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector
142 return NvResult::Success; 141 return NvResult::Success;
143} 142}
144 143
145NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) { 144NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) {
146 IoctlMapBuffer params{}; 145 IoctlMapBuffer params{};
147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 146 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 147 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -159,7 +158,7 @@ NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u
159 return NvResult::Success; 158 return NvResult::Success;
160} 159}
161 160
162NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { 161NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
163 IoctlMapBuffer params{}; 162 IoctlMapBuffer params{};
164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 163 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 164 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -173,7 +172,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector
173 return NvResult::Success; 172 return NvResult::Success;
174} 173}
175 174
176NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) { 175NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) {
177 std::memcpy(&submit_timeout, input.data(), input.size()); 176 std::memcpy(&submit_timeout, input.data(), input.size());
178 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 177 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
179 return NvResult::Success; 178 return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 5af26a26f..9bb573bfe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -108,12 +108,12 @@ protected:
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(std::span<const u8> input); 110 NvResult SetNVMAPfd(std::span<const u8> input);
111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output); 111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output);
112 NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output); 112 NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output);
113 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); 113 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
114 NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output); 114 NvResult MapBuffer(std::span<const u8> input, std::span<u8> output);
115 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); 115 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
116 NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output);
117 117
118 Kernel::KEvent* QueryEvent(u32 event_id) override; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119 119
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 39f30e7c8..a05c8cdae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,7 +13,7 @@ nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}
13nvhost_nvjpg::~nvhost_nvjpg() = default; 13nvhost_nvjpg::~nvhost_nvjpg() = default;
14 14
15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) { 16 std::span<u8> output) {
17 switch (command.group) { 17 switch (command.group) {
18 case 'H': 18 case 'H':
19 switch (command.cmd) { 19 switch (command.cmd) {
@@ -32,13 +32,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
32} 32}
33 33
34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 std::span<const u8> inline_input, std::vector<u8>& output) { 35 std::span<const u8> inline_input, std::span<u8> output) {
36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
37 return NvResult::NotImplemented; 37 return NvResult::NotImplemented;
38} 38}
39 39
40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
41 std::vector<u8>& output, std::vector<u8>& inline_output) { 41 std::span<u8> output, std::span<u8> inline_output) {
42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
43 return NvResult::NotImplemented; 43 return NvResult::NotImplemented;
44} 44}
@@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 46void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 47void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 48
49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { 49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
50 IoctlSetNvmapFD params{}; 50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size()); 51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 41b57e872..5623e0d47 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,11 +16,11 @@ public:
16 ~nvhost_nvjpg() override; 16 ~nvhost_nvjpg() override;
17 17
18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) override; 19 std::span<u8> output) override;
20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::span<const u8> inline_input, std::vector<u8>& output) override; 21 std::span<const u8> inline_input, std::span<u8> output) override;
22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
23 std::vector<u8>& inline_output) override; 23 std::span<u8> inline_output) override;
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
@@ -33,7 +33,7 @@ private:
33 33
34 s32_le nvmap_fd{}; 34 s32_le nvmap_fd{};
35 35
36 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); 36 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
37}; 37};
38 38
39} // namespace Service::Nvidia::Devices 39} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index b0ea402a7..c0b8684c3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -16,7 +16,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::span<u8> output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -56,13 +56,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
56} 56}
57 57
58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::span<const u8> inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::span<u8> output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::span<u8> output, std::span<u8> inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
68} 68}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b5e350a83..cadbcb0a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,11 +13,11 @@ public:
13 ~nvhost_vic(); 13 ~nvhost_vic();
14 14
15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) override; 16 std::span<u8> output) override;
17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
18 std::span<const u8> inline_input, std::vector<u8>& output) override; 18 std::span<const u8> inline_input, std::span<u8> output) override;
19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
20 std::vector<u8>& inline_output) override; 20 std::span<u8> inline_output) override;
21 21
22 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
23 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 07417f045..e7f7e273b 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -26,7 +26,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
26nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
27 27
28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) { 29 std::span<u8> output) {
30 switch (command.group) { 30 switch (command.group) {
31 case 0x1: 31 case 0x1:
32 switch (command.cmd) { 32 switch (command.cmd) {
@@ -55,13 +55,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
55} 55}
56 56
57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
58 std::span<const u8> inline_input, std::vector<u8>& output) { 58 std::span<const u8> inline_input, std::span<u8> output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
64 std::vector<u8>& output, std::vector<u8>& inline_output) { 64 std::span<u8> inline_output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
@@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
69void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
71 71
72NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
73 IocCreateParams params; 73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
89 return NvResult::Success; 89 return NvResult::Success;
90} 90}
91 91
92NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
93 IocAllocParams params; 93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
@@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
137 return result; 137 return result;
138} 138}
139 139
140NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) { 140NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
141 IocGetIdParams params; 141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params)); 142 std::memcpy(&params, input.data(), sizeof(params));
143 143
@@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
161 return NvResult::Success; 161 return NvResult::Success;
162} 162}
163 163
164NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
165 IocFromIdParams params; 165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
167 167
@@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
192 return NvResult::Success; 192 return NvResult::Success;
193} 193}
194 194
195NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) { 195NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 197
198 IocParamParams params; 198 IocParamParams params;
@@ -241,7 +241,7 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
241 return NvResult::Success; 241 return NvResult::Success;
242} 242}
243 243
244NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) { 244NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
245 IocFreeParams params; 245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params)); 246 std::memcpy(&params, input.data(), sizeof(params));
247 247
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 82bd3b118..40c65b430 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -27,11 +27,11 @@ public:
27 nvmap& operator=(const nvmap&) = delete; 27 nvmap& operator=(const nvmap&) = delete;
28 28
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
30 std::vector<u8>& output) override; 30 std::span<u8> output) override;
31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
32 std::span<const u8> inline_input, std::vector<u8>& output) override; 32 std::span<const u8> inline_input, std::span<u8> output) override;
33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
34 std::vector<u8>& inline_output) override; 34 std::span<u8> inline_output) override;
35 35
36 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
@@ -106,12 +106,12 @@ private:
106 }; 106 };
107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
108 108
109 NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output); 109 NvResult IocCreate(std::span<const u8> input, std::span<u8> output);
110 NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output); 110 NvResult IocAlloc(std::span<const u8> input, std::span<u8> output);
111 NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output); 111 NvResult IocGetId(std::span<const u8> input, std::span<u8> output);
112 NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output); 112 NvResult IocFromId(std::span<const u8> input, std::span<u8> output);
113 NvResult IocParam(std::span<const u8> input, std::vector<u8>& output); 113 NvResult IocParam(std::span<const u8> input, std::span<u8> output);
114 NvResult IocFree(std::span<const u8> input, std::vector<u8>& output); 114 NvResult IocFree(std::span<const u8> input, std::span<u8> output);
115 115
116 NvCore::Container& container; 116 NvCore::Container& container;
117 NvCore::NvMap& file; 117 NvCore::NvMap& file;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 3d774eec4..9e46ee8dd 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -130,7 +130,7 @@ DeviceFD Module::Open(const std::string& device_name) {
130} 130}
131 131
132NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 132NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
133 std::vector<u8>& output) { 133 std::span<u8> output) {
134 if (fd < 0) { 134 if (fd < 0) {
135 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 135 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
136 return NvResult::InvalidState; 136 return NvResult::InvalidState;
@@ -147,7 +147,7 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
147} 147}
148 148
149NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 149NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
150 std::span<const u8> inline_input, std::vector<u8>& output) { 150 std::span<const u8> inline_input, std::span<u8> output) {
151 if (fd < 0) { 151 if (fd < 0) {
152 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 152 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
153 return NvResult::InvalidState; 153 return NvResult::InvalidState;
@@ -163,8 +163,8 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
163 return itr->second->Ioctl2(fd, command, input, inline_input, output); 163 return itr->second->Ioctl2(fd, command, input, inline_input, output);
164} 164}
165 165
166NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 166NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
167 std::vector<u8>& output, std::vector<u8>& inline_output) { 167 std::span<u8> inline_output) {
168 if (fd < 0) { 168 if (fd < 0) {
169 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 169 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
170 return NvResult::InvalidState; 170 return NvResult::InvalidState;
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 668be742b..d8622b3ca 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -80,13 +80,13 @@ public:
80 DeviceFD Open(const std::string& device_name); 80 DeviceFD Open(const std::string& device_name);
81 81
82 /// Sends an ioctl command to the specified file descriptor. 82 /// Sends an ioctl command to the specified file descriptor.
83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output); 83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output);
84 84
85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
86 std::span<const u8> inline_input, std::vector<u8>& output); 86 std::span<const u8> inline_input, std::span<u8> output);
87 87
88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
89 std::vector<u8>& inline_output); 89 std::span<u8> inline_output);
90 90
91 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
92 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index d010a1e03..348207e25 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -63,12 +63,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) {
63 } 63 }
64 64
65 // Check device 65 // Check device
66 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 66 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
67 const auto input_buffer = ctx.ReadBuffer(0); 67 const auto input_buffer = ctx.ReadBuffer(0);
68 68
69 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); 69 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output);
70 if (command.is_out != 0) { 70 if (command.is_out != 0) {
71 ctx.WriteBuffer(output_buffer); 71 ctx.WriteBuffer(tmp_output);
72 } 72 }
73 73
74 IPC::ResponseBuilder rb{ctx, 3}; 74 IPC::ResponseBuilder rb{ctx, 3};
@@ -90,12 +90,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) {
90 90
91 const auto input_buffer = ctx.ReadBuffer(0); 91 const auto input_buffer = ctx.ReadBuffer(0);
92 const auto input_inlined_buffer = ctx.ReadBuffer(1); 92 const auto input_inlined_buffer = ctx.ReadBuffer(1);
93 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 93 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
94 94
95 const auto nv_result = 95 const auto nv_result =
96 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); 96 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output);
97 if (command.is_out != 0) { 97 if (command.is_out != 0) {
98 ctx.WriteBuffer(output_buffer); 98 ctx.WriteBuffer(tmp_output);
99 } 99 }
100 100
101 IPC::ResponseBuilder rb{ctx, 3}; 101 IPC::ResponseBuilder rb{ctx, 3};
@@ -116,14 +116,12 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) {
116 } 116 }
117 117
118 const auto input_buffer = ctx.ReadBuffer(0); 118 const auto input_buffer = ctx.ReadBuffer(0);
119 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 119 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
120 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); 120 tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1));
121 121 const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline);
122 const auto nv_result =
123 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
124 if (command.is_out != 0) { 122 if (command.is_out != 0) {
125 ctx.WriteBuffer(output_buffer, 0); 123 ctx.WriteBuffer(tmp_output, 0);
126 ctx.WriteBuffer(output_buffer_inline, 1); 124 ctx.WriteBuffer(tmp_output_inline, 1);
127 } 125 }
128 126
129 IPC::ResponseBuilder rb{ctx, 3}; 127 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index 881ea1a6b..4b593ff90 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include "common/scratch_buffer.h"
7#include "core/hle/service/nvdrv/nvdrv.h" 8#include "core/hle/service/nvdrv/nvdrv.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
@@ -33,6 +34,8 @@ private:
33 34
34 u64 pid{}; 35 u64 pid{};
35 bool is_initialized{}; 36 bool is_initialized{};
37 Common::ScratchBuffer<u8> tmp_output;
38 Common::ScratchBuffer<u8> tmp_output_inline;
36}; 39};
37 40
38} // namespace Service::Nvidia 41} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index da2d5890f..5f55cd31e 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -43,14 +43,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
43 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 43 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
44 44
45 while (!stop_token.stop_requested()) { 45 while (!stop_token.stop_requested()) {
46 vsync_signal.wait(false); 46 vsync_signal.Wait();
47 vsync_signal.store(false);
48
49 guard->lock();
50 47
48 const auto lock_guard = Lock();
51 Compose(); 49 Compose();
52
53 guard->unlock();
54 } 50 }
55} 51}
56 52
@@ -69,8 +65,8 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
69 "ScreenComposition", 65 "ScreenComposition",
70 [this](std::uintptr_t, s64 time, 66 [this](std::uintptr_t, s64 time,
71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 67 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
72 vsync_signal.store(true); 68 { const auto lock_guard = Lock(); }
73 vsync_signal.notify_all(); 69 vsync_signal.Set();
74 return std::chrono::nanoseconds(GetNextTicks()); 70 return std::chrono::nanoseconds(GetNextTicks());
75 }); 71 });
76 72
@@ -96,8 +92,7 @@ Nvnflinger::~Nvnflinger() {
96 if (system.IsMulticore()) { 92 if (system.IsMulticore()) {
97 system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); 93 system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
98 vsync_thread.request_stop(); 94 vsync_thread.request_stop();
99 vsync_signal.store(true); 95 vsync_signal.Set();
100 vsync_signal.notify_all();
101 } else { 96 } else {
102 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 97 system.CoreTiming().UnscheduleEvent(single_composition_event, {});
103 } 98 }
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index a043cceb2..ef236303a 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -12,6 +12,7 @@
12 12
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/polyfill_thread.h" 14#include "common/polyfill_thread.h"
15#include "common/thread.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16#include "core/hle/service/kernel_helpers.h" 17#include "core/hle/service/kernel_helpers.h"
17 18
@@ -143,7 +144,7 @@ private:
143 144
144 Core::System& system; 145 Core::System& system;
145 146
146 std::atomic<bool> vsync_signal; 147 Common::Event vsync_signal;
147 148
148 std::jthread vsync_thread; 149 std::jthread vsync_thread;
149 150
diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h
index fb56d75d7..23ba315a0 100644
--- a/src/core/hle/service/nvnflinger/parcel.h
+++ b/src/core/hle/service/nvnflinger/parcel.h
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <span> 7#include <span>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9 10
10#include "common/alignment.h" 11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
@@ -167,7 +168,7 @@ public:
167private: 168private:
168 template <typename T> 169 template <typename T>
169 requires(std::is_trivially_copyable_v<T>) 170 requires(std::is_trivially_copyable_v<T>)
170 void WriteImpl(const T& val, std::vector<u8>& buffer) { 171 void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) {
171 const size_t aligned_size = Common::AlignUp(sizeof(T), 4); 172 const size_t aligned_size = Common::AlignUp(sizeof(T), 4);
172 const size_t old_size = buffer.size(); 173 const size_t old_size = buffer.size();
173 buffer.resize(old_size + aligned_size); 174 buffer.resize(old_size + aligned_size);
@@ -176,8 +177,8 @@ private:
176 } 177 }
177 178
178private: 179private:
179 std::vector<u8> m_data_buffer; 180 boost::container::small_vector<u8, 0x200> m_data_buffer;
180 std::vector<u8> m_object_buffer; 181 boost::container::small_vector<u8, 0x200> m_object_buffer;
181}; 182};
182 183
183} // namespace Service::android 184} // namespace Service::android
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 156bc27d8..d1e99b184 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -44,7 +44,7 @@ ServerManager::~ServerManager() {
44 m_event->Signal(); 44 m_event->Signal();
45 45
46 // Wait for processing to stop. 46 // Wait for processing to stop.
47 m_stopped.wait(false); 47 m_stopped.Wait();
48 m_threads.clear(); 48 m_threads.clear();
49 49
50 // Clean up ports. 50 // Clean up ports.
@@ -182,10 +182,7 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
182} 182}
183 183
184Result ServerManager::LoopProcess() { 184Result ServerManager::LoopProcess() {
185 SCOPE_EXIT({ 185 SCOPE_EXIT({ m_stopped.Set(); });
186 m_stopped.store(true);
187 m_stopped.notify_all();
188 });
189 186
190 R_RETURN(this->LoopProcessImpl()); 187 R_RETURN(this->LoopProcessImpl());
191} 188}
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h
index fdb8af2ff..58b0a0832 100644
--- a/src/core/hle/service/server_manager.h
+++ b/src/core/hle/service/server_manager.h
@@ -3,7 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <atomic>
7#include <functional> 6#include <functional>
8#include <list> 7#include <list>
9#include <map> 8#include <map>
@@ -12,6 +11,7 @@
12#include <vector> 11#include <vector>
13 12
14#include "common/polyfill_thread.h" 13#include "common/polyfill_thread.h"
14#include "common/thread.h"
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16#include "core/hle/service/mutex.h" 16#include "core/hle/service/mutex.h"
17 17
@@ -82,7 +82,7 @@ private:
82 std::list<RequestState> m_deferrals{}; 82 std::list<RequestState> m_deferrals{};
83 83
84 // Host state tracking 84 // Host state tracking
85 std::atomic<bool> m_stopped{}; 85 Common::Event m_stopped{};
86 std::vector<std::jthread> m_threads{}; 86 std::vector<std::jthread> m_threads{};
87 std::stop_source m_stop_source{}; 87 std::stop_source m_stop_source{};
88}; 88};
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index e6293ffb9..9fc01ea90 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <ratio>
7
6#include "common/common_funcs.h" 8#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/uuid.h" 10#include "common/uuid.h"
@@ -74,18 +76,19 @@ static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
74/// https://switchbrew.org/wiki/Glue_services#TimeSpanType 76/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
75struct TimeSpanType { 77struct TimeSpanType {
76 s64 nanoseconds{}; 78 s64 nanoseconds{};
77 static constexpr s64 ns_per_second{1000000000ULL};
78 79
79 s64 ToSeconds() const { 80 s64 ToSeconds() const {
80 return nanoseconds / ns_per_second; 81 return nanoseconds / std::nano::den;
81 } 82 }
82 83
83 static TimeSpanType FromSeconds(s64 seconds) { 84 static TimeSpanType FromSeconds(s64 seconds) {
84 return {seconds * ns_per_second}; 85 return {seconds * std::nano::den};
85 } 86 }
86 87
87 static TimeSpanType FromTicks(u64 ticks, u64 frequency) { 88 template <u64 Frequency>
88 return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency)); 89 static TimeSpanType FromTicks(u64 ticks) {
90 using TicksToNSRatio = std::ratio<std::nano::den, Frequency>;
91 return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)};
89 } 92 }
90}; 93};
91static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); 94static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index 3dbbb9850..5627b7003 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -10,7 +10,7 @@ namespace Service::Time::Clock {
10 10
11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { 11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{ 12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; 14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
15 15
16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { 16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 27600413e..0d9fb3143 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -10,7 +10,7 @@ namespace Service::Time::Clock {
10 10
11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { 11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{ 12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 14
15 return {ticks_time_span.ToSeconds(), GetClockSourceId()}; 15 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
16} 16}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 868be60c5..7197ca30f 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -240,8 +240,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestCon
240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; 240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
241 241
242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { 242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
243 const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), 243 const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
244 Core::Hardware::CNTFREQ)}; 244 system.CoreTiming().GetClockTicks())};
245 const s64 base_time_point{context.offset + current_time_point.time_point - 245 const s64 base_time_point{context.offset + current_time_point.time_point -
246 ticks.ToSeconds()}; 246 ticks.ToSeconds()};
247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; 247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index ce1c85bcc..a00676669 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -21,8 +21,9 @@ SharedMemory::~SharedMemory() = default;
21 21
22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, 22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
23 Clock::TimeSpanType current_time_point) { 23 Clock::TimeSpanType current_time_point) {
24 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( 24 const Clock::TimeSpanType ticks_time_span{
25 system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 25 Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
26 system.CoreTiming().GetClockTicks())};
26 const Clock::SteadyClockContext context{ 27 const Clock::SteadyClockContext context{
27 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), 28 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
28 clock_source_id}; 29 clock_source_id};
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 63aacd19f..205371a26 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -911,9 +911,13 @@ static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
911 911
912 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; 912 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
913 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; 913 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
914 for (int index{}; time_zone[index] != '\0'; ++index) { 914 u32 index;
915 for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' &&
916 index < calendar_additional_info.timezone_name.size() - 1;
917 ++index) {
915 calendar_additional_info.timezone_name[index] = time_zone[index]; 918 calendar_additional_info.timezone_name[index] = time_zone[index];
916 } 919 }
920 calendar_additional_info.timezone_name[index] = '\0';
917 return ResultSuccess; 921 return ResultSuccess;
918} 922}
919 923
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index b2b5677c8..0aca5a3a3 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -102,12 +102,12 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
102 Joycon::SerialNumber serial_number{}; 102 Joycon::SerialNumber serial_number{};
103 103
104 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); 104 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
105 if (result != Joycon::DriverResult::Success) { 105 if (result != Common::Input::DriverResult::Success) {
106 return false; 106 return false;
107 } 107 }
108 108
109 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number); 109 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
110 if (result2 != Joycon::DriverResult::Success) { 110 if (result2 != Common::Input::DriverResult::Success) {
111 return false; 111 return false;
112 } 112 }
113 113
@@ -171,10 +171,10 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
171 LOG_WARNING(Input, "No free handles available"); 171 LOG_WARNING(Input, "No free handles available");
172 return; 172 return;
173 } 173 }
174 if (result == Joycon::DriverResult::Success) { 174 if (result == Common::Input::DriverResult::Success) {
175 result = handle->RequestDeviceAccess(device_info); 175 result = handle->RequestDeviceAccess(device_info);
176 } 176 }
177 if (result == Joycon::DriverResult::Success) { 177 if (result == Common::Input::DriverResult::Success) {
178 LOG_WARNING(Input, "Initialize device"); 178 LOG_WARNING(Input, "Initialize device");
179 179
180 const std::size_t port = handle->GetDevicePort(); 180 const std::size_t port = handle->GetDevicePort();
@@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
195 OnMotionUpdate(port, type, id, value); 195 OnMotionUpdate(port, type, id, value);
196 }}, 196 }},
197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, 197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
198 .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { 198 .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
199 OnAmiiboUpdate(port, type, amiibo_data); 199 OnAmiiboUpdate(port, type, tag_info);
200 }}, 200 }},
201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data, 201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
202 Joycon::IrsResolution format) { 202 Joycon::IrsResolution format) {
@@ -273,8 +273,7 @@ Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
273 led_config += led_status.led_3 ? 4 : 0; 273 led_config += led_status.led_3 ? 4 : 0;
274 led_config += led_status.led_4 ? 8 : 0; 274 led_config += led_status.led_4 ? 8 : 0;
275 275
276 return static_cast<Common::Input::DriverResult>( 276 return handle->SetLedConfig(static_cast<u8>(led_config));
277 handle->SetLedConfig(static_cast<u8>(led_config)));
278} 277}
279 278
280Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier, 279Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
@@ -283,21 +282,113 @@ Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identi
283 if (handle == nullptr) { 282 if (handle == nullptr) {
284 return Common::Input::DriverResult::InvalidHandle; 283 return Common::Input::DriverResult::InvalidHandle;
285 } 284 }
286 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig( 285 return handle->SetIrsConfig(Joycon::IrsMode::ImageTransfer,
287 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format))); 286 static_cast<Joycon::IrsResolution>(camera_format));
288}; 287};
289 288
290Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { 289Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
291 return Common::Input::NfcState::Success; 290 return Common::Input::NfcState::Success;
292}; 291};
293 292
293Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
294 auto handle = GetHandle(identifier);
295 if (handle == nullptr) {
296 return Common::Input::NfcState::Unknown;
297 }
298 return TranslateDriverResult(handle->StartNfcPolling());
299};
300
301Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
302 auto handle = GetHandle(identifier);
303 if (handle == nullptr) {
304 return Common::Input::NfcState::Unknown;
305 }
306 return TranslateDriverResult(handle->StopNfcPolling());
307};
308
309Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
310 std::vector<u8>& out_data) {
311 auto handle = GetHandle(identifier);
312 if (handle == nullptr) {
313 return Common::Input::NfcState::Unknown;
314 }
315 return TranslateDriverResult(handle->ReadAmiiboData(out_data));
316}
317
294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, 318Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
295 const std::vector<u8>& data) { 319 const std::vector<u8>& data) {
296 auto handle = GetHandle(identifier); 320 auto handle = GetHandle(identifier);
297 if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { 321 if (handle == nullptr) {
298 return Common::Input::NfcState::WriteFailed; 322 return Common::Input::NfcState::Unknown;
299 } 323 }
300 return Common::Input::NfcState::Success; 324 return TranslateDriverResult(handle->WriteNfcData(data));
325};
326
327Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
328 const Common::Input::MifareRequest& request,
329 Common::Input::MifareRequest& data) {
330 auto handle = GetHandle(identifier);
331 if (handle == nullptr) {
332 return Common::Input::NfcState::Unknown;
333 }
334
335 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
336 std::vector<Joycon::MifareReadChunk> read_request{};
337 for (const auto& request_data : request.data) {
338 if (request_data.command == 0) {
339 continue;
340 }
341 Joycon::MifareReadChunk chunk = {
342 .command = command,
343 .sector_key = {},
344 .sector = request_data.sector,
345 };
346 memcpy(chunk.sector_key.data(), request_data.key.data(),
347 sizeof(Joycon::MifareReadChunk::sector_key));
348 read_request.emplace_back(chunk);
349 }
350
351 std::vector<Joycon::MifareReadData> read_data(read_request.size());
352 const auto result = handle->ReadMifareData(read_request, read_data);
353 if (result == Common::Input::DriverResult::Success) {
354 for (std::size_t i = 0; i < read_request.size(); i++) {
355 data.data[i] = {
356 .command = static_cast<u8>(command),
357 .sector = read_data[i].sector,
358 .key = {},
359 .data = read_data[i].data,
360 };
361 }
362 }
363 return TranslateDriverResult(result);
364};
365
366Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
367 const Common::Input::MifareRequest& request) {
368 auto handle = GetHandle(identifier);
369 if (handle == nullptr) {
370 return Common::Input::NfcState::Unknown;
371 }
372
373 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
374 std::vector<Joycon::MifareWriteChunk> write_request{};
375 for (const auto& request_data : request.data) {
376 if (request_data.command == 0) {
377 continue;
378 }
379 Joycon::MifareWriteChunk chunk = {
380 .command = command,
381 .sector_key = {},
382 .sector = request_data.sector,
383 .data = {},
384 };
385 memcpy(chunk.sector_key.data(), request_data.key.data(),
386 sizeof(Joycon::MifareReadChunk::sector_key));
387 memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
388 write_request.emplace_back(chunk);
389 }
390
391 return TranslateDriverResult(handle->WriteMifareData(write_request));
301}; 392};
302 393
303Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, 394Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -310,15 +401,15 @@ Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identif
310 401
311 switch (polling_mode) { 402 switch (polling_mode) {
312 case Common::Input::PollingMode::Active: 403 case Common::Input::PollingMode::Active:
313 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); 404 return handle->SetActiveMode();
314 case Common::Input::PollingMode::Passive: 405 case Common::Input::PollingMode::Passive:
315 return static_cast<Common::Input::DriverResult>(handle->SetPassiveMode()); 406 return handle->SetPassiveMode();
316 case Common::Input::PollingMode::IR: 407 case Common::Input::PollingMode::IR:
317 return static_cast<Common::Input::DriverResult>(handle->SetIrMode()); 408 return handle->SetIrMode();
318 case Common::Input::PollingMode::NFC: 409 case Common::Input::PollingMode::NFC:
319 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); 410 return handle->SetNfcMode();
320 case Common::Input::PollingMode::Ring: 411 case Common::Input::PollingMode::Ring:
321 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); 412 return handle->SetRingConMode();
322 default: 413 default:
323 return Common::Input::DriverResult::NotSupported; 414 return Common::Input::DriverResult::NotSupported;
324 } 415 }
@@ -403,11 +494,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
403} 494}
404 495
405void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 496void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
406 const std::vector<u8>& amiibo_data) { 497 const Joycon::TagInfo& tag_info) {
407 const auto identifier = GetIdentifier(port, type); 498 const auto identifier = GetIdentifier(port, type);
408 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved 499 const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
409 : Common::Input::NfcState::NewAmiibo; 500 : Common::Input::NfcState::NewAmiibo;
410 SetNfc(identifier, {nfc_state, amiibo_data}); 501
502 const Common::Input::NfcStatus nfc_status{
503 .state = nfc_state,
504 .uuid_length = tag_info.uuid_length,
505 .protocol = tag_info.protocol,
506 .tag_type = tag_info.tag_type,
507 .uuid = tag_info.uuid,
508 };
509
510 SetNfc(identifier, nfc_status);
411} 511}
412 512
413void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 513void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
@@ -726,4 +826,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
726 return "Unknown Switch Controller"; 826 return "Unknown Switch Controller";
727 } 827 }
728} 828}
829
830Common::Input::NfcState Joycons::TranslateDriverResult(Common::Input::DriverResult result) const {
831 switch (result) {
832 case Common::Input::DriverResult::Success:
833 return Common::Input::NfcState::Success;
834 case Common::Input::DriverResult::Disabled:
835 return Common::Input::NfcState::WrongDeviceState;
836 case Common::Input::DriverResult::NotSupported:
837 return Common::Input::NfcState::NotSupported;
838 default:
839 return Common::Input::NfcState::Unknown;
840 }
841}
842
729} // namespace InputCommon 843} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index e3f0ad78f..112e970e1 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,8 +15,8 @@ using SerialNumber = std::array<u8, 15>;
15struct Battery; 15struct Battery;
16struct Color; 16struct Color;
17struct MotionData; 17struct MotionData;
18struct TagInfo;
18enum class ControllerType : u8; 19enum class ControllerType : u8;
19enum class DriverResult;
20enum class IrsResolution; 20enum class IrsResolution;
21class JoyconDriver; 21class JoyconDriver;
22} // namespace InputCommon::Joycon 22} // namespace InputCommon::Joycon
@@ -39,9 +39,18 @@ public:
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, 39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override; 40 Common::Input::CameraFormat camera_format) override;
41 41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 43 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
44 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
45 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
46 std::vector<u8>& out_data) override;
47 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
44 const std::vector<u8>& data) override; 48 const std::vector<u8>& data) override;
49 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
50 const Common::Input::MifareRequest& request,
51 Common::Input::MifareRequest& out_data) override;
52 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
53 const Common::Input::MifareRequest& request) override;
45 54
46 Common::Input::DriverResult SetPollingMode( 55 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; 56 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
@@ -82,7 +91,7 @@ private:
82 const Joycon::MotionData& value); 91 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data); 92 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 93 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
85 const std::vector<u8>& amiibo_data); 94 const Joycon::TagInfo& amiibo_data);
86 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 95 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
87 Joycon::IrsResolution format); 96 Joycon::IrsResolution format);
88 97
@@ -102,6 +111,8 @@ private:
102 /// Returns the name of the device in text format 111 /// Returns the name of the device in text format
103 std::string JoyconName(Joycon::ControllerType type) const; 112 std::string JoyconName(Joycon::ControllerType type) const;
104 113
114 Common::Input::NfcState TranslateDriverResult(Common::Input::DriverResult result) const;
115
105 std::jthread scan_thread; 116 std::jthread scan_thread;
106 117
107 // Joycon types are split by type to ease supporting dualjoycon configurations 118 // Joycon types are split by type to ease supporting dualjoycon configurations
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 0c9f642bb..f07cf8a0e 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -76,9 +76,6 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
76 UpdateStickInput(); 76 UpdateStickInput();
77 UpdateMotionInput(); 77 UpdateMotionInput();
78 78
79 if (mouse_panning_timeout++ > 20) {
80 StopPanning();
81 }
82 std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); 79 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
83 } 80 }
84} 81}
@@ -88,18 +85,45 @@ void Mouse::UpdateStickInput() {
88 return; 85 return;
89 } 86 }
90 87
91 const float sensitivity = 88 const float length = last_mouse_change.Length();
92 Settings::values.mouse_panning_sensitivity.GetValue() * default_stick_sensitivity;
93 89
94 // Slow movement by 4% 90 // Prevent input from exceeding the max range (1.0f) too much,
95 last_mouse_change *= 0.96f; 91 // but allow some room to make it easier to sustain
96 SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity); 92 if (length > 1.2f) {
97 SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); 93 last_mouse_change /= length;
94 last_mouse_change *= 1.2f;
95 }
96
97 auto mouse_change = last_mouse_change;
98
99 // Bind the mouse change to [0 <= deadzone_counterweight <= 1,1]
100 if (length < 1.0f) {
101 const float deadzone_h_counterweight =
102 Settings::values.mouse_panning_deadzone_x_counterweight.GetValue();
103 const float deadzone_v_counterweight =
104 Settings::values.mouse_panning_deadzone_y_counterweight.GetValue();
105 mouse_change /= length;
106 mouse_change.x *= length + (1 - length) * deadzone_h_counterweight * 0.01f;
107 mouse_change.y *= length + (1 - length) * deadzone_v_counterweight * 0.01f;
108 }
109
110 SetAxis(identifier, mouse_axis_x, mouse_change.x);
111 SetAxis(identifier, mouse_axis_y, -mouse_change.y);
112
113 // Decay input over time
114 const float clamped_length = std::min(1.0f, length);
115 const float decay_strength = Settings::values.mouse_panning_decay_strength.GetValue();
116 const float decay = 1 - clamped_length * clamped_length * decay_strength * 0.01f;
117 const float min_decay = Settings::values.mouse_panning_min_decay.GetValue();
118 const float clamped_decay = std::min(1 - min_decay / 100.0f, decay);
119 last_mouse_change *= clamped_decay;
98} 120}
99 121
100void Mouse::UpdateMotionInput() { 122void Mouse::UpdateMotionInput() {
101 const float sensitivity = 123 // This may need its own sensitivity instead of using the average
102 Settings::values.mouse_panning_sensitivity.GetValue() * default_motion_sensitivity; 124 const float sensitivity = (Settings::values.mouse_panning_x_sensitivity.GetValue() +
125 Settings::values.mouse_panning_y_sensitivity.GetValue()) /
126 2.0f * default_motion_sensitivity;
103 127
104 const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x + 128 const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x +
105 last_motion_change.y * last_motion_change.y); 129 last_motion_change.y * last_motion_change.y);
@@ -131,49 +155,28 @@ void Mouse::UpdateMotionInput() {
131 155
132void Mouse::Move(int x, int y, int center_x, int center_y) { 156void Mouse::Move(int x, int y, int center_x, int center_y) {
133 if (Settings::values.mouse_panning) { 157 if (Settings::values.mouse_panning) {
134 mouse_panning_timeout = 0; 158 const auto mouse_change =
135
136 auto mouse_change =
137 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); 159 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
138 last_motion_change += {-mouse_change.y, -mouse_change.x, 0}; 160 const float x_sensitivity =
139 161 Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
140 const auto move_distance = mouse_change.Length(); 162 const float y_sensitivity =
141 if (move_distance == 0) { 163 Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
142 return;
143 }
144 164
145 // Make slow movements at least 3 units on length 165 last_motion_change += {-mouse_change.y, -mouse_change.x, 0};
146 if (move_distance < 3.0f) { 166 last_mouse_change.x += mouse_change.x * x_sensitivity * 0.09f;
147 // Normalize value 167 last_mouse_change.y += mouse_change.y * y_sensitivity * 0.09f;
148 mouse_change /= move_distance;
149 mouse_change *= 3.0f;
150 }
151
152 // Average mouse movements
153 last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f);
154
155 const auto last_move_distance = last_mouse_change.Length();
156
157 // Make fast movements clamp to 8 units on length
158 if (last_move_distance > 8.0f) {
159 // Normalize value
160 last_mouse_change /= last_move_distance;
161 last_mouse_change *= 8.0f;
162 }
163
164 // Ignore average if it's less than 1 unit and use current movement value
165 if (last_move_distance < 1.0f) {
166 last_mouse_change = mouse_change / mouse_change.Length();
167 }
168 168
169 return; 169 return;
170 } 170 }
171 171
172 if (button_pressed) { 172 if (button_pressed) {
173 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; 173 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
174 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; 174 const float x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue();
175 SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity); 175 const float y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue();
176 SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity); 176 SetAxis(identifier, mouse_axis_x,
177 static_cast<float>(mouse_move.x) * x_sensitivity * 0.0012f);
178 SetAxis(identifier, mouse_axis_y,
179 static_cast<float>(-mouse_move.y) * y_sensitivity * 0.0012f);
177 180
178 last_motion_change = { 181 last_motion_change = {
179 static_cast<float>(-mouse_move.y) / 50.0f, 182 static_cast<float>(-mouse_move.y) / 50.0f,
@@ -241,10 +244,6 @@ void Mouse::ReleaseAllButtons() {
241 button_pressed = false; 244 button_pressed = false;
242} 245}
243 246
244void Mouse::StopPanning() {
245 last_mouse_change = {};
246}
247
248std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { 247std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
249 std::vector<Common::ParamPackage> devices; 248 std::vector<Common::ParamPackage> devices;
250 devices.emplace_back(Common::ParamPackage{ 249 devices.emplace_back(Common::ParamPackage{
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index b872c7a0f..0e8edcce1 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -98,7 +98,6 @@ private:
98 void UpdateThread(std::stop_token stop_token); 98 void UpdateThread(std::stop_token stop_token);
99 void UpdateStickInput(); 99 void UpdateStickInput();
100 void UpdateMotionInput(); 100 void UpdateMotionInput();
101 void StopPanning();
102 101
103 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; 102 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
104 103
@@ -108,7 +107,6 @@ private:
108 Common::Vec3<float> last_motion_change; 107 Common::Vec3<float> last_motion_change;
109 Common::Vec2<int> wheel_position; 108 Common::Vec2<int> wheel_position;
110 bool button_pressed; 109 bool button_pressed;
111 int mouse_panning_timeout{};
112 std::jthread update_thread; 110 std::jthread update_thread;
113}; 111};
114 112
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 9a0439bb5..9f26392b1 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -150,6 +150,8 @@ public:
150 if (sdl_controller) { 150 if (sdl_controller) {
151 const auto type = SDL_GameControllerGetType(sdl_controller.get()); 151 const auto type = SDL_GameControllerGetType(sdl_controller.get());
152 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || 152 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
153 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) ||
154 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) ||
153 (type == SDL_CONTROLLER_TYPE_PS5); 155 (type == SDL_CONTROLLER_TYPE_PS5);
154 } 156 }
155 return false; 157 return false;
@@ -228,9 +230,8 @@ public:
228 return false; 230 return false;
229 } 231 }
230 232
231 Common::Input::BatteryLevel GetBatteryLevel() { 233 Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) {
232 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); 234 switch (battery_level) {
233 switch (level) {
234 case SDL_JOYSTICK_POWER_EMPTY: 235 case SDL_JOYSTICK_POWER_EMPTY:
235 return Common::Input::BatteryLevel::Empty; 236 return Common::Input::BatteryLevel::Empty;
236 case SDL_JOYSTICK_POWER_LOW: 237 case SDL_JOYSTICK_POWER_LOW:
@@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
378 if (joystick_map.find(guid) == joystick_map.end()) { 379 if (joystick_map.find(guid) == joystick_map.end()) {
379 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 380 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
380 PreSetController(joystick->GetPadIdentifier()); 381 PreSetController(joystick->GetPadIdentifier());
381 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
382 joystick->EnableMotion(); 382 joystick->EnableMotion();
383 joystick_map[guid].emplace_back(std::move(joystick)); 383 joystick_map[guid].emplace_back(std::move(joystick));
384 return; 384 return;
@@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
398 const int port = static_cast<int>(joystick_guid_list.size()); 398 const int port = static_cast<int>(joystick_guid_list.size());
399 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 399 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
400 PreSetController(joystick->GetPadIdentifier()); 400 PreSetController(joystick->GetPadIdentifier());
401 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
402 joystick->EnableMotion(); 401 joystick->EnableMotion();
403 joystick_guid_list.emplace_back(std::move(joystick)); 402 joystick_guid_list.emplace_back(std::move(joystick));
404} 403}
@@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
438 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { 437 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
439 const PadIdentifier identifier = joystick->GetPadIdentifier(); 438 const PadIdentifier identifier = joystick->GetPadIdentifier();
440 SetButton(identifier, event.jbutton.button, true); 439 SetButton(identifier, event.jbutton.button, true);
441 // Battery doesn't trigger an event so just update every button press
442 SetBattery(identifier, joystick->GetBatteryLevel());
443 } 440 }
444 break; 441 break;
445 } 442 }
@@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
466 } 463 }
467 break; 464 break;
468 } 465 }
466 case SDL_JOYBATTERYUPDATED: {
467 if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) {
468 const PadIdentifier identifier = joystick->GetPadIdentifier();
469 SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level));
470 }
471 break;
472 }
469 case SDL_JOYDEVICEREMOVED: 473 case SDL_JOYDEVICEREMOVED:
470 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); 474 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
471 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); 475 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
@@ -483,6 +487,10 @@ void SDLDriver::CloseJoysticks() {
483} 487}
484 488
485SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 489SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
490 // Set our application name. Currently passed to DBus by SDL and visible to the user through
491 // their desktop environment.
492 SDL_SetHint(SDL_HINT_APP_NAME, "yuzu");
493
486 if (!Settings::values.enable_raw_input) { 494 if (!Settings::values.enable_raw_input) {
487 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens 495 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
488 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); 496 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
@@ -501,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
501 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); 509 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
502 } else { 510 } else {
503 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 511 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
512 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0");
513 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
514 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1");
504 } 515 }
505 516
506 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled 517 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
@@ -508,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
508 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); 519 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
509 } else { 520 } else {
510 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); 521 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
522 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
511 } 523 }
512 524
525 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
526
513 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 527 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
514 // driver on Linux. 528 // driver on Linux.
515 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); 529 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
@@ -789,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
789 // This list also excludes Screenshot since there's not really a mapping for that 803 // This list also excludes Screenshot since there's not really a mapping for that
790 ButtonBindings switch_to_sdl_button; 804 ButtonBindings switch_to_sdl_button;
791 805
792 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { 806 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
807 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
808 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
793 switch_to_sdl_button = GetNintendoButtonBinding(joystick); 809 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
794 } else { 810 } else {
795 switch_to_sdl_button = GetDefaultButtonBinding(); 811 switch_to_sdl_button = GetDefaultButtonBinding();
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 6435b8af8..180eb53ef 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
29 29
30 switch (polling_mode) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC: 31 case Common::Input::PollingMode::NFC:
32 if (state == State::Initialized) { 32 state = State::Initialized;
33 state = State::WaitingForAmiibo;
34 }
35 return Common::Input::DriverResult::Success; 33 return Common::Input::DriverResult::Success;
36 default: 34 default:
37 if (state == State::AmiiboIsOpen) { 35 if (state == State::TagNearby) {
38 CloseAmiibo(); 36 CloseAmiibo();
39 } 37 }
38 state = State::Disabled;
40 return Common::Input::DriverResult::NotSupported; 39 return Common::Input::DriverResult::NotSupported;
41 } 40 }
42} 41}
@@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
45 [[maybe_unused]] const PadIdentifier& identifier_) const { 44 [[maybe_unused]] const PadIdentifier& identifier_) const {
46 return Common::Input::NfcState::Success; 45 return Common::Input::NfcState::Success;
47} 46}
47Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) {
48 if (state != State::Initialized) {
49 return Common::Input::NfcState::WrongDeviceState;
50 }
51 state = State::WaitingForAmiibo;
52 return Common::Input::NfcState::Success;
53}
54
55Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) {
56 if (state == State::Disabled) {
57 return Common::Input::NfcState::WrongDeviceState;
58 }
59 if (state == State::TagNearby) {
60 CloseAmiibo();
61 }
62 state = State::Initialized;
63 return Common::Input::NfcState::Success;
64}
65
66Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_,
67 std::vector<u8>& out_data) {
68 if (state != State::TagNearby) {
69 return Common::Input::NfcState::WrongDeviceState;
70 }
71
72 if (status.tag_type != 1U << 1) {
73 return Common::Input::NfcState::InvalidTagType;
74 }
75
76 out_data.resize(nfc_data.size());
77 memcpy(out_data.data(), nfc_data.data(), nfc_data.size());
78 return Common::Input::NfcState::Success;
79}
48 80
49Common::Input::NfcState VirtualAmiibo::WriteNfcData( 81Common::Input::NfcState VirtualAmiibo::WriteNfcData(
50 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { 82 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
@@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
66 return Common::Input::NfcState::Success; 98 return Common::Input::NfcState::Success;
67} 99}
68 100
101Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_,
102 const Common::Input::MifareRequest& request,
103 Common::Input::MifareRequest& out_data) {
104 if (state != State::TagNearby) {
105 return Common::Input::NfcState::WrongDeviceState;
106 }
107
108 if (status.tag_type != 1U << 6) {
109 return Common::Input::NfcState::InvalidTagType;
110 }
111
112 for (std::size_t i = 0; i < request.data.size(); i++) {
113 if (request.data[i].command == 0) {
114 continue;
115 }
116 out_data.data[i].command = request.data[i].command;
117 out_data.data[i].sector = request.data[i].sector;
118
119 const std::size_t sector_index =
120 request.data[i].sector * sizeof(Common::Input::MifareData::data);
121
122 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
123 return Common::Input::NfcState::WriteFailed;
124 }
125
126 // Ignore the sector key as we don't support it
127 memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index,
128 sizeof(Common::Input::MifareData::data));
129 }
130
131 return Common::Input::NfcState::Success;
132}
133
134Common::Input::NfcState VirtualAmiibo::WriteMifareData(
135 const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) {
136 if (state != State::TagNearby) {
137 return Common::Input::NfcState::WrongDeviceState;
138 }
139
140 if (status.tag_type != 1U << 6) {
141 return Common::Input::NfcState::InvalidTagType;
142 }
143
144 for (std::size_t i = 0; i < request.data.size(); i++) {
145 if (request.data[i].command == 0) {
146 continue;
147 }
148
149 const std::size_t sector_index =
150 request.data[i].sector * sizeof(Common::Input::MifareData::data);
151
152 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
153 return Common::Input::NfcState::WriteFailed;
154 }
155
156 // Ignore the sector key as we don't support it
157 memcpy(nfc_data.data() + sector_index, request.data[i].data.data(),
158 sizeof(Common::Input::MifareData::data));
159 }
160
161 return Common::Input::NfcState::Success;
162}
163
69VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { 164VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
70 return state; 165 return state;
71} 166}
@@ -112,23 +207,31 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
112 case AmiiboSizeWithoutPassword: 207 case AmiiboSizeWithoutPassword:
113 case AmiiboSizeWithSignature: 208 case AmiiboSizeWithSignature:
114 nfc_data.resize(AmiiboSize); 209 nfc_data.resize(AmiiboSize);
210 status.tag_type = 1U << 1;
211 status.uuid_length = 7;
115 break; 212 break;
116 case MifareSize: 213 case MifareSize:
117 nfc_data.resize(MifareSize); 214 nfc_data.resize(MifareSize);
215 status.tag_type = 1U << 6;
216 status.uuid_length = 4;
118 break; 217 break;
119 default: 218 default:
120 return Info::NotAnAmiibo; 219 return Info::NotAnAmiibo;
121 } 220 }
122 221
123 state = State::AmiiboIsOpen; 222 status.uuid = {};
223 status.protocol = 1;
224 state = State::TagNearby;
225 status.state = Common::Input::NfcState::NewAmiibo,
124 memcpy(nfc_data.data(), data.data(), data.size_bytes()); 226 memcpy(nfc_data.data(), data.data(), data.size_bytes());
125 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 227 memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
228 SetNfc(identifier, status);
126 return Info::Success; 229 return Info::Success;
127} 230}
128 231
129VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { 232VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
130 if (state == State::AmiiboIsOpen) { 233 if (state == State::TagNearby) {
131 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 234 SetNfc(identifier, status);
132 return Info::Success; 235 return Info::Success;
133 } 236 }
134 237
@@ -136,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
136} 239}
137 240
138VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 241VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
139 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 242 if (state != State::TagNearby) {
140 : State::Initialized; 243 return Info::Success;
141 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); 244 }
245
246 state = State::WaitingForAmiibo;
247 status.state = Common::Input::NfcState::AmiiboRemoved;
248 SetNfc(identifier, status);
249 status.tag_type = 0;
142 return Info::Success; 250 return Info::Success;
143} 251}
144 252
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 09ca09e68..490f38e05 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -20,9 +20,10 @@ namespace InputCommon {
20class VirtualAmiibo final : public InputEngine { 20class VirtualAmiibo final : public InputEngine {
21public: 21public:
22 enum class State { 22 enum class State {
23 Disabled,
23 Initialized, 24 Initialized,
24 WaitingForAmiibo, 25 WaitingForAmiibo,
25 AmiiboIsOpen, 26 TagNearby,
26 }; 27 };
27 28
28 enum class Info { 29 enum class Info {
@@ -41,9 +42,17 @@ public:
41 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 42 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
42 43
43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 44 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
44 45 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override;
46 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override;
47 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_,
48 std::vector<u8>& out_data) override;
45 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 49 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
46 const std::vector<u8>& data) override; 50 const std::vector<u8>& data) override;
51 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_,
52 const Common::Input::MifareRequest& data,
53 Common::Input::MifareRequest& out_data) override;
54 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_,
55 const Common::Input::MifareRequest& data) override;
47 56
48 State GetCurrentState() const; 57 State GetCurrentState() const;
49 58
@@ -61,8 +70,9 @@ private:
61 static constexpr std::size_t MifareSize = 0x400; 70 static constexpr std::size_t MifareSize = 0x400;
62 71
63 std::string file_path{}; 72 std::string file_path{};
64 State state{State::Initialized}; 73 State state{State::Disabled};
65 std::vector<u8> nfc_data; 74 std::vector<u8> nfc_data;
75 Common::Input::NfcStatus status;
66 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive}; 76 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};
67}; 77};
68} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 95106f16d..cf51f3481 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -1,7 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/scope_exit.h"
5#include "common/swap.h" 7#include "common/swap.h"
6#include "common/thread.h" 8#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h" 9#include "input_common/helpers/joycon_driver.h"
@@ -27,13 +29,13 @@ void JoyconDriver::Stop() {
27 input_thread = {}; 29 input_thread = {};
28} 30}
29 31
30DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) { 32Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
31 std::scoped_lock lock{mutex}; 33 std::scoped_lock lock{mutex};
32 34
33 handle_device_type = ControllerType::None; 35 handle_device_type = ControllerType::None;
34 GetDeviceType(device_info, handle_device_type); 36 GetDeviceType(device_info, handle_device_type);
35 if (handle_device_type == ControllerType::None) { 37 if (handle_device_type == ControllerType::None) {
36 return DriverResult::UnsupportedControllerType; 38 return Common::Input::DriverResult::UnsupportedControllerType;
37 } 39 }
38 40
39 hidapi_handle->handle = 41 hidapi_handle->handle =
@@ -42,15 +44,15 @@ DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info)
42 if (!hidapi_handle->handle) { 44 if (!hidapi_handle->handle) {
43 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", 45 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
44 device_info->vendor_id, device_info->product_id); 46 device_info->vendor_id, device_info->product_id);
45 return DriverResult::HandleInUse; 47 return Common::Input::DriverResult::HandleInUse;
46 } 48 }
47 SDL_hid_set_nonblocking(hidapi_handle->handle, 1); 49 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
48 return DriverResult::Success; 50 return Common::Input::DriverResult::Success;
49} 51}
50 52
51DriverResult JoyconDriver::InitializeDevice() { 53Common::Input::DriverResult JoyconDriver::InitializeDevice() {
52 if (!hidapi_handle->handle) { 54 if (!hidapi_handle->handle) {
53 return DriverResult::InvalidHandle; 55 return Common::Input::DriverResult::InvalidHandle;
54 } 56 }
55 std::scoped_lock lock{mutex}; 57 std::scoped_lock lock{mutex};
56 disable_input_thread = true; 58 disable_input_thread = true;
@@ -71,6 +73,7 @@ DriverResult JoyconDriver::InitializeDevice() {
71 nfc_enabled = false; 73 nfc_enabled = false;
72 passive_enabled = false; 74 passive_enabled = false;
73 irs_enabled = false; 75 irs_enabled = false;
76 input_only_device = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; 77 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833; 78 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; 79 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
@@ -85,16 +88,23 @@ DriverResult JoyconDriver::InitializeDevice() {
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); 88 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86 89
87 // Get fixed joycon info 90 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version); 91 if (generic_protocol->GetVersionNumber(version) != Common::Input::DriverResult::Success) {
89 generic_protocol->SetLowPowerMode(false); 92 // If this command fails the device doesn't accept configuration commands
90 generic_protocol->GetColor(color); 93 input_only_device = true;
91 if (handle_device_type == ControllerType::Pro) { 94 }
92 // Some 3rd party controllers aren't pro controllers 95
93 generic_protocol->GetControllerType(device_type); 96 if (!input_only_device) {
94 } else { 97 generic_protocol->SetLowPowerMode(false);
95 device_type = handle_device_type; 98 generic_protocol->GetColor(color);
99 if (handle_device_type == ControllerType::Pro) {
100 // Some 3rd party controllers aren't pro controllers
101 generic_protocol->GetControllerType(device_type);
102 } else {
103 device_type = handle_device_type;
104 }
105 generic_protocol->GetSerialNumber(serial_number);
96 } 106 }
97 generic_protocol->GetSerialNumber(serial_number); 107
98 supported_features = GetSupportedFeatures(); 108 supported_features = GetSupportedFeatures();
99 109
100 // Get Calibration data 110 // Get Calibration data
@@ -112,7 +122,7 @@ DriverResult JoyconDriver::InitializeDevice() {
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, 122 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration); 123 right_stick_calibration, motion_calibration);
114 124
115 // Start pooling for data 125 // Start polling for data
116 is_connected = true; 126 is_connected = true;
117 if (!input_thread_running) { 127 if (!input_thread_running) {
118 input_thread = 128 input_thread =
@@ -120,7 +130,7 @@ DriverResult JoyconDriver::InitializeDevice() {
120 } 130 }
121 131
122 disable_input_thread = false; 132 disable_input_thread = false;
123 return DriverResult::Success; 133 return Common::Input::DriverResult::Success;
124} 134}
125 135
126void JoyconDriver::InputThread(std::stop_token stop_token) { 136void JoyconDriver::InputThread(std::stop_token stop_token) {
@@ -208,7 +218,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); 218 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 } 219 }
210 220
211 if (nfc_protocol->IsEnabled()) { 221 if (nfc_protocol->IsPolling()) {
212 if (amiibo_detected) { 222 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) { 223 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({}); 224 joycon_poller->UpdateAmiibo({});
@@ -218,10 +228,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
218 } 228 }
219 229
220 if (!amiibo_detected) { 230 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C); 231 Joycon::TagInfo tag_info;
222 const auto result = nfc_protocol->ScanAmiibo(data); 232 const auto result = nfc_protocol->GetTagInfo(tag_info);
223 if (result == DriverResult::Success) { 233 if (result == Common::Input::DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data); 234 joycon_poller->UpdateAmiibo(tag_info);
225 amiibo_detected = true; 235 amiibo_detected = true;
226 } 236 }
227 } 237 }
@@ -246,7 +256,8 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
246 } 256 }
247} 257}
248 258
249DriverResult JoyconDriver::SetPollingMode() { 259Common::Input::DriverResult JoyconDriver::SetPollingMode() {
260 SCOPE_EXIT({ disable_input_thread = false; });
250 disable_input_thread = true; 261 disable_input_thread = true;
251 262
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); 263 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
@@ -259,6 +270,10 @@ DriverResult JoyconDriver::SetPollingMode() {
259 generic_protocol->EnableImu(false); 270 generic_protocol->EnableImu(false);
260 } 271 }
261 272
273 if (input_only_device) {
274 return Common::Input::DriverResult::NotSupported;
275 }
276
262 if (irs_protocol->IsEnabled()) { 277 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs(); 278 irs_protocol->DisableIrs();
264 } 279 }
@@ -275,46 +290,42 @@ DriverResult JoyconDriver::SetPollingMode() {
275 290
276 if (irs_enabled && supported_features.irs) { 291 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs(); 292 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) { 293 if (result == Common::Input::DriverResult::Success) {
279 disable_input_thread = false;
280 return result; 294 return result;
281 } 295 }
282 irs_protocol->DisableIrs(); 296 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS"); 297 LOG_ERROR(Input, "Error enabling IRS");
298 return result;
284 } 299 }
285 300
286 if (nfc_enabled && supported_features.nfc) { 301 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc(); 302 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) { 303 if (result == Common::Input::DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result; 304 return result;
294 } 305 }
295 nfc_protocol->DisableNfc(); 306 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC"); 307 LOG_ERROR(Input, "Error enabling NFC");
308 return result;
297 } 309 }
298 310
299 if (hidbus_enabled && supported_features.hidbus) { 311 if (hidbus_enabled && supported_features.hidbus) {
300 auto result = ring_protocol->EnableRingCon(); 312 auto result = ring_protocol->EnableRingCon();
301 if (result == DriverResult::Success) { 313 if (result == Common::Input::DriverResult::Success) {
302 result = ring_protocol->StartRingconPolling(); 314 result = ring_protocol->StartRingconPolling();
303 } 315 }
304 if (result == DriverResult::Success) { 316 if (result == Common::Input::DriverResult::Success) {
305 ring_connected = true; 317 ring_connected = true;
306 disable_input_thread = false;
307 return result; 318 return result;
308 } 319 }
309 ring_connected = false; 320 ring_connected = false;
310 ring_protocol->DisableRingCon(); 321 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon"); 322 LOG_ERROR(Input, "Error enabling Ringcon");
323 return result;
312 } 324 }
313 325
314 if (passive_enabled && supported_features.passive) { 326 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode(); 327 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) { 328 if (result == Common::Input::DriverResult::Success) {
317 disable_input_thread = false;
318 return result; 329 return result;
319 } 330 }
320 LOG_ERROR(Input, "Error enabling passive mode"); 331 LOG_ERROR(Input, "Error enabling passive mode");
@@ -322,13 +333,12 @@ DriverResult JoyconDriver::SetPollingMode() {
322 333
323 // Default Mode 334 // Default Mode
324 const auto result = generic_protocol->EnableActiveMode(); 335 const auto result = generic_protocol->EnableActiveMode();
325 if (result != DriverResult::Success) { 336 if (result != Common::Input::DriverResult::Success) {
326 LOG_ERROR(Input, "Error enabling active mode"); 337 LOG_ERROR(Input, "Error enabling active mode");
327 } 338 }
328 // Switch calls this function after enabling active mode 339 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed(); 340 generic_protocol->TriggersElapsed();
330 341
331 disable_input_thread = false;
332 return result; 342 return result;
333} 343}
334 344
@@ -339,6 +349,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
339 .vibration = true, 349 .vibration = true,
340 }; 350 };
341 351
352 if (input_only_device) {
353 return features;
354 }
355
342 if (device_type == ControllerType::Right) { 356 if (device_type == ControllerType::Right) {
343 features.nfc = true; 357 features.nfc = true;
344 features.irs = true; 358 features.irs = true;
@@ -383,26 +397,26 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
383 return true; 397 return true;
384} 398}
385 399
386DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { 400Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
387 std::scoped_lock lock{mutex}; 401 std::scoped_lock lock{mutex};
388 if (disable_input_thread) { 402 if (disable_input_thread) {
389 return DriverResult::HandleInUse; 403 return Common::Input::DriverResult::HandleInUse;
390 } 404 }
391 return rumble_protocol->SendVibration(vibration); 405 return rumble_protocol->SendVibration(vibration);
392} 406}
393 407
394DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { 408Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
395 std::scoped_lock lock{mutex}; 409 std::scoped_lock lock{mutex};
396 if (disable_input_thread) { 410 if (disable_input_thread) {
397 return DriverResult::HandleInUse; 411 return Common::Input::DriverResult::HandleInUse;
398 } 412 }
399 return generic_protocol->SetLedPattern(led_pattern); 413 return generic_protocol->SetLedPattern(led_pattern);
400} 414}
401 415
402DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { 416Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
403 std::scoped_lock lock{mutex}; 417 std::scoped_lock lock{mutex};
404 if (disable_input_thread) { 418 if (disable_input_thread) {
405 return DriverResult::HandleInUse; 419 return Common::Input::DriverResult::HandleInUse;
406 } 420 }
407 disable_input_thread = true; 421 disable_input_thread = true;
408 const auto result = irs_protocol->SetIrsConfig(mode_, format_); 422 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
@@ -410,7 +424,7 @@ DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
410 return result; 424 return result;
411} 425}
412 426
413DriverResult JoyconDriver::SetPassiveMode() { 427Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
414 std::scoped_lock lock{mutex}; 428 std::scoped_lock lock{mutex};
415 motion_enabled = false; 429 motion_enabled = false;
416 hidbus_enabled = false; 430 hidbus_enabled = false;
@@ -420,7 +434,7 @@ DriverResult JoyconDriver::SetPassiveMode() {
420 return SetPollingMode(); 434 return SetPollingMode();
421} 435}
422 436
423DriverResult JoyconDriver::SetActiveMode() { 437Common::Input::DriverResult JoyconDriver::SetActiveMode() {
424 if (is_ring_disabled_by_irs) { 438 if (is_ring_disabled_by_irs) {
425 is_ring_disabled_by_irs = false; 439 is_ring_disabled_by_irs = false;
426 SetActiveMode(); 440 SetActiveMode();
@@ -436,11 +450,11 @@ DriverResult JoyconDriver::SetActiveMode() {
436 return SetPollingMode(); 450 return SetPollingMode();
437} 451}
438 452
439DriverResult JoyconDriver::SetIrMode() { 453Common::Input::DriverResult JoyconDriver::SetIrMode() {
440 std::scoped_lock lock{mutex}; 454 std::scoped_lock lock{mutex};
441 455
442 if (!supported_features.irs) { 456 if (!supported_features.irs) {
443 return DriverResult::NotSupported; 457 return Common::Input::DriverResult::NotSupported;
444 } 458 }
445 459
446 if (ring_connected) { 460 if (ring_connected) {
@@ -455,11 +469,11 @@ DriverResult JoyconDriver::SetIrMode() {
455 return SetPollingMode(); 469 return SetPollingMode();
456} 470}
457 471
458DriverResult JoyconDriver::SetNfcMode() { 472Common::Input::DriverResult JoyconDriver::SetNfcMode() {
459 std::scoped_lock lock{mutex}; 473 std::scoped_lock lock{mutex};
460 474
461 if (!supported_features.nfc) { 475 if (!supported_features.nfc) {
462 return DriverResult::NotSupported; 476 return Common::Input::DriverResult::NotSupported;
463 } 477 }
464 478
465 motion_enabled = true; 479 motion_enabled = true;
@@ -470,11 +484,11 @@ DriverResult JoyconDriver::SetNfcMode() {
470 return SetPollingMode(); 484 return SetPollingMode();
471} 485}
472 486
473DriverResult JoyconDriver::SetRingConMode() { 487Common::Input::DriverResult JoyconDriver::SetRingConMode() {
474 std::scoped_lock lock{mutex}; 488 std::scoped_lock lock{mutex};
475 489
476 if (!supported_features.hidbus) { 490 if (!supported_features.hidbus) {
477 return DriverResult::NotSupported; 491 return Common::Input::DriverResult::NotSupported;
478 } 492 }
479 493
480 motion_enabled = true; 494 motion_enabled = true;
@@ -486,29 +500,130 @@ DriverResult JoyconDriver::SetRingConMode() {
486 const auto result = SetPollingMode(); 500 const auto result = SetPollingMode();
487 501
488 if (!ring_connected) { 502 if (!ring_connected) {
489 return DriverResult::NoDeviceDetected; 503 return Common::Input::DriverResult::NoDeviceDetected;
504 }
505
506 return result;
507}
508
509Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
510 std::scoped_lock lock{mutex};
511
512 if (!supported_features.nfc) {
513 return Common::Input::DriverResult::NotSupported;
514 }
515 if (!nfc_protocol->IsEnabled()) {
516 return Common::Input::DriverResult::Disabled;
490 } 517 }
491 518
519 disable_input_thread = true;
520 const auto result = nfc_protocol->StartNFCPollingMode();
521 disable_input_thread = false;
522
492 return result; 523 return result;
493} 524}
494 525
495DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { 526Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
496 std::scoped_lock lock{mutex}; 527 std::scoped_lock lock{mutex};
528
529 if (!supported_features.nfc) {
530 return Common::Input::DriverResult::NotSupported;
531 }
532 if (!nfc_protocol->IsEnabled()) {
533 return Common::Input::DriverResult::Disabled;
534 }
535
497 disable_input_thread = true; 536 disable_input_thread = true;
537 const auto result = nfc_protocol->StopNFCPollingMode();
538 disable_input_thread = false;
539
540 if (amiibo_detected) {
541 amiibo_detected = false;
542 joycon_poller->UpdateAmiibo({});
543 }
544
545 return result;
546}
547
548Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
549 std::scoped_lock lock{mutex};
498 550
499 if (!supported_features.nfc) { 551 if (!supported_features.nfc) {
500 return DriverResult::NotSupported; 552 return Common::Input::DriverResult::NotSupported;
501 } 553 }
502 if (!nfc_protocol->IsEnabled()) { 554 if (!nfc_protocol->IsEnabled()) {
503 return DriverResult::Disabled; 555 return Common::Input::DriverResult::Disabled;
504 } 556 }
505 if (!amiibo_detected) { 557 if (!amiibo_detected) {
506 return DriverResult::ErrorWritingData; 558 return Common::Input::DriverResult::ErrorWritingData;
507 } 559 }
508 560
561 out_data.resize(0x21C);
562 disable_input_thread = true;
563 const auto result = nfc_protocol->ReadAmiibo(out_data);
564 disable_input_thread = false;
565
566 return result;
567}
568
569Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
570 std::scoped_lock lock{mutex};
571
572 if (!supported_features.nfc) {
573 return Common::Input::DriverResult::NotSupported;
574 }
575 if (!nfc_protocol->IsEnabled()) {
576 return Common::Input::DriverResult::Disabled;
577 }
578 if (!amiibo_detected) {
579 return Common::Input::DriverResult::ErrorWritingData;
580 }
581
582 disable_input_thread = true;
509 const auto result = nfc_protocol->WriteAmiibo(data); 583 const auto result = nfc_protocol->WriteAmiibo(data);
584 disable_input_thread = false;
585
586 return result;
587}
510 588
589Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
590 std::span<MifareReadData> out_data) {
591 std::scoped_lock lock{mutex};
592
593 if (!supported_features.nfc) {
594 return Common::Input::DriverResult::NotSupported;
595 }
596 if (!nfc_protocol->IsEnabled()) {
597 return Common::Input::DriverResult::Disabled;
598 }
599 if (!amiibo_detected) {
600 return Common::Input::DriverResult::ErrorWritingData;
601 }
602
603 disable_input_thread = true;
604 const auto result = nfc_protocol->ReadMifare(data, out_data);
511 disable_input_thread = false; 605 disable_input_thread = false;
606
607 return result;
608}
609
610Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
611 std::scoped_lock lock{mutex};
612
613 if (!supported_features.nfc) {
614 return Common::Input::DriverResult::NotSupported;
615 }
616 if (!nfc_protocol->IsEnabled()) {
617 return Common::Input::DriverResult::Disabled;
618 }
619 if (!amiibo_detected) {
620 return Common::Input::DriverResult::ErrorWritingData;
621 }
622
623 disable_input_thread = true;
624 const auto result = nfc_protocol->WriteMifare(data);
625 disable_input_thread = false;
626
512 return result; 627 return result;
513} 628}
514 629
@@ -561,8 +676,8 @@ void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
561 joycon_poller->SetCallbacks(callbacks); 676 joycon_poller->SetCallbacks(callbacks);
562} 677}
563 678
564DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, 679Common::Input::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
565 ControllerType& controller_type) { 680 ControllerType& controller_type) {
566 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{ 681 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{
567 std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, 682 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
568 {0x2007, ControllerType::Right}, 683 {0x2007, ControllerType::Right},
@@ -572,25 +687,25 @@ DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
572 687
573 controller_type = ControllerType::None; 688 controller_type = ControllerType::None;
574 if (device_info->vendor_id != nintendo_vendor_id) { 689 if (device_info->vendor_id != nintendo_vendor_id) {
575 return DriverResult::UnsupportedControllerType; 690 return Common::Input::DriverResult::UnsupportedControllerType;
576 } 691 }
577 692
578 for (const auto& [product_id, type] : supported_devices) { 693 for (const auto& [product_id, type] : supported_devices) {
579 if (device_info->product_id == static_cast<u16>(product_id)) { 694 if (device_info->product_id == static_cast<u16>(product_id)) {
580 controller_type = type; 695 controller_type = type;
581 return Joycon::DriverResult::Success; 696 return Common::Input::DriverResult::Success;
582 } 697 }
583 } 698 }
584 return Joycon::DriverResult::UnsupportedControllerType; 699 return Common::Input::DriverResult::UnsupportedControllerType;
585} 700}
586 701
587DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info, 702Common::Input::DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
588 SerialNumber& serial_number) { 703 SerialNumber& serial_number) {
589 if (device_info->serial_number == nullptr) { 704 if (device_info->serial_number == nullptr) {
590 return DriverResult::Unknown; 705 return Common::Input::DriverResult::Unknown;
591 } 706 }
592 std::memcpy(&serial_number, device_info->serial_number, 15); 707 std::memcpy(&serial_number, device_info->serial_number, 15);
593 return Joycon::DriverResult::Success; 708 return Common::Input::DriverResult::Success;
594} 709}
595 710
596} // namespace InputCommon::Joycon 711} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index e9b2fccbb..335e12cc3 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -11,6 +11,10 @@
11 11
12#include "input_common/helpers/joycon_protocol/joycon_types.h" 12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13 13
14namespace Common::Input {
15enum class DriverResult;
16}
17
14namespace InputCommon::Joycon { 18namespace InputCommon::Joycon {
15class CalibrationProtocol; 19class CalibrationProtocol;
16class GenericProtocol; 20class GenericProtocol;
@@ -26,8 +30,8 @@ public:
26 30
27 ~JoyconDriver(); 31 ~JoyconDriver();
28 32
29 DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info); 33 Common::Input::DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
30 DriverResult InitializeDevice(); 34 Common::Input::DriverResult InitializeDevice();
31 void Stop(); 35 void Stop();
32 36
33 bool IsConnected() const; 37 bool IsConnected() const;
@@ -41,25 +45,31 @@ public:
41 SerialNumber GetSerialNumber() const; 45 SerialNumber GetSerialNumber() const;
42 SerialNumber GetHandleSerialNumber() const; 46 SerialNumber GetHandleSerialNumber() const;
43 47
44 DriverResult SetVibration(const VibrationValue& vibration); 48 Common::Input::DriverResult SetVibration(const VibrationValue& vibration);
45 DriverResult SetLedConfig(u8 led_pattern); 49 Common::Input::DriverResult SetLedConfig(u8 led_pattern);
46 DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_); 50 Common::Input::DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
47 DriverResult SetPassiveMode(); 51 Common::Input::DriverResult SetPassiveMode();
48 DriverResult SetActiveMode(); 52 Common::Input::DriverResult SetActiveMode();
49 DriverResult SetIrMode(); 53 Common::Input::DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 54 Common::Input::DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 55 Common::Input::DriverResult SetRingConMode();
52 DriverResult WriteNfcData(std::span<const u8> data); 56 Common::Input::DriverResult StartNfcPolling();
57 Common::Input::DriverResult StopNfcPolling();
58 Common::Input::DriverResult ReadAmiiboData(std::vector<u8>& out_data);
59 Common::Input::DriverResult WriteNfcData(std::span<const u8> data);
60 Common::Input::DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
61 std::span<MifareReadData> out_data);
62 Common::Input::DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
53 63
54 void SetCallbacks(const JoyconCallbacks& callbacks); 64 void SetCallbacks(const JoyconCallbacks& callbacks);
55 65
56 // Returns device type from hidapi handle 66 // Returns device type from hidapi handle
57 static DriverResult GetDeviceType(SDL_hid_device_info* device_info, 67 static Common::Input::DriverResult GetDeviceType(SDL_hid_device_info* device_info,
58 ControllerType& controller_type); 68 ControllerType& controller_type);
59 69
60 // Returns serial number from hidapi handle 70 // Returns serial number from hidapi handle
61 static DriverResult GetSerialNumber(SDL_hid_device_info* device_info, 71 static Common::Input::DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
62 SerialNumber& serial_number); 72 SerialNumber& serial_number);
63 73
64private: 74private:
65 struct SupportedFeatures { 75 struct SupportedFeatures {
@@ -78,7 +88,7 @@ private:
78 void OnNewData(std::span<u8> buffer); 88 void OnNewData(std::span<u8> buffer);
79 89
80 /// Updates device configuration to enable or disable features 90 /// Updates device configuration to enable or disable features
81 DriverResult SetPollingMode(); 91 Common::Input::DriverResult SetPollingMode();
82 92
83 /// Returns true if input thread is valid and doesn't need to be stopped 93 /// Returns true if input thread is valid and doesn't need to be stopped
84 bool IsInputThreadValid() const; 94 bool IsInputThreadValid() const;
@@ -114,6 +124,7 @@ private:
114 // Hardware configuration 124 // Hardware configuration
115 u8 leds{}; 125 u8 leds{};
116 ReportMode mode{}; 126 ReportMode mode{};
127 bool input_only_device{};
117 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time 128 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
118 bool hidbus_enabled{}; // External device support 129 bool hidbus_enabled{}; // External device support
119 bool irs_enabled{}; // Infrared camera input 130 bool irs_enabled{}; // Infrared camera input
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
index d8f040f75..1300ecaf5 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.cpp
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cstring> 4#include <cstring>
5 5
6#include "common/input.h"
6#include "input_common/helpers/joycon_protocol/calibration.h" 7#include "input_common/helpers/joycon_protocol/calibration.h"
7#include "input_common/helpers/joycon_protocol/joycon_types.h" 8#include "input_common/helpers/joycon_protocol/joycon_types.h"
8 9
@@ -11,28 +12,29 @@ namespace InputCommon::Joycon {
11CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) 12CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
12 : JoyconCommonProtocol(std::move(handle)) {} 13 : JoyconCommonProtocol(std::move(handle)) {}
13 14
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { 15Common::Input::DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(
16 JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this); 17 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 18 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{}; 19 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false; 20 bool has_user_calibration = false;
19 calibration = {}; 21 calibration = {};
20 22
21 if (result == DriverResult::Success) { 23 if (result == Common::Input::DriverResult::Success) {
22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration); 24 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
23 } 25 }
24 26
25 // Read User defined calibration 27 // Read User defined calibration
26 if (result == DriverResult::Success && has_user_calibration) { 28 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration); 29 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
28 } 30 }
29 31
30 // Read Factory calibration 32 // Read Factory calibration
31 if (result == DriverResult::Success && !has_user_calibration) { 33 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration); 34 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 } 35 }
34 36
35 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); 38 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); 39 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); 40 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
@@ -47,28 +49,29 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration
47 return result; 49 return result;
48} 50}
49 51
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { 52Common::Input::DriverResult CalibrationProtocol::GetRightJoyStickCalibration(
53 JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this); 54 ScopedSetBlocking sb(this);
52 DriverResult result{DriverResult::Success}; 55 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{}; 56 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false; 57 bool has_user_calibration = false;
55 calibration = {}; 58 calibration = {};
56 59
57 if (result == DriverResult::Success) { 60 if (result == Common::Input::DriverResult::Success) {
58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration); 61 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
59 } 62 }
60 63
61 // Read User defined calibration 64 // Read User defined calibration
62 if (result == DriverResult::Success && has_user_calibration) { 65 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration); 66 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
64 } 67 }
65 68
66 // Read Factory calibration 69 // Read Factory calibration
67 if (result == DriverResult::Success && !has_user_calibration) { 70 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration); 71 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
69 } 72 }
70 73
71 if (result == DriverResult::Success) { 74 if (result == Common::Input::DriverResult::Success) {
72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); 75 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); 76 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); 77 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
@@ -83,28 +86,28 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio
83 return result; 86 return result;
84} 87}
85 88
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { 89Common::Input::DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this); 90 ScopedSetBlocking sb(this);
88 DriverResult result{DriverResult::Success}; 91 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
89 ImuSpiCalibration spi_calibration{}; 92 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false; 93 bool has_user_calibration = false;
91 calibration = {}; 94 calibration = {};
92 95
93 if (result == DriverResult::Success) { 96 if (result == Common::Input::DriverResult::Success) {
94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration); 97 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
95 } 98 }
96 99
97 // Read User defined calibration 100 // Read User defined calibration
98 if (result == DriverResult::Success && has_user_calibration) { 101 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration); 102 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 } 103 }
101 104
102 // Read Factory calibration 105 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) { 106 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration); 107 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
105 } 108 }
106 109
107 if (result == DriverResult::Success) { 110 if (result == Common::Input::DriverResult::Success) {
108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0]; 111 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1]; 112 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2]; 113 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
@@ -127,8 +130,8 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati
127 return result; 130 return result;
128} 131}
129 132
130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, 133Common::Input::DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
131 s16 current_value) { 134 s16 current_value) {
132 constexpr s16 DefaultRingRange{800}; 135 constexpr s16 DefaultRingRange{800};
133 136
134 // TODO: Get default calibration form ring itself 137 // TODO: Get default calibration form ring itself
@@ -144,15 +147,15 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio
144 .max_value = ring_data_max, 147 .max_value = ring_data_max,
145 .min_value = ring_data_min, 148 .min_value = ring_data_min,
146 }; 149 };
147 return DriverResult::Success; 150 return Common::Input::DriverResult::Success;
148} 151}
149 152
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address, 153Common::Input::DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) { 154 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{}; 155 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)}; 156 const Common::Input::DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false; 157 has_user_calibration = false;
155 if (result == DriverResult::Success) { 158 if (result == Common::Input::DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 && 159 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1; 160 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 } 161 }
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
index c6fd0f729..82d94b366 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.h
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -12,8 +12,11 @@
12 12
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14 14
15namespace InputCommon::Joycon { 15namespace Common::Input {
16enum class DriverResult; 16enum class DriverResult;
17}
18
19namespace InputCommon::Joycon {
17struct JoyStickCalibration; 20struct JoyStickCalibration;
18struct IMUCalibration; 21struct IMUCalibration;
19struct JoyconHandle; 22struct JoyconHandle;
@@ -31,30 +34,30 @@ public:
31 * @param is_factory_calibration if true factory values will be returned 34 * @param is_factory_calibration if true factory values will be returned
32 * @returns JoyStickCalibration of the left joystick 35 * @returns JoyStickCalibration of the left joystick
33 */ 36 */
34 DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration); 37 Common::Input::DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
35 38
36 /** 39 /**
37 * Sends a request to obtain the right stick calibration from memory 40 * Sends a request to obtain the right stick calibration from memory
38 * @param is_factory_calibration if true factory values will be returned 41 * @param is_factory_calibration if true factory values will be returned
39 * @returns JoyStickCalibration of the right joystick 42 * @returns JoyStickCalibration of the right joystick
40 */ 43 */
41 DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration); 44 Common::Input::DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
42 45
43 /** 46 /**
44 * Sends a request to obtain the motion calibration from memory 47 * Sends a request to obtain the motion calibration from memory
45 * @returns ImuCalibration of the motion sensor 48 * @returns ImuCalibration of the motion sensor
46 */ 49 */
47 DriverResult GetImuCalibration(MotionCalibration& calibration); 50 Common::Input::DriverResult GetImuCalibration(MotionCalibration& calibration);
48 51
49 /** 52 /**
50 * Calculates on run time the proper calibration of the ring controller 53 * Calculates on run time the proper calibration of the ring controller
51 * @returns RingCalibration of the ring sensor 54 * @returns RingCalibration of the ring sensor
52 */ 55 */
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); 56 Common::Input::DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54 57
55private: 58private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration 59 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration); 60 Common::Input::DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58 61
59 /// Converts a raw calibration block to an u16 value containing the x axis value 62 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const; 63 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 51669261a..a6eecf980 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/common_protocol.h" 6#include "input_common/helpers/joycon_protocol/common_protocol.h"
6 7
@@ -21,10 +22,10 @@ void JoyconCommonProtocol::SetNonBlocking() {
21 SDL_hid_set_nonblocking(hidapi_handle->handle, 1); 22 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
22} 23}
23 24
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { 25Common::Input::DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type); 26 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26 27
27 if (result == DriverResult::Success) { 28 if (result == Common::Input::DriverResult::Success) {
28 // Fallback to 3rd party pro controllers 29 // Fallback to 3rd party pro controllers
29 if (controller_type == ControllerType::None) { 30 if (controller_type == ControllerType::None) {
30 controller_type = ControllerType::Pro; 31 controller_type = ControllerType::Pro;
@@ -34,12 +35,13 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type
34 return result; 35 return result;
35} 36}
36 37
37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { 38Common::Input::DriverResult JoyconCommonProtocol::CheckDeviceAccess(
39 SDL_hid_device_info* device_info) {
38 ControllerType controller_type{ControllerType::None}; 40 ControllerType controller_type{ControllerType::None};
39 const auto result = GetDeviceType(controller_type); 41 const auto result = GetDeviceType(controller_type);
40 42
41 if (result != DriverResult::Success || controller_type == ControllerType::None) { 43 if (result != Common::Input::DriverResult::Success || controller_type == ControllerType::None) {
42 return DriverResult::UnsupportedControllerType; 44 return Common::Input::DriverResult::UnsupportedControllerType;
43 } 45 }
44 46
45 hidapi_handle->handle = 47 hidapi_handle->handle =
@@ -48,32 +50,32 @@ DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device
48 if (!hidapi_handle->handle) { 50 if (!hidapi_handle->handle) {
49 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", 51 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
50 device_info->vendor_id, device_info->product_id); 52 device_info->vendor_id, device_info->product_id);
51 return DriverResult::HandleInUse; 53 return Common::Input::DriverResult::HandleInUse;
52 } 54 }
53 55
54 SetNonBlocking(); 56 SetNonBlocking();
55 return DriverResult::Success; 57 return Common::Input::DriverResult::Success;
56} 58}
57 59
58DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) { 60Common::Input::DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
59 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)}; 61 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); 62 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
61} 63}
62 64
63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) { 65Common::Input::DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); 66 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
65 67
66 if (result == -1) { 68 if (result == -1) {
67 return DriverResult::ErrorWritingData; 69 return Common::Input::DriverResult::ErrorWritingData;
68 } 70 }
69 71
70 return DriverResult::Success; 72 return Common::Input::DriverResult::Success;
71} 73}
72 74
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, 75Common::Input::DriverResult JoyconCommonProtocol::GetSubCommandResponse(
74 SubCommandResponse& output) { 76 SubCommand sc, SubCommandResponse& output) {
75 constexpr int timeout_mili = 66; 77 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 15; 78 constexpr int MaxTries = 10;
77 int tries = 0; 79 int tries = 0;
78 80
79 do { 81 do {
@@ -84,16 +86,17 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
84 LOG_ERROR(Input, "No response from joycon"); 86 LOG_ERROR(Input, "No response from joycon");
85 } 87 }
86 if (tries++ > MaxTries) { 88 if (tries++ > MaxTries) {
87 return DriverResult::Timeout; 89 return Common::Input::DriverResult::Timeout;
88 } 90 }
89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY && 91 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
90 output.sub_command != sc); 92 output.sub_command != sc);
91 93
92 return DriverResult::Success; 94 return Common::Input::DriverResult::Success;
93} 95}
94 96
95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, 97Common::Input::DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc,
96 SubCommandResponse& output) { 98 std::span<const u8> buffer,
99 SubCommandResponse& output) {
97 SubCommandPacket packet{ 100 SubCommandPacket packet{
98 .output_report = OutputReport::RUMBLE_AND_SUBCMD, 101 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
99 .packet_counter = GetCounter(), 102 .packet_counter = GetCounter(),
@@ -102,28 +105,28 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
102 }; 105 };
103 106
104 if (buffer.size() > packet.command_data.size()) { 107 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters; 108 return Common::Input::DriverResult::InvalidParameters;
106 } 109 }
107 110
108 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 111 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109 112
110 auto result = SendData(packet); 113 auto result = SendData(packet);
111 114
112 if (result != DriverResult::Success) { 115 if (result != Common::Input::DriverResult::Success) {
113 return result; 116 return result;
114 } 117 }
115 118
116 result = GetSubCommandResponse(sc, output); 119 return GetSubCommandResponse(sc, output);
117
118 return DriverResult::Success;
119} 120}
120 121
121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 122Common::Input::DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc,
123 std::span<const u8> buffer) {
122 SubCommandResponse output{}; 124 SubCommandResponse output{};
123 return SendSubCommand(sc, buffer, output); 125 return SendSubCommand(sc, buffer, output);
124} 126}
125 127
126DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { 128Common::Input::DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc,
129 std::span<const u8> buffer) {
127 SubCommandPacket packet{ 130 SubCommandPacket packet{
128 .output_report = OutputReport::MCU_DATA, 131 .output_report = OutputReport::MCU_DATA,
129 .packet_counter = GetCounter(), 132 .packet_counter = GetCounter(),
@@ -132,7 +135,7 @@ DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const
132 }; 135 };
133 136
134 if (buffer.size() > packet.command_data.size()) { 137 if (buffer.size() > packet.command_data.size()) {
135 return DriverResult::InvalidParameters; 138 return Common::Input::DriverResult::InvalidParameters;
136 } 139 }
137 140
138 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 141 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
@@ -140,7 +143,7 @@ DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const
140 return SendData(packet); 143 return SendData(packet);
141} 144}
142 145
143DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { 146Common::Input::DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
144 VibrationPacket packet{ 147 VibrationPacket packet{
145 .output_report = OutputReport::RUMBLE_ONLY, 148 .output_report = OutputReport::RUMBLE_ONLY,
146 .packet_counter = GetCounter(), 149 .packet_counter = GetCounter(),
@@ -148,7 +151,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
148 }; 151 };
149 152
150 if (buffer.size() > packet.vibration_data.size()) { 153 if (buffer.size() > packet.vibration_data.size()) {
151 return DriverResult::InvalidParameters; 154 return Common::Input::DriverResult::InvalidParameters;
152 } 155 }
153 156
154 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size()); 157 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
@@ -156,9 +159,10 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
156 return SendData(packet); 159 return SendData(packet);
157} 160}
158 161
159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { 162Common::Input::DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr,
163 std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5; 164 constexpr std::size_t HeaderSize = 5;
161 constexpr std::size_t MaxTries = 10; 165 constexpr std::size_t MaxTries = 5;
162 std::size_t tries = 0; 166 std::size_t tries = 0;
163 SubCommandResponse response{}; 167 SubCommandResponse response{};
164 std::array<u8, sizeof(ReadSpiPacket)> buffer{}; 168 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
@@ -170,36 +174,36 @@ DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> out
170 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket)); 174 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
171 do { 175 do {
172 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response); 176 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
173 if (result != DriverResult::Success) { 177 if (result != Common::Input::DriverResult::Success) {
174 return result; 178 return result;
175 } 179 }
176 180
177 if (tries++ > MaxTries) { 181 if (tries++ > MaxTries) {
178 return DriverResult::Timeout; 182 return Common::Input::DriverResult::Timeout;
179 } 183 }
180 } while (response.spi_address != addr); 184 } while (response.spi_address != addr);
181 185
182 if (response.command_data.size() < packet_data.size + HeaderSize) { 186 if (response.command_data.size() < packet_data.size + HeaderSize) {
183 return DriverResult::WrongReply; 187 return Common::Input::DriverResult::WrongReply;
184 } 188 }
185 189
186 // Remove header from output 190 // Remove header from output
187 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size); 191 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
188 return DriverResult::Success; 192 return Common::Input::DriverResult::Success;
189} 193}
190 194
191DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { 195Common::Input::DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
192 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)}; 196 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
193 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); 197 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
194 198
195 if (result != DriverResult::Success) { 199 if (result != Common::Input::DriverResult::Success) {
196 LOG_ERROR(Input, "Failed with error {}", result); 200 LOG_ERROR(Input, "Failed with error {}", result);
197 } 201 }
198 202
199 return result; 203 return result;
200} 204}
201 205
202DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) { 206Common::Input::DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
203 LOG_DEBUG(Input, "ConfigureMCU"); 207 LOG_DEBUG(Input, "ConfigureMCU");
204 std::array<u8, sizeof(MCUConfig)> config_buffer; 208 std::array<u8, sizeof(MCUConfig)> config_buffer;
205 memcpy(config_buffer.data(), &config, sizeof(MCUConfig)); 209 memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
@@ -207,15 +211,15 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
207 211
208 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); 212 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
209 213
210 if (result != DriverResult::Success) { 214 if (result != Common::Input::DriverResult::Success) {
211 LOG_ERROR(Input, "Failed with error {}", result); 215 LOG_ERROR(Input, "Failed with error {}", result);
212 } 216 }
213 217
214 return result; 218 return result;
215} 219}
216 220
217DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode, 221Common::Input::DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
218 MCUCommandResponse& output) { 222 MCUCommandResponse& output) {
219 constexpr int TimeoutMili = 200; 223 constexpr int TimeoutMili = 200;
220 constexpr int MaxTries = 9; 224 constexpr int MaxTries = 9;
221 int tries = 0; 225 int tries = 0;
@@ -228,17 +232,18 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
228 LOG_ERROR(Input, "No response from joycon attempt {}", tries); 232 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
229 } 233 }
230 if (tries++ > MaxTries) { 234 if (tries++ > MaxTries) {
231 return DriverResult::Timeout; 235 return Common::Input::DriverResult::Timeout;
232 } 236 }
233 } while (output.input_report.report_mode != report_mode || 237 } while (output.input_report.report_mode != report_mode ||
234 output.mcu_report == MCUReport::EmptyAwaitingCmd); 238 output.mcu_report == MCUReport::EmptyAwaitingCmd);
235 239
236 return DriverResult::Success; 240 return Common::Input::DriverResult::Success;
237} 241}
238 242
239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCommand sc, 243Common::Input::DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode,
240 std::span<const u8> buffer, 244 MCUSubCommand sc,
241 MCUCommandResponse& output) { 245 std::span<const u8> buffer,
246 MCUCommandResponse& output) {
242 SubCommandPacket packet{ 247 SubCommandPacket packet{
243 .output_report = OutputReport::MCU_DATA, 248 .output_report = OutputReport::MCU_DATA,
244 .packet_counter = GetCounter(), 249 .packet_counter = GetCounter(),
@@ -247,23 +252,24 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCom
247 }; 252 };
248 253
249 if (buffer.size() > packet.command_data.size()) { 254 if (buffer.size() > packet.command_data.size()) {
250 return DriverResult::InvalidParameters; 255 return Common::Input::DriverResult::InvalidParameters;
251 } 256 }
252 257
253 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 258 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
254 259
255 auto result = SendData(packet); 260 auto result = SendData(packet);
256 261
257 if (result != DriverResult::Success) { 262 if (result != Common::Input::DriverResult::Success) {
258 return result; 263 return result;
259 } 264 }
260 265
261 result = GetMCUDataResponse(report_mode, output); 266 result = GetMCUDataResponse(report_mode, output);
262 267
263 return DriverResult::Success; 268 return Common::Input::DriverResult::Success;
264} 269}
265 270
266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { 271Common::Input::DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode,
272 MCUMode mode) {
267 MCUCommandResponse output{}; 273 MCUCommandResponse output{};
268 constexpr std::size_t MaxTries{16}; 274 constexpr std::size_t MaxTries{16};
269 std::size_t tries{}; 275 std::size_t tries{};
@@ -271,17 +277,17 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
271 do { 277 do {
272 const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output); 278 const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output);
273 279
274 if (result != DriverResult::Success) { 280 if (result != Common::Input::DriverResult::Success) {
275 return result; 281 return result;
276 } 282 }
277 283
278 if (tries++ > MaxTries) { 284 if (tries++ > MaxTries) {
279 return DriverResult::WrongReply; 285 return Common::Input::DriverResult::WrongReply;
280 } 286 }
281 } while (output.mcu_report != MCUReport::StateReport || 287 } while (output.mcu_report != MCUReport::StateReport ||
282 output.mcu_data[6] != static_cast<u8>(mode)); 288 output.mcu_data[6] != static_cast<u8>(mode));
283 289
284 return DriverResult::Success; 290 return Common::Input::DriverResult::Success;
285} 291}
286 292
287// crc-8-ccitt / polynomial 0x07 look up table 293// crc-8-ccitt / polynomial 0x07 look up table
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 411ec018a..dd667ca2b 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -38,30 +38,30 @@ public:
38 * Sends a request to obtain the joycon type from device 38 * Sends a request to obtain the joycon type from device
39 * @returns controller type of the joycon 39 * @returns controller type of the joycon
40 */ 40 */
41 DriverResult GetDeviceType(ControllerType& controller_type); 41 Common::Input::DriverResult GetDeviceType(ControllerType& controller_type);
42 42
43 /** 43 /**
44 * Verifies and sets the joycon_handle if device is valid 44 * Verifies and sets the joycon_handle if device is valid
45 * @param device info from the driver 45 * @param device info from the driver
46 * @returns success if the device is valid 46 * @returns success if the device is valid
47 */ 47 */
48 DriverResult CheckDeviceAccess(SDL_hid_device_info* device); 48 Common::Input::DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
49 49
50 /** 50 /**
51 * Sends a request to set the polling mode of the joycon 51 * Sends a request to set the polling mode of the joycon
52 * @param report_mode polling mode to be set 52 * @param report_mode polling mode to be set
53 */ 53 */
54 DriverResult SetReportMode(Joycon::ReportMode report_mode); 54 Common::Input::DriverResult SetReportMode(Joycon::ReportMode report_mode);
55 55
56 /** 56 /**
57 * Sends data to the joycon device 57 * Sends data to the joycon device
58 * @param buffer data to be send 58 * @param buffer data to be send
59 */ 59 */
60 DriverResult SendRawData(std::span<const u8> buffer); 60 Common::Input::DriverResult SendRawData(std::span<const u8> buffer);
61 61
62 template <typename Output> 62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output> 63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) { 64 Common::Input::DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer; 65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output)); 66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer); 67 return SendRawData(buffer);
@@ -72,7 +72,8 @@ public:
72 * @param sub_command type of data to be returned 72 * @param sub_command type of data to be returned
73 * @returns a buffer containing the response 73 * @returns a buffer containing the response
74 */ 74 */
75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output); 75 Common::Input::DriverResult GetSubCommandResponse(SubCommand sub_command,
76 SubCommandResponse& output);
76 77
77 /** 78 /**
78 * Sends a sub command to the device and waits for it's reply 79 * Sends a sub command to the device and waits for it's reply
@@ -80,35 +81,35 @@ public:
80 * @param buffer data to be send 81 * @param buffer data to be send
81 * @returns output buffer containing the response 82 * @returns output buffer containing the response
82 */ 83 */
83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, 84 Common::Input::DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output); 85 SubCommandResponse& output);
85 86
86 /** 87 /**
87 * Sends a sub command to the device and waits for it's reply and ignores the output 88 * 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 sc sub command to be send
89 * @param buffer data to be send 90 * @param buffer data to be send
90 */ 91 */
91 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer); 92 Common::Input::DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
92 93
93 /** 94 /**
94 * Sends a mcu command to the device 95 * Sends a mcu command to the device
95 * @param sc sub command to be send 96 * @param sc sub command to be send
96 * @param buffer data to be send 97 * @param buffer data to be send
97 */ 98 */
98 DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer); 99 Common::Input::DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
99 100
100 /** 101 /**
101 * Sends vibration data to the joycon 102 * Sends vibration data to the joycon
102 * @param buffer data to be send 103 * @param buffer data to be send
103 */ 104 */
104 DriverResult SendVibrationReport(std::span<const u8> buffer); 105 Common::Input::DriverResult SendVibrationReport(std::span<const u8> buffer);
105 106
106 /** 107 /**
107 * Reads the SPI memory stored on the joycon 108 * Reads the SPI memory stored on the joycon
108 * @param Initial address location 109 * @param Initial address location
109 * @returns output buffer containing the response 110 * @returns output buffer containing the response
110 */ 111 */
111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output); 112 Common::Input::DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112 113
113 /** 114 /**
114 * Reads the SPI memory stored on the joycon 115 * Reads the SPI memory stored on the joycon
@@ -117,37 +118,38 @@ public:
117 */ 118 */
118 template <typename Output> 119 template <typename Output>
119 requires std::is_trivially_copyable_v<Output> 120 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) { 121 Common::Input::DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer; 122 std::array<u8, sizeof(Output)> buffer;
122 output = {}; 123 output = {};
123 124
124 const auto result = ReadRawSPI(addr, buffer); 125 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) { 126 if (result != Common::Input::DriverResult::Success) {
126 return result; 127 return result;
127 } 128 }
128 129
129 std::memcpy(&output, buffer.data(), sizeof(Output)); 130 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success; 131 return Common::Input::DriverResult::Success;
131 } 132 }
132 133
133 /** 134 /**
134 * Enables MCU chip on the joycon 135 * Enables MCU chip on the joycon
135 * @param enable if true the chip will be enabled 136 * @param enable if true the chip will be enabled
136 */ 137 */
137 DriverResult EnableMCU(bool enable); 138 Common::Input::DriverResult EnableMCU(bool enable);
138 139
139 /** 140 /**
140 * Configures the MCU to the corresponding mode 141 * Configures the MCU to the corresponding mode
141 * @param MCUConfig configuration 142 * @param MCUConfig configuration
142 */ 143 */
143 DriverResult ConfigureMCU(const MCUConfig& config); 144 Common::Input::DriverResult ConfigureMCU(const MCUConfig& config);
144 145
145 /** 146 /**
146 * Waits until there's MCU data available. On timeout returns error 147 * Waits until there's MCU data available. On timeout returns error
147 * @param report mode of the expected reply 148 * @param report mode of the expected reply
148 * @returns a buffer containing the response 149 * @returns a buffer containing the response
149 */ 150 */
150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output); 151 Common::Input::DriverResult GetMCUDataResponse(ReportMode report_mode_,
152 MCUCommandResponse& output);
151 153
152 /** 154 /**
153 * Sends data to the MCU chip and waits for it's reply 155 * Sends data to the MCU chip and waits for it's reply
@@ -156,15 +158,15 @@ public:
156 * @param buffer data to be send 158 * @param buffer data to be send
157 * @returns output buffer containing the response 159 * @returns output buffer containing the response
158 */ 160 */
159 DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc, std::span<const u8> buffer, 161 Common::Input::DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc,
160 MCUCommandResponse& output); 162 std::span<const u8> buffer, MCUCommandResponse& output);
161 163
162 /** 164 /**
163 * Wait's until the MCU chip is on the specified mode 165 * Wait's until the MCU chip is on the specified mode
164 * @param report mode of the expected reply 166 * @param report mode of the expected reply
165 * @param MCUMode configuration 167 * @param MCUMode configuration
166 */ 168 */
167 DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode); 169 Common::Input::DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
168 170
169 /** 171 /**
170 * Calculates the checksum from the MCU data 172 * Calculates the checksum from the MCU data
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 548a4b9e3..e9a056448 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/generic_functions.h" 6#include "input_common/helpers/joycon_protocol/generic_functions.h"
6 7
@@ -9,73 +10,74 @@ namespace InputCommon::Joycon {
9GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) 10GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
11 12
12DriverResult GenericProtocol::EnablePassiveMode() { 13Common::Input::DriverResult GenericProtocol::EnablePassiveMode() {
13 ScopedSetBlocking sb(this); 14 ScopedSetBlocking sb(this);
14 return SetReportMode(ReportMode::SIMPLE_HID_MODE); 15 return SetReportMode(ReportMode::SIMPLE_HID_MODE);
15} 16}
16 17
17DriverResult GenericProtocol::EnableActiveMode() { 18Common::Input::DriverResult GenericProtocol::EnableActiveMode() {
18 ScopedSetBlocking sb(this); 19 ScopedSetBlocking sb(this);
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ); 20 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20} 21}
21 22
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) { 23Common::Input::DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this); 24 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; 25 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer); 26 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26} 27}
27 28
28DriverResult GenericProtocol::TriggersElapsed() { 29Common::Input::DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this); 30 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {}); 31 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31} 32}
32 33
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { 34Common::Input::DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this); 35 ScopedSetBlocking sb(this);
35 SubCommandResponse output{}; 36 SubCommandResponse output{};
36 37
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); 38 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38 39
39 device_info = {}; 40 device_info = {};
40 if (result == DriverResult::Success) { 41 if (result == Common::Input::DriverResult::Success) {
41 device_info = output.device_info; 42 device_info = output.device_info;
42 } 43 }
43 44
44 return result; 45 return result;
45} 46}
46 47
47DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) { 48Common::Input::DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
48 return GetDeviceType(controller_type); 49 return GetDeviceType(controller_type);
49} 50}
50 51
51DriverResult GenericProtocol::EnableImu(bool enable) { 52Common::Input::DriverResult GenericProtocol::EnableImu(bool enable) {
52 ScopedSetBlocking sb(this); 53 ScopedSetBlocking sb(this);
53 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; 54 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
54 return SendSubCommand(SubCommand::ENABLE_IMU, buffer); 55 return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
55} 56}
56 57
57DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, 58Common::Input::DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen,
58 AccelerometerSensitivity asen, 59 GyroPerformance gfrec,
59 AccelerometerPerformance afrec) { 60 AccelerometerSensitivity asen,
61 AccelerometerPerformance afrec) {
60 ScopedSetBlocking sb(this); 62 ScopedSetBlocking sb(this);
61 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), 63 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
62 static_cast<u8>(gfrec), static_cast<u8>(afrec)}; 64 static_cast<u8>(gfrec), static_cast<u8>(afrec)};
63 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer); 65 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
64} 66}
65 67
66DriverResult GenericProtocol::GetBattery(u32& battery_level) { 68Common::Input::DriverResult GenericProtocol::GetBattery(u32& battery_level) {
67 // This function is meant to request the high resolution battery status 69 // This function is meant to request the high resolution battery status
68 battery_level = 0; 70 battery_level = 0;
69 return DriverResult::NotSupported; 71 return Common::Input::DriverResult::NotSupported;
70} 72}
71 73
72DriverResult GenericProtocol::GetColor(Color& color) { 74Common::Input::DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this); 75 ScopedSetBlocking sb(this);
74 std::array<u8, 12> buffer{}; 76 std::array<u8, 12> buffer{};
75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer); 77 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76 78
77 color = {}; 79 color = {};
78 if (result == DriverResult::Success) { 80 if (result == Common::Input::DriverResult::Success) {
79 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]); 81 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]); 82 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]); 83 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
@@ -85,26 +87,26 @@ DriverResult GenericProtocol::GetColor(Color& color) {
85 return result; 87 return result;
86} 88}
87 89
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { 90Common::Input::DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this); 91 ScopedSetBlocking sb(this);
90 std::array<u8, 16> buffer{}; 92 std::array<u8, 16> buffer{};
91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer); 93 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92 94
93 serial_number = {}; 95 serial_number = {};
94 if (result == DriverResult::Success) { 96 if (result == Common::Input::DriverResult::Success) {
95 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber)); 97 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
96 } 98 }
97 99
98 return result; 100 return result;
99} 101}
100 102
101DriverResult GenericProtocol::GetTemperature(u32& temperature) { 103Common::Input::DriverResult GenericProtocol::GetTemperature(u32& temperature) {
102 // Not all devices have temperature sensor 104 // Not all devices have temperature sensor
103 temperature = 25; 105 temperature = 25;
104 return DriverResult::NotSupported; 106 return Common::Input::DriverResult::NotSupported;
105} 107}
106 108
107DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { 109Common::Input::DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
108 DeviceInfo device_info{}; 110 DeviceInfo device_info{};
109 111
110 const auto result = GetDeviceInfo(device_info); 112 const auto result = GetDeviceInfo(device_info);
@@ -113,23 +115,23 @@ DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
113 return result; 115 return result;
114} 116}
115 117
116DriverResult GenericProtocol::SetHomeLight() { 118Common::Input::DriverResult GenericProtocol::SetHomeLight() {
117 ScopedSetBlocking sb(this); 119 ScopedSetBlocking sb(this);
118 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00}; 120 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
119 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer); 121 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
120} 122}
121 123
122DriverResult GenericProtocol::SetLedBusy() { 124Common::Input::DriverResult GenericProtocol::SetLedBusy() {
123 return DriverResult::NotSupported; 125 return Common::Input::DriverResult::NotSupported;
124} 126}
125 127
126DriverResult GenericProtocol::SetLedPattern(u8 leds) { 128Common::Input::DriverResult GenericProtocol::SetLedPattern(u8 leds) {
127 ScopedSetBlocking sb(this); 129 ScopedSetBlocking sb(this);
128 const std::array<u8, 1> buffer{leds}; 130 const std::array<u8, 1> buffer{leds};
129 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer); 131 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
130} 132}
131 133
132DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) { 134Common::Input::DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
133 return SetLedPattern(static_cast<u8>(leds << 4)); 135 return SetLedPattern(static_cast<u8>(leds << 4));
134} 136}
135 137
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
index 424831e81..90fcd17f6 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.h
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -11,6 +11,10 @@
11#include "input_common/helpers/joycon_protocol/common_protocol.h" 11#include "input_common/helpers/joycon_protocol/common_protocol.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h" 12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13 13
14namespace Common::Input {
15enum class DriverResult;
16}
17
14namespace InputCommon::Joycon { 18namespace InputCommon::Joycon {
15 19
16/// Joycon driver functions that easily implemented 20/// Joycon driver functions that easily implemented
@@ -20,34 +24,34 @@ public:
20 24
21 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital 25 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
22 /// data instead of analog. Motion will be disabled 26 /// data instead of analog. Motion will be disabled
23 DriverResult EnablePassiveMode(); 27 Common::Input::DriverResult EnablePassiveMode();
24 28
25 /// Enables active mode. This mode will return the current status every 5-15ms 29 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode(); 30 Common::Input::DriverResult EnableActiveMode();
27 31
28 /// Enables or disables the low power mode 32 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable); 33 Common::Input::DriverResult SetLowPowerMode(bool enable);
30 34
31 /// Unknown function used by the switch 35 /// Unknown function used by the switch
32 DriverResult TriggersElapsed(); 36 Common::Input::DriverResult TriggersElapsed();
33 37
34 /** 38 /**
35 * Sends a request to obtain the joycon firmware and mac from handle 39 * Sends a request to obtain the joycon firmware and mac from handle
36 * @returns controller device info 40 * @returns controller device info
37 */ 41 */
38 DriverResult GetDeviceInfo(DeviceInfo& controller_type); 42 Common::Input::DriverResult GetDeviceInfo(DeviceInfo& controller_type);
39 43
40 /** 44 /**
41 * Sends a request to obtain the joycon type from handle 45 * Sends a request to obtain the joycon type from handle
42 * @returns controller type of the joycon 46 * @returns controller type of the joycon
43 */ 47 */
44 DriverResult GetControllerType(ControllerType& controller_type); 48 Common::Input::DriverResult GetControllerType(ControllerType& controller_type);
45 49
46 /** 50 /**
47 * Enables motion input 51 * Enables motion input
48 * @param enable if true motion data will be enabled 52 * @param enable if true motion data will be enabled
49 */ 53 */
50 DriverResult EnableImu(bool enable); 54 Common::Input::DriverResult EnableImu(bool enable);
51 55
52 /** 56 /**
53 * Configures the motion sensor with the specified parameters 57 * Configures the motion sensor with the specified parameters
@@ -56,59 +60,60 @@ public:
56 * @param asen accelerometer sensitivity in G force 60 * @param asen accelerometer sensitivity in G force
57 * @param afrec accelerometer frequency in hertz 61 * @param afrec accelerometer frequency in hertz
58 */ 62 */
59 DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, 63 Common::Input::DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
60 AccelerometerSensitivity asen, AccelerometerPerformance afrec); 64 AccelerometerSensitivity asen,
65 AccelerometerPerformance afrec);
61 66
62 /** 67 /**
63 * Request battery level from the device 68 * Request battery level from the device
64 * @returns battery level 69 * @returns battery level
65 */ 70 */
66 DriverResult GetBattery(u32& battery_level); 71 Common::Input::DriverResult GetBattery(u32& battery_level);
67 72
68 /** 73 /**
69 * Request joycon colors from the device 74 * Request joycon colors from the device
70 * @returns colors of the body and buttons 75 * @returns colors of the body and buttons
71 */ 76 */
72 DriverResult GetColor(Color& color); 77 Common::Input::DriverResult GetColor(Color& color);
73 78
74 /** 79 /**
75 * Request joycon serial number from the device 80 * Request joycon serial number from the device
76 * @returns 16 byte serial number 81 * @returns 16 byte serial number
77 */ 82 */
78 DriverResult GetSerialNumber(SerialNumber& serial_number); 83 Common::Input::DriverResult GetSerialNumber(SerialNumber& serial_number);
79 84
80 /** 85 /**
81 * Request joycon serial number from the device 86 * Request joycon serial number from the device
82 * @returns 16 byte serial number 87 * @returns 16 byte serial number
83 */ 88 */
84 DriverResult GetTemperature(u32& temperature); 89 Common::Input::DriverResult GetTemperature(u32& temperature);
85 90
86 /** 91 /**
87 * Request joycon serial number from the device 92 * Request joycon serial number from the device
88 * @returns 16 byte serial number 93 * @returns 16 byte serial number
89 */ 94 */
90 DriverResult GetVersionNumber(FirmwareVersion& version); 95 Common::Input::DriverResult GetVersionNumber(FirmwareVersion& version);
91 96
92 /** 97 /**
93 * Sets home led behaviour 98 * Sets home led behaviour
94 */ 99 */
95 DriverResult SetHomeLight(); 100 Common::Input::DriverResult SetHomeLight();
96 101
97 /** 102 /**
98 * Sets home led into a slow breathing state 103 * Sets home led into a slow breathing state
99 */ 104 */
100 DriverResult SetLedBusy(); 105 Common::Input::DriverResult SetLedBusy();
101 106
102 /** 107 /**
103 * Sets the 4 player leds on the joycon on a solid state 108 * Sets the 4 player leds on the joycon on a solid state
104 * @params bit flag containing the led state 109 * @params bit flag containing the led state
105 */ 110 */
106 DriverResult SetLedPattern(u8 leds); 111 Common::Input::DriverResult SetLedPattern(u8 leds);
107 112
108 /** 113 /**
109 * Sets the 4 player leds on the joycon on a blinking state 114 * Sets the 4 player leds on the joycon on a blinking state
110 * @returns bit flag containing the led state 115 * @returns bit flag containing the led state
111 */ 116 */
112 DriverResult SetLedBlinkPattern(u8 leds); 117 Common::Input::DriverResult SetLedBlinkPattern(u8 leds);
113}; 118};
114} // namespace InputCommon::Joycon 119} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
index 731fd5981..68b0589e3 100644
--- a/src/input_common/helpers/joycon_protocol/irs.cpp
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <thread> 4#include "common/input.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h" 6#include "input_common/helpers/joycon_protocol/irs.h"
7 7
@@ -10,21 +10,21 @@ namespace InputCommon::Joycon {
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) 10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
12 12
13DriverResult IrsProtocol::EnableIrs() { 13Common::Input::DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS"); 14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 17
18 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); 19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 } 20 }
21 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
22 result = EnableMCU(true); 22 result = EnableMCU(true);
23 } 23 }
24 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); 25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 } 26 }
27 if (result == DriverResult::Success) { 27 if (result == Common::Input::DriverResult::Success) {
28 const MCUConfig config{ 28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU, 29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode, 30 .sub_command = MCUSubCommand::SetMCUMode,
@@ -34,16 +34,16 @@ DriverResult IrsProtocol::EnableIrs() {
34 34
35 result = ConfigureMCU(config); 35 result = ConfigureMCU(config);
36 } 36 }
37 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR); 38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 } 39 }
40 if (result == DriverResult::Success) { 40 if (result == Common::Input::DriverResult::Success) {
41 result = ConfigureIrs(); 41 result = ConfigureIrs();
42 } 42 }
43 if (result == DriverResult::Success) { 43 if (result == Common::Input::DriverResult::Success) {
44 result = WriteRegistersStep1(); 44 result = WriteRegistersStep1();
45 } 45 }
46 if (result == DriverResult::Success) { 46 if (result == Common::Input::DriverResult::Success) {
47 result = WriteRegistersStep2(); 47 result = WriteRegistersStep2();
48 } 48 }
49 49
@@ -52,12 +52,12 @@ DriverResult IrsProtocol::EnableIrs() {
52 return result; 52 return result;
53} 53}
54 54
55DriverResult IrsProtocol::DisableIrs() { 55Common::Input::DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS"); 56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this); 57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success}; 58 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
59 59
60 if (result == DriverResult::Success) { 60 if (result == Common::Input::DriverResult::Success) {
61 result = EnableMCU(false); 61 result = EnableMCU(false);
62 } 62 }
63 63
@@ -66,7 +66,7 @@ DriverResult IrsProtocol::DisableIrs() {
66 return result; 66 return result;
67} 67}
68 68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) { 69Common::Input::DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode; 70 irs_mode = mode;
71 switch (format) { 71 switch (format) {
72 case IrsResolution::Size320x240: 72 case IrsResolution::Size320x240:
@@ -103,10 +103,10 @@ DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
103 return EnableIrs(); 103 return EnableIrs();
104 } 104 }
105 105
106 return DriverResult::Success; 106 return Common::Input::DriverResult::Success;
107} 107}
108 108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) { 109Common::Input::DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment = 110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1)); 111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112 112
@@ -129,7 +129,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
129 return RequestFrame(packet_fragment); 129 return RequestFrame(packet_fragment);
130} 130}
131 131
132DriverResult IrsProtocol::ConfigureIrs() { 132Common::Input::DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS"); 133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28; 134 constexpr std::size_t max_tries = 28;
135 SubCommandResponse output{}; 135 SubCommandResponse output{};
@@ -152,20 +152,20 @@ DriverResult IrsProtocol::ConfigureIrs() {
152 do { 152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154 154
155 if (result != DriverResult::Success) { 155 if (result != Common::Input::DriverResult::Success) {
156 return result; 156 return result;
157 } 157 }
158 if (tries++ >= max_tries) { 158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply; 159 return Common::Input::DriverResult::WrongReply;
160 } 160 }
161 } while (output.command_data[0] != 0x0b); 161 } while (output.command_data[0] != 0x0b);
162 162
163 return DriverResult::Success; 163 return Common::Input::DriverResult::Success;
164} 164}
165 165
166DriverResult IrsProtocol::WriteRegistersStep1() { 166Common::Input::DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1"); 167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success}; 168 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
169 constexpr std::size_t max_tries = 28; 169 constexpr std::size_t max_tries = 28;
170 SubCommandResponse output{}; 170 SubCommandResponse output{};
171 std::size_t tries = 0; 171 std::size_t tries = 0;
@@ -197,7 +197,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); 197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF; 198 mcu_request[37] = 0xFF;
199 199
200 if (result != DriverResult::Success) { 200 if (result != Common::Input::DriverResult::Success) {
201 return result; 201 return result;
202 } 202 }
203 203
@@ -205,26 +205,26 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206 206
207 // First time we need to set the report mode 207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) { 208 if (result == Common::Input::DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); 209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 } 210 }
211 if (result == DriverResult::Success && tries == 0) { 211 if (result == Common::Input::DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); 212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 } 213 }
214 214
215 if (result != DriverResult::Success) { 215 if (result != Common::Input::DriverResult::Success) {
216 return result; 216 return result;
217 } 217 }
218 if (tries++ >= max_tries) { 218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply; 219 return Common::Input::DriverResult::WrongReply;
220 } 220 }
221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) && 221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23); 222 output.command_data[0] != 0x23);
223 223
224 return DriverResult::Success; 224 return Common::Input::DriverResult::Success;
225} 225}
226 226
227DriverResult IrsProtocol::WriteRegistersStep2() { 227Common::Input::DriverResult IrsProtocol::WriteRegistersStep2() {
228 LOG_DEBUG(Input, "WriteRegistersStep2"); 228 LOG_DEBUG(Input, "WriteRegistersStep2");
229 constexpr std::size_t max_tries = 28; 229 constexpr std::size_t max_tries = 28;
230 SubCommandResponse output{}; 230 SubCommandResponse output{};
@@ -255,18 +255,18 @@ DriverResult IrsProtocol::WriteRegistersStep2() {
255 do { 255 do {
256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
257 257
258 if (result != DriverResult::Success) { 258 if (result != Common::Input::DriverResult::Success) {
259 return result; 259 return result;
260 } 260 }
261 if (tries++ >= max_tries) { 261 if (tries++ >= max_tries) {
262 return DriverResult::WrongReply; 262 return Common::Input::DriverResult::WrongReply;
263 } 263 }
264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23); 264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
265 265
266 return DriverResult::Success; 266 return Common::Input::DriverResult::Success;
267} 267}
268 268
269DriverResult IrsProtocol::RequestFrame(u8 frame) { 269Common::Input::DriverResult IrsProtocol::RequestFrame(u8 frame) {
270 std::array<u8, 38> mcu_request{}; 270 std::array<u8, 38> mcu_request{};
271 mcu_request[3] = frame; 271 mcu_request[3] = frame;
272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); 272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
@@ -274,7 +274,7 @@ DriverResult IrsProtocol::RequestFrame(u8 frame) {
274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); 274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
275} 275}
276 276
277DriverResult IrsProtocol::ResendFrame(u8 frame) { 277Common::Input::DriverResult IrsProtocol::ResendFrame(u8 frame) {
278 std::array<u8, 38> mcu_request{}; 278 std::array<u8, 38> mcu_request{};
279 mcu_request[1] = 0x1; 279 mcu_request[1] = 0x1;
280 mcu_request[2] = frame; 280 mcu_request[2] = frame;
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h
index 76dfa02ea..714cbb6b2 100644
--- a/src/input_common/helpers/joycon_protocol/irs.h
+++ b/src/input_common/helpers/joycon_protocol/irs.h
@@ -13,19 +13,23 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class IrsProtocol final : private JoyconCommonProtocol { 22class IrsProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableIrs(); 26 Common::Input::DriverResult EnableIrs();
23 27
24 DriverResult DisableIrs(); 28 Common::Input::DriverResult DisableIrs();
25 29
26 DriverResult SetIrsConfig(IrsMode mode, IrsResolution format); 30 Common::Input::DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
27 31
28 DriverResult RequestImage(std::span<u8> buffer); 32 Common::Input::DriverResult RequestImage(std::span<u8> buffer);
29 33
30 std::vector<u8> GetImage() const; 34 std::vector<u8> GetImage() const;
31 35
@@ -34,13 +38,13 @@ public:
34 bool IsEnabled() const; 38 bool IsEnabled() const;
35 39
36private: 40private:
37 DriverResult ConfigureIrs(); 41 Common::Input::DriverResult ConfigureIrs();
38 42
39 DriverResult WriteRegistersStep1(); 43 Common::Input::DriverResult WriteRegistersStep1();
40 DriverResult WriteRegistersStep2(); 44 Common::Input::DriverResult WriteRegistersStep2();
41 45
42 DriverResult RequestFrame(u8 frame); 46 Common::Input::DriverResult RequestFrame(u8 frame);
43 DriverResult ResendFrame(u8 frame); 47 Common::Input::DriverResult ResendFrame(u8 frame);
44 48
45 IrsMode irs_mode{IrsMode::ImageTransfer}; 49 IrsMode irs_mode{IrsMode::ImageTransfer};
46 IrsResolution resolution{IrsResolution::Size40x30}; 50 IrsResolution resolution{IrsResolution::Size40x30};
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 5007b0e18..77a43c67a 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
24using MacAddress = std::array<u8, 6>; 24using MacAddress = std::array<u8, 6>;
25using SerialNumber = std::array<u8, 15>; 25using SerialNumber = std::array<u8, 15>;
26using TagUUID = std::array<u8, 7>; 26using TagUUID = std::array<u8, 7>;
27using MifareUUID = std::array<u8, 4>;
27 28
28enum class ControllerType : u8 { 29enum class ControllerType : u8 {
29 None = 0x00, 30 None = 0x00,
@@ -307,6 +308,19 @@ enum class NFCStatus : u8 {
307 WriteDone = 0x05, 308 WriteDone = 0x05,
308 TagLost = 0x07, 309 TagLost = 0x07,
309 WriteReady = 0x09, 310 WriteReady = 0x09,
311 MifareDone = 0x10,
312};
313
314enum class MifareCmd : u8 {
315 None = 0x00,
316 Read = 0x30,
317 AuthA = 0x60,
318 AuthB = 0x61,
319 Write = 0xA0,
320 Transfer = 0xB0,
321 Decrement = 0xC0,
322 Increment = 0xC1,
323 Store = 0xC2
310}; 324};
311 325
312enum class IrsMode : u8 { 326enum class IrsMode : u8 {
@@ -388,23 +402,6 @@ enum class ExternalDeviceId : u16 {
388 Starlink = 0x2800, 402 Starlink = 0x2800,
389}; 403};
390 404
391enum class DriverResult {
392 Success,
393 WrongReply,
394 Timeout,
395 InvalidParameters,
396 UnsupportedControllerType,
397 HandleInUse,
398 ErrorReadingData,
399 ErrorWritingData,
400 NoDeviceDetected,
401 InvalidHandle,
402 NotSupported,
403 Disabled,
404 Delayed,
405 Unknown,
406};
407
408struct MotionSensorCalibration { 405struct MotionSensorCalibration {
409 s16 offset; 406 s16 offset;
410 s16 scale; 407 s16 scale;
@@ -592,6 +589,14 @@ struct NFCWriteCommandData {
592static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); 589static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
593#pragma pack(pop) 590#pragma pack(pop)
594 591
592struct MifareCommandData {
593 u8 unknown1;
594 u8 unknown2;
595 u8 number_of_short_bytes;
596 MifareUUID uid;
597};
598static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size");
599
595struct NFCPollingCommandData { 600struct NFCPollingCommandData {
596 u8 enable_mifare; 601 u8 enable_mifare;
597 u8 unknown_1; 602 u8 unknown_1;
@@ -629,6 +634,41 @@ struct NFCWritePackage {
629 std::array<NFCDataChunk, 4> data_chunks; 634 std::array<NFCDataChunk, 4> data_chunks;
630}; 635};
631 636
637struct MifareReadChunk {
638 MifareCmd command;
639 std::array<u8, 0x6> sector_key;
640 u8 sector;
641};
642
643struct MifareWriteChunk {
644 MifareCmd command;
645 std::array<u8, 0x6> sector_key;
646 u8 sector;
647 std::array<u8, 0x10> data;
648};
649
650struct MifareReadData {
651 u8 sector;
652 std::array<u8, 0x10> data;
653};
654
655struct MifareReadPackage {
656 MifareCommandData command_data;
657 std::array<MifareReadChunk, 0x10> data_chunks;
658};
659
660struct MifareWritePackage {
661 MifareCommandData command_data;
662 std::array<MifareWriteChunk, 0x10> data_chunks;
663};
664
665struct TagInfo {
666 u8 uuid_length;
667 u8 protocol;
668 u8 tag_type;
669 std::array<u8, 10> uuid;
670};
671
632struct IrsConfigure { 672struct IrsConfigure {
633 MCUCommand command; 673 MCUCommand command;
634 MCUSubCommand sub_command; 674 MCUSubCommand sub_command;
@@ -744,7 +784,7 @@ struct JoyconCallbacks {
744 std::function<void(int, f32)> on_stick_data; 784 std::function<void(int, f32)> on_stick_data;
745 std::function<void(int, const MotionData&)> on_motion_data; 785 std::function<void(int, const MotionData&)> on_motion_data;
746 std::function<void(f32)> on_ring_data; 786 std::function<void(f32)> on_ring_data;
747 std::function<void(const std::vector<u8>&)> on_amiibo_data; 787 std::function<void(const Joycon::TagInfo&)> on_amiibo_data;
748 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; 788 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
749}; 789};
750 790
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index f7058c4a7..09953394b 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <thread> 4#include "common/input.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h" 6#include "input_common/helpers/joycon_protocol/nfc.h"
7 7
@@ -10,21 +10,21 @@ namespace InputCommon::Joycon {
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) 10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
12 12
13DriverResult NfcProtocol::EnableNfc() { 13Common::Input::DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC"); 14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 17
18 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); 19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 } 20 }
21 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
22 result = EnableMCU(true); 22 result = EnableMCU(true);
23 } 23 }
24 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); 25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 } 26 }
27 if (result == DriverResult::Success) { 27 if (result == Common::Input::DriverResult::Success) {
28 const MCUConfig config{ 28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU, 29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode, 30 .sub_command = MCUSubCommand::SetMCUMode,
@@ -34,123 +34,240 @@ DriverResult NfcProtocol::EnableNfc() {
34 34
35 result = ConfigureMCU(config); 35 result = ConfigureMCU(config);
36 } 36 }
37 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); 38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
39 } 39 }
40 if (result == DriverResult::Success) { 40 if (result == Common::Input::DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready); 41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 } 42 }
43 if (result == Common::Input::DriverResult::Success) {
44 MCUCommandResponse output{};
45 result = SendStopPollingRequest(output);
46 }
47 if (result == Common::Input::DriverResult::Success) {
48 result = WaitUntilNfcIs(NFCStatus::Ready);
49 }
50 if (result == Common::Input::DriverResult::Success) {
51 is_enabled = true;
52 }
43 53
44 return result; 54 return result;
45} 55}
46 56
47DriverResult NfcProtocol::DisableNfc() { 57Common::Input::DriverResult NfcProtocol::DisableNfc() {
48 LOG_DEBUG(Input, "Disable NFC"); 58 LOG_DEBUG(Input, "Disable NFC");
49 ScopedSetBlocking sb(this); 59 ScopedSetBlocking sb(this);
50 DriverResult result{DriverResult::Success}; 60 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
51 61
52 if (result == DriverResult::Success) { 62 if (result == Common::Input::DriverResult::Success) {
53 result = EnableMCU(false); 63 result = EnableMCU(false);
54 } 64 }
55 65
56 is_enabled = false; 66 is_enabled = false;
67 is_polling = false;
57 68
58 return result; 69 return result;
59} 70}
60 71
61DriverResult NfcProtocol::StartNFCPollingMode() { 72Common::Input::DriverResult NfcProtocol::StartNFCPollingMode() {
62 LOG_DEBUG(Input, "Start NFC pooling Mode"); 73 LOG_DEBUG(Input, "Start NFC polling Mode");
63 ScopedSetBlocking sb(this); 74 ScopedSetBlocking sb(this);
64 DriverResult result{DriverResult::Success}; 75 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
65 76
66 if (result == DriverResult::Success) { 77 if (result == Common::Input::DriverResult::Success) {
67 MCUCommandResponse output{}; 78 MCUCommandResponse output{};
68 result = SendStopPollingRequest(output); 79 result = SendStartPollingRequest(output);
69 } 80 }
70 if (result == DriverResult::Success) { 81 if (result == Common::Input::DriverResult::Success) {
71 result = WaitUntilNfcIs(NFCStatus::Ready); 82 result = WaitUntilNfcIs(NFCStatus::Polling);
83 }
84 if (result == Common::Input::DriverResult::Success) {
85 is_polling = true;
72 } 86 }
73 if (result == DriverResult::Success) { 87
88 return result;
89}
90
91Common::Input::DriverResult NfcProtocol::StopNFCPollingMode() {
92 LOG_DEBUG(Input, "Stop NFC polling Mode");
93 ScopedSetBlocking sb(this);
94 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
95
96 if (result == Common::Input::DriverResult::Success) {
74 MCUCommandResponse output{}; 97 MCUCommandResponse output{};
75 result = SendStartPollingRequest(output); 98 result = SendStopPollingRequest(output);
76 } 99 }
77 if (result == DriverResult::Success) { 100 if (result == Common::Input::DriverResult::Success) {
78 result = WaitUntilNfcIs(NFCStatus::Polling); 101 result = WaitUntilNfcIs(NFCStatus::WriteReady);
79 } 102 }
80 if (result == DriverResult::Success) { 103 if (result == Common::Input::DriverResult::Success) {
81 is_enabled = true; 104 is_polling = false;
82 } 105 }
83 106
84 return result; 107 return result;
85} 108}
86 109
87DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { 110Common::Input::DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
88 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 111 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
89 return DriverResult::Delayed; 112 return Common::Input::DriverResult::Delayed;
90 } 113 }
91 update_counter = 0; 114 update_counter = 0;
92 115
93 LOG_DEBUG(Input, "Scan for amiibos"); 116 LOG_DEBUG(Input, "Scan for amiibos");
94 ScopedSetBlocking sb(this); 117 ScopedSetBlocking sb(this);
95 DriverResult result{DriverResult::Success}; 118 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
96 TagFoundData tag_data{}; 119 TagFoundData tag_data{};
97 120
98 if (result == DriverResult::Success) { 121 if (result == Common::Input::DriverResult::Success) {
99 result = IsTagInRange(tag_data); 122 result = IsTagInRange(tag_data);
100 } 123 }
101 124
102 if (result == DriverResult::Success) { 125 if (result == Common::Input::DriverResult::Success) {
126 tag_info = {
127 .uuid_length = tag_data.uuid_size,
128 .protocol = 1,
129 .tag_type = tag_data.type,
130 .uuid = {},
131 };
132
133 memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size);
134
135 // Investigate why mifare type is not correct
136 if (tag_info.tag_type == 144) {
137 tag_info.tag_type = 1U << 6;
138 }
139
103 std::string uuid_string; 140 std::string uuid_string;
104 for (auto& content : tag_data.uuid) { 141 for (auto& content : tag_data.uuid) {
105 uuid_string += fmt::format(" {:02x}", content); 142 uuid_string += fmt::format(" {:02x}", content);
106 } 143 }
107 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); 144 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
145 }
146
147 return result;
148}
149
150Common::Input::DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
151 LOG_DEBUG(Input, "Scan for amiibos");
152 ScopedSetBlocking sb(this);
153 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
154 TagFoundData tag_data{};
155
156 if (result == Common::Input::DriverResult::Success) {
157 result = IsTagInRange(tag_data, 7);
158 }
159
160 if (result == Common::Input::DriverResult::Success) {
108 result = GetAmiiboData(data); 161 result = GetAmiiboData(data);
109 } 162 }
110 163
111 return result; 164 return result;
112} 165}
113 166
114DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) { 167Common::Input::DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
115 LOG_DEBUG(Input, "Write amiibo"); 168 LOG_DEBUG(Input, "Write amiibo");
116 ScopedSetBlocking sb(this); 169 ScopedSetBlocking sb(this);
117 DriverResult result{DriverResult::Success}; 170 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
118 TagUUID tag_uuid = GetTagUUID(data); 171 TagUUID tag_uuid = GetTagUUID(data);
119 TagFoundData tag_data{}; 172 TagFoundData tag_data{};
120 173
121 if (result == DriverResult::Success) { 174 if (result == Common::Input::DriverResult::Success) {
122 result = IsTagInRange(tag_data, 7); 175 result = IsTagInRange(tag_data, 7);
123 } 176 }
124 if (result == DriverResult::Success) { 177 if (result == Common::Input::DriverResult::Success) {
125 if (tag_data.uuid != tag_uuid) { 178 if (tag_data.uuid != tag_uuid) {
126 result = DriverResult::InvalidParameters; 179 result = Common::Input::DriverResult::InvalidParameters;
127 } 180 }
128 } 181 }
129 if (result == DriverResult::Success) { 182 if (result == Common::Input::DriverResult::Success) {
130 MCUCommandResponse output{}; 183 MCUCommandResponse output{};
131 result = SendStopPollingRequest(output); 184 result = SendStopPollingRequest(output);
132 } 185 }
133 if (result == DriverResult::Success) { 186 if (result == Common::Input::DriverResult::Success) {
134 result = WaitUntilNfcIs(NFCStatus::Ready); 187 result = WaitUntilNfcIs(NFCStatus::Ready);
135 } 188 }
136 if (result == DriverResult::Success) { 189 if (result == Common::Input::DriverResult::Success) {
137 MCUCommandResponse output{}; 190 MCUCommandResponse output{};
138 result = SendStartPollingRequest(output, true); 191 result = SendStartPollingRequest(output, true);
139 } 192 }
140 if (result == DriverResult::Success) { 193 if (result == Common::Input::DriverResult::Success) {
141 result = WaitUntilNfcIs(NFCStatus::WriteReady); 194 result = WaitUntilNfcIs(NFCStatus::WriteReady);
142 } 195 }
143 if (result == DriverResult::Success) { 196 if (result == Common::Input::DriverResult::Success) {
144 result = WriteAmiiboData(tag_uuid, data); 197 result = WriteAmiiboData(tag_uuid, data);
145 } 198 }
146 if (result == DriverResult::Success) { 199 if (result == Common::Input::DriverResult::Success) {
147 result = WaitUntilNfcIs(NFCStatus::WriteDone); 200 result = WaitUntilNfcIs(NFCStatus::WriteDone);
148 } 201 }
149 if (result == DriverResult::Success) { 202 if (result == Common::Input::DriverResult::Success) {
203 MCUCommandResponse output{};
204 result = SendStopPollingRequest(output);
205 }
206
207 return result;
208}
209
210Common::Input::DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request,
211 std::span<MifareReadData> out_data) {
212 LOG_DEBUG(Input, "Read mifare");
213 ScopedSetBlocking sb(this);
214 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
215 TagFoundData tag_data{};
216 MifareUUID tag_uuid{};
217
218 if (result == Common::Input::DriverResult::Success) {
219 result = IsTagInRange(tag_data, 7);
220 }
221 if (result == Common::Input::DriverResult::Success) {
222 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
223 result = GetMifareData(tag_uuid, read_request, out_data);
224 }
225 if (result == Common::Input::DriverResult::Success) {
150 MCUCommandResponse output{}; 226 MCUCommandResponse output{};
151 result = SendStopPollingRequest(output); 227 result = SendStopPollingRequest(output);
152 } 228 }
229 if (result == Common::Input::DriverResult::Success) {
230 result = WaitUntilNfcIs(NFCStatus::Ready);
231 }
232 if (result == Common::Input::DriverResult::Success) {
233 MCUCommandResponse output{};
234 result = SendStartPollingRequest(output, true);
235 }
236 if (result == Common::Input::DriverResult::Success) {
237 result = WaitUntilNfcIs(NFCStatus::WriteReady);
238 }
239 return result;
240}
241
242Common::Input::DriverResult NfcProtocol::WriteMifare(
243 std::span<const MifareWriteChunk> write_request) {
244 LOG_DEBUG(Input, "Write mifare");
245 ScopedSetBlocking sb(this);
246 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
247 TagFoundData tag_data{};
248 MifareUUID tag_uuid{};
153 249
250 if (result == Common::Input::DriverResult::Success) {
251 result = IsTagInRange(tag_data, 7);
252 }
253 if (result == Common::Input::DriverResult::Success) {
254 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
255 result = WriteMifareData(tag_uuid, write_request);
256 }
257 if (result == Common::Input::DriverResult::Success) {
258 MCUCommandResponse output{};
259 result = SendStopPollingRequest(output);
260 }
261 if (result == Common::Input::DriverResult::Success) {
262 result = WaitUntilNfcIs(NFCStatus::Ready);
263 }
264 if (result == Common::Input::DriverResult::Success) {
265 MCUCommandResponse output{};
266 result = SendStartPollingRequest(output, true);
267 }
268 if (result == Common::Input::DriverResult::Success) {
269 result = WaitUntilNfcIs(NFCStatus::WriteReady);
270 }
154 return result; 271 return result;
155} 272}
156 273
@@ -161,17 +278,17 @@ bool NfcProtocol::HasAmiibo() {
161 update_counter = 0; 278 update_counter = 0;
162 279
163 ScopedSetBlocking sb(this); 280 ScopedSetBlocking sb(this);
164 DriverResult result{DriverResult::Success}; 281 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
165 TagFoundData tag_data{}; 282 TagFoundData tag_data{};
166 283
167 if (result == DriverResult::Success) { 284 if (result == Common::Input::DriverResult::Success) {
168 result = IsTagInRange(tag_data, 7); 285 result = IsTagInRange(tag_data, 7);
169 } 286 }
170 287
171 return result == DriverResult::Success; 288 return result == Common::Input::DriverResult::Success;
172} 289}
173 290
174DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) { 291Common::Input::DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
175 constexpr std::size_t timeout_limit = 10; 292 constexpr std::size_t timeout_limit = 10;
176 MCUCommandResponse output{}; 293 MCUCommandResponse output{};
177 std::size_t tries = 0; 294 std::size_t tries = 0;
@@ -179,30 +296,31 @@ DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
179 do { 296 do {
180 auto result = SendNextPackageRequest(output, {}); 297 auto result = SendNextPackageRequest(output, {});
181 298
182 if (result != DriverResult::Success) { 299 if (result != Common::Input::DriverResult::Success) {
183 return result; 300 return result;
184 } 301 }
185 if (tries++ > timeout_limit) { 302 if (tries++ > timeout_limit) {
186 return DriverResult::Timeout; 303 return Common::Input::DriverResult::Timeout;
187 } 304 }
188 } while (output.mcu_report != MCUReport::NFCState || 305 } while (output.mcu_report != MCUReport::NFCState ||
189 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 306 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
190 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status)); 307 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
191 308
192 return DriverResult::Success; 309 return Common::Input::DriverResult::Success;
193} 310}
194 311
195DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) { 312Common::Input::DriverResult NfcProtocol::IsTagInRange(TagFoundData& data,
313 std::size_t timeout_limit) {
196 MCUCommandResponse output{}; 314 MCUCommandResponse output{};
197 std::size_t tries = 0; 315 std::size_t tries = 0;
198 316
199 do { 317 do {
200 const auto result = SendNextPackageRequest(output, {}); 318 const auto result = SendNextPackageRequest(output, {});
201 if (result != DriverResult::Success) { 319 if (result != Common::Input::DriverResult::Success) {
202 return result; 320 return result;
203 } 321 }
204 if (tries++ > timeout_limit) { 322 if (tries++ > timeout_limit) {
205 return DriverResult::Timeout; 323 return Common::Input::DriverResult::Timeout;
206 } 324 }
207 } while (output.mcu_report != MCUReport::NFCState || 325 } while (output.mcu_report != MCUReport::NFCState ||
208 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 326 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
@@ -212,10 +330,10 @@ DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_l
212 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID))); 330 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
213 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); 331 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
214 332
215 return DriverResult::Success; 333 return Common::Input::DriverResult::Success;
216} 334}
217 335
218DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { 336Common::Input::DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
219 constexpr std::size_t timeout_limit = 60; 337 constexpr std::size_t timeout_limit = 60;
220 MCUCommandResponse output{}; 338 MCUCommandResponse output{};
221 std::size_t tries = 0; 339 std::size_t tries = 0;
@@ -224,7 +342,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
224 std::size_t ntag_buffer_pos = 0; 342 std::size_t ntag_buffer_pos = 0;
225 auto result = SendReadAmiiboRequest(output, NFCPages::Block135); 343 auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
226 344
227 if (result != DriverResult::Success) { 345 if (result != Common::Input::DriverResult::Success) {
228 return result; 346 return result;
229 } 347 }
230 348
@@ -233,14 +351,14 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
233 result = SendNextPackageRequest(output, package_index); 351 result = SendNextPackageRequest(output, package_index);
234 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 352 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
235 353
236 if (result != DriverResult::Success) { 354 if (result != Common::Input::DriverResult::Success) {
237 return result; 355 return result;
238 } 356 }
239 357
240 if ((output.mcu_report == MCUReport::NFCReadData || 358 if ((output.mcu_report == MCUReport::NFCReadData ||
241 output.mcu_report == MCUReport::NFCState) && 359 output.mcu_report == MCUReport::NFCState) &&
242 nfc_status == NFCStatus::TagLost) { 360 nfc_status == NFCStatus::TagLost) {
243 return DriverResult::ErrorReadingData; 361 return Common::Input::DriverResult::ErrorReadingData;
244 } 362 }
245 363
246 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { 364 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
@@ -259,14 +377,15 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
259 377
260 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 378 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
261 LOG_INFO(Input, "Finished reading amiibo"); 379 LOG_INFO(Input, "Finished reading amiibo");
262 return DriverResult::Success; 380 return Common::Input::DriverResult::Success;
263 } 381 }
264 } 382 }
265 383
266 return DriverResult::Timeout; 384 return Common::Input::DriverResult::Timeout;
267} 385}
268 386
269DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) { 387Common::Input::DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid,
388 std::span<const u8> data) {
270 constexpr std::size_t timeout_limit = 60; 389 constexpr std::size_t timeout_limit = 60;
271 const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data); 390 const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data);
272 const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data); 391 const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data);
@@ -281,7 +400,7 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
281 400
282 auto result = SendWriteAmiiboRequest(output, tag_uuid); 401 auto result = SendWriteAmiiboRequest(output, tag_uuid);
283 402
284 if (result != DriverResult::Success) { 403 if (result != Common::Input::DriverResult::Success) {
285 return result; 404 return result;
286 } 405 }
287 406
@@ -290,14 +409,14 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
290 result = SendNextPackageRequest(output, package_index); 409 result = SendNextPackageRequest(output, package_index);
291 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 410 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
292 411
293 if (result != DriverResult::Success) { 412 if (result != Common::Input::DriverResult::Success) {
294 return result; 413 return result;
295 } 414 }
296 415
297 if ((output.mcu_report == MCUReport::NFCReadData || 416 if ((output.mcu_report == MCUReport::NFCReadData ||
298 output.mcu_report == MCUReport::NFCState) && 417 output.mcu_report == MCUReport::NFCState) &&
299 nfc_status == NFCStatus::TagLost) { 418 nfc_status == NFCStatus::TagLost) {
300 return DriverResult::ErrorReadingData; 419 return Common::Input::DriverResult::ErrorReadingData;
301 } 420 }
302 421
303 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { 422 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
@@ -326,7 +445,131 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
326 if ((output.mcu_report == MCUReport::NFCReadData || 445 if ((output.mcu_report == MCUReport::NFCReadData ||
327 output.mcu_report == MCUReport::NFCState) && 446 output.mcu_report == MCUReport::NFCState) &&
328 nfc_status == NFCStatus::TagLost) { 447 nfc_status == NFCStatus::TagLost) {
329 return DriverResult::ErrorReadingData; 448 return Common::Input::DriverResult::ErrorReadingData;
449 }
450
451 // Increase position when data is confirmed by the joycon
452 if (output.mcu_report == MCUReport::NFCState &&
453 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
454 output.mcu_data[3] == block_id) {
455 block_id++;
456 current_position = next_position;
457 }
458 }
459
460 return result;
461}
462
463Common::Input::DriverResult NfcProtocol::GetMifareData(
464 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request,
465 std::span<MifareReadData> out_data) {
466 constexpr std::size_t timeout_limit = 60;
467 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
468 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
469 std::span<const u8> buffer(nfc_buffer_data);
470 Common::Input::DriverResult result = Common::Input::DriverResult::Success;
471 MCUCommandResponse output{};
472 u8 block_id = 1;
473 u8 package_index = 0;
474 std::size_t tries = 0;
475 std::size_t current_position = 0;
476
477 LOG_INFO(Input, "Reading Mifare data");
478
479 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
480 while (current_position < buffer.size() && tries++ < timeout_limit) {
481 const std::size_t next_position =
482 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
483 const std::size_t block_size = next_position - current_position;
484 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
485
486 SendReadDataMifareRequest(output, block_id, is_last_packet,
487 buffer.subspan(current_position, block_size));
488
489 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
490
491 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
492 return Common::Input::DriverResult::ErrorReadingData;
493 }
494
495 // Increase position when data is confirmed by the joycon
496 if (output.mcu_report == MCUReport::NFCState &&
497 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
498 output.mcu_data[3] == block_id) {
499 block_id++;
500 current_position = next_position;
501 }
502 }
503
504 if (result != Common::Input::DriverResult::Success) {
505 return result;
506 }
507
508 // Wait for reply and save the output data
509 while (tries++ < timeout_limit) {
510 result = SendNextPackageRequest(output, package_index);
511 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
512
513 if (result != Common::Input::DriverResult::Success) {
514 return result;
515 }
516
517 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
518 return Common::Input::DriverResult::ErrorReadingData;
519 }
520
521 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
522 constexpr std::size_t DATA_LENGHT = 0x10 + 1;
523 constexpr std::size_t DATA_START = 11;
524 const u8 number_of_elements = output.mcu_data[10];
525 for (std::size_t i = 0; i < number_of_elements; i++) {
526 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)];
527 memcpy(out_data[i].data.data(),
528 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT),
529 sizeof(MifareReadData::data));
530 }
531 package_index++;
532 continue;
533 }
534
535 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
536 LOG_INFO(Input, "Finished reading mifare");
537 break;
538 }
539 }
540
541 return result;
542}
543
544Common::Input::DriverResult NfcProtocol::WriteMifareData(
545 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> write_request) {
546 constexpr std::size_t timeout_limit = 60;
547 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
548 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
549 std::span<const u8> buffer(nfc_buffer_data);
550 Common::Input::DriverResult result = Common::Input::DriverResult::Success;
551 MCUCommandResponse output{};
552 u8 block_id = 1;
553 u8 package_index = 0;
554 std::size_t tries = 0;
555 std::size_t current_position = 0;
556
557 LOG_INFO(Input, "Writing Mifare data");
558
559 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
560 while (current_position < buffer.size() && tries++ < timeout_limit) {
561 const std::size_t next_position =
562 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
563 const std::size_t block_size = next_position - current_position;
564 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
565
566 SendReadDataMifareRequest(output, block_id, is_last_packet,
567 buffer.subspan(current_position, block_size));
568
569 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
570
571 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
572 return Common::Input::DriverResult::ErrorReadingData;
330 } 573 }
331 574
332 // Increase position when data is confirmed by the joycon 575 // Increase position when data is confirmed by the joycon
@@ -338,11 +581,39 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
338 } 581 }
339 } 582 }
340 583
584 if (result != Common::Input::DriverResult::Success) {
585 return result;
586 }
587
588 // Wait for reply and ignore the output data
589 while (tries++ < timeout_limit) {
590 result = SendNextPackageRequest(output, package_index);
591 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
592
593 if (result != Common::Input::DriverResult::Success) {
594 return result;
595 }
596
597 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
598 return Common::Input::DriverResult::ErrorReadingData;
599 }
600
601 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
602 package_index++;
603 continue;
604 }
605
606 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
607 LOG_INFO(Input, "Finished writing mifare");
608 break;
609 }
610 }
611
341 return result; 612 return result;
342} 613}
343 614
344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, 615Common::Input::DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) { 616 bool is_second_attempt) {
346 NFCRequestState request{ 617 NFCRequestState request{
347 .command_argument = NFCCommand::StartPolling, 618 .command_argument = NFCCommand::StartPolling,
348 .block_id = {}, 619 .block_id = {},
@@ -367,7 +638,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
367 output); 638 output);
368} 639}
369 640
370DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { 641Common::Input::DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
371 NFCRequestState request{ 642 NFCRequestState request{
372 .command_argument = NFCCommand::StopPolling, 643 .command_argument = NFCCommand::StopPolling,
373 .block_id = {}, 644 .block_id = {},
@@ -385,7 +656,8 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
385 output); 656 output);
386} 657}
387 658
388DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) { 659Common::Input::DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output,
660 u8 packet_id) {
389 NFCRequestState request{ 661 NFCRequestState request{
390 .command_argument = NFCCommand::StartWaitingRecieve, 662 .command_argument = NFCCommand::StartWaitingRecieve,
391 .block_id = {}, 663 .block_id = {},
@@ -403,7 +675,8 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8
403 output); 675 output);
404} 676}
405 677
406DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { 678Common::Input::DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output,
679 NFCPages ntag_pages) {
407 NFCRequestState request{ 680 NFCRequestState request{
408 .command_argument = NFCCommand::ReadNtag, 681 .command_argument = NFCCommand::ReadNtag,
409 .block_id = {}, 682 .block_id = {},
@@ -428,8 +701,8 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
428 output); 701 output);
429} 702}
430 703
431DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output, 704Common::Input::DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
432 const TagUUID& tag_uuid) { 705 const TagUUID& tag_uuid) {
433 NFCRequestState request{ 706 NFCRequestState request{
434 .command_argument = NFCCommand::ReadNtag, 707 .command_argument = NFCCommand::ReadNtag,
435 .block_id = {}, 708 .block_id = {},
@@ -454,9 +727,10 @@ DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
454 output); 727 output);
455} 728}
456 729
457DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 730Common::Input::DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
458 bool is_last_packet, 731 u8 block_id,
459 std::span<const u8> data) { 732 bool is_last_packet,
733 std::span<const u8> data) {
460 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); 734 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
461 NFCRequestState request{ 735 NFCRequestState request{
462 .command_argument = NFCCommand::WriteNtag, 736 .command_argument = NFCCommand::WriteNtag,
@@ -477,6 +751,29 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
477 output); 751 output);
478} 752}
479 753
754Common::Input::DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output,
755 u8 block_id, bool is_last_packet,
756 std::span<const u8> data) {
757 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
758 NFCRequestState request{
759 .command_argument = NFCCommand::Mifare,
760 .block_id = block_id,
761 .packet_id = {},
762 .packet_flag =
763 is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
764 .data_length = static_cast<u8>(data_size),
765 .raw_data = {},
766 .crc = {},
767 };
768 memcpy(request.raw_data.data(), data.data(), data_size);
769
770 std::array<u8, sizeof(NFCRequestState)> request_data{};
771 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
772 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
773 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
774 output);
775}
776
480std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { 777std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
481 const std::size_t header_size = 778 const std::size_t header_size =
482 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); 779 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
@@ -498,6 +795,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag
498 return serialized_data; 795 return serialized_data;
499} 796}
500 797
798std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const {
799 const std::size_t header_size = sizeof(MifareCommandData);
800 std::vector<u8> serialized_data(header_size);
801 std::size_t start_index = 0;
802
803 memcpy(serialized_data.data(), &package, header_size);
804 start_index += header_size;
805
806 for (const auto& data_chunk : package.data_chunks) {
807 const std::size_t chunk_size = sizeof(MifareReadChunk);
808 if (data_chunk.command == MifareCmd::None) {
809 continue;
810 }
811 serialized_data.resize(start_index + chunk_size);
812 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
813 start_index += chunk_size;
814 }
815
816 return serialized_data;
817}
818
819std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const {
820 const std::size_t header_size = sizeof(MifareCommandData);
821 std::vector<u8> serialized_data(header_size);
822 std::size_t start_index = 0;
823
824 memcpy(serialized_data.data(), &package, header_size);
825 start_index += header_size;
826
827 for (const auto& data_chunk : package.data_chunks) {
828 const std::size_t chunk_size = sizeof(MifareWriteChunk);
829 if (data_chunk.command == MifareCmd::None) {
830 continue;
831 }
832 serialized_data.resize(start_index + chunk_size);
833 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
834 start_index += chunk_size;
835 }
836
837 return serialized_data;
838}
839
501NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, 840NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
502 std::span<const u8> data) const { 841 std::span<const u8> data) const {
503 return { 842 return {
@@ -527,6 +866,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
527 }; 866 };
528} 867}
529 868
869MifareReadPackage NfcProtocol::MakeMifareReadPackage(
870 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const {
871 MifareReadPackage package{
872 .command_data{
873 .unknown1 = 0xd0,
874 .unknown2 = 0x07,
875 .number_of_short_bytes = static_cast<u8>(
876 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2),
877 .uid = tag_uuid,
878 },
879 .data_chunks = {},
880 };
881
882 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
883 package.data_chunks[i] = read_request[i];
884 }
885
886 return package;
887}
888
889MifareWritePackage NfcProtocol::MakeMifareWritePackage(
890 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const {
891 MifareWritePackage package{
892 .command_data{
893 .unknown1 = 0xd0,
894 .unknown2 = 0x07,
895 .number_of_short_bytes = static_cast<u8>(
896 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2),
897 .uid = tag_uuid,
898 },
899 .data_chunks = {},
900 };
901
902 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
903 package.data_chunks[i] = read_request[i];
904 }
905
906 return package;
907}
908
530NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { 909NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
531 constexpr u8 NFC_PAGE_SIZE = 4; 910 constexpr u8 NFC_PAGE_SIZE = 4;
532 911
@@ -606,4 +985,8 @@ bool NfcProtocol::IsEnabled() const {
606 return is_enabled; 985 return is_enabled;
607} 986}
608 987
988bool NfcProtocol::IsPolling() const {
989 return is_polling;
990}
991
609} // namespace InputCommon::Joycon 992} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index eb58c427d..22db95170 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -13,26 +13,41 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class NfcProtocol final : private JoyconCommonProtocol { 22class NfcProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableNfc(); 26 Common::Input::DriverResult EnableNfc();
27
28 Common::Input::DriverResult DisableNfc();
29
30 Common::Input::DriverResult StartNFCPollingMode();
31
32 Common::Input::DriverResult StopNFCPollingMode();
23 33
24 DriverResult DisableNfc(); 34 Common::Input::DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
25 35
26 DriverResult StartNFCPollingMode(); 36 Common::Input::DriverResult ReadAmiibo(std::vector<u8>& data);
27 37
28 DriverResult ScanAmiibo(std::vector<u8>& data); 38 Common::Input::DriverResult WriteAmiibo(std::span<const u8> data);
29 39
30 DriverResult WriteAmiibo(std::span<const u8> data); 40 Common::Input::DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
41 std::span<MifareReadData> out_data);
42
43 Common::Input::DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
31 44
32 bool HasAmiibo(); 45 bool HasAmiibo();
33 46
34 bool IsEnabled() const; 47 bool IsEnabled() const;
35 48
49 bool IsPolling() const;
50
36private: 51private:
37 // Number of times the function will be delayed until it outputs valid data 52 // Number of times the function will be delayed until it outputs valid data
38 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; 53 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
@@ -43,39 +58,64 @@ private:
43 TagUUID uuid; 58 TagUUID uuid;
44 }; 59 };
45 60
46 DriverResult WaitUntilNfcIs(NFCStatus status); 61 Common::Input::DriverResult WaitUntilNfcIs(NFCStatus status);
62
63 Common::Input::DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
47 64
48 DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1); 65 Common::Input::DriverResult GetAmiiboData(std::vector<u8>& data);
49 66
50 DriverResult GetAmiiboData(std::vector<u8>& data); 67 Common::Input::DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
51 68
52 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); 69 Common::Input::DriverResult GetMifareData(const MifareUUID& tag_uuid,
70 std::span<const MifareReadChunk> read_request,
71 std::span<MifareReadData> out_data);
53 72
54 DriverResult SendStartPollingRequest(MCUCommandResponse& output, 73 Common::Input::DriverResult WriteMifareData(const MifareUUID& tag_uuid,
55 bool is_second_attempt = false); 74 std::span<const MifareWriteChunk> write_request);
56 75
57 DriverResult SendStopPollingRequest(MCUCommandResponse& output); 76 Common::Input::DriverResult SendStartPollingRequest(MCUCommandResponse& output,
77 bool is_second_attempt = false);
58 78
59 DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id); 79 Common::Input::DriverResult SendStopPollingRequest(MCUCommandResponse& output);
60 80
61 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); 81 Common::Input::DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
62 82
63 DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid); 83 Common::Input::DriverResult SendReadAmiiboRequest(MCUCommandResponse& output,
84 NFCPages ntag_pages);
64 85
65 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 86 Common::Input::DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output,
66 bool is_last_packet, std::span<const u8> data); 87 const TagUUID& tag_uuid);
88
89 Common::Input::DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
90 bool is_last_packet,
91 std::span<const u8> data);
92
93 Common::Input::DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
94 bool is_last_packet,
95 std::span<const u8> data);
67 96
68 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; 97 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
69 98
99 std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const;
100
101 std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const;
102
70 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; 103 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
71 104
72 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; 105 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
73 106
107 MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid,
108 std::span<const MifareReadChunk> read_request) const;
109
110 MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid,
111 std::span<const MifareWriteChunk> read_request) const;
112
74 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 113 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
75 114
76 TagUUID GetTagUUID(std::span<const u8> data) const; 115 TagUUID GetTagUUID(std::span<const u8> data) const;
77 116
78 bool is_enabled{}; 117 bool is_enabled{};
118 bool is_polling{};
79 std::size_t update_counter{}; 119 std::size_t update_counter{};
80}; 120};
81 121
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
index dca797f7a..1aab9e12a 100644
--- a/src/input_common/helpers/joycon_protocol/poller.cpp
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {
70 callbacks.on_color_data(color); 70 callbacks.on_color_data(color);
71} 71}
72 72
73void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { 73void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) {
74 callbacks.on_amiibo_data(amiibo_data); 74 callbacks.on_amiibo_data(tag_info);
75} 75}
76 76
77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { 77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
index 0fa72c6db..3746abe5d 100644
--- a/src/input_common/helpers/joycon_protocol/poller.h
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -36,8 +36,8 @@ public:
36 36
37 void UpdateColor(const Color& color); 37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status); 38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data); 39 void UpdateAmiibo(const Joycon::TagInfo& tag_info);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); 40 void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);
41 41
42private: 42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input, 43 void UpdateActiveLeftPadInput(const InputReportActive& input,
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
index 190cef812..96414fb67 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.cpp
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/ringcon.h" 6#include "input_common/helpers/joycon_protocol/ringcon.h"
6 7
@@ -9,18 +10,18 @@ namespace InputCommon::Joycon {
9RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle) 10RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
11 12
12DriverResult RingConProtocol::EnableRingCon() { 13Common::Input::DriverResult RingConProtocol::EnableRingCon() {
13 LOG_DEBUG(Input, "Enable Ringcon"); 14 LOG_DEBUG(Input, "Enable Ringcon");
14 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
15 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
16 17
17 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
18 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); 19 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
19 } 20 }
20 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
21 result = EnableMCU(true); 22 result = EnableMCU(true);
22 } 23 }
23 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
24 const MCUConfig config{ 25 const MCUConfig config{
25 .command = MCUCommand::ConfigureMCU, 26 .command = MCUCommand::ConfigureMCU,
26 .sub_command = MCUSubCommand::SetDeviceMode, 27 .sub_command = MCUSubCommand::SetDeviceMode,
@@ -33,12 +34,12 @@ DriverResult RingConProtocol::EnableRingCon() {
33 return result; 34 return result;
34} 35}
35 36
36DriverResult RingConProtocol::DisableRingCon() { 37Common::Input::DriverResult RingConProtocol::DisableRingCon() {
37 LOG_DEBUG(Input, "Disable RingCon"); 38 LOG_DEBUG(Input, "Disable RingCon");
38 ScopedSetBlocking sb(this); 39 ScopedSetBlocking sb(this);
39 DriverResult result{DriverResult::Success}; 40 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
40 41
41 if (result == DriverResult::Success) { 42 if (result == Common::Input::DriverResult::Success) {
42 result = EnableMCU(false); 43 result = EnableMCU(false);
43 } 44 }
44 45
@@ -47,29 +48,29 @@ DriverResult RingConProtocol::DisableRingCon() {
47 return result; 48 return result;
48} 49}
49 50
50DriverResult RingConProtocol::StartRingconPolling() { 51Common::Input::DriverResult RingConProtocol::StartRingconPolling() {
51 LOG_DEBUG(Input, "Enable Ringcon"); 52 LOG_DEBUG(Input, "Enable Ringcon");
52 ScopedSetBlocking sb(this); 53 ScopedSetBlocking sb(this);
53 DriverResult result{DriverResult::Success}; 54 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
54 bool is_connected = false; 55 bool is_connected = false;
55 56
56 if (result == DriverResult::Success) { 57 if (result == Common::Input::DriverResult::Success) {
57 result = IsRingConnected(is_connected); 58 result = IsRingConnected(is_connected);
58 } 59 }
59 if (result == DriverResult::Success && is_connected) { 60 if (result == Common::Input::DriverResult::Success && is_connected) {
60 LOG_INFO(Input, "Ringcon detected"); 61 LOG_INFO(Input, "Ringcon detected");
61 result = ConfigureRing(); 62 result = ConfigureRing();
62 } 63 }
63 if (result == DriverResult::Success) { 64 if (result == Common::Input::DriverResult::Success) {
64 is_enabled = true; 65 is_enabled = true;
65 } 66 }
66 67
67 return result; 68 return result;
68} 69}
69 70
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { 71Common::Input::DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected"); 72 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28; 73 constexpr std::size_t max_tries = 42;
73 SubCommandResponse output{}; 74 SubCommandResponse output{};
74 std::size_t tries = 0; 75 std::size_t tries = 0;
75 is_connected = false; 76 is_connected = false;
@@ -77,20 +78,21 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
77 do { 78 do {
78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output); 79 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
79 80
80 if (result != DriverResult::Success) { 81 if (result != Common::Input::DriverResult::Success &&
82 result != Common::Input::DriverResult::Timeout) {
81 return result; 83 return result;
82 } 84 }
83 85
84 if (tries++ >= max_tries) { 86 if (tries++ >= max_tries) {
85 return DriverResult::NoDeviceDetected; 87 return Common::Input::DriverResult::NoDeviceDetected;
86 } 88 }
87 } while (output.external_device_id != ExternalDeviceId::RingController); 89 } while (output.external_device_id != ExternalDeviceId::RingController);
88 90
89 is_connected = true; 91 is_connected = true;
90 return DriverResult::Success; 92 return Common::Input::DriverResult::Success;
91} 93}
92 94
93DriverResult RingConProtocol::ConfigureRing() { 95Common::Input::DriverResult RingConProtocol::ConfigureRing() {
94 LOG_DEBUG(Input, "ConfigureRing"); 96 LOG_DEBUG(Input, "ConfigureRing");
95 97
96 static constexpr std::array<u8, 37> ring_config{ 98 static constexpr std::array<u8, 37> ring_config{
@@ -98,9 +100,10 @@ DriverResult RingConProtocol::ConfigureRing() {
98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 100 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}; 101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
100 102
101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config); 103 const Common::Input::DriverResult result =
104 SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
102 105
103 if (result != DriverResult::Success) { 106 if (result != Common::Input::DriverResult::Success) {
104 return result; 107 return result;
105 } 108 }
106 109
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h
index 6e858f3fc..9f0888de3 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.h
+++ b/src/input_common/helpers/joycon_protocol/ringcon.h
@@ -13,24 +13,28 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class RingConProtocol final : private JoyconCommonProtocol { 22class RingConProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableRingCon(); 26 Common::Input::DriverResult EnableRingCon();
23 27
24 DriverResult DisableRingCon(); 28 Common::Input::DriverResult DisableRingCon();
25 29
26 DriverResult StartRingconPolling(); 30 Common::Input::DriverResult StartRingconPolling();
27 31
28 bool IsEnabled() const; 32 bool IsEnabled() const;
29 33
30private: 34private:
31 DriverResult IsRingConnected(bool& is_connected); 35 Common::Input::DriverResult IsRingConnected(bool& is_connected);
32 36
33 DriverResult ConfigureRing(); 37 Common::Input::DriverResult ConfigureRing();
34 38
35 bool is_enabled{}; 39 bool is_enabled{};
36}; 40};
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
index 63b60c946..7647f505e 100644
--- a/src/input_common/helpers/joycon_protocol/rumble.cpp
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5#include <cmath> 5#include <cmath>
6 6
7#include "common/input.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "input_common/helpers/joycon_protocol/rumble.h" 9#include "input_common/helpers/joycon_protocol/rumble.h"
9 10
@@ -12,14 +13,14 @@ namespace InputCommon::Joycon {
12RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) 13RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
13 : JoyconCommonProtocol(std::move(handle)) {} 14 : JoyconCommonProtocol(std::move(handle)) {}
14 15
15DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { 16Common::Input::DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
16 LOG_DEBUG(Input, "Enable Rumble"); 17 LOG_DEBUG(Input, "Enable Rumble");
17 ScopedSetBlocking sb(this); 18 ScopedSetBlocking sb(this);
18 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; 19 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
19 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer); 20 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
20} 21}
21 22
22DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { 23Common::Input::DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
23 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{}; 24 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
24 25
25 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { 26 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h
index 6c12b7925..5e50e531a 100644
--- a/src/input_common/helpers/joycon_protocol/rumble.h
+++ b/src/input_common/helpers/joycon_protocol/rumble.h
@@ -13,15 +13,19 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class RumbleProtocol final : private JoyconCommonProtocol { 22class RumbleProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableRumble(bool is_enabled); 26 Common::Input::DriverResult EnableRumble(bool is_enabled);
23 27
24 DriverResult SendVibration(const VibrationValue& vibration); 28 Common::Input::DriverResult SendVibration(const VibrationValue& vibration);
25 29
26private: 30private:
27 u16 EncodeHighFrequency(f32 frequency) const; 31 u16 EncodeHighFrequency(f32 frequency) const;
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 50b5a3dc8..c2d0cbb34 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -143,12 +143,46 @@ public:
143 return Common::Input::NfcState::NotSupported; 143 return Common::Input::NfcState::NotSupported;
144 } 144 }
145 145
146 // Start scanning for nfc tags
147 virtual Common::Input::NfcState StartNfcPolling(
148 [[maybe_unused]] const PadIdentifier& identifier_) {
149 return Common::Input::NfcState::NotSupported;
150 }
151
152 // Start scanning for nfc tags
153 virtual Common::Input::NfcState StopNfcPolling(
154 [[maybe_unused]] const PadIdentifier& identifier_) {
155 return Common::Input::NfcState::NotSupported;
156 }
157
158 // Reads data from amiibo tag
159 virtual Common::Input::NfcState ReadAmiiboData(
160 [[maybe_unused]] const PadIdentifier& identifier_,
161 [[maybe_unused]] std::vector<u8>& out_data) {
162 return Common::Input::NfcState::NotSupported;
163 }
164
146 // Writes data to an nfc tag 165 // Writes data to an nfc tag
147 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, 166 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
148 [[maybe_unused]] const std::vector<u8>& data) { 167 [[maybe_unused]] const std::vector<u8>& data) {
149 return Common::Input::NfcState::NotSupported; 168 return Common::Input::NfcState::NotSupported;
150 } 169 }
151 170
171 // Reads data from mifare tag
172 virtual Common::Input::NfcState ReadMifareData(
173 [[maybe_unused]] const PadIdentifier& identifier_,
174 [[maybe_unused]] const Common::Input::MifareRequest& request,
175 [[maybe_unused]] Common::Input::MifareRequest& out_data) {
176 return Common::Input::NfcState::NotSupported;
177 }
178
179 // Write data to mifare tag
180 virtual Common::Input::NfcState WriteMifareData(
181 [[maybe_unused]] const PadIdentifier& identifier_,
182 [[maybe_unused]] const Common::Input::MifareRequest& request) {
183 return Common::Input::NfcState::NotSupported;
184 }
185
152 // Returns the engine name 186 // Returns the engine name
153 [[nodiscard]] const std::string& GetEngineName() const; 187 [[nodiscard]] const std::string& GetEngineName() const;
154 188
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 380a01542..870e76ab0 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -792,8 +792,7 @@ public:
792 792
793 const Common::Input::CallbackStatus status{ 793 const Common::Input::CallbackStatus status{
794 .type = Common::Input::InputType::Nfc, 794 .type = Common::Input::InputType::Nfc,
795 .nfc_status = nfc_status.state, 795 .nfc_status = nfc_status,
796 .raw_data = nfc_status.data,
797 }; 796 };
798 797
799 TriggerOnChange(status); 798 TriggerOnChange(status);
@@ -836,10 +835,31 @@ public:
836 return input_engine->SupportsNfc(identifier); 835 return input_engine->SupportsNfc(identifier);
837 } 836 }
838 837
838 Common::Input::NfcState StartNfcPolling() {
839 return input_engine->StartNfcPolling(identifier);
840 }
841
842 Common::Input::NfcState StopNfcPolling() {
843 return input_engine->StopNfcPolling(identifier);
844 }
845
846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) {
847 return input_engine->ReadAmiiboData(identifier, out_data);
848 }
849
839 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override { 850 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
840 return input_engine->WriteNfcData(identifier, data); 851 return input_engine->WriteNfcData(identifier, data);
841 } 852 }
842 853
854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
855 Common::Input::MifareRequest& out_data) {
856 return input_engine->ReadMifareData(identifier, request, out_data);
857 }
858
859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) {
860 return input_engine->WriteMifareData(identifier, request);
861 }
862
843private: 863private:
844 const PadIdentifier identifier; 864 const PadIdentifier identifier;
845 InputEngine* input_engine; 865 InputEngine* input_engine;
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index fd4a61a4d..b795c0179 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -461,7 +461,7 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
461 header += fmt::format("R{},", index); 461 header += fmt::format("R{},", index);
462 } 462 }
463 if (program.local_memory_size > 0) { 463 if (program.local_memory_size > 0) {
464 header += fmt::format("lmem[{}],", program.local_memory_size); 464 header += fmt::format("lmem[{}],", Common::DivCeil(program.local_memory_size, 4U));
465 } 465 }
466 if (program.info.uses_fswzadd) { 466 if (program.info.uses_fswzadd) {
467 header += "FSWZA[4],FSWZB[4],"; 467 header += "FSWZA[4],FSWZB[4],";
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index c3c2281bb..9ff4028c2 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -479,7 +479,7 @@ void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
479 const u32 remainder{4 - element}; 479 const u32 remainder{4 - element};
480 const TransformFeedbackVarying* xfb_varying{}; 480 const TransformFeedbackVarying* xfb_varying{};
481 const size_t xfb_varying_index{base_index + element}; 481 const size_t xfb_varying_index{base_index + element};
482 if (xfb_varying_index < runtime_info.xfb_varyings.size()) { 482 if (xfb_varying_index < runtime_info.xfb_count) {
483 xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index]; 483 xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index];
484 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; 484 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
485 } 485 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 0f86a8004..34592a01f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -387,7 +387,7 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr
387} 387}
388 388
389void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) { 389void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) {
390 if (ctx.runtime_info.xfb_varyings.empty()) { 390 if (ctx.runtime_info.xfb_count == 0) {
391 return; 391 return;
392 } 392 }
393 ctx.AddCapability(spv::Capability::TransformFeedback); 393 ctx.AddCapability(spv::Capability::TransformFeedback);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index fd15f47ea..bec5db173 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -160,7 +160,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
160 const u32 remainder{4 - element}; 160 const u32 remainder{4 - element};
161 const TransformFeedbackVarying* xfb_varying{}; 161 const TransformFeedbackVarying* xfb_varying{};
162 const size_t xfb_varying_index{base_attr_index + element}; 162 const size_t xfb_varying_index{base_attr_index + element};
163 if (xfb_varying_index < ctx.runtime_info.xfb_varyings.size()) { 163 if (xfb_varying_index < ctx.runtime_info.xfb_count) {
164 xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index]; 164 xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index];
165 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; 165 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
166 } 166 }
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 5a4195217..70292686f 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -424,6 +424,10 @@ void VisitUsages(Info& info, IR::Inst& inst) {
424 info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2; 424 info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2;
425 info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4; 425 info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4;
426 break; 426 break;
427 case IR::Opcode::LoadLocal:
428 case IR::Opcode::WriteLocal:
429 info.uses_local_memory = true;
430 break;
427 default: 431 default:
428 break; 432 break;
429 } 433 }
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index 3b63c249f..619c0b138 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -84,7 +84,8 @@ struct RuntimeInfo {
84 bool glasm_use_storage_buffers{}; 84 bool glasm_use_storage_buffers{};
85 85
86 /// Transform feedback state for each varying 86 /// Transform feedback state for each varying
87 std::vector<TransformFeedbackVarying> xfb_varyings; 87 std::array<TransformFeedbackVarying, 256> xfb_varyings{};
88 u32 xfb_count{0};
88}; 89};
89 90
90} // namespace Shader 91} // namespace Shader
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index d308db942..b4b4afd37 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -172,6 +172,7 @@ struct Info {
172 bool stores_indexed_attributes{}; 172 bool stores_indexed_attributes{};
173 173
174 bool stores_global_memory{}; 174 bool stores_global_memory{};
175 bool uses_local_memory{};
175 176
176 bool uses_fp16{}; 177 bool uses_fp16{};
177 bool uses_fp64{}; 178 bool uses_fp64{};
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index bf6439530..3b2fe01da 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -220,8 +220,8 @@ add_library(video_core STATIC
220 surface.h 220 surface.h
221 texture_cache/accelerated_swizzle.cpp 221 texture_cache/accelerated_swizzle.cpp
222 texture_cache/accelerated_swizzle.h 222 texture_cache/accelerated_swizzle.h
223 texture_cache/decode_bc4.cpp 223 texture_cache/decode_bc.cpp
224 texture_cache/decode_bc4.h 224 texture_cache/decode_bc.h
225 texture_cache/descriptor_table.h 225 texture_cache/descriptor_table.h
226 texture_cache/formatter.cpp 226 texture_cache/formatter.cpp
227 texture_cache/formatter.h 227 texture_cache/formatter.h
@@ -279,7 +279,7 @@ add_library(video_core STATIC
279create_target_directory_groups(video_core) 279create_target_directory_groups(video_core)
280 280
281target_link_libraries(video_core PUBLIC common core) 281target_link_libraries(video_core PUBLIC common core)
282target_link_libraries(video_core PUBLIC glad shader_recompiler stb) 282target_link_libraries(video_core PUBLIC glad shader_recompiler stb bc_decoder)
283 283
284if (YUZU_USE_BUNDLED_FFMPEG AND NOT (WIN32 OR ANDROID)) 284if (YUZU_USE_BUNDLED_FFMPEG AND NOT (WIN32 OR ANDROID))
285 add_dependencies(video_core ffmpeg-build) 285 add_dependencies(video_core ffmpeg-build)
@@ -291,7 +291,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
291 291
292add_dependencies(video_core host_shaders) 292add_dependencies(video_core host_shaders)
293target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) 293target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
294target_link_libraries(video_core PRIVATE sirit Vulkan::Headers) 294target_link_libraries(video_core PRIVATE sirit Vulkan::Headers vma)
295 295
296if (ENABLE_NSIGHT_AFTERMATH) 296if (ENABLE_NSIGHT_AFTERMATH)
297 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) 297 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 45977d578..58a45ab67 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -207,7 +207,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
207 if (has_new_downloads) { 207 if (has_new_downloads) {
208 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); 208 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
209 } 209 }
210 tmp_buffer.resize(amount); 210 tmp_buffer.resize_destructive(amount);
211 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); 211 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
212 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); 212 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
213 return true; 213 return true;
@@ -1279,7 +1279,7 @@ template <class P>
1279typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr, 1279typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr,
1280 u32 wanted_size) { 1280 u32 wanted_size) {
1281 static constexpr int STREAM_LEAP_THRESHOLD = 16; 1281 static constexpr int STREAM_LEAP_THRESHOLD = 16;
1282 std::vector<BufferId> overlap_ids; 1282 boost::container::small_vector<BufferId, 16> overlap_ids;
1283 VAddr begin = cpu_addr; 1283 VAddr begin = cpu_addr;
1284 VAddr end = cpu_addr + wanted_size; 1284 VAddr end = cpu_addr + wanted_size;
1285 int stream_score = 0; 1285 int stream_score = 0;
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 63a120f7a..fe6068cfe 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -229,7 +229,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
229 using OverlapCounter = boost::icl::split_interval_map<VAddr, int>; 229 using OverlapCounter = boost::icl::split_interval_map<VAddr, int>;
230 230
231 struct OverlapResult { 231 struct OverlapResult {
232 std::vector<BufferId> ids; 232 boost::container::small_vector<BufferId, 16> ids;
233 VAddr begin; 233 VAddr begin;
234 VAddr end; 234 VAddr end;
235 bool has_stream_leap = false; 235 bool has_stream_leap = false;
@@ -582,7 +582,7 @@ private:
582 BufferId inline_buffer_id; 582 BufferId inline_buffer_id;
583 583
584 std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table; 584 std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
585 std::vector<u8> tmp_buffer; 585 Common::ScratchBuffer<u8> tmp_buffer;
586}; 586};
587 587
588} // namespace VideoCommon 588} // namespace VideoCommon
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index 83112dfce..7d660af47 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -63,7 +63,6 @@ struct ChCommand {
63}; 63};
64 64
65using ChCommandHeaderList = std::vector<ChCommandHeader>; 65using ChCommandHeaderList = std::vector<ChCommandHeader>;
66using ChCommandList = std::vector<ChCommand>;
67 66
68struct ThiRegisters { 67struct ThiRegisters {
69 u32_le increment_syncpt{}; 68 u32_le increment_syncpt{};
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 1cdb690ed..8a2784cdc 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9#include <queue> 10#include <queue>
10 11
11#include "common/bit_field.h" 12#include "common/bit_field.h"
@@ -102,11 +103,12 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
102struct CommandList final { 103struct CommandList final {
103 CommandList() = default; 104 CommandList() = default;
104 explicit CommandList(std::size_t size) : command_lists(size) {} 105 explicit CommandList(std::size_t size) : command_lists(size) {}
105 explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_) 106 explicit CommandList(
107 boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
106 : prefetch_command_list{std::move(prefetch_command_list_)} {} 108 : prefetch_command_list{std::move(prefetch_command_list_)} {}
107 109
108 std::vector<CommandListHeader> command_lists; 110 boost::container::small_vector<CommandListHeader, 512> command_lists;
109 std::vector<CommandHeader> prefetch_command_list; 111 boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
110}; 112};
111 113
112/** 114/**
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ebe5536de..a290d6ea7 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -108,9 +108,11 @@ void MaxwellDMA::Launch() {
108 if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { 108 if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
109 ASSERT(regs.remap_const.component_size_minus_one == 3); 109 ASSERT(regs.remap_const.component_size_minus_one == 3);
110 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); 110 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
111 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); 111 read_buffer.resize_destructive(regs.line_length_in * sizeof(u32));
112 std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in);
113 std::ranges::fill(span, regs.remap_consta_value);
112 memory_manager.WriteBlockUnsafe(regs.offset_out, 114 memory_manager.WriteBlockUnsafe(regs.offset_out,
113 reinterpret_cast<u8*>(tmp_buffer.data()), 115 reinterpret_cast<u8*>(read_buffer.data()),
114 regs.line_length_in * sizeof(u32)); 116 regs.line_length_in * sizeof(u32));
115 } else { 117 } else {
116 memory_manager.FlushCaching(); 118 memory_manager.FlushCaching();
@@ -126,32 +128,33 @@ void MaxwellDMA::Launch() {
126 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 128 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
127 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 129 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
128 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 130 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
129 std::vector<u8> tmp_buffer(16); 131 read_buffer.resize_destructive(16);
130 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 132 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
131 memory_manager.ReadBlockUnsafe( 133 memory_manager.ReadBlock(
132 convert_linear_2_blocklinear_addr(regs.offset_in + offset), 134 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
133 tmp_buffer.data(), tmp_buffer.size()); 135 read_buffer.data(), read_buffer.size());
134 memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(), 136 memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(),
135 tmp_buffer.size()); 137 read_buffer.size());
136 } 138 }
137 } else if (is_src_pitch && !is_dst_pitch) { 139 } else if (is_src_pitch && !is_dst_pitch) {
138 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 140 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
139 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 141 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
140 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 142 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
141 std::vector<u8> tmp_buffer(16); 143 read_buffer.resize_destructive(16);
142 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 144 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
143 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), 145 memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(),
144 tmp_buffer.size()); 146 read_buffer.size());
145 memory_manager.WriteBlockCached( 147 memory_manager.WriteBlockCached(
146 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 148 convert_linear_2_blocklinear_addr(regs.offset_out + offset),
147 tmp_buffer.data(), tmp_buffer.size()); 149 read_buffer.data(), read_buffer.size());
148 } 150 }
149 } else { 151 } else {
150 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { 152 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
151 std::vector<u8> tmp_buffer(regs.line_length_in); 153 read_buffer.resize_destructive(regs.line_length_in);
152 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 154 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(),
153 regs.line_length_in); 155 regs.line_length_in,
154 memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(), 156 VideoCommon::CacheType::NoBufferCache);
157 memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
155 regs.line_length_in); 158 regs.line_length_in);
156 } 159 }
157 } 160 }
@@ -171,7 +174,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
171 src_operand.address = regs.offset_in; 174 src_operand.address = regs.offset_in;
172 175
173 DMA::BufferOperand dst_operand; 176 DMA::BufferOperand dst_operand;
174 dst_operand.pitch = regs.pitch_out; 177 u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out));
178 dst_operand.pitch = abs_pitch_out;
175 dst_operand.width = regs.line_length_in; 179 dst_operand.width = regs.line_length_in;
176 dst_operand.height = regs.line_count; 180 dst_operand.height = regs.line_count;
177 dst_operand.address = regs.offset_out; 181 dst_operand.address = regs.offset_out;
@@ -218,7 +222,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
218 const size_t src_size = 222 const size_t src_size =
219 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 223 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
220 224
221 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 225 const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count;
222 read_buffer.resize_destructive(src_size); 226 read_buffer.resize_destructive(src_size);
223 write_buffer.resize_destructive(dst_size); 227 write_buffer.resize_destructive(dst_size);
224 228
@@ -227,7 +231,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
227 231
228 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 232 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
229 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 233 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
230 regs.pitch_out); 234 abs_pitch_out);
231 235
232 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); 236 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
233} 237}
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 456f733cf..db385076d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -193,18 +193,13 @@ struct GPU::Impl {
193 } 193 }
194 194
195 [[nodiscard]] u64 GetTicks() const { 195 [[nodiscard]] u64 GetTicks() const {
196 // This values were reversed engineered by fincs from NVN 196 u64 gpu_tick = system.CoreTiming().GetGPUTicks();
197 // The gpu clock is reported in units of 385/625 nanoseconds
198 constexpr u64 gpu_ticks_num = 384;
199 constexpr u64 gpu_ticks_den = 625;
200 197
201 u64 nanoseconds = system.CoreTiming().GetCPUTimeNs().count();
202 if (Settings::values.use_fast_gpu_time.GetValue()) { 198 if (Settings::values.use_fast_gpu_time.GetValue()) {
203 nanoseconds /= 256; 199 gpu_tick /= 256;
204 } 200 }
205 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; 201
206 const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; 202 return gpu_tick;
207 return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den;
208 } 203 }
209 204
210 [[nodiscard]] bool IsAsync() const { 205 [[nodiscard]] bool IsAsync() const {
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index 6ce179167..ce827eb6c 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -4,6 +4,7 @@
4#include <array> 4#include <array>
5#include <bit> 5#include <bit>
6 6
7#include "common/scratch_buffer.h"
7#include "common/settings.h" 8#include "common/settings.h"
8#include "video_core/host1x/codecs/h264.h" 9#include "video_core/host1x/codecs/h264.h"
9#include "video_core/host1x/host1x.h" 10#include "video_core/host1x/host1x.h"
@@ -188,7 +189,8 @@ void H264BitWriter::WriteBit(bool state) {
188} 189}
189 190
190void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { 191void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
191 std::vector<u8> scan(count); 192 static Common::ScratchBuffer<u8> scan{};
193 scan.resize_destructive(count);
192 if (count == 16) { 194 if (count == 16) {
193 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); 195 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
194 } else { 196 } else {
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 2442c3c29..e61d9af80 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -33,6 +33,7 @@ set(SHADER_FILES
33 opengl_fidelityfx_fsr.frag 33 opengl_fidelityfx_fsr.frag
34 opengl_fidelityfx_fsr_easu.frag 34 opengl_fidelityfx_fsr_easu.frag
35 opengl_fidelityfx_fsr_rcas.frag 35 opengl_fidelityfx_fsr_rcas.frag
36 opengl_lmem_warmup.comp
36 opengl_present.frag 37 opengl_present.frag
37 opengl_present.vert 38 opengl_present.vert
38 opengl_present_scaleforce.frag 39 opengl_present_scaleforce.frag
diff --git a/src/video_core/host_shaders/opengl_lmem_warmup.comp b/src/video_core/host_shaders/opengl_lmem_warmup.comp
new file mode 100644
index 000000000..518268477
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_lmem_warmup.comp
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// This shader is a workaround for a quirk in NVIDIA OpenGL drivers
5// Shaders using local memory see a great performance benefit if a shader that was dispatched
6// before it had more local memory allocated.
7// This shader allocates the maximum local memory allowed on NVIDIA drivers to ensure that
8// subsequent shaders see the performance boost.
9
10// NOTE: This shader does no actual meaningful work and returns immediately,
11// it is simply a means to have the driver expect a shader using lots of local memory.
12
13#version 450
14
15layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
16
17layout(location = 0) uniform uint uniform_data;
18
19layout(binding = 0, rgba8) uniform writeonly restrict image2DArray dest_image;
20
21#define MAX_LMEM_SIZE 4080 // Size chosen to avoid errors in Nvidia's GLSL compiler
22#define NUM_LMEM_CONSTANTS 1
23#define ARRAY_SIZE MAX_LMEM_SIZE - NUM_LMEM_CONSTANTS
24
25uint lmem_0[ARRAY_SIZE];
26const uvec4 constant_values[NUM_LMEM_CONSTANTS] = uvec4[](uvec4(0));
27
28void main() {
29 const uint global_id = gl_GlobalInvocationID.x;
30 if (global_id <= 128) {
31 // Since the shader is called with a dispatch of 1x1x1
32 // This should always be the case, and this shader will not actually execute
33 return;
34 }
35 for (uint t = 0; t < uniform_data; t++) {
36 const uint offset = (t * uniform_data);
37 lmem_0[offset] = t;
38 }
39 const uint offset = (gl_GlobalInvocationID.y * uniform_data + gl_GlobalInvocationID.x);
40 const uint value = lmem_0[offset];
41 const uint const_value = constant_values[offset / 4][offset % 4];
42 const uvec4 color = uvec4(value + const_value);
43
44 // A "side-effect" is needed so the variables don't get optimized out,
45 // but this should never execute so there should be no clobbering of previously bound state.
46 imageStore(dest_image, ivec3(gl_GlobalInvocationID), color);
47}
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 7b2cde7a7..45141e488 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -111,7 +111,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
111 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); 111 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
112 SetEntry<false>(current_gpu_addr, entry_type); 112 SetEntry<false>(current_gpu_addr, entry_type);
113 if (current_entry_type != entry_type) { 113 if (current_entry_type != entry_type) {
114 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); 114 rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, page_size);
115 } 115 }
116 if constexpr (entry_type == EntryType::Mapped) { 116 if constexpr (entry_type == EntryType::Mapped) {
117 const VAddr current_cpu_addr = cpu_addr + offset; 117 const VAddr current_cpu_addr = cpu_addr + offset;
@@ -134,7 +134,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
134 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); 134 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
135 SetEntry<true>(current_gpu_addr, entry_type); 135 SetEntry<true>(current_gpu_addr, entry_type);
136 if (current_entry_type != entry_type) { 136 if (current_entry_type != entry_type) {
137 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); 137 rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, big_page_size);
138 } 138 }
139 if constexpr (entry_type == EntryType::Mapped) { 139 if constexpr (entry_type == EntryType::Mapped) {
140 const VAddr current_cpu_addr = cpu_addr + offset; 140 const VAddr current_cpu_addr = cpu_addr + offset;
@@ -587,7 +587,7 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
587 587
588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, 588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
589 VideoCommon::CacheType which) { 589 VideoCommon::CacheType which) {
590 std::vector<u8> tmp_buffer(size); 590 tmp_buffer.resize_destructive(size);
591 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); 591 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
592 592
593 // The output block must be flushed in case it has data modified from the GPU. 593 // The output block must be flushed in case it has data modified from the GPU.
@@ -670,9 +670,9 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
670 return result; 670 return result;
671} 671}
672 672
673std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( 673boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32>
674 GPUVAddr gpu_addr, std::size_t size) const { 674MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const {
675 std::vector<std::pair<GPUVAddr, std::size_t>> result{}; 675 boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> result{};
676 GetSubmappedRangeImpl<true>(gpu_addr, size, result); 676 GetSubmappedRangeImpl<true>(gpu_addr, size, result);
677 return result; 677 return result;
678} 678}
@@ -680,8 +680,9 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
680template <bool is_gpu_address> 680template <bool is_gpu_address>
681void MemoryManager::GetSubmappedRangeImpl( 681void MemoryManager::GetSubmappedRangeImpl(
682 GPUVAddr gpu_addr, std::size_t size, 682 GPUVAddr gpu_addr, std::size_t size,
683 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& 683 boost::container::small_vector<
684 result) const { 684 std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result)
685 const {
685 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>> 686 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>
686 last_segment{}; 687 last_segment{};
687 std::optional<VAddr> old_page_addr{}; 688 std::optional<VAddr> old_page_addr{};
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 794535122..4202c26ff 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -8,10 +8,12 @@
8#include <mutex> 8#include <mutex>
9#include <optional> 9#include <optional>
10#include <vector> 10#include <vector>
11#include <boost/container/small_vector.hpp>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/multi_level_page_table.h" 14#include "common/multi_level_page_table.h"
14#include "common/range_map.h" 15#include "common/range_map.h"
16#include "common/scratch_buffer.h"
15#include "common/virtual_buffer.h" 17#include "common/virtual_buffer.h"
16#include "video_core/cache_types.h" 18#include "video_core/cache_types.h"
17#include "video_core/pte_kind.h" 19#include "video_core/pte_kind.h"
@@ -107,8 +109,8 @@ public:
107 * if the region is continuous, a single pair will be returned. If it's unmapped, an empty 109 * if the region is continuous, a single pair will be returned. If it's unmapped, an empty
108 * vector will be returned; 110 * vector will be returned;
109 */ 111 */
110 std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, 112 boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> GetSubmappedRange(
111 std::size_t size) const; 113 GPUVAddr gpu_addr, std::size_t size) const;
112 114
113 GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, 115 GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size,
114 PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); 116 PTEKind kind = PTEKind::INVALID, bool is_big_pages = true);
@@ -165,7 +167,8 @@ private:
165 template <bool is_gpu_address> 167 template <bool is_gpu_address>
166 void GetSubmappedRangeImpl( 168 void GetSubmappedRangeImpl(
167 GPUVAddr gpu_addr, std::size_t size, 169 GPUVAddr gpu_addr, std::size_t size,
168 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& 170 boost::container::small_vector<
171 std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>&
169 result) const; 172 result) const;
170 173
171 Core::System& system; 174 Core::System& system;
@@ -215,8 +218,8 @@ private:
215 Common::VirtualBuffer<u32> big_page_table_cpu; 218 Common::VirtualBuffer<u32> big_page_table_cpu;
216 219
217 std::vector<u64> big_page_continuous; 220 std::vector<u64> big_page_continuous;
218 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 221 boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash{};
219 std::vector<std::pair<VAddr, std::size_t>> page_stash2{}; 222 boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash2{};
220 223
221 mutable std::mutex guard; 224 mutable std::mutex guard;
222 225
@@ -226,6 +229,8 @@ private:
226 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator; 229 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
227 230
228 static std::atomic<size_t> unique_identifier_generator; 231 static std::atomic<size_t> unique_identifier_generator;
232
233 Common::ScratchBuffer<u8> tmp_buffer;
229}; 234};
230 235
231} // namespace Tegra 236} // namespace Tegra
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 3151c0db8..f9ca55c36 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -63,6 +63,7 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
63 writes_global_memory = !use_storage_buffers && 63 writes_global_memory = !use_storage_buffers &&
64 std::ranges::any_of(info.storage_buffers_descriptors, 64 std::ranges::any_of(info.storage_buffers_descriptors,
65 [](const auto& desc) { return desc.is_written; }); 65 [](const auto& desc) { return desc.is_written; });
66 uses_local_memory = info.uses_local_memory;
66 if (force_context_flush) { 67 if (force_context_flush) {
67 std::scoped_lock lock{built_mutex}; 68 std::scoped_lock lock{built_mutex};
68 built_fence.Create(); 69 built_fence.Create();
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 9bcc72b59..c26b4fa5e 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -59,6 +59,10 @@ public:
59 return writes_global_memory; 59 return writes_global_memory;
60 } 60 }
61 61
62 [[nodiscard]] bool UsesLocalMemory() const noexcept {
63 return uses_local_memory;
64 }
65
62 void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, 66 void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_,
63 Tegra::MemoryManager* gpu_memory_) { 67 Tegra::MemoryManager* gpu_memory_) {
64 kepler_compute = kepler_compute_; 68 kepler_compute = kepler_compute_;
@@ -84,6 +88,7 @@ private:
84 88
85 bool use_storage_buffers{}; 89 bool use_storage_buffers{};
86 bool writes_global_memory{}; 90 bool writes_global_memory{};
91 bool uses_local_memory{};
87 92
88 std::mutex built_mutex; 93 std::mutex built_mutex;
89 std::condition_variable built_condvar; 94 std::condition_variable built_condvar;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 03d234f2f..33e63c17d 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -194,6 +194,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
194 has_bool_ref_bug = true; 194 has_bool_ref_bug = true;
195 } 195 }
196 } 196 }
197 has_lmem_perf_bug = is_nvidia;
197 198
198 strict_context_required = emu_window.StrictContextRequired(); 199 strict_context_required = emu_window.StrictContextRequired();
199 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. 200 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index ad27264e5..a5a6bbbba 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -192,6 +192,10 @@ public:
192 return supports_conditional_barriers; 192 return supports_conditional_barriers;
193 } 193 }
194 194
195 bool HasLmemPerfBug() const {
196 return has_lmem_perf_bug;
197 }
198
195private: 199private:
196 static bool TestVariableAoffi(); 200 static bool TestVariableAoffi();
197 static bool TestPreciseBug(); 201 static bool TestPreciseBug();
@@ -238,6 +242,7 @@ private:
238 bool can_report_memory{}; 242 bool can_report_memory{};
239 bool strict_context_required{}; 243 bool strict_context_required{};
240 bool supports_conditional_barriers{}; 244 bool supports_conditional_barriers{};
245 bool has_lmem_perf_bug{};
241 246
242 std::string vendor_name; 247 std::string vendor_name;
243}; 248};
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c58f760b8..23a48c6fe 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -215,6 +215,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
215 215
216 writes_global_memory |= std::ranges::any_of( 216 writes_global_memory |= std::ranges::any_of(
217 info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); 217 info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; });
218 uses_local_memory |= info.uses_local_memory;
218 } 219 }
219 ASSERT(num_textures <= MAX_TEXTURES); 220 ASSERT(num_textures <= MAX_TEXTURES);
220 ASSERT(num_images <= MAX_IMAGES); 221 ASSERT(num_images <= MAX_IMAGES);
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 7bab3be0a..7b3d7eae8 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -98,6 +98,10 @@ public:
98 return writes_global_memory; 98 return writes_global_memory;
99 } 99 }
100 100
101 [[nodiscard]] bool UsesLocalMemory() const noexcept {
102 return uses_local_memory;
103 }
104
101 [[nodiscard]] bool IsBuilt() noexcept; 105 [[nodiscard]] bool IsBuilt() noexcept;
102 106
103 template <typename Spec> 107 template <typename Spec>
@@ -146,6 +150,7 @@ private:
146 150
147 bool use_storage_buffers{}; 151 bool use_storage_buffers{};
148 bool writes_global_memory{}; 152 bool writes_global_memory{};
153 bool uses_local_memory{};
149 154
150 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
151 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index fc711c44a..edf527f2d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -222,6 +222,9 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
222 gpu.TickWork(); 222 gpu.TickWork();
223 223
224 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 224 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
225 if (pipeline->UsesLocalMemory()) {
226 program_manager.LocalMemoryWarmup();
227 }
225 pipeline->SetEngine(maxwell3d, gpu_memory); 228 pipeline->SetEngine(maxwell3d, gpu_memory);
226 pipeline->Configure(is_indexed); 229 pipeline->Configure(is_indexed);
227 230
@@ -371,6 +374,9 @@ void RasterizerOpenGL::DispatchCompute() {
371 if (!pipeline) { 374 if (!pipeline) {
372 return; 375 return;
373 } 376 }
377 if (pipeline->UsesLocalMemory()) {
378 program_manager.LocalMemoryWarmup();
379 }
374 pipeline->SetEngine(kepler_compute, gpu_memory); 380 pipeline->SetEngine(kepler_compute, gpu_memory);
375 pipeline->Configure(); 381 pipeline->Configure();
376 const auto& qmd{kepler_compute->launch_description}; 382 const auto& qmd{kepler_compute->launch_description};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 3f077311e..0329ed820 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -85,7 +85,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
85 case Shader::Stage::VertexB: 85 case Shader::Stage::VertexB:
86 case Shader::Stage::Geometry: 86 case Shader::Stage::Geometry:
87 if (!use_assembly_shaders && key.xfb_enabled != 0) { 87 if (!use_assembly_shaders && key.xfb_enabled != 0) {
88 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); 88 auto [varyings, count] = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state);
89 info.xfb_varyings = varyings;
90 info.xfb_count = count;
89 } 91 }
90 break; 92 break;
91 case Shader::Stage::TessellationEval: 93 case Shader::Stage::TessellationEval:
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 98841ae65..03d4b9d06 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -3,7 +3,9 @@
3 3
4#include <glad/glad.h> 4#include <glad/glad.h>
5 5
6#include "video_core/host_shaders/opengl_lmem_warmup_comp.h"
6#include "video_core/renderer_opengl/gl_shader_manager.h" 7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
7 9
8namespace OpenGL { 10namespace OpenGL {
9 11
@@ -17,6 +19,10 @@ ProgramManager::ProgramManager(const Device& device) {
17 if (device.UseAssemblyShaders()) { 19 if (device.UseAssemblyShaders()) {
18 glEnable(GL_COMPUTE_PROGRAM_NV); 20 glEnable(GL_COMPUTE_PROGRAM_NV);
19 } 21 }
22 if (device.HasLmemPerfBug()) {
23 lmem_warmup_program =
24 CreateProgram(HostShaders::OPENGL_LMEM_WARMUP_COMP, GL_COMPUTE_SHADER);
25 }
20} 26}
21 27
22void ProgramManager::BindComputeProgram(GLuint program) { 28void ProgramManager::BindComputeProgram(GLuint program) {
@@ -98,6 +104,13 @@ void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NU
98 104
99void ProgramManager::RestoreGuestCompute() {} 105void ProgramManager::RestoreGuestCompute() {}
100 106
107void ProgramManager::LocalMemoryWarmup() {
108 if (lmem_warmup_program.handle != 0) {
109 BindComputeProgram(lmem_warmup_program.handle);
110 glDispatchCompute(1, 1, 1);
111 }
112}
113
101void ProgramManager::BindPipeline() { 114void ProgramManager::BindPipeline() {
102 if (!is_pipeline_bound) { 115 if (!is_pipeline_bound) {
103 is_pipeline_bound = true; 116 is_pipeline_bound = true;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 07ffab77f..852d8c88e 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -30,6 +30,8 @@ public:
30 30
31 void RestoreGuestCompute(); 31 void RestoreGuestCompute();
32 32
33 void LocalMemoryWarmup();
34
33private: 35private:
34 void BindPipeline(); 36 void BindPipeline();
35 37
@@ -44,6 +46,7 @@ private:
44 u32 current_stage_mask = 0; 46 u32 current_stage_mask = 0;
45 std::array<GLuint, NUM_STAGES> current_programs{}; 47 std::array<GLuint, NUM_STAGES> current_programs{};
46 GLuint current_assembly_compute_program = 0; 48 GLuint current_assembly_compute_program = 0;
49 OGLProgram lmem_warmup_program;
47}; 50};
48 51
49} // namespace OpenGL 52} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index cf2964a3f..28d4b15a0 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -495,6 +495,9 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
495 const Region2D& dst_region, const Region2D& src_region, 495 const Region2D& dst_region, const Region2D& src_region,
496 Tegra::Engines::Fermi2D::Filter filter, 496 Tegra::Engines::Fermi2D::Filter filter,
497 Tegra::Engines::Fermi2D::Operation operation) { 497 Tegra::Engines::Fermi2D::Operation operation) {
498 if (!device.IsExtShaderStencilExportSupported()) {
499 return;
500 }
498 ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point); 501 ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
499 ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy); 502 ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
500 const BlitImagePipelineKey key{ 503 const BlitImagePipelineKey key{
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 9a0b10568..a8540339d 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -259,6 +259,26 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
259 break; 259 break;
260 } 260 }
261 } 261 }
262 // Transcode on hardware that doesn't support BCn natively
263 if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
264 const bool is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
265 if (pixel_format == PixelFormat::BC4_SNORM) {
266 tuple.format = VK_FORMAT_R8_SNORM;
267 } else if (pixel_format == PixelFormat::BC4_UNORM) {
268 tuple.format = VK_FORMAT_R8_UNORM;
269 } else if (pixel_format == PixelFormat::BC5_SNORM) {
270 tuple.format = VK_FORMAT_R8G8_SNORM;
271 } else if (pixel_format == PixelFormat::BC5_UNORM) {
272 tuple.format = VK_FORMAT_R8G8_UNORM;
273 } else if (pixel_format == PixelFormat::BC6H_SFLOAT ||
274 pixel_format == PixelFormat::BC6H_UFLOAT) {
275 tuple.format = VK_FORMAT_R16G16B16A16_SFLOAT;
276 } else if (is_srgb) {
277 tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
278 } else {
279 tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
280 }
281 }
262 const bool attachable = (tuple.usage & Attachable) != 0; 282 const bool attachable = (tuple.usage & Attachable) != 0;
263 const bool storage = (tuple.usage & Storage) != 0; 283 const bool storage = (tuple.usage & Storage) != 0;
264 284
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 77128c6e2..454bb66a4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -12,6 +12,7 @@
12#include <fmt/format.h> 12#include <fmt/format.h>
13 13
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "common/polyfill_ranges.h"
15#include "common/scope_exit.h" 16#include "common/scope_exit.h"
16#include "common/settings.h" 17#include "common/settings.h"
17#include "common/telemetry.h" 18#include "common/telemetry.h"
@@ -65,6 +66,21 @@ std::string BuildCommaSeparatedExtensions(
65 return fmt::format("{}", fmt::join(available_extensions, ",")); 66 return fmt::format("{}", fmt::join(available_extensions, ","));
66} 67}
67 68
69DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) {
70 if (!Settings::values.renderer_debug) {
71 return DebugCallback{};
72 }
73 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
74 const auto it = std::ranges::find_if(*properties, [](const auto& prop) {
75 return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0;
76 });
77 if (it != properties->end()) {
78 return CreateDebugUtilsCallback(instance);
79 } else {
80 return CreateDebugReportCallback(instance);
81 }
82}
83
68} // Anonymous namespace 84} // Anonymous namespace
69 85
70Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 86Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
@@ -87,10 +103,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
87 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), 103 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
88 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 104 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
89 Settings::values.renderer_debug.GetValue())), 105 Settings::values.renderer_debug.GetValue())),
90 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 106 debug_callback(MakeDebugCallback(instance, dld)),
91 surface(CreateSurface(instance, render_window.GetWindowInfo())), 107 surface(CreateSurface(instance, render_window.GetWindowInfo())),
92 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 108 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
93 state_tracker(), scheduler(device, state_tracker), 109 scheduler(device, state_tracker),
94 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 110 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
95 render_window.GetFramebufferLayout().height, false), 111 render_window.GetFramebufferLayout().height, false),
96 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 112 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
@@ -173,7 +189,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
173 return; 189 return;
174 } 190 }
175 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 191 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
176 vk::Image staging_image = device.GetLogical().CreateImage(VkImageCreateInfo{ 192 vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{
177 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 193 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
178 .pNext = nullptr, 194 .pNext = nullptr,
179 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, 195 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
@@ -196,7 +212,6 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
196 .pQueueFamilyIndices = nullptr, 212 .pQueueFamilyIndices = nullptr,
197 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 213 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
198 }); 214 });
199 const auto image_commit = memory_allocator.Commit(staging_image, MemoryUsage::DeviceLocal);
200 215
201 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 216 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
202 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 217 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -234,8 +249,8 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
234 .queueFamilyIndexCount = 0, 249 .queueFamilyIndexCount = 0,
235 .pQueueFamilyIndices = nullptr, 250 .pQueueFamilyIndices = nullptr,
236 }; 251 };
237 const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_buffer_info); 252 const vk::Buffer dst_buffer =
238 MemoryCommit dst_buffer_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download); 253 memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download);
239 254
240 scheduler.RequestOutsideRenderPassOperationContext(); 255 scheduler.RequestOutsideRenderPassOperationContext();
241 scheduler.Record([&](vk::CommandBuffer cmdbuf) { 256 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
@@ -309,8 +324,9 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
309 scheduler.Finish(); 324 scheduler.Finish();
310 325
311 // Copy backing image data to the QImage screenshot buffer 326 // Copy backing image data to the QImage screenshot buffer
312 const auto dst_memory_map = dst_buffer_memory.Map(); 327 dst_buffer.Invalidate();
313 std::memcpy(renderer_settings.screenshot_bits, dst_memory_map.data(), dst_memory_map.size()); 328 std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
329 dst_buffer.Mapped().size());
314 renderer_settings.screenshot_complete_callback(false); 330 renderer_settings.screenshot_complete_callback(false);
315 renderer_settings.screenshot_requested = false; 331 renderer_settings.screenshot_requested = false;
316} 332}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index b2e8cbd1b..ca22c0baa 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -5,6 +5,7 @@
5 5
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <variant>
8 9
9#include "common/dynamic_library.h" 10#include "common/dynamic_library.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
@@ -33,6 +34,8 @@ class GPU;
33 34
34namespace Vulkan { 35namespace Vulkan {
35 36
37using DebugCallback = std::variant<vk::DebugUtilsMessenger, vk::DebugReportCallback>;
38
36Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 39Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
37 VkSurfaceKHR surface); 40 VkSurfaceKHR surface);
38 41
@@ -71,7 +74,7 @@ private:
71 vk::InstanceDispatch dld; 74 vk::InstanceDispatch dld;
72 75
73 vk::Instance instance; 76 vk::Instance instance;
74 vk::DebugUtilsMessenger debug_callback; 77 DebugCallback debug_callback;
75 vk::SurfaceKHR surface; 78 vk::SurfaceKHR surface;
76 79
77 ScreenInfo screen_info; 80 ScreenInfo screen_info;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index acb143fc7..ad3b29f0e 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -162,7 +162,7 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
162 SetUniformData(data, layout); 162 SetUniformData(data, layout);
163 SetVertexData(data, framebuffer, layout); 163 SetVertexData(data, framebuffer, layout);
164 164
165 const std::span<u8> mapped_span = buffer_commit.Map(); 165 const std::span<u8> mapped_span = buffer.Mapped();
166 std::memcpy(mapped_span.data(), &data, sizeof(data)); 166 std::memcpy(mapped_span.data(), &data, sizeof(data));
167 167
168 if (!use_accelerated) { 168 if (!use_accelerated) {
@@ -1071,14 +1071,9 @@ void BlitScreen::ReleaseRawImages() {
1071 scheduler.Wait(tick); 1071 scheduler.Wait(tick);
1072 } 1072 }
1073 raw_images.clear(); 1073 raw_images.clear();
1074 raw_buffer_commits.clear();
1075
1076 aa_image_view.reset(); 1074 aa_image_view.reset();
1077 aa_image.reset(); 1075 aa_image.reset();
1078 aa_commit = MemoryCommit{};
1079
1080 buffer.reset(); 1076 buffer.reset();
1081 buffer_commit = MemoryCommit{};
1082} 1077}
1083 1078
1084void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { 1079void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
@@ -1094,20 +1089,18 @@ void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer
1094 .pQueueFamilyIndices = nullptr, 1089 .pQueueFamilyIndices = nullptr,
1095 }; 1090 };
1096 1091
1097 buffer = device.GetLogical().CreateBuffer(ci); 1092 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
1098 buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload);
1099} 1093}
1100 1094
1101void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { 1095void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1102 raw_images.resize(image_count); 1096 raw_images.resize(image_count);
1103 raw_image_views.resize(image_count); 1097 raw_image_views.resize(image_count);
1104 raw_buffer_commits.resize(image_count);
1105 1098
1106 const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1, 1099 const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1,
1107 u32 down_shift = 0) { 1100 u32 down_shift = 0) {
1108 u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT 1101 u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
1109 : VK_IMAGE_USAGE_TRANSFER_DST_BIT; 1102 : VK_IMAGE_USAGE_TRANSFER_DST_BIT;
1110 return device.GetLogical().CreateImage(VkImageCreateInfo{ 1103 return memory_allocator.CreateImage(VkImageCreateInfo{
1111 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 1104 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1112 .pNext = nullptr, 1105 .pNext = nullptr,
1113 .flags = 0, 1106 .flags = 0,
@@ -1130,9 +1123,6 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1130 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 1123 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1131 }); 1124 });
1132 }; 1125 };
1133 const auto create_commit = [&](vk::Image& image) {
1134 return memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
1135 };
1136 const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) { 1126 const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
1137 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 1127 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
1138 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 1128 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -1161,7 +1151,6 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1161 1151
1162 for (size_t i = 0; i < image_count; ++i) { 1152 for (size_t i = 0; i < image_count; ++i) {
1163 raw_images[i] = create_image(); 1153 raw_images[i] = create_image();
1164 raw_buffer_commits[i] = create_commit(raw_images[i]);
1165 raw_image_views[i] = create_image_view(raw_images[i]); 1154 raw_image_views[i] = create_image_view(raw_images[i]);
1166 } 1155 }
1167 1156
@@ -1169,7 +1158,6 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1169 const u32 up_scale = Settings::values.resolution_info.up_scale; 1158 const u32 up_scale = Settings::values.resolution_info.up_scale;
1170 const u32 down_shift = Settings::values.resolution_info.down_shift; 1159 const u32 down_shift = Settings::values.resolution_info.down_shift;
1171 aa_image = create_image(true, up_scale, down_shift); 1160 aa_image = create_image(true, up_scale, down_shift);
1172 aa_commit = create_commit(aa_image);
1173 aa_image_view = create_image_view(aa_image, true); 1161 aa_image_view = create_image_view(aa_image, true);
1174 VkExtent2D size{ 1162 VkExtent2D size{
1175 .width = (up_scale * framebuffer.width) >> down_shift, 1163 .width = (up_scale * framebuffer.width) >> down_shift,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 68ec20253..8365b5668 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -142,13 +142,11 @@ private:
142 vk::Sampler sampler; 142 vk::Sampler sampler;
143 143
144 vk::Buffer buffer; 144 vk::Buffer buffer;
145 MemoryCommit buffer_commit;
146 145
147 std::vector<u64> resource_ticks; 146 std::vector<u64> resource_ticks;
148 147
149 std::vector<vk::Image> raw_images; 148 std::vector<vk::Image> raw_images;
150 std::vector<vk::ImageView> raw_image_views; 149 std::vector<vk::ImageView> raw_image_views;
151 std::vector<MemoryCommit> raw_buffer_commits;
152 150
153 vk::DescriptorPool aa_descriptor_pool; 151 vk::DescriptorPool aa_descriptor_pool;
154 vk::DescriptorSetLayout aa_descriptor_set_layout; 152 vk::DescriptorSetLayout aa_descriptor_set_layout;
@@ -159,7 +157,6 @@ private:
159 vk::DescriptorSets aa_descriptor_sets; 157 vk::DescriptorSets aa_descriptor_sets;
160 vk::Image aa_image; 158 vk::Image aa_image;
161 vk::ImageView aa_image_view; 159 vk::ImageView aa_image_view;
162 MemoryCommit aa_commit;
163 160
164 u32 raw_width = 0; 161 u32 raw_width = 0;
165 u32 raw_height = 0; 162 u32 raw_height = 0;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index e30fcb1ed..b72f95235 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -50,7 +50,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
50 } 50 }
51} 51}
52 52
53vk::Buffer CreateBuffer(const Device& device, u64 size) { 53vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size) {
54 VkBufferUsageFlags flags = 54 VkBufferUsageFlags flags =
55 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 55 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
56 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | 56 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
@@ -60,7 +60,7 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
60 if (device.IsExtTransformFeedbackSupported()) { 60 if (device.IsExtTransformFeedbackSupported()) {
61 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 61 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
62 } 62 }
63 return device.GetLogical().CreateBuffer({ 63 const VkBufferCreateInfo buffer_ci = {
64 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 64 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
65 .pNext = nullptr, 65 .pNext = nullptr,
66 .flags = 0, 66 .flags = 0,
@@ -69,7 +69,8 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
69 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 69 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
70 .queueFamilyIndexCount = 0, 70 .queueFamilyIndexCount = 0,
71 .pQueueFamilyIndices = nullptr, 71 .pQueueFamilyIndices = nullptr,
72 }); 72 };
73 return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
73} 74}
74} // Anonymous namespace 75} // Anonymous namespace
75 76
@@ -79,8 +80,8 @@ Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
79Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, 80Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
80 VAddr cpu_addr_, u64 size_bytes_) 81 VAddr cpu_addr_, u64 size_bytes_)
81 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), 82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
82 device{&runtime.device}, buffer{CreateBuffer(*device, SizeBytes())}, 83 device{&runtime.device}, buffer{
83 commit{runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal)} { 84 CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} {
84 if (runtime.device.HasDebuggingToolAttached()) { 85 if (runtime.device.HasDebuggingToolAttached()) {
85 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); 86 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
86 } 87 }
@@ -138,7 +139,7 @@ public:
138 const u32 num_first_offset_copies = 4; 139 const u32 num_first_offset_copies = 4;
139 const size_t bytes_per_index = BytesPerIndex(index_type); 140 const size_t bytes_per_index = BytesPerIndex(index_type);
140 const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies; 141 const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
141 buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ 142 const VkBufferCreateInfo buffer_ci = {
142 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 143 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
143 .pNext = nullptr, 144 .pNext = nullptr,
144 .flags = 0, 145 .flags = 0,
@@ -147,14 +148,21 @@ public:
147 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 148 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
148 .queueFamilyIndexCount = 0, 149 .queueFamilyIndexCount = 0,
149 .pQueueFamilyIndices = nullptr, 150 .pQueueFamilyIndices = nullptr,
150 }); 151 };
152 buffer = memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
151 if (device.HasDebuggingToolAttached()) { 153 if (device.HasDebuggingToolAttached()) {
152 buffer.SetObjectNameEXT("Quad LUT"); 154 buffer.SetObjectNameEXT("Quad LUT");
153 } 155 }
154 memory_commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
155 156
156 const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload); 157 const bool host_visible = buffer.IsHostVisible();
157 u8* staging_data = staging.mapped_span.data(); 158 const StagingBufferRef staging = [&] {
159 if (host_visible) {
160 return StagingBufferRef{};
161 }
162 return staging_pool.Request(size_bytes, MemoryUsage::Upload);
163 }();
164
165 u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
158 const size_t quad_size = bytes_per_index * 6; 166 const size_t quad_size = bytes_per_index * 6;
159 167
160 for (u32 first = 0; first < num_first_offset_copies; ++first) { 168 for (u32 first = 0; first < num_first_offset_copies; ++first) {
@@ -164,29 +172,33 @@ public:
164 } 172 }
165 } 173 }
166 174
167 scheduler.RequestOutsideRenderPassOperationContext(); 175 if (!host_visible) {
168 scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, 176 scheduler.RequestOutsideRenderPassOperationContext();
169 dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) { 177 scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset,
170 const VkBufferCopy copy{ 178 dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) {
171 .srcOffset = src_offset, 179 const VkBufferCopy copy{
172 .dstOffset = 0, 180 .srcOffset = src_offset,
173 .size = size_bytes, 181 .dstOffset = 0,
174 }; 182 .size = size_bytes,
175 const VkBufferMemoryBarrier write_barrier{ 183 };
176 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 184 const VkBufferMemoryBarrier write_barrier{
177 .pNext = nullptr, 185 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
178 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, 186 .pNext = nullptr,
179 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, 187 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
180 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 188 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
181 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 189 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
182 .buffer = dst_buffer, 190 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
183 .offset = 0, 191 .buffer = dst_buffer,
184 .size = size_bytes, 192 .offset = 0,
185 }; 193 .size = size_bytes,
186 cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy); 194 };
187 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, 195 cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy);
188 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier); 196 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
189 }); 197 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier);
198 });
199 } else {
200 buffer.Flush();
201 }
190 } 202 }
191 203
192 void BindBuffer(u32 first) { 204 void BindBuffer(u32 first) {
@@ -361,7 +373,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
361 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 373 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
362 }; 374 };
363 // Measuring a popular game, this number never exceeds the specified size once data is warmed up 375 // Measuring a popular game, this number never exceeds the specified size once data is warmed up
364 boost::container::small_vector<VkBufferCopy, 3> vk_copies(copies.size()); 376 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
365 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); 377 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
366 scheduler.RequestOutsideRenderPassOperationContext(); 378 scheduler.RequestOutsideRenderPassOperationContext();
367 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { 379 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
@@ -578,7 +590,8 @@ void BufferCacheRuntime::ReserveNullBuffer() {
578 .pNext = nullptr, 590 .pNext = nullptr,
579 .flags = 0, 591 .flags = 0,
580 .size = 4, 592 .size = 4,
581 .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, 593 .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
594 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
582 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 595 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
583 .queueFamilyIndexCount = 0, 596 .queueFamilyIndexCount = 0,
584 .pQueueFamilyIndices = nullptr, 597 .pQueueFamilyIndices = nullptr,
@@ -587,11 +600,10 @@ void BufferCacheRuntime::ReserveNullBuffer() {
587 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 600 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
588 } 601 }
589 create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; 602 create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
590 null_buffer = device.GetLogical().CreateBuffer(create_info); 603 null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
591 if (device.HasDebuggingToolAttached()) { 604 if (device.HasDebuggingToolAttached()) {
592 null_buffer.SetObjectNameEXT("Null buffer"); 605 null_buffer.SetObjectNameEXT("Null buffer");
593 } 606 }
594 null_buffer_commit = memory_allocator.Commit(null_buffer, MemoryUsage::DeviceLocal);
595 607
596 scheduler.RequestOutsideRenderPassOperationContext(); 608 scheduler.RequestOutsideRenderPassOperationContext();
597 scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) { 609 scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index cdeef8846..95446c732 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -48,7 +48,6 @@ private:
48 48
49 const Device* device{}; 49 const Device* device{};
50 vk::Buffer buffer; 50 vk::Buffer buffer;
51 MemoryCommit commit;
52 std::vector<BufferView> views; 51 std::vector<BufferView> views;
53}; 52};
54 53
@@ -142,7 +141,6 @@ private:
142 std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer; 141 std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
143 142
144 vk::Buffer null_buffer; 143 vk::Buffer null_buffer;
145 MemoryCommit null_buffer_commit;
146 144
147 std::unique_ptr<Uint8Pass> uint8_pass; 145 std::unique_ptr<Uint8Pass> uint8_pass;
148 QuadIndexedPass quad_index_pass; 146 QuadIndexedPass quad_index_pass;
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index df972cd54..9bcdca2fb 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -205,10 +205,9 @@ void FSR::CreateDescriptorSets() {
205void FSR::CreateImages() { 205void FSR::CreateImages() {
206 images.resize(image_count * 2); 206 images.resize(image_count * 2);
207 image_views.resize(image_count * 2); 207 image_views.resize(image_count * 2);
208 buffer_commits.resize(image_count * 2);
209 208
210 for (size_t i = 0; i < image_count * 2; ++i) { 209 for (size_t i = 0; i < image_count * 2; ++i) {
211 images[i] = device.GetLogical().CreateImage(VkImageCreateInfo{ 210 images[i] = memory_allocator.CreateImage(VkImageCreateInfo{
212 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 211 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
213 .pNext = nullptr, 212 .pNext = nullptr,
214 .flags = 0, 213 .flags = 0,
@@ -231,7 +230,6 @@ void FSR::CreateImages() {
231 .pQueueFamilyIndices = nullptr, 230 .pQueueFamilyIndices = nullptr,
232 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 231 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
233 }); 232 });
234 buffer_commits[i] = memory_allocator.Commit(images[i], MemoryUsage::DeviceLocal);
235 image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 233 image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
236 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 234 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
237 .pNext = nullptr, 235 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h
index 5d872861f..8bb9fc23a 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.h
+++ b/src/video_core/renderer_vulkan/vk_fsr.h
@@ -47,7 +47,6 @@ private:
47 vk::Sampler sampler; 47 vk::Sampler sampler;
48 std::vector<vk::Image> images; 48 std::vector<vk::Image> images;
49 std::vector<vk::ImageView> image_views; 49 std::vector<vk::ImageView> image_views;
50 std::vector<MemoryCommit> buffer_commits;
51}; 50};
52 51
53} // namespace Vulkan 52} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index c1595642e..ad35cacac 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -652,13 +652,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
652 .pNext = nullptr, 652 .pNext = nullptr,
653 .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE, 653 .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
654 }; 654 };
655 const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
655 VkPipelineViewportStateCreateInfo viewport_ci{ 656 VkPipelineViewportStateCreateInfo viewport_ci{
656 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 657 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
657 .pNext = nullptr, 658 .pNext = nullptr,
658 .flags = 0, 659 .flags = 0,
659 .viewportCount = Maxwell::NumViewports, 660 .viewportCount = num_viewports,
660 .pViewports = nullptr, 661 .pViewports = nullptr,
661 .scissorCount = Maxwell::NumViewports, 662 .scissorCount = num_viewports,
662 .pScissors = nullptr, 663 .pScissors = nullptr,
663 }; 664 };
664 if (device.IsNvViewportSwizzleSupported()) { 665 if (device.IsNvViewportSwizzleSupported()) {
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 5eeda08d2..6b288b994 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -75,15 +75,9 @@ void MasterSemaphore::Refresh() {
75 75
76void MasterSemaphore::Wait(u64 tick) { 76void MasterSemaphore::Wait(u64 tick) {
77 if (!semaphore) { 77 if (!semaphore) {
78 // If we don't support timeline semaphores, use an atomic wait 78 // If we don't support timeline semaphores, wait for the value normally
79 while (true) { 79 std::unique_lock lk{free_mutex};
80 u64 current_value = gpu_tick.load(std::memory_order_relaxed); 80 free_cv.wait(lk, [&] { return gpu_tick.load(std::memory_order_relaxed) >= tick; });
81 if (current_value >= tick) {
82 return;
83 }
84 gpu_tick.wait(current_value);
85 }
86
87 return; 81 return;
88 } 82 }
89 83
@@ -198,11 +192,13 @@ void MasterSemaphore::WaitThread(std::stop_token token) {
198 192
199 fence.Wait(); 193 fence.Wait();
200 fence.Reset(); 194 fence.Reset();
201 gpu_tick.store(host_tick);
202 gpu_tick.notify_all();
203 195
204 std::scoped_lock lock{free_mutex}; 196 {
205 free_queue.push_front(std::move(fence)); 197 std::scoped_lock lock{free_mutex};
198 free_queue.push_front(std::move(fence));
199 gpu_tick.store(host_tick);
200 }
201 free_cv.notify_one();
206 } 202 }
207} 203}
208 204
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 1e7c90215..3f599d7bd 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -72,6 +72,7 @@ private:
72 std::atomic<u64> current_tick{1}; ///< Current logical tick. 72 std::atomic<u64> current_tick{1}; ///< Current logical tick.
73 std::mutex wait_mutex; 73 std::mutex wait_mutex;
74 std::mutex free_mutex; 74 std::mutex free_mutex;
75 std::condition_variable free_cv;
75 std::condition_variable_any wait_cv; 76 std::condition_variable_any wait_cv;
76 std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread. 77 std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread.
77 std::deque<vk::Fence> free_queue; ///< Holds available fences for submission. 78 std::deque<vk::Fence> free_queue; ///< Holds available fences for submission.
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 18e040a1b..d600c4e61 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -167,7 +167,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
167 info.fixed_state_point_size = point_size; 167 info.fixed_state_point_size = point_size;
168 } 168 }
169 if (key.state.xfb_enabled) { 169 if (key.state.xfb_enabled) {
170 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); 170 auto [varyings, count] =
171 VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
172 info.xfb_varyings = varyings;
173 info.xfb_count = count;
171 } 174 }
172 info.convert_depth_mode = gl_ndc; 175 info.convert_depth_mode = gl_ndc;
173 } 176 }
@@ -214,7 +217,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
214 info.fixed_state_point_size = point_size; 217 info.fixed_state_point_size = point_size;
215 } 218 }
216 if (key.state.xfb_enabled != 0) { 219 if (key.state.xfb_enabled != 0) {
217 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); 220 auto [varyings, count] =
221 VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
222 info.xfb_varyings = varyings;
223 info.xfb_count = count;
218 } 224 }
219 info.convert_depth_mode = gl_ndc; 225 info.convert_depth_mode = gl_ndc;
220 break; 226 break;
@@ -303,7 +309,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
303 .support_int16 = device.IsShaderInt16Supported(), 309 .support_int16 = device.IsShaderInt16Supported(),
304 .support_int64 = device.IsShaderInt64Supported(), 310 .support_int64 = device.IsShaderInt64Supported(),
305 .support_vertex_instance_id = false, 311 .support_vertex_instance_id = false,
306 .support_float_controls = true, 312 .support_float_controls = device.IsKhrShaderFloatControlsSupported(),
307 .support_separate_denorm_behavior = 313 .support_separate_denorm_behavior =
308 float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, 314 float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
309 .support_separate_rounding_mode = 315 .support_separate_rounding_mode =
@@ -319,12 +325,13 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
319 .support_fp64_signed_zero_nan_preserve = 325 .support_fp64_signed_zero_nan_preserve =
320 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, 326 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
321 .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), 327 .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
322 .support_vote = true, 328 .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
323 .support_viewport_index_layer_non_geometry = 329 .support_viewport_index_layer_non_geometry =
324 device.IsExtShaderViewportIndexLayerSupported(), 330 device.IsExtShaderViewportIndexLayerSupported(),
325 .support_viewport_mask = device.IsNvViewportArray2Supported(), 331 .support_viewport_mask = device.IsNvViewportArray2Supported(),
326 .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), 332 .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(),
327 .support_demote_to_helper_invocation = true, 333 .support_demote_to_helper_invocation =
334 device.IsExtShaderDemoteToHelperInvocationSupported(),
328 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), 335 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
329 .support_derivative_control = true, 336 .support_derivative_control = true,
330 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 337 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
@@ -705,10 +712,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
705std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 712std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
706 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 713 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
707 PipelineStatistics* statistics, bool build_in_parallel) try { 714 PipelineStatistics* statistics, bool build_in_parallel) try {
708 // TODO: Remove this when Intel fixes their shader compiler. 715 if (device.HasBrokenCompute()) {
709 // https://github.com/IGCIT/Intel-GPU-Community-Issue-Tracker-IGCIT/issues/159
710 if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS &&
711 !Settings::values.enable_compute_pipelines.GetValue()) {
712 LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash()); 716 LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash());
713 return nullptr; 717 return nullptr;
714 } 718 }
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 10ace0420..d681bd22a 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -181,7 +181,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_
181 frame->height = height; 181 frame->height = height;
182 frame->is_srgb = is_srgb; 182 frame->is_srgb = is_srgb;
183 183
184 frame->image = dld.CreateImage({ 184 frame->image = memory_allocator.CreateImage({
185 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 185 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
186 .pNext = nullptr, 186 .pNext = nullptr,
187 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, 187 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
@@ -204,8 +204,6 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_
204 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 204 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
205 }); 205 });
206 206
207 frame->image_commit = memory_allocator.Commit(frame->image, MemoryUsage::DeviceLocal);
208
209 frame->image_view = dld.CreateImageView({ 207 frame->image_view = dld.CreateImageView({
210 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 208 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
211 .pNext = nullptr, 209 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h
index 4ac2e2395..83e859416 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.h
+++ b/src/video_core/renderer_vulkan/vk_present_manager.h
@@ -29,7 +29,6 @@ struct Frame {
29 vk::Image image; 29 vk::Image image;
30 vk::ImageView image_view; 30 vk::ImageView image_view;
31 vk::Framebuffer framebuffer; 31 vk::Framebuffer framebuffer;
32 MemoryCommit image_commit;
33 vk::CommandBuffer cmdbuf; 32 vk::CommandBuffer cmdbuf;
34 vk::Semaphore render_ready; 33 vk::Semaphore render_ready;
35 vk::Fence present_done; 34 vk::Fence present_done;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 84e3a30cc..f7c0d939a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -315,7 +315,14 @@ void RasterizerVulkan::Clear(u32 layer_count) {
315 FlushWork(); 315 FlushWork();
316 gpu_memory->FlushCaching(); 316 gpu_memory->FlushCaching();
317 317
318#if ANDROID
319 if (Settings::IsGPULevelHigh()) {
320 // This is problematic on Android, disable on GPU Normal.
321 query_cache.UpdateCounters();
322 }
323#else
318 query_cache.UpdateCounters(); 324 query_cache.UpdateCounters();
325#endif
319 326
320 auto& regs = maxwell3d->regs; 327 auto& regs = maxwell3d->regs;
321 const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || 328 const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
@@ -925,7 +932,7 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
925 } 932 }
926 const bool is_rescaling{texture_cache.IsRescaling()}; 933 const bool is_rescaling{texture_cache.IsRescaling()};
927 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 934 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
928 const std::array viewports{ 935 const std::array viewport_list{
929 GetViewportState(device, regs, 0, scale), GetViewportState(device, regs, 1, scale), 936 GetViewportState(device, regs, 0, scale), GetViewportState(device, regs, 1, scale),
930 GetViewportState(device, regs, 2, scale), GetViewportState(device, regs, 3, scale), 937 GetViewportState(device, regs, 2, scale), GetViewportState(device, regs, 3, scale),
931 GetViewportState(device, regs, 4, scale), GetViewportState(device, regs, 5, scale), 938 GetViewportState(device, regs, 4, scale), GetViewportState(device, regs, 5, scale),
@@ -935,7 +942,11 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
935 GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale), 942 GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale),
936 GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale), 943 GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale),
937 }; 944 };
938 scheduler.Record([viewports](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewports); }); 945 scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) {
946 const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
947 const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
948 cmdbuf.SetViewport(0, viewports);
949 });
939} 950}
940 951
941void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) { 952void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -948,7 +959,7 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
948 up_scale = Settings::values.resolution_info.up_scale; 959 up_scale = Settings::values.resolution_info.up_scale;
949 down_shift = Settings::values.resolution_info.down_shift; 960 down_shift = Settings::values.resolution_info.down_shift;
950 } 961 }
951 const std::array scissors{ 962 const std::array scissor_list{
952 GetScissorState(regs, 0, up_scale, down_shift), 963 GetScissorState(regs, 0, up_scale, down_shift),
953 GetScissorState(regs, 1, up_scale, down_shift), 964 GetScissorState(regs, 1, up_scale, down_shift),
954 GetScissorState(regs, 2, up_scale, down_shift), 965 GetScissorState(regs, 2, up_scale, down_shift),
@@ -966,7 +977,11 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
966 GetScissorState(regs, 14, up_scale, down_shift), 977 GetScissorState(regs, 14, up_scale, down_shift),
967 GetScissorState(regs, 15, up_scale, down_shift), 978 GetScissorState(regs, 15, up_scale, down_shift),
968 }; 979 };
969 scheduler.Record([scissors](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissors); }); 980 scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) {
981 const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
982 const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
983 cmdbuf.SetScissor(0, scissors);
984 });
970} 985}
971 986
972void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { 987void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp
index f8735189d..5efd7d66e 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.cpp
+++ b/src/video_core/renderer_vulkan/vk_smaa.cpp
@@ -25,9 +25,7 @@ namespace {
25 25
26#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) 26#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0])))
27 27
28std::pair<vk::Image, MemoryCommit> CreateWrappedImage(const Device& device, 28vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) {
29 MemoryAllocator& allocator,
30 VkExtent2D dimensions, VkFormat format) {
31 const VkImageCreateInfo image_ci{ 29 const VkImageCreateInfo image_ci{
32 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 30 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
33 .pNext = nullptr, 31 .pNext = nullptr,
@@ -46,11 +44,7 @@ std::pair<vk::Image, MemoryCommit> CreateWrappedImage(const Device& device,
46 .pQueueFamilyIndices = nullptr, 44 .pQueueFamilyIndices = nullptr,
47 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 45 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
48 }; 46 };
49 47 return allocator.CreateImage(image_ci);
50 auto image = device.GetLogical().CreateImage(image_ci);
51 auto commit = allocator.Commit(image, Vulkan::MemoryUsage::DeviceLocal);
52
53 return std::make_pair(std::move(image), std::move(commit));
54} 48}
55 49
56void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, 50void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
@@ -82,7 +76,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
82void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, 76void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
83 vk::Image& image, VkExtent2D dimensions, VkFormat format, 77 vk::Image& image, VkExtent2D dimensions, VkFormat format,
84 std::span<const u8> initial_contents = {}) { 78 std::span<const u8> initial_contents = {}) {
85 auto upload_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ 79 const VkBufferCreateInfo upload_ci = {
86 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 80 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
87 .pNext = nullptr, 81 .pNext = nullptr,
88 .flags = 0, 82 .flags = 0,
@@ -91,9 +85,10 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
91 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 85 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
92 .queueFamilyIndexCount = 0, 86 .queueFamilyIndexCount = 0,
93 .pQueueFamilyIndices = nullptr, 87 .pQueueFamilyIndices = nullptr,
94 }); 88 };
95 auto upload_commit = allocator.Commit(upload_buffer, MemoryUsage::Upload); 89 auto upload_buffer = allocator.CreateBuffer(upload_ci, MemoryUsage::Upload);
96 std::ranges::copy(initial_contents, upload_commit.Map().begin()); 90 std::ranges::copy(initial_contents, upload_buffer.Mapped().begin());
91 upload_buffer.Flush();
97 92
98 const std::array<VkBufferImageCopy, 1> regions{{{ 93 const std::array<VkBufferImageCopy, 1> regions{{{
99 .bufferOffset = 0, 94 .bufferOffset = 0,
@@ -117,9 +112,6 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
117 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); 112 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
118 }); 113 });
119 scheduler.Finish(); 114 scheduler.Finish();
120
121 // This should go out of scope before the commit
122 auto upload_buffer2 = std::move(upload_buffer);
123} 115}
124 116
125vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { 117vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
@@ -531,10 +523,8 @@ void SMAA::CreateImages() {
531 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; 523 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
532 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; 524 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
533 525
534 std::tie(m_static_images[Area], m_static_buffer_commits[Area]) = 526 m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
535 CreateWrappedImage(m_device, m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); 527 m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM);
536 std::tie(m_static_images[Search], m_static_buffer_commits[Search]) =
537 CreateWrappedImage(m_device, m_allocator, search_extent, VK_FORMAT_R8_UNORM);
538 528
539 m_static_image_views[Area] = 529 m_static_image_views[Area] =
540 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); 530 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
@@ -544,12 +534,11 @@ void SMAA::CreateImages() {
544 for (u32 i = 0; i < m_image_count; i++) { 534 for (u32 i = 0; i < m_image_count; i++) {
545 Images& images = m_dynamic_images.emplace_back(); 535 Images& images = m_dynamic_images.emplace_back();
546 536
547 std::tie(images.images[Blend], images.buffer_commits[Blend]) = 537 images.images[Blend] =
548 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); 538 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
549 std::tie(images.images[Edges], images.buffer_commits[Edges]) = 539 images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
550 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); 540 images.images[Output] =
551 std::tie(images.images[Output], images.buffer_commits[Output]) = 541 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
552 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
553 542
554 images.image_views[Blend] = 543 images.image_views[Blend] =
555 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); 544 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/vk_smaa.h
index 99a369148..0e214258a 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.h
+++ b/src/video_core/renderer_vulkan/vk_smaa.h
@@ -66,13 +66,11 @@ private:
66 std::array<vk::Pipeline, MaxSMAAStage> m_pipelines{}; 66 std::array<vk::Pipeline, MaxSMAAStage> m_pipelines{};
67 std::array<vk::RenderPass, MaxSMAAStage> m_renderpasses{}; 67 std::array<vk::RenderPass, MaxSMAAStage> m_renderpasses{};
68 68
69 std::array<MemoryCommit, MaxStaticImage> m_static_buffer_commits;
70 std::array<vk::Image, MaxStaticImage> m_static_images{}; 69 std::array<vk::Image, MaxStaticImage> m_static_images{};
71 std::array<vk::ImageView, MaxStaticImage> m_static_image_views{}; 70 std::array<vk::ImageView, MaxStaticImage> m_static_image_views{};
72 71
73 struct Images { 72 struct Images {
74 vk::DescriptorSets descriptor_sets{}; 73 vk::DescriptorSets descriptor_sets{};
75 std::array<MemoryCommit, MaxDynamicImage> buffer_commits;
76 std::array<vk::Image, MaxDynamicImage> images{}; 74 std::array<vk::Image, MaxDynamicImage> images{};
77 std::array<vk::ImageView, MaxDynamicImage> image_views{}; 75 std::array<vk::ImageView, MaxDynamicImage> image_views{};
78 std::array<vk::Framebuffer, MaxSMAAStage> framebuffers{}; 76 std::array<vk::Framebuffer, MaxSMAAStage> framebuffers{};
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 74ca77216..ce92f66ab 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -30,55 +30,6 @@ constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB;
30constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; 30constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB;
31constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; 31constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS;
32 32
33constexpr VkMemoryPropertyFlags HOST_FLAGS =
34 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
35constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS;
36
37bool IsStreamHeap(VkMemoryHeap heap) noexcept {
38 return STREAM_BUFFER_SIZE < (heap.size * 2) / 3;
39}
40
41std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask,
42 VkMemoryPropertyFlags flags) noexcept {
43 for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) {
44 if (((type_mask >> type_index) & 1) == 0) {
45 // Memory type is incompatible
46 continue;
47 }
48 const VkMemoryType& memory_type = props.memoryTypes[type_index];
49 if ((memory_type.propertyFlags & flags) != flags) {
50 // Memory type doesn't have the flags we want
51 continue;
52 }
53 if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex])) {
54 // Memory heap is not suitable for streaming
55 continue;
56 }
57 // Success!
58 return type_index;
59 }
60 return std::nullopt;
61}
62
63u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask,
64 bool try_device_local) {
65 std::optional<u32> type;
66 if (try_device_local) {
67 // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this
68 type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS);
69 if (type) {
70 return *type;
71 }
72 }
73 // Otherwise try without the DEVICE_LOCAL_BIT
74 type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS);
75 if (type) {
76 return *type;
77 }
78 // This should never happen, and in case it does, signal it as an out of memory situation
79 throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY);
80}
81
82size_t Region(size_t iterator) noexcept { 33size_t Region(size_t iterator) noexcept {
83 return iterator / REGION_SIZE; 34 return iterator / REGION_SIZE;
84} 35}
@@ -87,58 +38,26 @@ size_t Region(size_t iterator) noexcept {
87StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, 38StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
88 Scheduler& scheduler_) 39 Scheduler& scheduler_)
89 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { 40 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {
90 const vk::Device& dev = device.GetLogical(); 41 VkBufferCreateInfo stream_ci = {
91 stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{
92 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 42 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
93 .pNext = nullptr, 43 .pNext = nullptr,
94 .flags = 0, 44 .flags = 0,
95 .size = STREAM_BUFFER_SIZE, 45 .size = STREAM_BUFFER_SIZE,
96 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | 46 .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 | 47 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
98 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
99 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 48 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
100 .queueFamilyIndexCount = 0, 49 .queueFamilyIndexCount = 0,
101 .pQueueFamilyIndices = nullptr, 50 .pQueueFamilyIndices = nullptr,
102 });
103 if (device.HasDebuggingToolAttached()) {
104 stream_buffer.SetObjectNameEXT("Stream Buffer");
105 }
106 VkMemoryDedicatedRequirements dedicated_reqs{
107 .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS,
108 .pNext = nullptr,
109 .prefersDedicatedAllocation = VK_FALSE,
110 .requiresDedicatedAllocation = VK_FALSE,
111 };
112 const auto requirements = dev.GetBufferMemoryRequirements(*stream_buffer, &dedicated_reqs);
113 const bool make_dedicated = dedicated_reqs.prefersDedicatedAllocation == VK_TRUE ||
114 dedicated_reqs.requiresDedicatedAllocation == VK_TRUE;
115 const VkMemoryDedicatedAllocateInfo dedicated_info{
116 .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
117 .pNext = nullptr,
118 .image = nullptr,
119 .buffer = *stream_buffer,
120 }; 51 };
121 const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties; 52 if (device.IsExtTransformFeedbackSupported()) {
122 VkMemoryAllocateInfo stream_memory_info{ 53 stream_ci.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
124 .pNext = make_dedicated ? &dedicated_info : nullptr,
125 .allocationSize = requirements.size,
126 .memoryTypeIndex =
127 FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true),
128 };
129 stream_memory = dev.TryAllocateMemory(stream_memory_info);
130 if (!stream_memory) {
131 LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory");
132 stream_memory_info.memoryTypeIndex =
133 FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false);
134 stream_memory = dev.AllocateMemory(stream_memory_info);
135 } 54 }
136 55 stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
137 if (device.HasDebuggingToolAttached()) { 56 if (device.HasDebuggingToolAttached()) {
138 stream_memory.SetObjectNameEXT("Stream Buffer Memory"); 57 stream_buffer.SetObjectNameEXT("Stream Buffer");
139 } 58 }
140 stream_buffer.BindMemory(*stream_memory, 0); 59 stream_pointer = stream_buffer.Mapped();
141 stream_pointer = stream_memory.Map(0, STREAM_BUFFER_SIZE); 60 ASSERT_MSG(!stream_pointer.empty(), "Stream buffer must be host visible!");
142} 61}
143 62
144StagingBufferPool::~StagingBufferPool() = default; 63StagingBufferPool::~StagingBufferPool() = default;
@@ -199,7 +118,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
199 return StagingBufferRef{ 118 return StagingBufferRef{
200 .buffer = *stream_buffer, 119 .buffer = *stream_buffer,
201 .offset = static_cast<VkDeviceSize>(offset), 120 .offset = static_cast<VkDeviceSize>(offset),
202 .mapped_span = std::span<u8>(stream_pointer + offset, size), 121 .mapped_span = stream_pointer.subspan(offset, size),
203 .usage{}, 122 .usage{},
204 .log2_level{}, 123 .log2_level{},
205 .index{}, 124 .index{},
@@ -247,29 +166,29 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
247StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, 166StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
248 bool deferred) { 167 bool deferred) {
249 const u32 log2 = Common::Log2Ceil64(size); 168 const u32 log2 = Common::Log2Ceil64(size);
250 vk::Buffer buffer = device.GetLogical().CreateBuffer({ 169 VkBufferCreateInfo buffer_ci = {
251 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 170 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
252 .pNext = nullptr, 171 .pNext = nullptr,
253 .flags = 0, 172 .flags = 0,
254 .size = 1ULL << log2, 173 .size = 1ULL << log2,
255 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 174 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
256 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 175 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
257 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | 176 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
258 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
259 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 177 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
260 .queueFamilyIndexCount = 0, 178 .queueFamilyIndexCount = 0,
261 .pQueueFamilyIndices = nullptr, 179 .pQueueFamilyIndices = nullptr,
262 }); 180 };
181 if (device.IsExtTransformFeedbackSupported()) {
182 buffer_ci.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
183 }
184 vk::Buffer buffer = memory_allocator.CreateBuffer(buffer_ci, usage);
263 if (device.HasDebuggingToolAttached()) { 185 if (device.HasDebuggingToolAttached()) {
264 ++buffer_index; 186 ++buffer_index;
265 buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str()); 187 buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
266 } 188 }
267 MemoryCommit commit = memory_allocator.Commit(buffer, usage); 189 const std::span<u8> mapped_span = buffer.Mapped();
268 const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{};
269
270 StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{ 190 StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
271 .buffer = std::move(buffer), 191 .buffer = std::move(buffer),
272 .commit = std::move(commit),
273 .mapped_span = mapped_span, 192 .mapped_span = mapped_span,
274 .usage = usage, 193 .usage = usage,
275 .log2_level = log2, 194 .log2_level = log2,
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 4fd15f11a..5f69f08b1 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -46,7 +46,6 @@ private:
46 46
47 struct StagingBuffer { 47 struct StagingBuffer {
48 vk::Buffer buffer; 48 vk::Buffer buffer;
49 MemoryCommit commit;
50 std::span<u8> mapped_span; 49 std::span<u8> mapped_span;
51 MemoryUsage usage; 50 MemoryUsage usage;
52 u32 log2_level; 51 u32 log2_level;
@@ -97,8 +96,7 @@ private:
97 Scheduler& scheduler; 96 Scheduler& scheduler;
98 97
99 vk::Buffer stream_buffer; 98 vk::Buffer stream_buffer;
100 vk::DeviceMemory stream_memory; 99 std::span<u8> stream_pointer;
101 u8* stream_pointer = nullptr;
102 100
103 size_t iterator = 0; 101 size_t iterator = 0;
104 size_t used_iterator = 0; 102 size_t used_iterator = 0;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index f025f618b..8385b5509 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -15,7 +15,6 @@
15#include "video_core/renderer_vulkan/blit_image.h" 15#include "video_core/renderer_vulkan/blit_image.h"
16#include "video_core/renderer_vulkan/maxwell_to_vk.h" 16#include "video_core/renderer_vulkan/maxwell_to_vk.h"
17#include "video_core/renderer_vulkan/vk_compute_pass.h" 17#include "video_core/renderer_vulkan/vk_compute_pass.h"
18#include "video_core/renderer_vulkan/vk_rasterizer.h"
19#include "video_core/renderer_vulkan/vk_render_pass_cache.h" 18#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
20#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
21#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -163,11 +162,12 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
163 }; 162 };
164} 163}
165 164
166[[nodiscard]] vk::Image MakeImage(const Device& device, const ImageInfo& info) { 165[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
166 const ImageInfo& info) {
167 if (info.type == ImageType::Buffer) { 167 if (info.type == ImageType::Buffer) {
168 return vk::Image{}; 168 return vk::Image{};
169 } 169 }
170 return device.GetLogical().CreateImage(MakeImageCreateInfo(device, info)); 170 return allocator.CreateImage(MakeImageCreateInfo(device, info));
171} 171}
172 172
173[[nodiscard]] VkImageAspectFlags ImageAspectMask(PixelFormat format) { 173[[nodiscard]] VkImageAspectFlags ImageAspectMask(PixelFormat format) {
@@ -330,9 +330,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
330 }; 330 };
331} 331}
332 332
333[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( 333[[maybe_unused]] [[nodiscard]] boost::container::small_vector<VkBufferCopy, 16>
334 std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { 334TransformBufferCopies(std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) {
335 std::vector<VkBufferCopy> result(copies.size()); 335 boost::container::small_vector<VkBufferCopy, 16> result(copies.size());
336 std::ranges::transform( 336 std::ranges::transform(
337 copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) { 337 copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) {
338 return VkBufferCopy{ 338 return VkBufferCopy{
@@ -344,7 +344,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
344 return result; 344 return result;
345} 345}
346 346
347[[nodiscard]] std::vector<VkBufferImageCopy> TransformBufferImageCopies( 347[[nodiscard]] boost::container::small_vector<VkBufferImageCopy, 16> TransformBufferImageCopies(
348 std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) { 348 std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) {
349 struct Maker { 349 struct Maker {
350 VkBufferImageCopy operator()(const BufferImageCopy& copy) const { 350 VkBufferImageCopy operator()(const BufferImageCopy& copy) const {
@@ -377,14 +377,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
377 VkImageAspectFlags aspect_mask; 377 VkImageAspectFlags aspect_mask;
378 }; 378 };
379 if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { 379 if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
380 std::vector<VkBufferImageCopy> result(copies.size() * 2); 380 boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size() * 2);
381 std::ranges::transform(copies, result.begin(), 381 std::ranges::transform(copies, result.begin(),
382 Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT}); 382 Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT});
383 std::ranges::transform(copies, result.begin() + copies.size(), 383 std::ranges::transform(copies, result.begin() + copies.size(),
384 Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT}); 384 Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT});
385 return result; 385 return result;
386 } else { 386 } else {
387 std::vector<VkBufferImageCopy> result(copies.size()); 387 boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size());
388 std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask}); 388 std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask});
389 return result; 389 return result;
390 } 390 }
@@ -839,14 +839,14 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
839 839
840VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { 840VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
841 const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL); 841 const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL);
842 if (buffer_commits[level]) { 842 if (buffers[level]) {
843 return *buffers[level]; 843 return *buffers[level];
844 } 844 }
845 const auto new_size = Common::NextPow2(needed_size); 845 const auto new_size = Common::NextPow2(needed_size);
846 static constexpr VkBufferUsageFlags flags = 846 static constexpr VkBufferUsageFlags flags =
847 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 847 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
848 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; 848 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
849 buffers[level] = device.GetLogical().CreateBuffer({ 849 const VkBufferCreateInfo temp_ci = {
850 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 850 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
851 .pNext = nullptr, 851 .pNext = nullptr,
852 .flags = 0, 852 .flags = 0,
@@ -855,9 +855,8 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
855 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 855 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
856 .queueFamilyIndexCount = 0, 856 .queueFamilyIndexCount = 0,
857 .pQueueFamilyIndices = nullptr, 857 .pQueueFamilyIndices = nullptr,
858 }); 858 };
859 buffer_commits[level] = std::make_unique<MemoryCommit>( 859 buffers[level] = memory_allocator.CreateBuffer(temp_ci, MemoryUsage::DeviceLocal);
860 memory_allocator.Commit(buffers[level], MemoryUsage::DeviceLocal));
861 return *buffers[level]; 860 return *buffers[level];
862} 861}
863 862
@@ -867,8 +866,8 @@ void TextureCacheRuntime::BarrierFeedbackLoop() {
867 866
868void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, 867void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
869 std::span<const VideoCommon::ImageCopy> copies) { 868 std::span<const VideoCommon::ImageCopy> copies) {
870 std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); 869 boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size());
871 std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); 870 boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size());
872 const VkImageAspectFlags src_aspect_mask = src.AspectMask(); 871 const VkImageAspectFlags src_aspect_mask = src.AspectMask();
873 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); 872 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask();
874 873
@@ -1157,7 +1156,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
1157 1156
1158void TextureCacheRuntime::CopyImage(Image& dst, Image& src, 1157void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
1159 std::span<const VideoCommon::ImageCopy> copies) { 1158 std::span<const VideoCommon::ImageCopy> copies) {
1160 std::vector<VkImageCopy> vk_copies(copies.size()); 1159 boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size());
1161 const VkImageAspectFlags aspect_mask = dst.AspectMask(); 1160 const VkImageAspectFlags aspect_mask = dst.AspectMask();
1162 ASSERT(aspect_mask == src.AspectMask()); 1161 ASSERT(aspect_mask == src.AspectMask());
1163 1162
@@ -1266,8 +1265,8 @@ void TextureCacheRuntime::TickFrame() {}
1266Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, 1265Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
1267 VAddr cpu_addr_) 1266 VAddr cpu_addr_)
1268 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, 1267 : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler},
1269 runtime{&runtime_}, original_image(MakeImage(runtime_.device, info)), 1268 runtime{&runtime_},
1270 commit(runtime_.memory_allocator.Commit(original_image, MemoryUsage::DeviceLocal)), 1269 original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info)),
1271 aspect_mask(ImageAspectMask(info.format)) { 1270 aspect_mask(ImageAspectMask(info.format)) {
1272 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { 1271 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
1273 if (Settings::values.async_astc.GetValue()) { 1272 if (Settings::values.async_astc.GetValue()) {
@@ -1280,6 +1279,10 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1280 flags |= VideoCommon::ImageFlagBits::Converted; 1279 flags |= VideoCommon::ImageFlagBits::Converted;
1281 flags |= VideoCommon::ImageFlagBits::CostlyLoad; 1280 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1282 } 1281 }
1282 if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
1283 flags |= VideoCommon::ImageFlagBits::Converted;
1284 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1285 }
1283 if (runtime->device.HasDebuggingToolAttached()) { 1286 if (runtime->device.HasDebuggingToolAttached()) {
1284 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1287 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
1285 } 1288 }
@@ -1332,7 +1335,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
1332 ScaleDown(true); 1335 ScaleDown(true);
1333 } 1336 }
1334 scheduler->RequestOutsideRenderPassOperationContext(); 1337 scheduler->RequestOutsideRenderPassOperationContext();
1335 std::vector vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); 1338 auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
1336 const VkBuffer src_buffer = buffer; 1339 const VkBuffer src_buffer = buffer;
1337 const VkImage vk_image = *original_image; 1340 const VkImage vk_image = *original_image;
1338 const VkImageAspectFlags vk_aspect_mask = aspect_mask; 1341 const VkImageAspectFlags vk_aspect_mask = aspect_mask;
@@ -1367,8 +1370,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1367 if (is_rescaled) { 1370 if (is_rescaled) {
1368 ScaleDown(); 1371 ScaleDown();
1369 } 1372 }
1370 boost::container::small_vector<VkBuffer, 1> buffers_vector{}; 1373 boost::container::small_vector<VkBuffer, 8> buffers_vector{};
1371 boost::container::small_vector<std::vector<VkBufferImageCopy>, 1> vk_copies; 1374 boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8>
1375 vk_copies;
1372 for (size_t index = 0; index < buffers_span.size(); index++) { 1376 for (size_t index = 0; index < buffers_span.size(); index++) {
1373 buffers_vector.emplace_back(buffers_span[index]); 1377 buffers_vector.emplace_back(buffers_span[index]);
1374 vk_copies.emplace_back( 1378 vk_copies.emplace_back(
@@ -1467,9 +1471,7 @@ bool Image::ScaleUp(bool ignore) {
1467 auto scaled_info = info; 1471 auto scaled_info = info;
1468 scaled_info.size.width = scaled_width; 1472 scaled_info.size.width = scaled_width;
1469 scaled_info.size.height = scaled_height; 1473 scaled_info.size.height = scaled_height;
1470 scaled_image = MakeImage(runtime->device, scaled_info); 1474 scaled_image = MakeImage(runtime->device, runtime->memory_allocator, scaled_info);
1471 auto& allocator = runtime->memory_allocator;
1472 scaled_commit = MemoryCommit(allocator.Commit(scaled_image, MemoryUsage::DeviceLocal));
1473 ignore = false; 1475 ignore = false;
1474 } 1476 }
1475 current_image = *scaled_image; 1477 current_image = *scaled_image;
@@ -1858,7 +1860,7 @@ Framebuffer::~Framebuffer() = default;
1858void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, 1860void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1859 std::span<ImageView*, NUM_RT> color_buffers, 1861 std::span<ImageView*, NUM_RT> color_buffers,
1860 ImageView* depth_buffer, bool is_rescaled) { 1862 ImageView* depth_buffer, bool is_rescaled) {
1861 std::vector<VkImageView> attachments; 1863 boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
1862 RenderPassKey renderpass_key{}; 1864 RenderPassKey renderpass_key{};
1863 s32 num_layers = 1; 1865 s32 num_layers = 1;
1864 1866
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index f14525dcb..220943116 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -116,7 +116,6 @@ public:
116 116
117 static constexpr size_t indexing_slots = 8 * sizeof(size_t); 117 static constexpr size_t indexing_slots = 8 * sizeof(size_t);
118 std::array<vk::Buffer, indexing_slots> buffers{}; 118 std::array<vk::Buffer, indexing_slots> buffers{};
119 std::array<std::unique_ptr<MemoryCommit>, indexing_slots> buffer_commits{};
120}; 119};
121 120
122class Image : public VideoCommon::ImageBase { 121class Image : public VideoCommon::ImageBase {
@@ -180,12 +179,10 @@ private:
180 TextureCacheRuntime* runtime{}; 179 TextureCacheRuntime* runtime{};
181 180
182 vk::Image original_image; 181 vk::Image original_image;
183 MemoryCommit commit;
184 std::vector<vk::ImageView> storage_image_views; 182 std::vector<vk::ImageView> storage_image_views;
185 VkImageAspectFlags aspect_mask = 0; 183 VkImageAspectFlags aspect_mask = 0;
186 bool initialized = false; 184 bool initialized = false;
187 vk::Image scaled_image{}; 185 vk::Image scaled_image{};
188 MemoryCommit scaled_commit{};
189 VkImage current_image{}; 186 VkImage current_image{};
190 187
191 std::unique_ptr<Framebuffer> scale_framebuffer; 188 std::unique_ptr<Framebuffer> scale_framebuffer;
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
index a802d3c49..460d8d59d 100644
--- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
@@ -18,7 +18,7 @@ using namespace Common::Literals;
18 18
19TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld) 19TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld)
20#ifndef ANDROID 20#ifndef ANDROID
21 : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} 21 : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device}
22#endif 22#endif
23{ 23{
24 { 24 {
@@ -41,7 +41,7 @@ void TurboMode::Run(std::stop_token stop_token) {
41 auto& dld = m_device.GetLogical(); 41 auto& dld = m_device.GetLogical();
42 42
43 // Allocate buffer. 2MiB should be sufficient. 43 // Allocate buffer. 2MiB should be sufficient.
44 auto buffer = dld.CreateBuffer(VkBufferCreateInfo{ 44 const VkBufferCreateInfo buffer_ci = {
45 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 45 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
46 .pNext = nullptr, 46 .pNext = nullptr,
47 .flags = 0, 47 .flags = 0,
@@ -50,10 +50,8 @@ void TurboMode::Run(std::stop_token stop_token) {
50 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 50 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
51 .queueFamilyIndexCount = 0, 51 .queueFamilyIndexCount = 0,
52 .pQueueFamilyIndices = nullptr, 52 .pQueueFamilyIndices = nullptr,
53 }); 53 };
54 54 vk::Buffer buffer = m_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
55 // Commit some device local memory for the buffer.
56 auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
57 55
58 // Create the descriptor pool to contain our descriptor. 56 // Create the descriptor pool to contain our descriptor.
59 static constexpr VkDescriptorPoolSize pool_size{ 57 static constexpr VkDescriptorPoolSize pool_size{
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index c5213875b..4db948b6d 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -151,11 +151,9 @@ void ShaderCache::RemovePendingShaders() {
151 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), 151 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
152 marked_for_removal.end()); 152 marked_for_removal.end());
153 153
154 std::vector<ShaderInfo*> removed_shaders; 154 boost::container::small_vector<ShaderInfo*, 16> removed_shaders;
155 removed_shaders.reserve(marked_for_removal.size());
156 155
157 std::scoped_lock lock{lookup_mutex}; 156 std::scoped_lock lock{lookup_mutex};
158
159 for (Entry* const entry : marked_for_removal) { 157 for (Entry* const entry : marked_for_removal) {
160 removed_shaders.push_back(entry->data); 158 removed_shaders.push_back(entry->data);
161 159
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index cb51529e4..e16cd5e73 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -269,6 +269,28 @@ bool IsPixelFormatASTC(PixelFormat format) {
269 } 269 }
270} 270}
271 271
272bool IsPixelFormatBCn(PixelFormat format) {
273 switch (format) {
274 case PixelFormat::BC1_RGBA_UNORM:
275 case PixelFormat::BC2_UNORM:
276 case PixelFormat::BC3_UNORM:
277 case PixelFormat::BC4_UNORM:
278 case PixelFormat::BC4_SNORM:
279 case PixelFormat::BC5_UNORM:
280 case PixelFormat::BC5_SNORM:
281 case PixelFormat::BC1_RGBA_SRGB:
282 case PixelFormat::BC2_SRGB:
283 case PixelFormat::BC3_SRGB:
284 case PixelFormat::BC7_UNORM:
285 case PixelFormat::BC6H_UFLOAT:
286 case PixelFormat::BC6H_SFLOAT:
287 case PixelFormat::BC7_SRGB:
288 return true;
289 default:
290 return false;
291 }
292}
293
272bool IsPixelFormatSRGB(PixelFormat format) { 294bool IsPixelFormatSRGB(PixelFormat format) {
273 switch (format) { 295 switch (format) {
274 case PixelFormat::A8B8G8R8_SRGB: 296 case PixelFormat::A8B8G8R8_SRGB:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 0225d3287..9b9c4d9bc 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -501,6 +501,8 @@ SurfaceType GetFormatType(PixelFormat pixel_format);
501 501
502bool IsPixelFormatASTC(PixelFormat format); 502bool IsPixelFormatASTC(PixelFormat format);
503 503
504bool IsPixelFormatBCn(PixelFormat format);
505
504bool IsPixelFormatSRGB(PixelFormat format); 506bool IsPixelFormatSRGB(PixelFormat format);
505 507
506bool IsPixelFormatInteger(PixelFormat format); 508bool IsPixelFormatInteger(PixelFormat format);
diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp
new file mode 100644
index 000000000..3e26474a3
--- /dev/null
+++ b/src/video_core/texture_cache/decode_bc.cpp
@@ -0,0 +1,129 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <span>
7#include <bc_decoder.h>
8
9#include "common/common_types.h"
10#include "video_core/texture_cache/decode_bc.h"
11
12namespace VideoCommon {
13
14namespace {
15constexpr u32 BLOCK_SIZE = 4;
16
17using VideoCore::Surface::PixelFormat;
18
19constexpr bool IsSigned(PixelFormat pixel_format) {
20 switch (pixel_format) {
21 case PixelFormat::BC4_SNORM:
22 case PixelFormat::BC4_UNORM:
23 case PixelFormat::BC5_SNORM:
24 case PixelFormat::BC5_UNORM:
25 case PixelFormat::BC6H_SFLOAT:
26 case PixelFormat::BC6H_UFLOAT:
27 return true;
28 default:
29 return false;
30 }
31}
32
33constexpr u32 BlockSize(PixelFormat pixel_format) {
34 switch (pixel_format) {
35 case PixelFormat::BC1_RGBA_SRGB:
36 case PixelFormat::BC1_RGBA_UNORM:
37 case PixelFormat::BC4_SNORM:
38 case PixelFormat::BC4_UNORM:
39 return 8;
40 default:
41 return 16;
42 }
43}
44} // Anonymous namespace
45
46u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
47 switch (pixel_format) {
48 case PixelFormat::BC4_SNORM:
49 case PixelFormat::BC4_UNORM:
50 return 1;
51 case PixelFormat::BC5_SNORM:
52 case PixelFormat::BC5_UNORM:
53 return 2;
54 case PixelFormat::BC6H_SFLOAT:
55 case PixelFormat::BC6H_UFLOAT:
56 return 8;
57 default:
58 return 4;
59 }
60}
61
62template <auto decompress, PixelFormat pixel_format>
63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
64 bool is_signed = false) {
65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
66 const u32 block_width = std::min(extent.width, BLOCK_SIZE);
67 const u32 block_height = std::min(extent.height, BLOCK_SIZE);
68 const u32 pitch = extent.width * out_bpp;
69 size_t input_offset = 0;
70 size_t output_offset = 0;
71 for (u32 slice = 0; slice < extent.depth; ++slice) {
72 for (u32 y = 0; y < extent.height; y += block_height) {
73 size_t row_offset = 0;
74 for (u32 x = 0; x < extent.width;
75 x += block_width, row_offset += block_width * out_bpp) {
76 const u8* src = input.data() + input_offset;
77 u8* const dst = output.data() + output_offset + row_offset;
78 if constexpr (IsSigned(pixel_format)) {
79 decompress(src, dst, x, y, extent.width, extent.height, is_signed);
80 } else {
81 decompress(src, dst, x, y, extent.width, extent.height);
82 }
83 input_offset += BlockSize(pixel_format);
84 }
85 output_offset += block_height * pitch;
86 }
87 }
88}
89
90void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
91 VideoCore::Surface::PixelFormat pixel_format) {
92 switch (pixel_format) {
93 case PixelFormat::BC1_RGBA_UNORM:
94 case PixelFormat::BC1_RGBA_SRGB:
95 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent);
96 break;
97 case PixelFormat::BC2_UNORM:
98 case PixelFormat::BC2_SRGB:
99 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
100 break;
101 case PixelFormat::BC3_UNORM:
102 case PixelFormat::BC3_SRGB:
103 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
104 break;
105 case PixelFormat::BC4_SNORM:
106 case PixelFormat::BC4_UNORM:
107 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
108 input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
109 break;
110 case PixelFormat::BC5_SNORM:
111 case PixelFormat::BC5_UNORM:
112 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
113 input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
114 break;
115 case PixelFormat::BC6H_SFLOAT:
116 case PixelFormat::BC6H_UFLOAT:
117 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
118 input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
119 break;
120 case PixelFormat::BC7_SRGB:
121 case PixelFormat::BC7_UNORM:
122 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
123 break;
124 default:
125 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
126 }
127}
128
129} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/decode_bc4.h b/src/video_core/texture_cache/decode_bc.h
index ab2f735be..41d1ec0a3 100644
--- a/src/video_core/texture_cache/decode_bc4.h
+++ b/src/video_core/texture_cache/decode_bc.h
@@ -6,10 +6,14 @@
6#include <span> 6#include <span>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "video_core/surface.h"
9#include "video_core/texture_cache/types.h" 10#include "video_core/texture_cache/types.h"
10 11
11namespace VideoCommon { 12namespace VideoCommon {
12 13
13void DecompressBC4(std::span<const u8> data, Extent3D extent, std::span<u8> output); 14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
15
16void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
17 VideoCore::Surface::PixelFormat pixel_format);
14 18
15} // namespace VideoCommon 19} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/decode_bc4.cpp b/src/video_core/texture_cache/decode_bc4.cpp
deleted file mode 100644
index ef98afdca..000000000
--- a/src/video_core/texture_cache/decode_bc4.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <span>
7
8#include "common/assert.h"
9#include "common/common_types.h"
10#include "video_core/texture_cache/decode_bc4.h"
11#include "video_core/texture_cache/types.h"
12
13namespace VideoCommon {
14
15// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
16[[nodiscard]] constexpr u32 DecompressBlock(u64 bits, u32 x, u32 y) {
17 const u32 code_offset = 16 + 3 * (4 * y + x);
18 const u32 code = (bits >> code_offset) & 7;
19 const u32 red0 = (bits >> 0) & 0xff;
20 const u32 red1 = (bits >> 8) & 0xff;
21 if (red0 > red1) {
22 switch (code) {
23 case 0:
24 return red0;
25 case 1:
26 return red1;
27 case 2:
28 return (6 * red0 + 1 * red1) / 7;
29 case 3:
30 return (5 * red0 + 2 * red1) / 7;
31 case 4:
32 return (4 * red0 + 3 * red1) / 7;
33 case 5:
34 return (3 * red0 + 4 * red1) / 7;
35 case 6:
36 return (2 * red0 + 5 * red1) / 7;
37 case 7:
38 return (1 * red0 + 6 * red1) / 7;
39 }
40 } else {
41 switch (code) {
42 case 0:
43 return red0;
44 case 1:
45 return red1;
46 case 2:
47 return (4 * red0 + 1 * red1) / 5;
48 case 3:
49 return (3 * red0 + 2 * red1) / 5;
50 case 4:
51 return (2 * red0 + 3 * red1) / 5;
52 case 5:
53 return (1 * red0 + 4 * red1) / 5;
54 case 6:
55 return 0;
56 case 7:
57 return 0xff;
58 }
59 }
60 return 0;
61}
62
63void DecompressBC4(std::span<const u8> input, Extent3D extent, std::span<u8> output) {
64 UNIMPLEMENTED_IF_MSG(extent.width % 4 != 0, "Unaligned width={}", extent.width);
65 UNIMPLEMENTED_IF_MSG(extent.height % 4 != 0, "Unaligned height={}", extent.height);
66 static constexpr u32 BLOCK_SIZE = 4;
67 size_t input_offset = 0;
68 for (u32 slice = 0; slice < extent.depth; ++slice) {
69 for (u32 block_y = 0; block_y < extent.height / 4; ++block_y) {
70 for (u32 block_x = 0; block_x < extent.width / 4; ++block_x) {
71 u64 bits;
72 std::memcpy(&bits, &input[input_offset], sizeof(bits));
73 input_offset += sizeof(bits);
74
75 for (u32 y = 0; y < BLOCK_SIZE; ++y) {
76 for (u32 x = 0; x < BLOCK_SIZE; ++x) {
77 const u32 linear_z = slice;
78 const u32 linear_y = block_y * BLOCK_SIZE + y;
79 const u32 linear_x = block_x * BLOCK_SIZE + x;
80 const u32 offset_z = linear_z * extent.width * extent.height;
81 const u32 offset_y = linear_y * extent.width;
82 const u32 offset_x = linear_x;
83 const u32 output_offset = (offset_z + offset_y + offset_x) * 4ULL;
84 const u32 color = DecompressBlock(bits, x, y);
85 output[output_offset + 0] = static_cast<u8>(color);
86 output[output_offset + 1] = 0;
87 output[output_offset + 2] = 0;
88 output[output_offset + 3] = 0xff;
89 }
90 }
91 }
92 }
93 }
94}
95
96} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 1b8a17ee8..55d49d017 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <optional> 7#include <optional>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -108,8 +109,8 @@ struct ImageBase {
108 std::vector<ImageViewInfo> image_view_infos; 109 std::vector<ImageViewInfo> image_view_infos;
109 std::vector<ImageViewId> image_view_ids; 110 std::vector<ImageViewId> image_view_ids;
110 111
111 std::vector<u32> slice_offsets; 112 boost::container::small_vector<u32, 16> slice_offsets;
112 std::vector<SubresourceBase> slice_subresources; 113 boost::container::small_vector<SubresourceBase, 16> slice_subresources;
113 114
114 std::vector<AliasedImage> aliased_images; 115 std::vector<AliasedImage> aliased_images;
115 std::vector<ImageId> overlapping_images; 116 std::vector<ImageId> overlapping_images;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 4027d860b..8190f3ba1 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -186,6 +186,10 @@ void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
186 186
187template <class P> 187template <class P>
188void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) { 188void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
189 if (!Settings::values.barrier_feedback_loops.GetValue()) {
190 return;
191 }
192
189 const bool requires_barrier = [&] { 193 const bool requires_barrier = [&] {
190 for (const auto& view : views) { 194 for (const auto& view : views) {
191 if (!view.id) { 195 if (!view.id) {
@@ -300,7 +304,7 @@ void TextureCache<P>::SynchronizeComputeDescriptors() {
300} 304}
301 305
302template <class P> 306template <class P>
303bool TextureCache<P>::RescaleRenderTargets(bool is_clear) { 307bool TextureCache<P>::RescaleRenderTargets() {
304 auto& flags = maxwell3d->dirty.flags; 308 auto& flags = maxwell3d->dirty.flags;
305 u32 scale_rating = 0; 309 u32 scale_rating = 0;
306 bool rescaled = false; 310 bool rescaled = false;
@@ -338,13 +342,13 @@ bool TextureCache<P>::RescaleRenderTargets(bool is_clear) {
338 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; 342 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
339 if (flags[Dirty::ColorBuffer0 + index] || force) { 343 if (flags[Dirty::ColorBuffer0 + index] || force) {
340 flags[Dirty::ColorBuffer0 + index] = false; 344 flags[Dirty::ColorBuffer0 + index] = false;
341 BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear)); 345 BindRenderTarget(&color_buffer_id, FindColorBuffer(index));
342 } 346 }
343 check_rescale(color_buffer_id, tmp_color_images[index]); 347 check_rescale(color_buffer_id, tmp_color_images[index]);
344 } 348 }
345 if (flags[Dirty::ZetaBuffer] || force) { 349 if (flags[Dirty::ZetaBuffer] || force) {
346 flags[Dirty::ZetaBuffer] = false; 350 flags[Dirty::ZetaBuffer] = false;
347 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear)); 351 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer());
348 } 352 }
349 check_rescale(render_targets.depth_buffer_id, tmp_depth_image); 353 check_rescale(render_targets.depth_buffer_id, tmp_depth_image);
350 354
@@ -409,7 +413,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
409 return; 413 return;
410 } 414 }
411 415
412 const bool rescaled = RescaleRenderTargets(is_clear); 416 const bool rescaled = RescaleRenderTargets();
413 if (is_rescaling != rescaled) { 417 if (is_rescaling != rescaled) {
414 flags[Dirty::RescaleViewports] = true; 418 flags[Dirty::RescaleViewports] = true;
415 flags[Dirty::RescaleScissors] = true; 419 flags[Dirty::RescaleScissors] = true;
@@ -522,7 +526,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
522 526
523template <class P> 527template <class P>
524void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 528void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
525 std::vector<ImageId> images; 529 boost::container::small_vector<ImageId, 16> images;
526 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { 530 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
527 if (!image.IsSafeDownload()) { 531 if (!image.IsSafeDownload()) {
528 return; 532 return;
@@ -575,7 +579,7 @@ std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(V
575 579
576template <class P> 580template <class P>
577void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { 581void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
578 std::vector<ImageId> deleted_images; 582 boost::container::small_vector<ImageId, 16> deleted_images;
579 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); 583 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });
580 for (const ImageId id : deleted_images) { 584 for (const ImageId id : deleted_images) {
581 Image& image = slot_images[id]; 585 Image& image = slot_images[id];
@@ -589,19 +593,11 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
589 593
590template <class P> 594template <class P>
591void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { 595void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) {
592 std::vector<ImageId> deleted_images; 596 boost::container::small_vector<ImageId, 16> deleted_images;
593 ForEachImageInRegionGPU(as_id, gpu_addr, size, 597 ForEachImageInRegionGPU(as_id, gpu_addr, size,
594 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 598 [&](ImageId id, Image&) { deleted_images.push_back(id); });
595 for (const ImageId id : deleted_images) { 599 for (const ImageId id : deleted_images) {
596 Image& image = slot_images[id]; 600 Image& image = slot_images[id];
597 if (True(image.flags & ImageFlagBits::CpuModified)) {
598 return;
599 }
600 image.flags |= ImageFlagBits::CpuModified;
601 if (True(image.flags & ImageFlagBits::Tracked)) {
602 UntrackImage(image, id);
603 }
604 /*
605 if (True(image.flags & ImageFlagBits::Remapped)) { 601 if (True(image.flags & ImageFlagBits::Remapped)) {
606 continue; 602 continue;
607 } 603 }
@@ -609,7 +605,6 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
609 if (True(image.flags & ImageFlagBits::Tracked)) { 605 if (True(image.flags & ImageFlagBits::Tracked)) {
610 UntrackImage(image, id); 606 UntrackImage(image, id);
611 } 607 }
612 */
613 } 608 }
614} 609}
615 610
@@ -875,6 +870,10 @@ ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, boo
875 return NULL_IMAGE_ID; 870 return NULL_IMAGE_ID;
876 } 871 }
877 auto& image = slot_images[image_id]; 872 auto& image = slot_images[image_id];
873 if (image.info.type == ImageType::e3D) {
874 // Don't accelerate 3D images.
875 return NULL_IMAGE_ID;
876 }
878 if (!is_upload && !image.info.dma_downloaded) { 877 if (!is_upload && !image.info.dma_downloaded) {
879 // Force a full sync. 878 // Force a full sync.
880 image.info.dma_downloaded = true; 879 image.info.dma_downloaded = true;
@@ -1097,7 +1096,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
1097 const bool native_bgr = runtime.HasNativeBgr(); 1096 const bool native_bgr = runtime.HasNativeBgr();
1098 const bool flexible_formats = True(options & RelaxedOptions::Format); 1097 const bool flexible_formats = True(options & RelaxedOptions::Format);
1099 ImageId image_id{}; 1098 ImageId image_id{};
1100 boost::container::small_vector<ImageId, 1> image_ids; 1099 boost::container::small_vector<ImageId, 8> image_ids;
1101 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1100 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1102 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1101 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1103 return false; 1102 return false;
@@ -1618,7 +1617,7 @@ ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr)
1618 } 1617 }
1619 } 1618 }
1620 ImageId image_id{}; 1619 ImageId image_id{};
1621 boost::container::small_vector<ImageId, 1> image_ids; 1620 boost::container::small_vector<ImageId, 8> image_ids;
1622 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1621 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1623 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1622 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1624 return false; 1623 return false;
@@ -1678,7 +1677,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
1678} 1677}
1679 1678
1680template <class P> 1679template <class P>
1681ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { 1680ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
1682 const auto& regs = maxwell3d->regs; 1681 const auto& regs = maxwell3d->regs;
1683 if (index >= regs.rt_control.count) { 1682 if (index >= regs.rt_control.count) {
1684 return ImageViewId{}; 1683 return ImageViewId{};
@@ -1692,11 +1691,11 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
1692 return ImageViewId{}; 1691 return ImageViewId{};
1693 } 1692 }
1694 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); 1693 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode);
1695 return FindRenderTargetView(info, gpu_addr, is_clear); 1694 return FindRenderTargetView(info, gpu_addr);
1696} 1695}
1697 1696
1698template <class P> 1697template <class P>
1699ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { 1698ImageViewId TextureCache<P>::FindDepthBuffer() {
1700 const auto& regs = maxwell3d->regs; 1699 const auto& regs = maxwell3d->regs;
1701 if (!regs.zeta_enable) { 1700 if (!regs.zeta_enable) {
1702 return ImageViewId{}; 1701 return ImageViewId{};
@@ -1706,18 +1705,16 @@ ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
1706 return ImageViewId{}; 1705 return ImageViewId{};
1707 } 1706 }
1708 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); 1707 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
1709 return FindRenderTargetView(info, gpu_addr, is_clear); 1708 return FindRenderTargetView(info, gpu_addr);
1710} 1709}
1711 1710
1712template <class P> 1711template <class P>
1713ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 1712ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr) {
1714 bool is_clear) {
1715 const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{};
1716 ImageId image_id{}; 1713 ImageId image_id{};
1717 bool delete_state = has_deleted_images; 1714 bool delete_state = has_deleted_images;
1718 do { 1715 do {
1719 has_deleted_images = false; 1716 has_deleted_images = false;
1720 image_id = FindOrInsertImage(info, gpu_addr, options); 1717 image_id = FindOrInsertImage(info, gpu_addr);
1721 delete_state |= has_deleted_images; 1718 delete_state |= has_deleted_images;
1722 } while (has_deleted_images); 1719 } while (has_deleted_images);
1723 has_deleted_images = delete_state; 1720 has_deleted_images = delete_state;
@@ -1940,7 +1937,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1940 image.map_view_id = map_id; 1937 image.map_view_id = map_id;
1941 return; 1938 return;
1942 } 1939 }
1943 std::vector<ImageViewId> sparse_maps{}; 1940 boost::container::small_vector<ImageViewId, 16> sparse_maps;
1944 ForEachSparseSegment( 1941 ForEachSparseSegment(
1945 image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { 1942 image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
1946 auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); 1943 auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id);
@@ -2215,7 +2212,7 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept {
2215 2212
2216template <class P> 2213template <class P>
2217void TextureCache<P>::SynchronizeAliases(ImageId image_id) { 2214void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
2218 boost::container::small_vector<const AliasedImage*, 1> aliased_images; 2215 boost::container::small_vector<const AliasedImage*, 8> aliased_images;
2219 Image& image = slot_images[image_id]; 2216 Image& image = slot_images[image_id];
2220 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); 2217 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);
2221 bool any_modified = True(image.flags & ImageFlagBits::GpuModified); 2218 bool any_modified = True(image.flags & ImageFlagBits::GpuModified);
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index d96ddea9d..e9ec91265 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -56,7 +56,7 @@ struct ImageViewInOut {
56struct AsyncDecodeContext { 56struct AsyncDecodeContext {
57 ImageId image_id; 57 ImageId image_id;
58 Common::ScratchBuffer<u8> decoded_data; 58 Common::ScratchBuffer<u8> decoded_data;
59 std::vector<BufferImageCopy> copies; 59 boost::container::small_vector<BufferImageCopy, 16> copies;
60 std::mutex mutex; 60 std::mutex mutex;
61 std::atomic_bool complete; 61 std::atomic_bool complete;
62}; 62};
@@ -178,9 +178,8 @@ public:
178 void SynchronizeComputeDescriptors(); 178 void SynchronizeComputeDescriptors();
179 179
180 /// Updates the Render Targets if they can be rescaled 180 /// Updates the Render Targets if they can be rescaled
181 /// @param is_clear True when the render targets are being used for clears
182 /// @retval True if the Render Targets have been rescaled. 181 /// @retval True if the Render Targets have been rescaled.
183 bool RescaleRenderTargets(bool is_clear); 182 bool RescaleRenderTargets();
184 183
185 /// Update bound render targets and upload memory if necessary 184 /// Update bound render targets and upload memory if necessary
186 /// @param is_clear True when the render targets are being used for clears 185 /// @param is_clear True when the render targets are being used for clears
@@ -336,14 +335,13 @@ private:
336 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); 335 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
337 336
338 /// Find or create an image view for the given color buffer index 337 /// Find or create an image view for the given color buffer index
339 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear); 338 [[nodiscard]] ImageViewId FindColorBuffer(size_t index);
340 339
341 /// Find or create an image view for the depth buffer 340 /// Find or create an image view for the depth buffer
342 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear); 341 [[nodiscard]] ImageViewId FindDepthBuffer();
343 342
344 /// Find or create a view for a render target with the given image parameters 343 /// Find or create a view for a render target with the given image parameters
345 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 344 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr);
346 bool is_clear);
347 345
348 /// Iterates over all the images in a region calling func 346 /// Iterates over all the images in a region calling func
349 template <typename Func> 347 template <typename Func>
@@ -431,7 +429,7 @@ private:
431 429
432 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table; 430 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
433 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table; 431 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table;
434 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; 432 std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
435 433
436 VAddr virtual_invalid_space{}; 434 VAddr virtual_invalid_space{};
437 435
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 95a5b47d8..9a618a57a 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -24,7 +24,7 @@
24#include "video_core/engines/maxwell_3d.h" 24#include "video_core/engines/maxwell_3d.h"
25#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
26#include "video_core/surface.h" 26#include "video_core/surface.h"
27#include "video_core/texture_cache/decode_bc4.h" 27#include "video_core/texture_cache/decode_bc.h"
28#include "video_core/texture_cache/format_lookup_table.h" 28#include "video_core/texture_cache/format_lookup_table.h"
29#include "video_core/texture_cache/formatter.h" 29#include "video_core/texture_cache/formatter.h"
30#include "video_core/texture_cache/samples_helper.h" 30#include "video_core/texture_cache/samples_helper.h"
@@ -61,8 +61,6 @@ using VideoCore::Surface::PixelFormatFromDepthFormat;
61using VideoCore::Surface::PixelFormatFromRenderTargetFormat; 61using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
62using VideoCore::Surface::SurfaceType; 62using VideoCore::Surface::SurfaceType;
63 63
64constexpr u32 CONVERTED_BYTES_PER_BLOCK = BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
65
66struct LevelInfo { 64struct LevelInfo {
67 Extent3D size; 65 Extent3D size;
68 Extent3D block; 66 Extent3D block;
@@ -329,13 +327,13 @@ template <u32 GOB_EXTENT>
329 327
330[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D( 328[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D(
331 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { 329 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
332 const std::vector<u32> slice_offsets = CalculateSliceOffsets(new_info); 330 const auto slice_offsets = CalculateSliceOffsets(new_info);
333 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr); 331 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr);
334 const auto it = std::ranges::find(slice_offsets, diff); 332 const auto it = std::ranges::find(slice_offsets, diff);
335 if (it == slice_offsets.end()) { 333 if (it == slice_offsets.end()) {
336 return std::nullopt; 334 return std::nullopt;
337 } 335 }
338 const std::vector subresources = CalculateSliceSubresources(new_info); 336 const auto subresources = CalculateSliceSubresources(new_info);
339 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)]; 337 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)];
340 const ImageInfo& info = overlap.info; 338 const ImageInfo& info = overlap.info;
341 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) { 339 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) {
@@ -612,7 +610,8 @@ u32 CalculateConvertedSizeBytes(const ImageInfo& info) noexcept {
612 } 610 }
613 return output_size; 611 return output_size;
614 } 612 }
615 return NumBlocksPerLayer(info, TILE_SIZE) * info.resources.layers * CONVERTED_BYTES_PER_BLOCK; 613 return NumBlocksPerLayer(info, TILE_SIZE) * info.resources.layers *
614 ConvertedBytesPerBlock(info.format);
616} 615}
617 616
618u32 CalculateLayerStride(const ImageInfo& info) noexcept { 617u32 CalculateLayerStride(const ImageInfo& info) noexcept {
@@ -655,9 +654,9 @@ LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {
655 return sizes; 654 return sizes;
656} 655}
657 656
658std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { 657boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info) {
659 ASSERT(info.type == ImageType::e3D); 658 ASSERT(info.type == ImageType::e3D);
660 std::vector<u32> offsets; 659 boost::container::small_vector<u32, 16> offsets;
661 offsets.reserve(NumSlices(info)); 660 offsets.reserve(NumSlices(info));
662 661
663 const LevelInfo level_info = MakeLevelInfo(info); 662 const LevelInfo level_info = MakeLevelInfo(info);
@@ -679,9 +678,10 @@ std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) {
679 return offsets; 678 return offsets;
680} 679}
681 680
682std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info) { 681boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources(
682 const ImageInfo& info) {
683 ASSERT(info.type == ImageType::e3D); 683 ASSERT(info.type == ImageType::e3D);
684 std::vector<SubresourceBase> subresources; 684 boost::container::small_vector<SubresourceBase, 16> subresources;
685 subresources.reserve(NumSlices(info)); 685 subresources.reserve(NumSlices(info));
686 for (s32 level = 0; level < info.resources.levels; ++level) { 686 for (s32 level = 0; level < info.resources.levels; ++level) {
687 const s32 depth = AdjustMipSize(info.size.depth, level); 687 const s32 depth = AdjustMipSize(info.size.depth, level);
@@ -723,8 +723,10 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {
723 } 723 }
724} 724}
725 725
726std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, 726boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(const ImageInfo& dst,
727 SubresourceBase base, u32 up_scale, u32 down_shift) { 727 const ImageInfo& src,
728 SubresourceBase base,
729 u32 up_scale, u32 down_shift) {
728 ASSERT(dst.resources.levels >= src.resources.levels); 730 ASSERT(dst.resources.levels >= src.resources.levels);
729 731
730 const bool is_dst_3d = dst.type == ImageType::e3D; 732 const bool is_dst_3d = dst.type == ImageType::e3D;
@@ -733,7 +735,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
733 ASSERT(src.resources.levels == 1); 735 ASSERT(src.resources.levels == 1);
734 } 736 }
735 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D}; 737 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D};
736 std::vector<ImageCopy> copies; 738 boost::container::small_vector<ImageCopy, 16> copies;
737 copies.reserve(src.resources.levels); 739 copies.reserve(src.resources.levels);
738 for (s32 level = 0; level < src.resources.levels; ++level) { 740 for (s32 level = 0; level < src.resources.levels; ++level) {
739 ImageCopy& copy = copies.emplace_back(); 741 ImageCopy& copy = copies.emplace_back();
@@ -770,9 +772,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
770 return copies; 772 return copies;
771} 773}
772 774
773std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale, 775boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(const ImageInfo& src,
774 u32 down_shift) { 776 u32 up_scale,
775 std::vector<ImageCopy> copies; 777 u32 down_shift) {
778 boost::container::small_vector<ImageCopy, 16> copies;
776 copies.reserve(src.resources.levels); 779 copies.reserve(src.resources.levels);
777 const bool is_3d = src.type == ImageType::e3D; 780 const bool is_3d = src.type == ImageType::e3D;
778 for (s32 level = 0; level < src.resources.levels; ++level) { 781 for (s32 level = 0; level < src.resources.levels; ++level) {
@@ -824,9 +827,11 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
824 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value(); 827 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value();
825} 828}
826 829
827std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 830boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
828 const ImageInfo& info, std::span<const u8> input, 831 GPUVAddr gpu_addr,
829 std::span<u8> output) { 832 const ImageInfo& info,
833 std::span<const u8> input,
834 std::span<u8> output) {
830 const size_t guest_size_bytes = input.size_bytes(); 835 const size_t guest_size_bytes = input.size_bytes();
831 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 836 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
832 const Extent3D size = info.size; 837 const Extent3D size = info.size;
@@ -861,7 +866,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
861 info.tile_width_spacing); 866 info.tile_width_spacing);
862 size_t guest_offset = 0; 867 size_t guest_offset = 0;
863 u32 host_offset = 0; 868 u32 host_offset = 0;
864 std::vector<BufferImageCopy> copies(num_levels); 869 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
865 870
866 for (s32 level = 0; level < num_levels; ++level) { 871 for (s32 level = 0; level < num_levels; ++level) {
867 const Extent3D level_size = AdjustMipSize(size, level); 872 const Extent3D level_size = AdjustMipSize(size, level);
@@ -939,7 +944,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
939 tile_size.height, output.subspan(output_offset)); 944 tile_size.height, output.subspan(output_offset));
940 945
941 output_offset += copy.image_extent.width * copy.image_extent.height * 946 output_offset += copy.image_extent.width * copy.image_extent.height *
942 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 947 copy.image_subresource.num_layers *
948 BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
943 } else if (astc) { 949 } else if (astc) {
944 // BC1 uses 0.5 bytes per texel 950 // BC1 uses 0.5 bytes per texel
945 // BC3 uses 1 byte per texel 951 // BC3 uses 1 byte per texel
@@ -950,7 +956,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
950 956
951 const u32 plane_dim = copy.image_extent.width * copy.image_extent.height; 957 const u32 plane_dim = copy.image_extent.width * copy.image_extent.height;
952 const u32 level_size = plane_dim * copy.image_extent.depth * 958 const u32 level_size = plane_dim * copy.image_extent.depth *
953 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 959 copy.image_subresource.num_layers *
960 BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
954 decode_scratch.resize_destructive(level_size); 961 decode_scratch.resize_destructive(level_size);
955 962
956 Tegra::Texture::ASTC::Decompress( 963 Tegra::Texture::ASTC::Decompress(
@@ -970,15 +977,20 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
970 bpp_div; 977 bpp_div;
971 output_offset += static_cast<u32>(copy.buffer_size); 978 output_offset += static_cast<u32>(copy.buffer_size);
972 } else { 979 } else {
973 DecompressBC4(input_offset, copy.image_extent, output.subspan(output_offset)); 980 const Extent3D image_extent{
974 981 .width = copy.image_extent.width,
982 .height = copy.image_extent.height * copy.image_subresource.num_layers,
983 .depth = copy.image_extent.depth,
984 };
985 DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
975 output_offset += copy.image_extent.width * copy.image_extent.height * 986 output_offset += copy.image_extent.width * copy.image_extent.height *
976 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 987 copy.image_subresource.num_layers *
988 ConvertedBytesPerBlock(info.format);
977 } 989 }
978 } 990 }
979} 991}
980 992
981std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { 993boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) {
982 const Extent3D size = info.size; 994 const Extent3D size = info.size;
983 const u32 bytes_per_block = BytesPerBlock(info.format); 995 const u32 bytes_per_block = BytesPerBlock(info.format);
984 if (info.type == ImageType::Linear) { 996 if (info.type == ImageType::Linear) {
@@ -1006,7 +1018,7 @@ std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) {
1006 1018
1007 u32 host_offset = 0; 1019 u32 host_offset = 0;
1008 1020
1009 std::vector<BufferImageCopy> copies(num_levels); 1021 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
1010 for (s32 level = 0; level < num_levels; ++level) { 1022 for (s32 level = 0; level < num_levels; ++level) {
1011 const Extent3D level_size = AdjustMipSize(size, level); 1023 const Extent3D level_size = AdjustMipSize(size, level);
1012 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); 1024 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);
@@ -1042,10 +1054,10 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) {
1042 return AdjustMipBlockSize(num_tiles, level_info.block, level); 1054 return AdjustMipBlockSize(num_tiles, level_info.block, level);
1043} 1055}
1044 1056
1045std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { 1057boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) {
1046 const Extent2D tile_size = DefaultBlockSize(info.format); 1058 const Extent2D tile_size = DefaultBlockSize(info.format);
1047 if (info.type == ImageType::Linear) { 1059 if (info.type == ImageType::Linear) {
1048 return std::vector{SwizzleParameters{ 1060 return {SwizzleParameters{
1049 .num_tiles = AdjustTileSize(info.size, tile_size), 1061 .num_tiles = AdjustTileSize(info.size, tile_size),
1050 .block = {}, 1062 .block = {},
1051 .buffer_offset = 0, 1063 .buffer_offset = 0,
@@ -1057,7 +1069,7 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {
1057 const s32 num_levels = info.resources.levels; 1069 const s32 num_levels = info.resources.levels;
1058 1070
1059 u32 guest_offset = 0; 1071 u32 guest_offset = 0;
1060 std::vector<SwizzleParameters> params(num_levels); 1072 boost::container::small_vector<SwizzleParameters, 16> params(num_levels);
1061 for (s32 level = 0; level < num_levels; ++level) { 1073 for (s32 level = 0; level < num_levels; ++level) {
1062 const Extent3D level_size = AdjustMipSize(size, level); 1074 const Extent3D level_size = AdjustMipSize(size, level);
1063 const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); 1075 const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 84aa6880d..ab45a43c4 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -5,6 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7#include <span> 7#include <span>
8#include <boost/container/small_vector.hpp>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/scratch_buffer.h" 11#include "common/scratch_buffer.h"
@@ -40,9 +41,10 @@ struct OverlapResult {
40 41
41[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept; 42[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept;
42 43
43[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); 44[[nodiscard]] boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info);
44 45
45[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info); 46[[nodiscard]] boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources(
47 const ImageInfo& info);
46 48
47[[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level); 49[[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level);
48 50
@@ -51,21 +53,18 @@ struct OverlapResult {
51 53
52[[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept; 54[[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept;
53 55
54[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, 56[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(
55 const ImageInfo& src, 57 const ImageInfo& dst, const ImageInfo& src, SubresourceBase base, u32 up_scale = 1,
56 SubresourceBase base, u32 up_scale = 1, 58 u32 down_shift = 0);
57 u32 down_shift = 0);
58 59
59[[nodiscard]] std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, 60[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(
60 u32 up_scale = 1, 61 const ImageInfo& src, u32 up_scale = 1, u32 down_shift = 0);
61 u32 down_shift = 0);
62 62
63[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); 63[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
64 64
65[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, 65[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(
66 GPUVAddr gpu_addr, const ImageInfo& info, 66 Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
67 std::span<const u8> input, 67 std::span<const u8> input, std::span<u8> output);
68 std::span<u8> output);
69 68
70[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 69[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
71 const ImageBase& image, std::span<u8> output); 70 const ImageBase& image, std::span<u8> output);
@@ -73,13 +72,15 @@ struct OverlapResult {
73void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, 72void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
74 std::span<BufferImageCopy> copies); 73 std::span<BufferImageCopy> copies);
75 74
76[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info); 75[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(
76 const ImageInfo& info);
77 77
78[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level); 78[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level);
79 79
80[[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level); 80[[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level);
81 81
82[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); 82[[nodiscard]] boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(
83 const ImageInfo& info);
83 84
84void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 85void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
85 std::span<const BufferImageCopy> copies, std::span<const u8> memory, 86 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
diff --git a/src/video_core/textures/bcn.cpp b/src/video_core/textures/bcn.cpp
index 671212a49..16ddbe320 100644
--- a/src/video_core/textures/bcn.cpp
+++ b/src/video_core/textures/bcn.cpp
@@ -3,7 +3,6 @@
3 3
4#include <stb_dxt.h> 4#include <stb_dxt.h>
5#include <string.h> 5#include <string.h>
6
7#include "common/alignment.h" 6#include "common/alignment.h"
8#include "video_core/textures/bcn.h" 7#include "video_core/textures/bcn.h"
9#include "video_core/textures/workers.h" 8#include "video_core/textures/workers.h"
diff --git a/src/video_core/textures/bcn.h b/src/video_core/textures/bcn.h
index 6464af885..d5d2a16c9 100644
--- a/src/video_core/textures/bcn.h
+++ b/src/video_core/textures/bcn.h
@@ -4,14 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include <span> 6#include <span>
7#include <stdint.h> 7
8#include "common/common_types.h"
8 9
9namespace Tegra::Texture::BCN { 10namespace Tegra::Texture::BCN {
10 11
11void CompressBC1(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 12void CompressBC1(std::span<const u8> data, u32 width, u32 height, u32 depth, std::span<u8> output);
12 std::span<uint8_t> output);
13 13
14void CompressBC3(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 14void CompressBC3(std::span<const u8> data, u32 width, u32 height, u32 depth, std::span<u8> output);
15 std::span<uint8_t> output);
16 15
17} // namespace Tegra::Texture::BCN 16} // namespace Tegra::Texture::BCN
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
index 155599316..1f353d2df 100644
--- a/src/video_core/transform_feedback.cpp
+++ b/src/video_core/transform_feedback.cpp
@@ -13,7 +13,7 @@
13 13
14namespace VideoCommon { 14namespace VideoCommon {
15 15
16std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 16std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(
17 const TransformFeedbackState& state) { 17 const TransformFeedbackState& state) {
18 static constexpr std::array VECTORS{ 18 static constexpr std::array VECTORS{
19 28U, // gl_Position 19 28U, // gl_Position
@@ -62,7 +62,8 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
62 216U, // gl_TexCoord[6] 62 216U, // gl_TexCoord[6]
63 220U, // gl_TexCoord[7] 63 220U, // gl_TexCoord[7]
64 }; 64 };
65 std::vector<Shader::TransformFeedbackVarying> xfb(256); 65 std::array<Shader::TransformFeedbackVarying, 256> xfb{};
66 u32 count{0};
66 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { 67 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) {
67 const auto& locations = state.varyings[buffer]; 68 const auto& locations = state.varyings[buffer];
68 const auto& layout = state.layouts[buffer]; 69 const auto& layout = state.layouts[buffer];
@@ -103,11 +104,12 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
103 } 104 }
104 } 105 }
105 xfb[attribute] = varying; 106 xfb[attribute] = varying;
107 count = std::max(count, attribute);
106 highest = std::max(highest, (base_offset + varying.components) * 4); 108 highest = std::max(highest, (base_offset + varying.components) * 4);
107 } 109 }
108 UNIMPLEMENTED_IF(highest != layout.stride); 110 UNIMPLEMENTED_IF(highest != layout.stride);
109 } 111 }
110 return xfb; 112 return {xfb, count + 1};
111} 113}
112 114
113} // namespace VideoCommon 115} // namespace VideoCommon
diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h
index d13eb16c3..401b1352a 100644
--- a/src/video_core/transform_feedback.h
+++ b/src/video_core/transform_feedback.h
@@ -24,7 +24,7 @@ struct TransformFeedbackState {
24 varyings; 24 varyings;
25}; 25};
26 26
27std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 27std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(
28 const TransformFeedbackState& state); 28 const TransformFeedbackState& state);
29 29
30} // namespace VideoCommon 30} // namespace VideoCommon
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
index 9de484c29..67e8065a4 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -7,10 +7,10 @@
7 7
8namespace Vulkan { 8namespace Vulkan {
9namespace { 9namespace {
10VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, 10VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
11 VkDebugUtilsMessageTypeFlagsEXT type, 11 VkDebugUtilsMessageTypeFlagsEXT type,
12 const VkDebugUtilsMessengerCallbackDataEXT* data, 12 const VkDebugUtilsMessengerCallbackDataEXT* data,
13 [[maybe_unused]] void* user_data) { 13 [[maybe_unused]] void* user_data) {
14 // Skip logging known false-positive validation errors 14 // Skip logging known false-positive validation errors
15 switch (static_cast<u32>(data->messageIdNumber)) { 15 switch (static_cast<u32>(data->messageIdNumber)) {
16#ifdef ANDROID 16#ifdef ANDROID
@@ -62,9 +62,26 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
62 } 62 }
63 return VK_FALSE; 63 return VK_FALSE;
64} 64}
65
66VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
67 uint64_t object, size_t location, int32_t messageCode,
68 const char* pLayerPrefix, const char* pMessage, void* pUserData) {
69 const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags);
70 const std::string_view message{pMessage};
71 if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
72 LOG_CRITICAL(Render_Vulkan, "{}", message);
73 } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
74 LOG_WARNING(Render_Vulkan, "{}", message);
75 } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
76 LOG_INFO(Render_Vulkan, "{}", message);
77 } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
78 LOG_DEBUG(Render_Vulkan, "{}", message);
79 }
80 return VK_FALSE;
81}
65} // Anonymous namespace 82} // Anonymous namespace
66 83
67vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) { 84vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) {
68 return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{ 85 return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{
69 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 86 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
70 .pNext = nullptr, 87 .pNext = nullptr,
@@ -76,7 +93,18 @@ vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
76 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 93 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
77 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | 94 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
78 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, 95 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
79 .pfnUserCallback = Callback, 96 .pfnUserCallback = DebugUtilCallback,
97 .pUserData = nullptr,
98 });
99}
100
101vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) {
102 return instance.CreateDebugReportCallback({
103 .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
104 .pNext = nullptr,
105 .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
106 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
107 .pfnCallback = DebugReportCallback,
80 .pUserData = nullptr, 108 .pUserData = nullptr,
81 }); 109 });
82} 110}
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h
index 71b1f69ec..a8af7b406 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.h
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.h
@@ -7,6 +7,8 @@
7 7
8namespace Vulkan { 8namespace Vulkan {
9 9
10vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance); 10vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance);
11
12vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance);
11 13
12} // namespace Vulkan 14} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index dcedf4425..421e71e5a 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -22,6 +22,8 @@
22#include <adrenotools/bcenabler.h> 22#include <adrenotools/bcenabler.h>
23#endif 23#endif
24 24
25#include <vk_mem_alloc.h>
26
25namespace Vulkan { 27namespace Vulkan {
26using namespace Common::Literals; 28using namespace Common::Literals;
27namespace { 29namespace {
@@ -316,6 +318,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
316std::vector<const char*> ExtensionListForVulkan( 318std::vector<const char*> ExtensionListForVulkan(
317 const std::set<std::string, std::less<>>& extensions) { 319 const std::set<std::string, std::less<>>& extensions) {
318 std::vector<const char*> output; 320 std::vector<const char*> output;
321 output.reserve(extensions.size());
319 for (const auto& extension : extensions) { 322 for (const auto& extension : extensions) {
320 output.push_back(extension.c_str()); 323 output.push_back(extension.c_str());
321 } 324 }
@@ -346,7 +349,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
346 const bool is_s8gen2 = device_id == 0x43050a01; 349 const bool is_s8gen2 = device_id == 0x43050a01;
347 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; 350 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
348 351
349 if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) { 352 if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) {
350 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway"); 353 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
351 } else if (!is_suitable) { 354 } else if (!is_suitable) {
352 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER); 355 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
@@ -525,6 +528,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
525 } 528 }
526 529
527 sets_per_pool = 64; 530 sets_per_pool = 64;
531 if (extensions.extended_dynamic_state3 && is_amd_driver &&
532 properties.properties.driverVersion >= VK_MAKE_API_VERSION(0, 2, 0, 270)) {
533 LOG_WARNING(Render_Vulkan,
534 "AMD drivers after 23.5.2 have broken extendedDynamicState3ColorBlendEquation");
535 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
536 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
537 dynamic_state3_blending = false;
538 }
528 if (is_amd_driver) { 539 if (is_amd_driver) {
529 // AMD drivers need a higher amount of Sets per Pool in certain circumstances like in XC2. 540 // AMD drivers need a higher amount of Sets per Pool in certain circumstances like in XC2.
530 sets_per_pool = 96; 541 sets_per_pool = 96;
@@ -562,6 +573,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
562 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); 573 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
563 cant_blit_msaa = true; 574 cant_blit_msaa = true;
564 } 575 }
576 has_broken_compute =
577 CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
578 !Settings::values.enable_compute_pipelines.GetValue();
565 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { 579 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) {
566 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); 580 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
567 must_emulate_bgr565 = true; 581 must_emulate_bgr565 = true;
@@ -592,9 +606,31 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
592 606
593 graphics_queue = logical.GetQueue(graphics_family); 607 graphics_queue = logical.GetQueue(graphics_family);
594 present_queue = logical.GetQueue(present_family); 608 present_queue = logical.GetQueue(present_family);
609
610 VmaVulkanFunctions functions{};
611 functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr;
612 functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr;
613
614 const VmaAllocatorCreateInfo allocator_info = {
615 .flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT,
616 .physicalDevice = physical,
617 .device = *logical,
618 .preferredLargeHeapBlockSize = 0,
619 .pAllocationCallbacks = nullptr,
620 .pDeviceMemoryCallbacks = nullptr,
621 .pHeapSizeLimit = nullptr,
622 .pVulkanFunctions = &functions,
623 .instance = instance,
624 .vulkanApiVersion = VK_API_VERSION_1_1,
625 .pTypeExternalMemoryHandleTypes = nullptr,
626 };
627
628 vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
595} 629}
596 630
597Device::~Device() = default; 631Device::~Device() {
632 vmaDestroyAllocator(allocator);
633}
598 634
599VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, 635VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
600 FormatType format_type) const { 636 FormatType format_type) const {
@@ -877,6 +913,10 @@ bool Device::GetSuitability(bool requires_swapchain) {
877 properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; 913 properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
878 SetNext(next, properties.driver); 914 SetNext(next, properties.driver);
879 915
916 // Retrieve subgroup properties.
917 properties.subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
918 SetNext(next, properties.subgroup_properties);
919
880 // Retrieve relevant extension properties. 920 // Retrieve relevant extension properties.
881 if (extensions.shader_float_controls) { 921 if (extensions.shader_float_controls) {
882 properties.float_controls.sType = 922 properties.float_controls.sType =
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 8c7e44fcb..1f17265d5 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -10,9 +10,12 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/logging/log.h"
13#include "common/settings.h" 14#include "common/settings.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
15 16
17VK_DEFINE_HANDLE(VmaAllocator)
18
16// Define all features which may be used by the implementation here. 19// Define all features which may be used by the implementation here.
17// Vulkan version in the macro describes the minimum version required for feature availability. 20// Vulkan version in the macro describes the minimum version required for feature availability.
18// If the Vulkan version is lower than the required version, the named extension is required. 21// If the Vulkan version is lower than the required version, the named extension is required.
@@ -198,6 +201,11 @@ public:
198 return dld; 201 return dld;
199 } 202 }
200 203
204 /// Returns the VMA allocator.
205 VmaAllocator GetAllocator() const {
206 return allocator;
207 }
208
201 /// Returns the logical device. 209 /// Returns the logical device.
202 const vk::Device& GetLogical() const { 210 const vk::Device& GetLogical() const {
203 return logical; 211 return logical;
@@ -285,6 +293,11 @@ public:
285 return features.features.textureCompressionASTC_LDR; 293 return features.features.textureCompressionASTC_LDR;
286 } 294 }
287 295
296 /// Returns true if BCn is natively supported.
297 bool IsOptimalBcnSupported() const {
298 return features.features.textureCompressionBC;
299 }
300
288 /// Returns true if descriptor aliasing is natively supported. 301 /// Returns true if descriptor aliasing is natively supported.
289 bool IsDescriptorAliasingSupported() const { 302 bool IsDescriptorAliasingSupported() const {
290 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; 303 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
@@ -315,6 +328,11 @@ public:
315 return properties.subgroup_size_control.requiredSubgroupSizeStages & stage; 328 return properties.subgroup_size_control.requiredSubgroupSizeStages & stage;
316 } 329 }
317 330
331 /// Returns true if the device supports the provided subgroup feature.
332 bool IsSubgroupFeatureSupported(VkSubgroupFeatureFlagBits feature) const {
333 return properties.subgroup_properties.supportedOperations & feature;
334 }
335
318 /// Returns the maximum number of push descriptors. 336 /// Returns the maximum number of push descriptors.
319 u32 MaxPushDescriptors() const { 337 u32 MaxPushDescriptors() const {
320 return properties.push_descriptor.maxPushDescriptors; 338 return properties.push_descriptor.maxPushDescriptors;
@@ -380,6 +398,11 @@ public:
380 return extensions.swapchain_mutable_format; 398 return extensions.swapchain_mutable_format;
381 } 399 }
382 400
401 /// Returns true if VK_KHR_shader_float_controls is enabled.
402 bool IsKhrShaderFloatControlsSupported() const {
403 return extensions.shader_float_controls;
404 }
405
383 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 406 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
384 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 407 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
385 return extensions.workgroup_memory_explicit_layout; 408 return extensions.workgroup_memory_explicit_layout;
@@ -405,6 +428,11 @@ public:
405 return extensions.sampler_filter_minmax; 428 return extensions.sampler_filter_minmax;
406 } 429 }
407 430
431 /// Returns true if the device supports VK_EXT_shader_stencil_export.
432 bool IsExtShaderStencilExportSupported() const {
433 return extensions.shader_stencil_export;
434 }
435
408 /// Returns true if the device supports VK_EXT_depth_range_unrestricted. 436 /// Returns true if the device supports VK_EXT_depth_range_unrestricted.
409 bool IsExtDepthRangeUnrestrictedSupported() const { 437 bool IsExtDepthRangeUnrestrictedSupported() const {
410 return extensions.depth_range_unrestricted; 438 return extensions.depth_range_unrestricted;
@@ -474,9 +502,9 @@ public:
474 return extensions.vertex_input_dynamic_state; 502 return extensions.vertex_input_dynamic_state;
475 } 503 }
476 504
477 /// Returns true if the device supports VK_EXT_shader_stencil_export. 505 /// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation
478 bool IsExtShaderStencilExportSupported() const { 506 bool IsExtShaderDemoteToHelperInvocationSupported() const {
479 return extensions.shader_stencil_export; 507 return extensions.shader_demote_to_helper_invocation;
480 } 508 }
481 509
482 /// Returns true if the device supports VK_EXT_conservative_rasterization. 510 /// Returns true if the device supports VK_EXT_conservative_rasterization.
@@ -510,12 +538,17 @@ public:
510 if (extensions.spirv_1_4) { 538 if (extensions.spirv_1_4) {
511 return 0x00010400U; 539 return 0x00010400U;
512 } 540 }
513 return 0x00010000U; 541 return 0x00010300U;
514 } 542 }
515 543
516 /// Returns true when a known debugging tool is attached. 544 /// Returns true when a known debugging tool is attached.
517 bool HasDebuggingToolAttached() const { 545 bool HasDebuggingToolAttached() const {
518 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue(); 546 return has_renderdoc || has_nsight_graphics;
547 }
548
549 /// @returns True if compute pipelines can cause crashing.
550 bool HasBrokenCompute() const {
551 return has_broken_compute;
519 } 552 }
520 553
521 /// Returns true when the device does not properly support cube compatibility. 554 /// Returns true when the device does not properly support cube compatibility.
@@ -575,10 +608,30 @@ public:
575 return properties.properties.limits.maxVertexInputBindings; 608 return properties.properties.limits.maxVertexInputBindings;
576 } 609 }
577 610
611 u32 GetMaxViewports() const {
612 return properties.properties.limits.maxViewports;
613 }
614
578 bool SupportsConditionalBarriers() const { 615 bool SupportsConditionalBarriers() const {
579 return supports_conditional_barriers; 616 return supports_conditional_barriers;
580 } 617 }
581 618
619 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
620 u32 driver_version) {
621 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
622 const u32 major = VK_API_VERSION_MAJOR(driver_version);
623 const u32 minor = VK_API_VERSION_MINOR(driver_version);
624 const u32 patch = VK_API_VERSION_PATCH(driver_version);
625 if (major == 0 && minor == 405 && patch < 286) {
626 LOG_WARNING(
627 Render_Vulkan,
628 "Intel proprietary drivers 0.405.0 until 0.405.286 have broken compute");
629 return true;
630 }
631 }
632 return false;
633 }
634
582private: 635private:
583 /// Checks if the physical device is suitable and configures the object state 636 /// Checks if the physical device is suitable and configures the object state
584 /// with all necessary info about its properties. 637 /// with all necessary info about its properties.
@@ -608,6 +661,7 @@ private:
608 661
609private: 662private:
610 VkInstance instance; ///< Vulkan instance. 663 VkInstance instance; ///< Vulkan instance.
664 VmaAllocator allocator; ///< VMA allocator.
611 vk::DeviceDispatch dld; ///< Device function pointers. 665 vk::DeviceDispatch dld; ///< Device function pointers.
612 vk::PhysicalDevice physical; ///< Physical device. 666 vk::PhysicalDevice physical; ///< Physical device.
613 vk::Device logical; ///< Logical device. 667 vk::Device logical; ///< Logical device.
@@ -650,6 +704,7 @@ private:
650 704
651 struct Properties { 705 struct Properties {
652 VkPhysicalDeviceDriverProperties driver{}; 706 VkPhysicalDeviceDriverProperties driver{};
707 VkPhysicalDeviceSubgroupProperties subgroup_properties{};
653 VkPhysicalDeviceFloatControlsProperties float_controls{}; 708 VkPhysicalDeviceFloatControlsProperties float_controls{};
654 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; 709 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{};
655 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; 710 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
@@ -672,6 +727,7 @@ private:
672 bool is_integrated{}; ///< Is GPU an iGPU. 727 bool is_integrated{}; ///< Is GPU an iGPU.
673 bool is_virtual{}; ///< Is GPU a virtual GPU. 728 bool is_virtual{}; ///< Is GPU a virtual GPU.
674 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 729 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
730 bool has_broken_compute{}; ///< Compute shaders can cause crashes
675 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit 731 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
676 bool has_renderdoc{}; ///< Has RenderDoc attached 732 bool has_renderdoc{}; ///< Has RenderDoc attached
677 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 733 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index b6d83e446..7624a9b32 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -31,10 +31,34 @@
31 31
32namespace Vulkan { 32namespace Vulkan {
33namespace { 33namespace {
34
35[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
36 std::span<const char* const> extensions) {
37 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
38 if (!properties) {
39 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
40 return false;
41 }
42 for (const char* extension : extensions) {
43 const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
44 return std::strcmp(extension, prop.extensionName) == 0;
45 });
46 if (it == properties->end()) {
47 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
48 return false;
49 }
50 }
51 return true;
52}
53
34[[nodiscard]] std::vector<const char*> RequiredExtensions( 54[[nodiscard]] std::vector<const char*> RequiredExtensions(
35 Core::Frontend::WindowSystemType window_type, bool enable_validation) { 55 const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type,
56 bool enable_validation) {
36 std::vector<const char*> extensions; 57 std::vector<const char*> extensions;
37 extensions.reserve(6); 58 extensions.reserve(6);
59#ifdef __APPLE__
60 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
61#endif
38 switch (window_type) { 62 switch (window_type) {
39 case Core::Frontend::WindowSystemType::Headless: 63 case Core::Frontend::WindowSystemType::Headless:
40 break; 64 break;
@@ -66,35 +90,14 @@ namespace {
66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 90 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67 } 91 }
68 if (enable_validation) { 92 if (enable_validation) {
69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 93 const bool debug_utils =
94 AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME});
95 extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME
96 : VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
70 } 97 }
71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
72
73#ifdef __APPLE__
74 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
75#endif
76 return extensions; 98 return extensions;
77} 99}
78 100
79[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
80 std::span<const char* const> extensions) {
81 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
82 if (!properties) {
83 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
84 return false;
85 }
86 for (const char* extension : extensions) {
87 const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
88 return std::strcmp(extension, prop.extensionName) == 0;
89 });
90 if (it == properties->end()) {
91 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
92 return false;
93 }
94 }
95 return true;
96}
97
98[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) { 101[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
99 std::vector<const char*> layers; 102 std::vector<const char*> layers;
100 if (enable_validation) { 103 if (enable_validation) {
@@ -138,7 +141,8 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); 141 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 142 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
140 } 143 }
141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation); 144 const std::vector<const char*> extensions =
145 RequiredExtensions(dld, window_type, enable_validation);
142 if (!AreExtensionsSupported(dld, extensions)) { 146 if (!AreExtensionsSupported(dld, extensions)) {
143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 147 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
144 } 148 }
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index e28a556f8..a2ef0efa4 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -6,8 +6,6 @@
6#include <optional> 6#include <optional>
7#include <vector> 7#include <vector>
8 8
9#include <glad/glad.h>
10
11#include "common/alignment.h" 9#include "common/alignment.h"
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/common_types.h" 11#include "common/common_types.h"
@@ -17,6 +15,8 @@
17#include "video_core/vulkan_common/vulkan_memory_allocator.h" 15#include "video_core/vulkan_common/vulkan_memory_allocator.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h" 16#include "video_core/vulkan_common/vulkan_wrapper.h"
19 17
18#include <vk_mem_alloc.h>
19
20namespace Vulkan { 20namespace Vulkan {
21namespace { 21namespace {
22struct Range { 22struct Range {
@@ -49,22 +49,45 @@ struct Range {
49 case MemoryUsage::Download: 49 case MemoryUsage::Download:
50 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | 50 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
51 VK_MEMORY_PROPERTY_HOST_CACHED_BIT; 51 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
52 case MemoryUsage::Stream:
53 return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
54 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
52 } 55 }
53 ASSERT_MSG(false, "Invalid memory usage={}", usage); 56 ASSERT_MSG(false, "Invalid memory usage={}", usage);
54 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 57 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
55} 58}
56 59
57constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{ 60[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferedVmaFlags(MemoryUsage usage) {
58 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, 61 return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
59 .pNext = nullptr, 62 : VkMemoryPropertyFlagBits{};
60#ifdef _WIN32 63}
61 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, 64
62#elif __unix__ 65[[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) {
63 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, 66 switch (usage) {
64#else 67 case MemoryUsage::Upload:
65 .handleTypes = 0, 68 case MemoryUsage::Stream:
66#endif 69 return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
67}; 70 case MemoryUsage::Download:
71 return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
72 case MemoryUsage::DeviceLocal:
73 return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
74 VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT;
75 }
76 return {};
77}
78
79[[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) {
80 switch (usage) {
81 case MemoryUsage::DeviceLocal:
82 case MemoryUsage::Stream:
83 return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
84 case MemoryUsage::Upload:
85 case MemoryUsage::Download:
86 return VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
87 }
88 return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
89}
90
68} // Anonymous namespace 91} // Anonymous namespace
69 92
70class MemoryAllocation { 93class MemoryAllocation {
@@ -74,14 +97,6 @@ public:
74 : allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_}, 97 : allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
75 property_flags{properties}, shifted_memory_type{1U << type} {} 98 property_flags{properties}, shifted_memory_type{1U << type} {}
76 99
77#if defined(_WIN32) || defined(__unix__)
78 ~MemoryAllocation() {
79 if (owning_opengl_handle != 0) {
80 glDeleteMemoryObjectsEXT(1, &owning_opengl_handle);
81 }
82 }
83#endif
84
85 MemoryAllocation& operator=(const MemoryAllocation&) = delete; 100 MemoryAllocation& operator=(const MemoryAllocation&) = delete;
86 MemoryAllocation(const MemoryAllocation&) = delete; 101 MemoryAllocation(const MemoryAllocation&) = delete;
87 102
@@ -120,31 +135,6 @@ public:
120 return memory_mapped_span; 135 return memory_mapped_span;
121 } 136 }
122 137
123#ifdef _WIN32
124 [[nodiscard]] u32 ExportOpenGLHandle() {
125 if (!owning_opengl_handle) {
126 glCreateMemoryObjectsEXT(1, &owning_opengl_handle);
127 glImportMemoryWin32HandleEXT(owning_opengl_handle, allocation_size,
128 GL_HANDLE_TYPE_OPAQUE_WIN32_EXT,
129 memory.GetMemoryWin32HandleKHR());
130 }
131 return owning_opengl_handle;
132 }
133#elif __unix__
134 [[nodiscard]] u32 ExportOpenGLHandle() {
135 if (!owning_opengl_handle) {
136 glCreateMemoryObjectsEXT(1, &owning_opengl_handle);
137 glImportMemoryFdEXT(owning_opengl_handle, allocation_size, GL_HANDLE_TYPE_OPAQUE_FD_EXT,
138 memory.GetMemoryFdKHR());
139 }
140 return owning_opengl_handle;
141 }
142#else
143 [[nodiscard]] u32 ExportOpenGLHandle() {
144 return 0;
145 }
146#endif
147
148 /// Returns whether this allocation is compatible with the arguments. 138 /// Returns whether this allocation is compatible with the arguments.
149 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { 139 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
150 return (flags & property_flags) == flags && (type_mask & shifted_memory_type) != 0; 140 return (flags & property_flags) == flags && (type_mask & shifted_memory_type) != 0;
@@ -182,9 +172,6 @@ private:
182 const u32 shifted_memory_type; ///< Shifted Vulkan memory type. 172 const u32 shifted_memory_type; ///< Shifted Vulkan memory type.
183 std::vector<Range> commits; ///< All commit ranges done from this allocation. 173 std::vector<Range> commits; ///< All commit ranges done from this allocation.
184 std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. 174 std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before.
185#if defined(_WIN32) || defined(__unix__)
186 u32 owning_opengl_handle{}; ///< Owning OpenGL memory object handle.
187#endif
188}; 175};
189 176
190MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, 177MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
@@ -216,24 +203,70 @@ std::span<u8> MemoryCommit::Map() {
216 return span; 203 return span;
217} 204}
218 205
219u32 MemoryCommit::ExportOpenGLHandle() const {
220 return allocation->ExportOpenGLHandle();
221}
222
223void MemoryCommit::Release() { 206void MemoryCommit::Release() {
224 if (allocation) { 207 if (allocation) {
225 allocation->Free(begin); 208 allocation->Free(begin);
226 } 209 }
227} 210}
228 211
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 212MemoryAllocator::MemoryAllocator(const Device& device_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, 213 : device{device_}, allocator{device.GetAllocator()},
231 export_allocations{export_allocations_}, 214 properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
232 buffer_image_granularity{ 215 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} 216 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
234 217
235MemoryAllocator::~MemoryAllocator() = default; 218MemoryAllocator::~MemoryAllocator() = default;
236 219
220vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
221 const VmaAllocationCreateInfo alloc_ci = {
222 .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
223 .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
224 .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
225 .preferredFlags = 0,
226 .memoryTypeBits = 0,
227 .pool = VK_NULL_HANDLE,
228 .pUserData = nullptr,
229 .priority = 0.f,
230 };
231
232 VkImage handle{};
233 VmaAllocation allocation{};
234
235 vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
236
237 return vk::Image(handle, *device.GetLogical(), allocator, allocation,
238 device.GetDispatchLoader());
239}
240
241vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
242 const VmaAllocationCreateInfo alloc_ci = {
243 .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT |
244 MemoryUsageVmaFlags(usage),
245 .usage = MemoryUsageVma(usage),
246 .requiredFlags = 0,
247 .preferredFlags = MemoryUsagePreferedVmaFlags(usage),
248 .memoryTypeBits = 0,
249 .pool = VK_NULL_HANDLE,
250 .pUserData = nullptr,
251 .priority = 0.f,
252 };
253
254 VkBuffer handle{};
255 VmaAllocationInfo alloc_info{};
256 VmaAllocation allocation{};
257 VkMemoryPropertyFlags property_flags{};
258
259 vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info));
260 vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
261
262 u8* data = reinterpret_cast<u8*>(alloc_info.pMappedData);
263 const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
264 const bool is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
265
266 return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
267 device.GetDispatchLoader());
268}
269
237MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { 270MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
238 // Find the fastest memory flags we can afford with the current requirements 271 // Find the fastest memory flags we can afford with the current requirements
239 const u32 type_mask = requirements.memoryTypeBits; 272 const u32 type_mask = requirements.memoryTypeBits;
@@ -253,25 +286,11 @@ MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, M
253 return TryCommit(requirements, flags).value(); 286 return TryCommit(requirements, flags).value();
254} 287}
255 288
256MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) {
257 auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage);
258 buffer.BindMemory(commit.Memory(), commit.Offset());
259 return commit;
260}
261
262MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
263 VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image);
264 requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity);
265 auto commit = Commit(requirements, usage);
266 image.BindMemory(commit.Memory(), commit.Offset());
267 return commit;
268}
269
270bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { 289bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
271 const u32 type = FindType(flags, type_mask).value(); 290 const u32 type = FindType(flags, type_mask).value();
272 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ 291 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
273 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 292 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
274 .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr, 293 .pNext = nullptr,
275 .allocationSize = size, 294 .allocationSize = size,
276 .memoryTypeIndex = type, 295 .memoryTypeIndex = type,
277 }); 296 });
@@ -342,16 +361,4 @@ std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 ty
342 return std::nullopt; 361 return std::nullopt;
343} 362}
344 363
345bool IsHostVisible(MemoryUsage usage) noexcept {
346 switch (usage) {
347 case MemoryUsage::DeviceLocal:
348 return false;
349 case MemoryUsage::Upload:
350 case MemoryUsage::Download:
351 return true;
352 }
353 ASSERT_MSG(false, "Invalid memory usage={}", usage);
354 return false;
355}
356
357} // namespace Vulkan 364} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index a5bff03fe..f449bc8d0 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -9,6 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12VK_DEFINE_HANDLE(VmaAllocator)
13
12namespace Vulkan { 14namespace Vulkan {
13 15
14class Device; 16class Device;
@@ -17,9 +19,11 @@ class MemoryAllocation;
17 19
18/// Hints and requirements for the backing memory type of a commit 20/// Hints and requirements for the backing memory type of a commit
19enum class MemoryUsage { 21enum class MemoryUsage {
20 DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU 22 DeviceLocal, ///< Requests device local host visible buffer, falling back to device local
23 ///< memory.
21 Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads 24 Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads
22 Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks 25 Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks
26 Stream, ///< Requests device local host visible buffer, falling back host memory.
23}; 27};
24 28
25/// Ownership handle of a memory commitment. 29/// Ownership handle of a memory commitment.
@@ -41,9 +45,6 @@ public:
41 /// It will map the backing allocation if it hasn't been mapped before. 45 /// It will map the backing allocation if it hasn't been mapped before.
42 std::span<u8> Map(); 46 std::span<u8> Map();
43 47
44 /// Returns an non-owning OpenGL handle, creating one if it doesn't exist.
45 u32 ExportOpenGLHandle() const;
46
47 /// Returns the Vulkan memory handler. 48 /// Returns the Vulkan memory handler.
48 VkDeviceMemory Memory() const { 49 VkDeviceMemory Memory() const {
49 return memory; 50 return memory;
@@ -74,16 +75,19 @@ public:
74 * Construct memory allocator 75 * Construct memory allocator
75 * 76 *
76 * @param device_ Device to allocate from 77 * @param device_ Device to allocate from
77 * @param export_allocations_ True when allocations have to be exported
78 * 78 *
79 * @throw vk::Exception on failure 79 * @throw vk::Exception on failure
80 */ 80 */
81 explicit MemoryAllocator(const Device& device_, bool export_allocations_); 81 explicit MemoryAllocator(const Device& device_);
82 ~MemoryAllocator(); 82 ~MemoryAllocator();
83 83
84 MemoryAllocator& operator=(const MemoryAllocator&) = delete; 84 MemoryAllocator& operator=(const MemoryAllocator&) = delete;
85 MemoryAllocator(const MemoryAllocator&) = delete; 85 MemoryAllocator(const MemoryAllocator&) = delete;
86 86
87 vk::Image CreateImage(const VkImageCreateInfo& ci) const;
88
89 vk::Buffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const;
90
87 /** 91 /**
88 * Commits a memory with the specified requirements. 92 * Commits a memory with the specified requirements.
89 * 93 *
@@ -97,9 +101,6 @@ public:
97 /// Commits memory required by the buffer and binds it. 101 /// Commits memory required by the buffer and binds it.
98 MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); 102 MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage);
99 103
100 /// Commits memory required by the image and binds it.
101 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
102
103private: 104private:
104 /// Tries to allocate a chunk of memory. 105 /// Tries to allocate a chunk of memory.
105 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); 106 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
@@ -117,15 +118,12 @@ private:
117 /// Returns index to the fastest memory type compatible with the passed requirements. 118 /// Returns index to the fastest memory type compatible with the passed requirements.
118 std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const; 119 std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
119 120
120 const Device& device; ///< Device handle. 121 const Device& device; ///< Device handle.
121 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. 122 VmaAllocator allocator; ///< Vma allocator.
122 const bool export_allocations; ///< True when memory allocations have to be exported. 123 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
123 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. 124 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
124 VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers 125 VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
125 // and optimal images 126 // and optimal images
126}; 127};
127 128
128/// Returns true when a memory usage is guaranteed to be host visible.
129bool IsHostVisible(MemoryUsage usage) noexcept;
130
131} // namespace Vulkan 129} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 336f53700..2fa29793a 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -12,6 +12,8 @@
12 12
13#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
14 14
15#include <vk_mem_alloc.h>
16
15namespace Vulkan::vk { 17namespace Vulkan::vk {
16 18
17namespace { 19namespace {
@@ -257,7 +259,9 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
257 // These functions may fail to load depending on the enabled extensions. 259 // These functions may fail to load depending on the enabled extensions.
258 // Don't return a failure on these. 260 // Don't return a failure on these.
259 X(vkCreateDebugUtilsMessengerEXT); 261 X(vkCreateDebugUtilsMessengerEXT);
262 X(vkCreateDebugReportCallbackEXT);
260 X(vkDestroyDebugUtilsMessengerEXT); 263 X(vkDestroyDebugUtilsMessengerEXT);
264 X(vkDestroyDebugReportCallbackEXT);
261 X(vkDestroySurfaceKHR); 265 X(vkDestroySurfaceKHR);
262 X(vkGetPhysicalDeviceFeatures2); 266 X(vkGetPhysicalDeviceFeatures2);
263 X(vkGetPhysicalDeviceProperties2); 267 X(vkGetPhysicalDeviceProperties2);
@@ -479,6 +483,11 @@ void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle,
479 dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr); 483 dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr);
480} 484}
481 485
486void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle,
487 const InstanceDispatch& dld) noexcept {
488 dld.vkDestroyDebugReportCallbackEXT(instance, handle, nullptr);
489}
490
482void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { 491void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
483 dld.vkDestroySurfaceKHR(instance, handle, nullptr); 492 dld.vkDestroySurfaceKHR(instance, handle, nullptr);
484} 493}
@@ -547,24 +556,47 @@ DebugUtilsMessenger Instance::CreateDebugUtilsMessenger(
547 return DebugUtilsMessenger(object, handle, *dld); 556 return DebugUtilsMessenger(object, handle, *dld);
548} 557}
549 558
550void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { 559DebugReportCallback Instance::CreateDebugReportCallback(
551 Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); 560 const VkDebugReportCallbackCreateInfoEXT& create_info) const {
561 VkDebugReportCallbackEXT object;
562 Check(dld->vkCreateDebugReportCallbackEXT(handle, &create_info, nullptr, &object));
563 return DebugReportCallback(object, handle, *dld);
552} 564}
553 565
554void Buffer::SetObjectNameEXT(const char* name) const { 566void Image::SetObjectNameEXT(const char* name) const {
555 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name); 567 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name);
556} 568}
557 569
558void BufferView::SetObjectNameEXT(const char* name) const { 570void Image::Release() const noexcept {
559 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name); 571 if (handle) {
572 vmaDestroyImage(allocator, handle, allocation);
573 }
560} 574}
561 575
562void Image::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { 576void Buffer::Flush() const {
563 Check(dld->vkBindImageMemory(owner, handle, memory, offset)); 577 if (!is_coherent) {
578 vmaFlushAllocation(allocator, allocation, 0, VK_WHOLE_SIZE);
579 }
564} 580}
565 581
566void Image::SetObjectNameEXT(const char* name) const { 582void Buffer::Invalidate() const {
567 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); 583 if (!is_coherent) {
584 vmaInvalidateAllocation(allocator, allocation, 0, VK_WHOLE_SIZE);
585 }
586}
587
588void Buffer::SetObjectNameEXT(const char* name) const {
589 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name);
590}
591
592void Buffer::Release() const noexcept {
593 if (handle) {
594 vmaDestroyBuffer(allocator, handle, allocation);
595 }
596}
597
598void BufferView::SetObjectNameEXT(const char* name) const {
599 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name);
568} 600}
569 601
570void ImageView::SetObjectNameEXT(const char* name) const { 602void ImageView::SetObjectNameEXT(const char* name) const {
@@ -701,24 +733,12 @@ Queue Device::GetQueue(u32 family_index) const noexcept {
701 return Queue(queue, *dld); 733 return Queue(queue, *dld);
702} 734}
703 735
704Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const {
705 VkBuffer object;
706 Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object));
707 return Buffer(object, handle, *dld);
708}
709
710BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { 736BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const {
711 VkBufferView object; 737 VkBufferView object;
712 Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object)); 738 Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object));
713 return BufferView(object, handle, *dld); 739 return BufferView(object, handle, *dld);
714} 740}
715 741
716Image Device::CreateImage(const VkImageCreateInfo& ci) const {
717 VkImage object;
718 Check(dld->vkCreateImage(handle, &ci, nullptr, &object));
719 return Image(object, handle, *dld);
720}
721
722ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { 742ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const {
723 VkImageView object; 743 VkImageView object;
724 Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); 744 Check(dld->vkCreateImageView(handle, &ci, nullptr, &object));
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 4ff328a21..b5e70fcd4 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -32,6 +32,9 @@
32#pragma warning(disable : 26812) // Disable prefer enum class over enum 32#pragma warning(disable : 26812) // Disable prefer enum class over enum
33#endif 33#endif
34 34
35VK_DEFINE_HANDLE(VmaAllocator)
36VK_DEFINE_HANDLE(VmaAllocation)
37
35namespace Vulkan::vk { 38namespace Vulkan::vk {
36 39
37/** 40/**
@@ -161,8 +164,10 @@ struct InstanceDispatch {
161 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{}; 164 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
162 165
163 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{}; 166 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{};
167 PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT{};
164 PFN_vkCreateDevice vkCreateDevice{}; 168 PFN_vkCreateDevice vkCreateDevice{};
165 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{}; 169 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{};
170 PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT{};
166 PFN_vkDestroyDevice vkDestroyDevice{}; 171 PFN_vkDestroyDevice vkDestroyDevice{};
167 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{}; 172 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{};
168 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; 173 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
@@ -363,6 +368,7 @@ void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept;
363void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept; 368void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept;
364void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept; 369void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept;
365void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept; 370void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept;
371void Destroy(VkInstance, VkDebugReportCallbackEXT, const InstanceDispatch&) noexcept;
366void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept; 372void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept;
367 373
368VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept; 374VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept;
@@ -578,6 +584,7 @@ private:
578}; 584};
579 585
580using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; 586using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
587using DebugReportCallback = Handle<VkDebugReportCallbackEXT, VkInstance, InstanceDispatch>;
581using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; 588using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
582using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>; 589using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>;
583using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; 590using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
@@ -610,12 +617,149 @@ public:
610 DebugUtilsMessenger CreateDebugUtilsMessenger( 617 DebugUtilsMessenger CreateDebugUtilsMessenger(
611 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const; 618 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
612 619
620 /// Creates a debug report callback.
621 /// @throw Exception on creation failure.
622 DebugReportCallback CreateDebugReportCallback(
623 const VkDebugReportCallbackCreateInfoEXT& create_info) const;
624
613 /// Returns dispatch table. 625 /// Returns dispatch table.
614 const InstanceDispatch& Dispatch() const noexcept { 626 const InstanceDispatch& Dispatch() const noexcept {
615 return *dld; 627 return *dld;
616 } 628 }
617}; 629};
618 630
631class Image {
632public:
633 explicit Image(VkImage handle_, VkDevice owner_, VmaAllocator allocator_,
634 VmaAllocation allocation_, const DeviceDispatch& dld_) noexcept
635 : handle{handle_}, owner{owner_}, allocator{allocator_},
636 allocation{allocation_}, dld{&dld_} {}
637 Image() = default;
638
639 Image(const Image&) = delete;
640 Image& operator=(const Image&) = delete;
641
642 Image(Image&& rhs) noexcept
643 : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator},
644 allocation{rhs.allocation}, dld{rhs.dld} {}
645
646 Image& operator=(Image&& rhs) noexcept {
647 Release();
648 handle = std::exchange(rhs.handle, nullptr);
649 owner = rhs.owner;
650 allocator = rhs.allocator;
651 allocation = rhs.allocation;
652 dld = rhs.dld;
653 return *this;
654 }
655
656 ~Image() noexcept {
657 Release();
658 }
659
660 VkImage operator*() const noexcept {
661 return handle;
662 }
663
664 void reset() noexcept {
665 Release();
666 handle = nullptr;
667 }
668
669 explicit operator bool() const noexcept {
670 return handle != nullptr;
671 }
672
673 void SetObjectNameEXT(const char* name) const;
674
675private:
676 void Release() const noexcept;
677
678 VkImage handle = nullptr;
679 VkDevice owner = nullptr;
680 VmaAllocator allocator = nullptr;
681 VmaAllocation allocation = nullptr;
682 const DeviceDispatch* dld = nullptr;
683};
684
685class Buffer {
686public:
687 explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_,
688 VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_,
689 const DeviceDispatch& dld_) noexcept
690 : handle{handle_}, owner{owner_}, allocator{allocator_},
691 allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {}
692 Buffer() = default;
693
694 Buffer(const Buffer&) = delete;
695 Buffer& operator=(const Buffer&) = delete;
696
697 Buffer(Buffer&& rhs) noexcept
698 : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator},
699 allocation{rhs.allocation}, mapped{rhs.mapped},
700 is_coherent{rhs.is_coherent}, dld{rhs.dld} {}
701
702 Buffer& operator=(Buffer&& rhs) noexcept {
703 Release();
704 handle = std::exchange(rhs.handle, nullptr);
705 owner = rhs.owner;
706 allocator = rhs.allocator;
707 allocation = rhs.allocation;
708 mapped = rhs.mapped;
709 is_coherent = rhs.is_coherent;
710 dld = rhs.dld;
711 return *this;
712 }
713
714 ~Buffer() noexcept {
715 Release();
716 }
717
718 VkBuffer operator*() const noexcept {
719 return handle;
720 }
721
722 void reset() noexcept {
723 Release();
724 handle = nullptr;
725 }
726
727 explicit operator bool() const noexcept {
728 return handle != nullptr;
729 }
730
731 /// Returns the host mapped memory, an empty span otherwise.
732 std::span<u8> Mapped() noexcept {
733 return mapped;
734 }
735
736 std::span<const u8> Mapped() const noexcept {
737 return mapped;
738 }
739
740 /// Returns true if the buffer is mapped to the host.
741 bool IsHostVisible() const noexcept {
742 return !mapped.empty();
743 }
744
745 void Flush() const;
746
747 void Invalidate() const;
748
749 void SetObjectNameEXT(const char* name) const;
750
751private:
752 void Release() const noexcept;
753
754 VkBuffer handle = nullptr;
755 VkDevice owner = nullptr;
756 VmaAllocator allocator = nullptr;
757 VmaAllocation allocation = nullptr;
758 std::span<u8> mapped = {};
759 bool is_coherent = false;
760 const DeviceDispatch* dld = nullptr;
761};
762
619class Queue { 763class Queue {
620public: 764public:
621 /// Construct an empty queue handle. 765 /// Construct an empty queue handle.
@@ -639,17 +783,6 @@ private:
639 const DeviceDispatch* dld = nullptr; 783 const DeviceDispatch* dld = nullptr;
640}; 784};
641 785
642class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> {
643 using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle;
644
645public:
646 /// Attaches a memory allocation.
647 void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const;
648
649 /// Set object name.
650 void SetObjectNameEXT(const char* name) const;
651};
652
653class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> { 786class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> {
654 using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle; 787 using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle;
655 788
@@ -658,17 +791,6 @@ public:
658 void SetObjectNameEXT(const char* name) const; 791 void SetObjectNameEXT(const char* name) const;
659}; 792};
660 793
661class Image : public Handle<VkImage, VkDevice, DeviceDispatch> {
662 using Handle<VkImage, VkDevice, DeviceDispatch>::Handle;
663
664public:
665 /// Attaches a memory allocation.
666 void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const;
667
668 /// Set object name.
669 void SetObjectNameEXT(const char* name) const;
670};
671
672class ImageView : public Handle<VkImageView, VkDevice, DeviceDispatch> { 794class ImageView : public Handle<VkImageView, VkDevice, DeviceDispatch> {
673 using Handle<VkImageView, VkDevice, DeviceDispatch>::Handle; 795 using Handle<VkImageView, VkDevice, DeviceDispatch>::Handle;
674 796
@@ -840,12 +962,8 @@ public:
840 962
841 Queue GetQueue(u32 family_index) const noexcept; 963 Queue GetQueue(u32 family_index) const noexcept;
842 964
843 Buffer CreateBuffer(const VkBufferCreateInfo& ci) const;
844
845 BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; 965 BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
846 966
847 Image CreateImage(const VkImageCreateInfo& ci) const;
848
849 ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; 967 ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
850 968
851 Semaphore CreateSemaphore() const; 969 Semaphore CreateSemaphore() const;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 733c296e4..fe98e3605 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -98,6 +98,9 @@ add_executable(yuzu
98 configuration/configure_input_profile_dialog.cpp 98 configuration/configure_input_profile_dialog.cpp
99 configuration/configure_input_profile_dialog.h 99 configuration/configure_input_profile_dialog.h
100 configuration/configure_input_profile_dialog.ui 100 configuration/configure_input_profile_dialog.ui
101 configuration/configure_mouse_panning.cpp
102 configuration/configure_mouse_panning.h
103 configuration/configure_mouse_panning.ui
101 configuration/configure_motion_touch.cpp 104 configuration/configure_motion_touch.cpp
102 configuration/configure_motion_touch.h 105 configuration/configure_motion_touch.h
103 configuration/configure_motion_touch.ui 106 configuration/configure_motion_touch.ui
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index cc6b6a25a..bdd1497b5 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -105,14 +105,12 @@ void EmuThread::run() {
105 std::unique_lock lk{m_should_run_mutex}; 105 std::unique_lock lk{m_should_run_mutex};
106 if (m_should_run) { 106 if (m_should_run) {
107 m_system.Run(); 107 m_system.Run();
108 m_is_running.store(true); 108 m_stopped.Reset();
109 m_is_running.notify_all();
110 109
111 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); 110 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
112 } else { 111 } else {
113 m_system.Pause(); 112 m_system.Pause();
114 m_is_running.store(false); 113 m_stopped.Set();
115 m_is_running.notify_all();
116 114
117 EmulationPaused(lk); 115 EmulationPaused(lk);
118 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); 116 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index b7b9d4141..87b23df12 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -3,7 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <atomic>
7#include <condition_variable> 6#include <condition_variable>
8#include <cstddef> 7#include <cstddef>
9#include <memory> 8#include <memory>
@@ -88,7 +87,7 @@ public:
88 87
89 // Wait until paused, if pausing. 88 // Wait until paused, if pausing.
90 if (!should_run) { 89 if (!should_run) {
91 m_is_running.wait(true); 90 m_stopped.Wait();
92 } 91 }
93 } 92 }
94 93
@@ -97,7 +96,7 @@ public:
97 * @return True if the emulation thread is running, otherwise false 96 * @return True if the emulation thread is running, otherwise false
98 */ 97 */
99 bool IsRunning() const { 98 bool IsRunning() const {
100 return m_is_running.load() || m_should_run; 99 return m_should_run;
101 } 100 }
102 101
103 /** 102 /**
@@ -118,7 +117,7 @@ private:
118 std::stop_source m_stop_source; 117 std::stop_source m_stop_source;
119 std::mutex m_should_run_mutex; 118 std::mutex m_should_run_mutex;
120 std::condition_variable_any m_should_run_cv; 119 std::condition_variable_any m_should_run_cv;
121 std::atomic<bool> m_is_running{false}; 120 Common::Event m_stopped;
122 bool m_should_run{true}; 121 bool m_should_run{true};
123 122
124signals: 123signals:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bac9dff90..29467d380 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -351,6 +351,10 @@ void Config::ReadPlayerValue(std::size_t player_index) {
351 player_motions = default_param; 351 player_motions = default_param;
352 } 352 }
353 } 353 }
354
355 if (player_index == 0) {
356 ReadMousePanningValues();
357 }
354} 358}
355 359
356void Config::ReadDebugValues() { 360void Config::ReadDebugValues() {
@@ -471,6 +475,7 @@ void Config::ReadControlValues() {
471 ReadKeyboardValues(); 475 ReadKeyboardValues();
472 ReadMouseValues(); 476 ReadMouseValues();
473 ReadTouchscreenValues(); 477 ReadTouchscreenValues();
478 ReadMousePanningValues();
474 ReadMotionTouchValues(); 479 ReadMotionTouchValues();
475 ReadHidbusValues(); 480 ReadHidbusValues();
476 ReadIrCameraValues(); 481 ReadIrCameraValues();
@@ -481,8 +486,6 @@ void Config::ReadControlValues() {
481 Settings::values.enable_raw_input = false; 486 Settings::values.enable_raw_input = false;
482#endif 487#endif
483 ReadBasicSetting(Settings::values.emulate_analog_keyboard); 488 ReadBasicSetting(Settings::values.emulate_analog_keyboard);
484 Settings::values.mouse_panning = false;
485 ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
486 ReadBasicSetting(Settings::values.enable_joycon_driver); 489 ReadBasicSetting(Settings::values.enable_joycon_driver);
487 ReadBasicSetting(Settings::values.enable_procon_driver); 490 ReadBasicSetting(Settings::values.enable_procon_driver);
488 ReadBasicSetting(Settings::values.random_amiibo_id); 491 ReadBasicSetting(Settings::values.random_amiibo_id);
@@ -496,6 +499,16 @@ void Config::ReadControlValues() {
496 qt_config->endGroup(); 499 qt_config->endGroup();
497} 500}
498 501
502void Config::ReadMousePanningValues() {
503 ReadBasicSetting(Settings::values.mouse_panning);
504 ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity);
505 ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity);
506 ReadBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight);
507 ReadBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight);
508 ReadBasicSetting(Settings::values.mouse_panning_decay_strength);
509 ReadBasicSetting(Settings::values.mouse_panning_min_decay);
510}
511
499void Config::ReadMotionTouchValues() { 512void Config::ReadMotionTouchValues() {
500 int num_touch_from_button_maps = 513 int num_touch_from_button_maps =
501 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); 514 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
@@ -761,6 +774,7 @@ void Config::ReadRendererValues() {
761 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 774 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
762 ReadGlobalSetting(Settings::values.enable_compute_pipelines); 775 ReadGlobalSetting(Settings::values.enable_compute_pipelines);
763 ReadGlobalSetting(Settings::values.use_video_framerate); 776 ReadGlobalSetting(Settings::values.use_video_framerate);
777 ReadGlobalSetting(Settings::values.barrier_feedback_loops);
764 ReadGlobalSetting(Settings::values.bg_red); 778 ReadGlobalSetting(Settings::values.bg_red);
765 ReadGlobalSetting(Settings::values.bg_green); 779 ReadGlobalSetting(Settings::values.bg_green);
766 ReadGlobalSetting(Settings::values.bg_blue); 780 ReadGlobalSetting(Settings::values.bg_blue);
@@ -890,6 +904,7 @@ void Config::ReadUIValues() {
890 ReadBasicSetting(UISettings::values.pause_when_in_background); 904 ReadBasicSetting(UISettings::values.pause_when_in_background);
891 ReadBasicSetting(UISettings::values.mute_when_in_background); 905 ReadBasicSetting(UISettings::values.mute_when_in_background);
892 ReadBasicSetting(UISettings::values.hide_mouse); 906 ReadBasicSetting(UISettings::values.hide_mouse);
907 ReadBasicSetting(UISettings::values.controller_applet_disabled);
893 ReadBasicSetting(UISettings::values.disable_web_applet); 908 ReadBasicSetting(UISettings::values.disable_web_applet);
894 909
895 qt_config->endGroup(); 910 qt_config->endGroup();
@@ -1063,6 +1078,10 @@ void Config::SavePlayerValue(std::size_t player_index) {
1063 QString::fromStdString(player.motions[i]), 1078 QString::fromStdString(player.motions[i]),
1064 QString::fromStdString(default_param)); 1079 QString::fromStdString(default_param));
1065 } 1080 }
1081
1082 if (player_index == 0) {
1083 SaveMousePanningValues();
1084 }
1066} 1085}
1067 1086
1068void Config::SaveDebugValues() { 1087void Config::SaveDebugValues() {
@@ -1099,6 +1118,16 @@ void Config::SaveTouchscreenValues() {
1099 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); 1118 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
1100} 1119}
1101 1120
1121void Config::SaveMousePanningValues() {
1122 // Don't overwrite values.mouse_panning
1123 WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity);
1124 WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity);
1125 WriteBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight);
1126 WriteBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight);
1127 WriteBasicSetting(Settings::values.mouse_panning_decay_strength);
1128 WriteBasicSetting(Settings::values.mouse_panning_min_decay);
1129}
1130
1102void Config::SaveMotionTouchValues() { 1131void Config::SaveMotionTouchValues() {
1103 WriteBasicSetting(Settings::values.touch_device); 1132 WriteBasicSetting(Settings::values.touch_device);
1104 WriteBasicSetting(Settings::values.touch_from_button_map_index); 1133 WriteBasicSetting(Settings::values.touch_from_button_map_index);
@@ -1185,6 +1214,7 @@ void Config::SaveControlValues() {
1185 SaveDebugValues(); 1214 SaveDebugValues();
1186 SaveMouseValues(); 1215 SaveMouseValues();
1187 SaveTouchscreenValues(); 1216 SaveTouchscreenValues();
1217 SaveMousePanningValues();
1188 SaveMotionTouchValues(); 1218 SaveMotionTouchValues();
1189 SaveHidbusValues(); 1219 SaveHidbusValues();
1190 SaveIrCameraValues(); 1220 SaveIrCameraValues();
@@ -1199,7 +1229,6 @@ void Config::SaveControlValues() {
1199 WriteBasicSetting(Settings::values.random_amiibo_id); 1229 WriteBasicSetting(Settings::values.random_amiibo_id);
1200 WriteBasicSetting(Settings::values.keyboard_enabled); 1230 WriteBasicSetting(Settings::values.keyboard_enabled);
1201 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1231 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1202 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
1203 WriteBasicSetting(Settings::values.controller_navigation); 1232 WriteBasicSetting(Settings::values.controller_navigation);
1204 1233
1205 WriteBasicSetting(Settings::values.tas_enable); 1234 WriteBasicSetting(Settings::values.tas_enable);
@@ -1417,6 +1446,7 @@ void Config::SaveRendererValues() {
1417 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 1446 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1418 WriteGlobalSetting(Settings::values.enable_compute_pipelines); 1447 WriteGlobalSetting(Settings::values.enable_compute_pipelines);
1419 WriteGlobalSetting(Settings::values.use_video_framerate); 1448 WriteGlobalSetting(Settings::values.use_video_framerate);
1449 WriteGlobalSetting(Settings::values.barrier_feedback_loops);
1420 WriteGlobalSetting(Settings::values.bg_red); 1450 WriteGlobalSetting(Settings::values.bg_red);
1421 WriteGlobalSetting(Settings::values.bg_green); 1451 WriteGlobalSetting(Settings::values.bg_green);
1422 WriteGlobalSetting(Settings::values.bg_blue); 1452 WriteGlobalSetting(Settings::values.bg_blue);
@@ -1522,6 +1552,7 @@ void Config::SaveUIValues() {
1522 WriteBasicSetting(UISettings::values.pause_when_in_background); 1552 WriteBasicSetting(UISettings::values.pause_when_in_background);
1523 WriteBasicSetting(UISettings::values.mute_when_in_background); 1553 WriteBasicSetting(UISettings::values.mute_when_in_background);
1524 WriteBasicSetting(UISettings::values.hide_mouse); 1554 WriteBasicSetting(UISettings::values.hide_mouse);
1555 WriteBasicSetting(UISettings::values.controller_applet_disabled);
1525 WriteBasicSetting(UISettings::values.disable_web_applet); 1556 WriteBasicSetting(UISettings::values.disable_web_applet);
1526 1557
1527 qt_config->endGroup(); 1558 qt_config->endGroup();
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 0fd4baf6b..1211389d2 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -74,6 +74,7 @@ private:
74 void ReadKeyboardValues(); 74 void ReadKeyboardValues();
75 void ReadMouseValues(); 75 void ReadMouseValues();
76 void ReadTouchscreenValues(); 76 void ReadTouchscreenValues();
77 void ReadMousePanningValues();
77 void ReadMotionTouchValues(); 78 void ReadMotionTouchValues();
78 void ReadHidbusValues(); 79 void ReadHidbusValues();
79 void ReadIrCameraValues(); 80 void ReadIrCameraValues();
@@ -104,6 +105,7 @@ private:
104 void SaveDebugValues(); 105 void SaveDebugValues();
105 void SaveMouseValues(); 106 void SaveMouseValues();
106 void SaveTouchscreenValues(); 107 void SaveTouchscreenValues();
108 void SaveMousePanningValues();
107 void SaveMotionTouchValues(); 109 void SaveMotionTouchValues();
108 void SaveHidbusValues(); 110 void SaveHidbusValues();
109 void SaveIrCameraValues(); 111 void SaveIrCameraValues();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 26258d744..2f55159f5 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -40,6 +40,9 @@ void ConfigureGeneral::SetConfiguration() {
40 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); 40 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
41 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); 41 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
42 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); 42 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
43 ui->toggle_controller_applet_disabled->setEnabled(runtime_lock);
44 ui->toggle_controller_applet_disabled->setChecked(
45 UISettings::values.controller_applet_disabled.GetValue());
43 46
44 ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); 47 ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue());
45 ui->speed_limit->setValue(Settings::values.speed_limit.GetValue()); 48 ui->speed_limit->setValue(Settings::values.speed_limit.GetValue());
@@ -82,6 +85,8 @@ void ConfigureGeneral::ApplyConfiguration() {
82 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 85 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
83 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 86 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
84 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); 87 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
88 UISettings::values.controller_applet_disabled =
89 ui->toggle_controller_applet_disabled->isChecked();
85 90
86 // Guard if during game and set to game-specific value 91 // Guard if during game and set to game-specific value
87 if (Settings::values.use_speed_limit.UsingGlobal()) { 92 if (Settings::values.use_speed_limit.UsingGlobal()) {
@@ -128,6 +133,7 @@ void ConfigureGeneral::SetupPerGameUI() {
128 ui->toggle_user_on_boot->setVisible(false); 133 ui->toggle_user_on_boot->setVisible(false);
129 ui->toggle_background_pause->setVisible(false); 134 ui->toggle_background_pause->setVisible(false);
130 ui->toggle_hide_mouse->setVisible(false); 135 ui->toggle_hide_mouse->setVisible(false);
136 ui->toggle_controller_applet_disabled->setVisible(false);
131 137
132 ui->button_reset_defaults->setVisible(false); 138 ui->button_reset_defaults->setVisible(false);
133 139
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 986a1625b..fe757d011 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -89,6 +89,13 @@
89 </property> 89 </property>
90 </widget> 90 </widget>
91 </item> 91 </item>
92 <item>
93 <widget class="QCheckBox" name="toggle_controller_applet_disabled">
94 <property name="text">
95 <string>Disable controller applet</string>
96 </property>
97 </widget>
98 </item>
92 </layout> 99 </layout>
93 </item> 100 </item>
94 </layout> 101 </layout>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 78b487494..a4965524a 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -508,7 +508,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
508 vulkan_devices.push_back(QString::fromStdString(record.name)); 508 vulkan_devices.push_back(QString::fromStdString(record.name));
509 device_present_modes.push_back(record.vsync_support); 509 device_present_modes.push_back(record.vsync_support);
510 510
511 if (record.is_intel_proprietary) { 511 if (record.has_broken_compute) {
512 expose_compute_option(); 512 expose_compute_option();
513 } 513 }
514 } 514 }
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 0463ac8b9..c0a044767 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -43,6 +43,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
43 ui->enable_compute_pipelines_checkbox->setChecked( 43 ui->enable_compute_pipelines_checkbox->setChecked(
44 Settings::values.enable_compute_pipelines.GetValue()); 44 Settings::values.enable_compute_pipelines.GetValue());
45 ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); 45 ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
46 ui->barrier_feedback_loops_checkbox->setChecked(
47 Settings::values.barrier_feedback_loops.GetValue());
46 48
47 if (Settings::IsConfiguringGlobal()) { 49 if (Settings::IsConfiguringGlobal()) {
48 ui->gpu_accuracy->setCurrentIndex( 50 ui->gpu_accuracy->setCurrentIndex(
@@ -94,6 +96,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
94 enable_compute_pipelines); 96 enable_compute_pipelines);
95 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, 97 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
96 ui->use_video_framerate_checkbox, use_video_framerate); 98 ui->use_video_framerate_checkbox, use_video_framerate);
99 ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops,
100 ui->barrier_feedback_loops_checkbox,
101 barrier_feedback_loops);
97} 102}
98 103
99void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 104void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -130,6 +135,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
130 Settings::values.enable_compute_pipelines.UsingGlobal()); 135 Settings::values.enable_compute_pipelines.UsingGlobal());
131 ui->use_video_framerate_checkbox->setEnabled( 136 ui->use_video_framerate_checkbox->setEnabled(
132 Settings::values.use_video_framerate.UsingGlobal()); 137 Settings::values.use_video_framerate.UsingGlobal());
138 ui->barrier_feedback_loops_checkbox->setEnabled(
139 Settings::values.barrier_feedback_loops.UsingGlobal());
133 140
134 return; 141 return;
135 } 142 }
@@ -157,6 +164,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
157 ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, 164 ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
158 Settings::values.use_video_framerate, 165 Settings::values.use_video_framerate,
159 use_video_framerate); 166 use_video_framerate);
167 ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox,
168 Settings::values.barrier_feedback_loops,
169 barrier_feedback_loops);
160 ConfigurationShared::SetColoredComboBox( 170 ConfigurationShared::SetColoredComboBox(
161 ui->gpu_accuracy, ui->label_gpu_accuracy, 171 ui->gpu_accuracy, ui->label_gpu_accuracy,
162 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 172 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 a4dc8ceb0..369a7c83e 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -48,6 +48,7 @@ private:
48 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; 48 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
49 ConfigurationShared::CheckState enable_compute_pipelines; 49 ConfigurationShared::CheckState enable_compute_pipelines;
50 ConfigurationShared::CheckState use_video_framerate; 50 ConfigurationShared::CheckState use_video_framerate;
51 ConfigurationShared::CheckState barrier_feedback_loops;
51 52
52 const Core::System& system; 53 const Core::System& system;
53}; 54};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index e7f0ef6be..d527a6f38 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -202,6 +202,16 @@ Compute pipelines are always enabled on all other drivers.</string>
202 </widget> 202 </widget>
203 </item> 203 </item>
204 <item> 204 <item>
205 <widget class="QCheckBox" name="barrier_feedback_loops_checkbox">
206 <property name="toolTip">
207 <string>Improves rendering of transparency effects in specific games.</string>
208 </property>
209 <property name="text">
210 <string>Barrier feedback loops</string>
211 </property>
212 </widget>
213 </item>
214 <item>
205 <widget class="QWidget" name="af_layout" native="true"> 215 <widget class="QWidget" name="af_layout" native="true">
206 <layout class="QHBoxLayout" name="horizontalLayout_1"> 216 <layout class="QHBoxLayout" name="horizontalLayout_1">
207 <property name="leftMargin"> 217 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index f13156434..3cfd5d439 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -129,9 +129,6 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
129 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked(); 129 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
130 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked(); 130 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
131 Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked(); 131 Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked();
132 Settings::values.mouse_panning = ui->mouse_panning->isChecked();
133 Settings::values.mouse_panning_sensitivity =
134 static_cast<float>(ui->mouse_panning_sensitivity->value());
135 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); 132 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
136 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 133 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
137 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); 134 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
@@ -167,8 +164,6 @@ void ConfigureInputAdvanced::LoadConfiguration() {
167 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled.GetValue()); 164 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled.GetValue());
168 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled.GetValue()); 165 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled.GetValue());
169 ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue()); 166 ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
170 ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
171 ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
172 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); 167 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
173 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 168 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
174 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); 169 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
@@ -197,8 +192,6 @@ void ConfigureInputAdvanced::RetranslateUI() {
197void ConfigureInputAdvanced::UpdateUIEnabled() { 192void ConfigureInputAdvanced::UpdateUIEnabled() {
198 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); 193 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
199 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 194 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
200 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
201 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
202 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked()); 195 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
203#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA) 196#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA)
204 ui->enable_ir_sensor->setEnabled(false); 197 ui->enable_ir_sensor->setEnabled(false);
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 2e8b13660..2994d0ab4 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2744,48 +2744,13 @@
2744 </widget> 2744 </widget>
2745 </item> 2745 </item>
2746 <item row="8" column="0"> 2746 <item row="8" column="0">
2747 <widget class="QCheckBox" name="mouse_panning">
2748 <property name="minimumSize">
2749 <size>
2750 <width>0</width>
2751 <height>23</height>
2752 </size>
2753 </property>
2754 <property name="text">
2755 <string>Enable mouse panning</string>
2756 </property>
2757 </widget>
2758 </item>
2759 <item row="8" column="2">
2760 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2761 <property name="toolTip">
2762 <string>Mouse sensitivity</string>
2763 </property>
2764 <property name="alignment">
2765 <set>Qt::AlignCenter</set>
2766 </property>
2767 <property name="suffix">
2768 <string>%</string>
2769 </property>
2770 <property name="minimum">
2771 <number>1</number>
2772 </property>
2773 <property name="maximum">
2774 <number>100</number>
2775 </property>
2776 <property name="value">
2777 <number>100</number>
2778 </property>
2779 </widget>
2780 </item>
2781 <item row="9" column="0">
2782 <widget class="QLabel" name="motion_touch"> 2747 <widget class="QLabel" name="motion_touch">
2783 <property name="text"> 2748 <property name="text">
2784 <string>Motion / Touch</string> 2749 <string>Motion / Touch</string>
2785 </property> 2750 </property>
2786 </widget> 2751 </widget>
2787 </item> 2752 </item>
2788 <item row="9" column="2"> 2753 <item row="8" column="2">
2789 <widget class="QPushButton" name="buttonMotionTouch"> 2754 <widget class="QPushButton" name="buttonMotionTouch">
2790 <property name="text"> 2755 <property name="text">
2791 <string>Configure</string> 2756 <string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 2c2e7e47b..576f5b571 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -23,6 +23,7 @@
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/configure_input_player_widget.h" 25#include "yuzu/configuration/configure_input_player_widget.h"
26#include "yuzu/configuration/configure_mouse_panning.h"
26#include "yuzu/configuration/input_profiles.h" 27#include "yuzu/configuration/input_profiles.h"
27#include "yuzu/util/limitable_input_dialog.h" 28#include "yuzu/util/limitable_input_dialog.h"
28 29
@@ -711,6 +712,21 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
711 }); 712 });
712 } 713 }
713 714
715 if (player_index_ == 0) {
716 connect(ui->mousePanningButton, &QPushButton::clicked, [this, input_subsystem_] {
717 const auto right_stick_param =
718 emulated_controller->GetStickParam(Settings::NativeAnalog::RStick);
719 ConfigureMousePanning dialog(this, input_subsystem_,
720 right_stick_param.Get("deadzone", 0.0f),
721 right_stick_param.Get("range", 1.0f));
722 if (dialog.exec() == QDialog::Accepted) {
723 dialog.ApplyConfiguration();
724 }
725 });
726 } else {
727 ui->mousePanningWidget->hide();
728 }
729
714 // Player Connected checkbox 730 // Player Connected checkbox
715 connect(ui->groupConnectedController, &QGroupBox::toggled, 731 connect(ui->groupConnectedController, &QGroupBox::toggled,
716 [this](bool checked) { emit Connected(checked); }); 732 [this](bool checked) { emit Connected(checked); });
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index a9567c6ee..43f6c7b50 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -3048,6 +3048,102 @@
3048 </item> 3048 </item>
3049 </layout> 3049 </layout>
3050 </item> 3050 </item>
3051 <item>
3052 <widget class="QWidget" name="mousePanningWidget" native="true">
3053 <layout class="QHBoxLayout" name="mousePanningHorizontalLayout">
3054 <property name="spacing">
3055 <number>0</number>
3056 </property>
3057 <property name="leftMargin">
3058 <number>0</number>
3059 </property>
3060 <property name="topMargin">
3061 <number>0</number>
3062 </property>
3063 <property name="rightMargin">
3064 <number>0</number>
3065 </property>
3066 <property name="bottomMargin">
3067 <number>3</number>
3068 </property>
3069 <item>
3070 <spacer name="mousePanningHorizontalSpacerLeft">
3071 <property name="orientation">
3072 <enum>Qt::Horizontal</enum>
3073 </property>
3074 <property name="sizeHint" stdset="0">
3075 <size>
3076 <width>20</width>
3077 <height>20</height>
3078 </size>
3079 </property>
3080 </spacer>
3081 </item>
3082 <item>
3083 <widget class="QGroupBox" name="mousePanningGroup">
3084 <property name="title">
3085 <string>Mouse panning</string>
3086 </property>
3087 <property name="alignment">
3088 <set>Qt::AlignCenter</set>
3089 </property>
3090 <layout class="QVBoxLayout" name="mousePanningVerticalLayout">
3091 <property name="spacing">
3092 <number>3</number>
3093 </property>
3094 <property name="leftMargin">
3095 <number>3</number>
3096 </property>
3097 <property name="topMargin">
3098 <number>3</number>
3099 </property>
3100 <property name="rightMargin">
3101 <number>3</number>
3102 </property>
3103 <property name="bottomMargin">
3104 <number>3</number>
3105 </property>
3106 <item>
3107 <widget class="QPushButton" name="mousePanningButton">
3108 <property name="minimumSize">
3109 <size>
3110 <width>68</width>
3111 <height>0</height>
3112 </size>
3113 </property>
3114 <property name="maximumSize">
3115 <size>
3116 <width>68</width>
3117 <height>16777215</height>
3118 </size>
3119 </property>
3120 <property name="styleSheet">
3121 <string notr="true">min-width: 68px;</string>
3122 </property>
3123 <property name="text">
3124 <string>Configure</string>
3125 </property>
3126 </widget>
3127 </item>
3128 </layout>
3129 </widget>
3130 </item>
3131 <item>
3132 <spacer name="mousePanningHorizontalSpacerRight">
3133 <property name="orientation">
3134 <enum>Qt::Horizontal</enum>
3135 </property>
3136 <property name="sizeHint" stdset="0">
3137 <size>
3138 <width>20</width>
3139 <height>20</height>
3140 </size>
3141 </property>
3142 </spacer>
3143 </item>
3144 </layout>
3145 </widget>
3146 </item>
3051 </layout> 3147 </layout>
3052 </widget> 3148 </widget>
3053 </item> 3149 </item>
diff --git a/src/yuzu/configuration/configure_mouse_panning.cpp b/src/yuzu/configuration/configure_mouse_panning.cpp
new file mode 100644
index 000000000..f183d2740
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_panning.cpp
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <QCloseEvent>
5
6#include "common/settings.h"
7#include "ui_configure_mouse_panning.h"
8#include "yuzu/configuration/configure_mouse_panning.h"
9
10ConfigureMousePanning::ConfigureMousePanning(QWidget* parent,
11 InputCommon::InputSubsystem* input_subsystem_,
12 float right_stick_deadzone, float right_stick_range)
13 : QDialog(parent), input_subsystem{input_subsystem_},
14 ui(std::make_unique<Ui::ConfigureMousePanning>()) {
15 ui->setupUi(this);
16 SetConfiguration(right_stick_deadzone, right_stick_range);
17 ConnectEvents();
18}
19
20ConfigureMousePanning::~ConfigureMousePanning() = default;
21
22void ConfigureMousePanning::closeEvent(QCloseEvent* event) {
23 event->accept();
24}
25
26void ConfigureMousePanning::SetConfiguration(float right_stick_deadzone, float right_stick_range) {
27 ui->enable->setChecked(Settings::values.mouse_panning.GetValue());
28 ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetValue());
29 ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetValue());
30 ui->deadzone_x_counterweight->setValue(
31 Settings::values.mouse_panning_deadzone_x_counterweight.GetValue());
32 ui->deadzone_y_counterweight->setValue(
33 Settings::values.mouse_panning_deadzone_y_counterweight.GetValue());
34 ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetValue());
35 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetValue());
36
37 if (right_stick_deadzone > 0.0f || right_stick_range != 1.0f) {
38 ui->warning_label->setText(QString::fromStdString(
39 "Mouse panning works better with a deadzone of 0% and a range of 100%.\n"
40 "Current values are " +
41 std::to_string(static_cast<int>(right_stick_deadzone * 100.0f)) + "% and " +
42 std::to_string(static_cast<int>(right_stick_range * 100.0f)) + "% respectively."));
43 } else {
44 ui->warning_label->hide();
45 }
46}
47
48void ConfigureMousePanning::SetDefaultConfiguration() {
49 ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetDefault());
50 ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetDefault());
51 ui->deadzone_x_counterweight->setValue(
52 Settings::values.mouse_panning_deadzone_x_counterweight.GetDefault());
53 ui->deadzone_y_counterweight->setValue(
54 Settings::values.mouse_panning_deadzone_y_counterweight.GetDefault());
55 ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetDefault());
56 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetDefault());
57}
58
59void ConfigureMousePanning::ConnectEvents() {
60 connect(ui->default_button, &QPushButton::clicked, this,
61 &ConfigureMousePanning::SetDefaultConfiguration);
62 connect(ui->button_box, &QDialogButtonBox::accepted, this,
63 &ConfigureMousePanning::ApplyConfiguration);
64 connect(ui->button_box, &QDialogButtonBox::rejected, this, [this] { reject(); });
65}
66
67void ConfigureMousePanning::ApplyConfiguration() {
68 Settings::values.mouse_panning = ui->enable->isChecked();
69 Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value());
70 Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value());
71 Settings::values.mouse_panning_deadzone_x_counterweight =
72 static_cast<float>(ui->deadzone_x_counterweight->value());
73 Settings::values.mouse_panning_deadzone_y_counterweight =
74 static_cast<float>(ui->deadzone_y_counterweight->value());
75 Settings::values.mouse_panning_decay_strength = static_cast<float>(ui->decay_strength->value());
76 Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value());
77
78 accept();
79}
diff --git a/src/yuzu/configuration/configure_mouse_panning.h b/src/yuzu/configuration/configure_mouse_panning.h
new file mode 100644
index 000000000..08c6e1f62
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_panning.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <QDialog>
8
9namespace InputCommon {
10class InputSubsystem;
11}
12
13namespace Ui {
14class ConfigureMousePanning;
15}
16
17class ConfigureMousePanning : public QDialog {
18 Q_OBJECT
19public:
20 explicit ConfigureMousePanning(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_,
21 float right_stick_deadzone, float right_stick_range);
22 ~ConfigureMousePanning() override;
23
24public slots:
25 void ApplyConfiguration();
26
27private:
28 void closeEvent(QCloseEvent* event) override;
29 void SetConfiguration(float right_stick_deadzone, float right_stick_range);
30 void SetDefaultConfiguration();
31 void ConnectEvents();
32
33 InputCommon::InputSubsystem* input_subsystem;
34 std::unique_ptr<Ui::ConfigureMousePanning> ui;
35};
diff --git a/src/yuzu/configuration/configure_mouse_panning.ui b/src/yuzu/configuration/configure_mouse_panning.ui
new file mode 100644
index 000000000..75795b727
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_panning.ui
@@ -0,0 +1,238 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMousePanning</class>
4 <widget class="QDialog" name="configure_mouse_panning">
5 <property name="windowTitle">
6 <string>Configure mouse panning</string>
7 </property>
8 <layout class="QVBoxLayout">
9 <item>
10 <widget class="QCheckBox" name="enable">
11 <property name="text">
12 <string>Enable</string>
13 </property>
14 <property name="toolTip">
15 <string>Can be toggled via a hotkey</string>
16 </property>
17 </widget>
18 </item>
19 <item>
20 <layout class="QHBoxLayout">
21 <item>
22 <widget class="QGroupBox" name="sensitivity_box">
23 <property name="title">
24 <string>Sensitivity</string>
25 </property>
26 <layout class="QGridLayout">
27 <item row="0" column="0">
28 <widget class="QLabel" name="x_sensitivity_label">
29 <property name="text">
30 <string>Horizontal</string>
31 </property>
32 </widget>
33 </item>
34 <item row="0" column="1">
35 <widget class="QSpinBox" name="x_sensitivity">
36 <property name="alignment">
37 <set>Qt::AlignCenter</set>
38 </property>
39 <property name="suffix">
40 <string>%</string>
41 </property>
42 <property name="minimum">
43 <number>1</number>
44 </property>
45 <property name="maximum">
46 <number>100</number>
47 </property>
48 <property name="value">
49 <number>50</number>
50 </property>
51 </widget>
52 </item>
53 <item row="1" column="0">
54 <widget class="QLabel" name="y_sensitivity_label">
55 <property name="text">
56 <string>Vertical</string>
57 </property>
58 </widget>
59 </item>
60 <item row="1" column="1">
61 <widget class="QSpinBox" name="y_sensitivity">
62 <property name="alignment">
63 <set>Qt::AlignCenter</set>
64 </property>
65 <property name="suffix">
66 <string>%</string>
67 </property>
68 <property name="minimum">
69 <number>1</number>
70 </property>
71 <property name="maximum">
72 <number>100</number>
73 </property>
74 <property name="value">
75 <number>50</number>
76 </property>
77 </widget>
78 </item>
79 </layout>
80 </widget>
81 </item>
82 <item>
83 <widget class="QGroupBox" name="deadzone_counterweight_box">
84 <property name="title">
85 <string>Deadzone counterweight</string>
86 </property>
87 <property name="toolTip">
88 <string>Counteracts a game's built-in deadzone</string>
89 </property>
90 <layout class="QGridLayout">
91 <item row="0" column="0">
92 <widget class="QLabel" name="deadzone_x_counterweight_label">
93 <property name="text">
94 <string>Horizontal</string>
95 </property>
96 </widget>
97 </item>
98 <item row="0" column="1">
99 <widget class="QSpinBox" name="deadzone_x_counterweight">
100 <property name="alignment">
101 <set>Qt::AlignCenter</set>
102 </property>
103 <property name="suffix">
104 <string>%</string>
105 </property>
106 <property name="minimum">
107 <number>0</number>
108 </property>
109 <property name="maximum">
110 <number>100</number>
111 </property>
112 <property name="value">
113 <number>0</number>
114 </property>
115 </widget>
116 </item>
117 <item row="1" column="0">
118 <widget class="QLabel" name="deadzone_y_counterweight_label">
119 <property name="text">
120 <string>Vertical</string>
121 </property>
122 </widget>
123 </item>
124 <item row="1" column="1">
125 <widget class="QSpinBox" name="deadzone_y_counterweight">
126 <property name="alignment">
127 <set>Qt::AlignCenter</set>
128 </property>
129 <property name="suffix">
130 <string>%</string>
131 </property>
132 <property name="minimum">
133 <number>0</number>
134 </property>
135 <property name="maximum">
136 <number>100</number>
137 </property>
138 <property name="value">
139 <number>0</number>
140 </property>
141 </widget>
142 </item>
143 </layout>
144 </widget>
145 </item>
146 <item>
147 <widget class="QGroupBox" name="decay_box">
148 <property name="title">
149 <string>Stick decay</string>
150 </property>
151 <layout class="QGridLayout">
152 <item row="0" column="0">
153 <widget class="QLabel" name="decay_strength_label">
154 <property name="text">
155 <string>Strength</string>
156 </property>
157 </widget>
158 </item>
159 <item row="0" column="1">
160 <widget class="QSpinBox" name="decay_strength">
161 <property name="alignment">
162 <set>Qt::AlignCenter</set>
163 </property>
164 <property name="suffix">
165 <string>%</string>
166 </property>
167 <property name="minimum">
168 <number>0</number>
169 </property>
170 <property name="maximum">
171 <number>100</number>
172 </property>
173 <property name="value">
174 <number>22</number>
175 </property>
176 </widget>
177 </item>
178 <item row="1" column="0">
179 <widget class="QLabel" name="min_decay_label">
180 <property name="text">
181 <string>Minimum</string>
182 </property>
183 </widget>
184 </item>
185 <item row="1" column="1">
186 <widget class="QSpinBox" name="min_decay">
187 <property name="alignment">
188 <set>Qt::AlignCenter</set>
189 </property>
190 <property name="suffix">
191 <string>%</string>
192 </property>
193 <property name="minimum">
194 <number>0</number>
195 </property>
196 <property name="maximum">
197 <number>100</number>
198 </property>
199 <property name="value">
200 <number>5</number>
201 </property>
202 </widget>
203 </item>
204 </layout>
205 </widget>
206 </item>
207 </layout>
208 </item>
209 <item>
210 <widget class="QLabel" name="warning_label">
211 <property name="text">
212 <string/>
213 </property>
214 </widget>
215 </item>
216 <item>
217 <layout class="QHBoxLayout">
218 <item>
219 <widget class="QPushButton" name="default_button">
220 <property name="text">
221 <string>Default</string>
222 </property>
223 </widget>
224 </item>
225 <item>
226 <widget class="QDialogButtonBox" name="button_box">
227 <property name="standardButtons">
228 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
229 </property>
230 </widget>
231 </item>
232 </layout>
233 </item>
234 </layout>
235 </widget>
236 <resources/>
237 <connections/>
238</ui>
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 71afbc423..f83705544 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -305,6 +305,9 @@ void ConfigureRingController::EnableRingController() {
305 QMessageBox::warning(this, dialog_title, 305 QMessageBox::warning(this, dialog_title,
306 tr("The current mapped device doesn't have a ring attached")); 306 tr("The current mapped device doesn't have a ring attached"));
307 break; 307 break;
308 case Common::Input::DriverResult::InvalidHandle:
309 QMessageBox::warning(this, dialog_title, tr("The current mapped device is not connected"));
310 break;
308 default: 311 default:
309 QMessageBox::warning(this, dialog_title, 312 QMessageBox::warning(this, dialog_title,
310 tr("Unexpected driver result %1").arg(static_cast<int>(result))); 313 tr("Unexpected driver result %1").arg(static_cast<int>(result)));
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 514dff372..38ecccc3d 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -23,7 +23,7 @@
23 </size> 23 </size>
24 </property> 24 </property>
25 <property name="text"> 25 <property name="text">
26 <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string> 26 <string>To use Ring-Con, configure player 1 as right Joy-Con (both physical and emulated), and player 2 as left Joy-Con (left physical and dual emulated) before starting the game.</string>
27 </property> 27 </property>
28 <property name="wordWrap"> 28 <property name="wordWrap">
29 <bool>true</bool> 29 <bool>true</bool>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 8768a7c3c..e8418b302 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -447,6 +447,14 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
447 447
448#if defined(HAVE_SDL2) && !defined(_WIN32) 448#if defined(HAVE_SDL2) && !defined(_WIN32)
449 SDL_InitSubSystem(SDL_INIT_VIDEO); 449 SDL_InitSubSystem(SDL_INIT_VIDEO);
450
451 // Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to
452 // the user through their desktop environment.
453 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
454 //: computer from sleeping
455 QByteArray wakelock_reason = tr("Running a game").toLatin1();
456 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
457
450 // SDL disables the screen saver by default, and setting the hint 458 // SDL disables the screen saver by default, and setting the hint
451 // SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver 459 // SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver
452 // for now. 460 // for now.
@@ -1623,45 +1631,6 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
1623} 1631}
1624 1632
1625#ifdef __unix__ 1633#ifdef __unix__
1626static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1627 if (!QDBusConnection::sessionBus().isConnected()) {
1628 return {};
1629 }
1630 // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
1631 QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"),
1632 QString::fromLatin1("/org/freedesktop/portal/desktop"),
1633 QString::fromLatin1("org.freedesktop.portal.Inhibit"));
1634 if (!xdp.isValid()) {
1635 LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
1636 return {};
1637 }
1638 QVariantMap options = {};
1639 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
1640 //: computer from sleeping
1641 options.insert(QString::fromLatin1("reason"),
1642 QCoreApplication::translate("GMainWindow", "yuzu is running a game"));
1643 // 0x4: Suspend lock; 0x8: Idle lock
1644 QDBusReply<QDBusObjectPath> reply =
1645 xdp.call(QString::fromLatin1("Inhibit"),
1646 QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
1647
1648 if (reply.isValid()) {
1649 return reply.value();
1650 }
1651 LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
1652 reply.error().message().toStdString());
1653 return {};
1654}
1655
1656static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
1657 if (!QDBusConnection::sessionBus().isConnected()) {
1658 return;
1659 }
1660 QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
1661 QString::fromLatin1("org.freedesktop.portal.Request"));
1662 unlocker.call(QString::fromLatin1("Close"));
1663}
1664
1665std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0}; 1634std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
1666 1635
1667void GMainWindow::SetupSigInterrupts() { 1636void GMainWindow::SetupSigInterrupts() {
@@ -1714,12 +1683,6 @@ void GMainWindow::PreventOSSleep() {
1714 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1683 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1715#elif defined(HAVE_SDL2) 1684#elif defined(HAVE_SDL2)
1716 SDL_DisableScreenSaver(); 1685 SDL_DisableScreenSaver();
1717#ifdef __unix__
1718 auto reply = HoldWakeLockLinux(winId());
1719 if (reply) {
1720 wake_lock = std::move(reply.value());
1721 }
1722#endif
1723#endif 1686#endif
1724} 1687}
1725 1688
@@ -1728,11 +1691,6 @@ void GMainWindow::AllowOSSleep() {
1728 SetThreadExecutionState(ES_CONTINUOUS); 1691 SetThreadExecutionState(ES_CONTINUOUS);
1729#elif defined(HAVE_SDL2) 1692#elif defined(HAVE_SDL2)
1730 SDL_EnableScreenSaver(); 1693 SDL_EnableScreenSaver();
1731#ifdef __unix__
1732 if (!wake_lock.path().isEmpty()) {
1733 ReleaseWakeLockLinux(wake_lock);
1734 }
1735#endif
1736#endif 1694#endif
1737} 1695}
1738 1696
@@ -1749,15 +1707,17 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1749 system->SetFilesystem(vfs); 1707 system->SetFilesystem(vfs);
1750 1708
1751 system->SetAppletFrontendSet({ 1709 system->SetAppletFrontendSet({
1752 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings 1710 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
1753 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1711 (UISettings::values.controller_applet_disabled.GetValue() == true)
1754 std::make_unique<QtErrorDisplay>(*this), // Error Display 1712 ? nullptr
1755 nullptr, // Mii Editor 1713 : std::make_unique<QtControllerSelector>(*this), // Controller Selector
1756 nullptr, // Parental Controls 1714 std::make_unique<QtErrorDisplay>(*this), // Error Display
1757 nullptr, // Photo Viewer 1715 nullptr, // Mii Editor
1758 std::make_unique<QtProfileSelector>(*this), // Profile Selector 1716 nullptr, // Parental Controls
1759 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard 1717 nullptr, // Photo Viewer
1760 std::make_unique<QtWebBrowser>(*this), // Web Browser 1718 std::make_unique<QtProfileSelector>(*this), // Profile Selector
1719 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
1720 std::make_unique<QtWebBrowser>(*this), // Web Browser
1761 }); 1721 });
1762 1722
1763 const Core::SystemResultStatus result{ 1723 const Core::SystemResultStatus result{
@@ -3840,7 +3800,7 @@ void GMainWindow::OnLoadAmiibo() {
3840 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3800 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3841 3801
3842 // Remove amiibo if one is connected 3802 // Remove amiibo if one is connected
3843 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3803 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3844 virtual_amiibo->CloseAmiibo(); 3804 virtual_amiibo->CloseAmiibo();
3845 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3805 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3846 return; 3806 return;
@@ -3868,7 +3828,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
3868 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3828 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3869 const QString title = tr("Error loading Amiibo data"); 3829 const QString title = tr("Error loading Amiibo data");
3870 // Remove amiibo if one is connected 3830 // Remove amiibo if one is connected
3871 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3831 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3872 virtual_amiibo->CloseAmiibo(); 3832 virtual_amiibo->CloseAmiibo();
3873 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3833 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3874 return; 3834 return;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index e0e775d87..2cfb96257 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -504,8 +504,6 @@ private:
504#ifdef __unix__ 504#ifdef __unix__
505 QSocketNotifier* sig_interrupt_notifier; 505 QSocketNotifier* sig_interrupt_notifier;
506 static std::array<int, 3> sig_interrupt_fds; 506 static std::array<int, 3> sig_interrupt_fds;
507
508 QDBusObjectPath wake_lock{};
509#endif 507#endif
510 508
511protected: 509protected:
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index db43b7033..20a517d34 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -77,6 +77,8 @@ struct Values {
77 Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 77 Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
78 Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 78 Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
79 Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; 79 Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
80 Settings::Setting<bool> controller_applet_disabled{false, "disableControllerApplet"};
81
80 // Set when Vulkan is known to crash the application 82 // Set when Vulkan is known to crash the application
81 bool has_broken_vulkan = false; 83 bool has_broken_vulkan = false;
82 84
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
index 9bd1ec686..7c26a3dc7 100644
--- a/src/yuzu/vk_device_info.cpp
+++ b/src/yuzu/vk_device_info.cpp
@@ -5,10 +5,12 @@
5#include <vector> 5#include <vector>
6#include "common/dynamic_library.h" 6#include "common/dynamic_library.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_device.h"
8#include "video_core/vulkan_common/vulkan_instance.h" 9#include "video_core/vulkan_common/vulkan_instance.h"
9#include "video_core/vulkan_common/vulkan_library.h" 10#include "video_core/vulkan_common/vulkan_library.h"
10#include "video_core/vulkan_common/vulkan_surface.h" 11#include "video_core/vulkan_common/vulkan_surface.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h" 12#include "video_core/vulkan_common/vulkan_wrapper.h"
13#include "vulkan/vulkan_core.h"
12#include "yuzu/qt_common.h" 14#include "yuzu/qt_common.h"
13#include "yuzu/vk_device_info.h" 15#include "yuzu/vk_device_info.h"
14 16
@@ -16,8 +18,8 @@ class QWindow;
16 18
17namespace VkDeviceInfo { 19namespace VkDeviceInfo {
18Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_, 20Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_,
19 bool is_intel_proprietary_) 21 bool has_broken_compute_)
20 : name{name_}, vsync_support{vsync_modes_}, is_intel_proprietary{is_intel_proprietary_} {} 22 : name{name_}, vsync_support{vsync_modes_}, has_broken_compute{has_broken_compute_} {}
21 23
22Record::~Record() = default; 24Record::~Record() = default;
23 25
@@ -48,9 +50,10 @@ void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
48 properties.pNext = &driver_properties; 50 properties.pNext = &driver_properties;
49 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties); 51 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
50 52
51 records.push_back(VkDeviceInfo::Record(name, present_modes, 53 bool has_broken_compute{Vulkan::Device::CheckBrokenCompute(
52 driver_properties.driverID == 54 driver_properties.driverID, properties.properties.driverVersion)};
53 VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS)); 55
56 records.push_back(VkDeviceInfo::Record(name, present_modes, has_broken_compute));
54 } 57 }
55} catch (const Vulkan::vk::Exception& exception) { 58} catch (const Vulkan::vk::Exception& exception) {
56 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 59 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h
index 5a6c64416..bda8262f4 100644
--- a/src/yuzu/vk_device_info.h
+++ b/src/yuzu/vk_device_info.h
@@ -24,12 +24,12 @@ namespace VkDeviceInfo {
24class Record { 24class Record {
25public: 25public:
26 explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes, 26 explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
27 bool is_intel_proprietary); 27 bool has_broken_compute);
28 ~Record(); 28 ~Record();
29 29
30 const std::string name; 30 const std::string name;
31 const std::vector<VkPresentModeKHR> vsync_support; 31 const std::vector<VkPresentModeKHR> vsync_support;
32 const bool is_intel_proprietary; 32 const bool has_broken_compute;
33}; 33};
34 34
35void PopulateRecords(std::vector<Record>& records, QWindow* window); 35void PopulateRecords(std::vector<Record>& records, QWindow* window);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 911d461e4..119e22183 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -140,9 +140,29 @@ udp_input_servers =
140# 0 (default): Off, 1: On 140# 0 (default): Off, 1: On
141mouse_panning = 141mouse_panning =
142 142
143# Set mouse sensitivity. 143# Set mouse panning horizontal sensitivity.
144# Default: 1.0 144# Default: 50.0
145mouse_panning_sensitivity = 145mouse_panning_x_sensitivity =
146
147# Set mouse panning vertical sensitivity.
148# Default: 50.0
149mouse_panning_y_sensitivity =
150
151# Set mouse panning deadzone horizontal counterweight.
152# Default: 0.0
153mouse_panning_deadzone_x_counterweight =
154
155# Set mouse panning deadzone vertical counterweight.
156# Default: 0.0
157mouse_panning_deadzone_y_counterweight =
158
159# Set mouse panning stick decay strength.
160# Default: 22.0
161mouse_panning_decay_strength =
162
163# Set mouse panning stick minimum decay.
164# Default: 5.0
165mouse_panning_minimum_decay =
146 166
147# Emulate an analog control stick from keyboard inputs. 167# Emulate an analog control stick from keyboard inputs.
148# 0 (default): Disabled, 1: Enabled 168# 0 (default): Disabled, 1: Enabled