summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/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.h43
-rw-r--r--src/common/ring_buffer.h3
-rw-r--r--src/common/scratch_buffer.h9
-rw-r--r--src/common/settings.h12
-rw-r--r--src/common/steady_clock.cpp5
-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.cpp3
-rw-r--r--src/common/x64/cpu_wait.cpp20
-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/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.cpp133
-rw-r--r--src/input_common/drivers/joycon.h18
-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.cpp160
-rw-r--r--src/input_common/helpers/joycon_driver.h7
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h59
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp390
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h34
-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/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/buffer_cache/buffer_cache.h13
-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/draw_manager.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.cpp35
-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/vk_buffer_cache.cpp22
-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.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp27
-rw-r--r--src/video_core/shader_cache.cpp4
-rw-r--r--src/video_core/texture_cache/image_base.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h40
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h14
-rw-r--r--src/video_core/texture_cache/util.cpp48
-rw-r--r--src/video_core/texture_cache/util.h31
-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_device.cpp12
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h34
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp35
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp5
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp55
-rw-r--r--src/yuzu/configuration/configure_graphics.h3
-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_per_game.cpp5
-rw-r--r--src/yuzu/configuration/configure_per_game.h3
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui2
-rw-r--r--src/yuzu/main.cpp70
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/vk_device_info.cpp61
-rw-r--r--src/yuzu/vk_device_info.h36
-rw-r--r--src/yuzu_cmd/default_ini.h26
216 files changed, 3644 insertions, 1919 deletions
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/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..ea30770ae 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -86,7 +86,7 @@ enum class NfcState {
86 NewAmiibo, 86 NewAmiibo,
87 WaitingForAmiibo, 87 WaitingForAmiibo,
88 AmiiboRemoved, 88 AmiiboRemoved,
89 NotAnAmiibo, 89 InvalidTagType,
90 NotSupported, 90 NotSupported,
91 WrongDeviceState, 91 WrongDeviceState,
92 WriteFailed, 92 WriteFailed,
@@ -218,8 +218,22 @@ struct CameraStatus {
218}; 218};
219 219
220struct NfcStatus { 220struct NfcStatus {
221 NfcState state{}; 221 NfcState state{NfcState::Unknown};
222 std::vector<u8> data{}; 222 u8 uuid_length;
223 u8 protocol;
224 u8 tag_type;
225 std::array<u8, 10> uuid;
226};
227
228struct MifareData {
229 u8 command;
230 u8 sector;
231 std::array<u8, 0x6> key;
232 std::array<u8, 0x10> data;
233};
234
235struct MifareRequest {
236 std::array<MifareData, 0x10> data;
223}; 237};
224 238
225// List of buttons to be passed to Qt that can be translated 239// List of buttons to be passed to Qt that can be translated
@@ -294,7 +308,7 @@ struct CallbackStatus {
294 BatteryStatus battery_status{}; 308 BatteryStatus battery_status{};
295 VibrationStatus vibration_status{}; 309 VibrationStatus vibration_status{};
296 CameraFormat camera_status{CameraFormat::None}; 310 CameraFormat camera_status{CameraFormat::None};
297 NfcState nfc_status{NfcState::Unknown}; 311 NfcStatus nfc_status{};
298 std::vector<u8> raw_data{}; 312 std::vector<u8> raw_data{};
299}; 313};
300 314
@@ -356,9 +370,30 @@ public:
356 return NfcState::NotSupported; 370 return NfcState::NotSupported;
357 } 371 }
358 372
373 virtual NfcState StartNfcPolling() {
374 return NfcState::NotSupported;
375 }
376
377 virtual NfcState StopNfcPolling() {
378 return NfcState::NotSupported;
379 }
380
381 virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
382 return NfcState::NotSupported;
383 }
384
359 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { 385 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
360 return NfcState::NotSupported; 386 return NfcState::NotSupported;
361 } 387 }
388
389 virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
390 [[maybe_unused]] MifareRequest& out_data) {
391 return NfcState::NotSupported;
392 }
393
394 virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
395 return NfcState::NotSupported;
396 }
362}; 397};
363 398
364/// An abstract class template for a factory that can create input devices. 399/// 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.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/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..c998b1197 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>
@@ -187,6 +188,8 @@ static CPUCaps Detect() {
187 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * 188 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
188 caps.tsc_crystal_ratio_numerator / 189 caps.tsc_crystal_ratio_numerator /
189 caps.tsc_crystal_ratio_denominator; 190 caps.tsc_crystal_ratio_denominator;
191 } else {
192 caps.tsc_frequency = X64::EstimateRDTSCFrequency();
190 } 193 }
191 } 194 }
192 195
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp
index cfeef6a3d..c53dd4945 100644
--- a/src/common/x64/cpu_wait.cpp
+++ b/src/common/x64/cpu_wait.cpp
@@ -9,19 +9,11 @@
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
15#ifdef _MSC_VER 16#ifdef _MSC_VER
16__forceinline static u64 FencedRDTSC() {
17 _mm_lfence();
18 _ReadWriteBarrier();
19 const u64 result = __rdtsc();
20 _mm_lfence();
21 _ReadWriteBarrier();
22 return result;
23}
24
25__forceinline static void TPAUSE() { 17__forceinline static void TPAUSE() {
26 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 18 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
27 // For reference: 19 // For reference:
@@ -32,16 +24,6 @@ __forceinline static void TPAUSE() {
32 _tpause(0, FencedRDTSC() + PauseCycles); 24 _tpause(0, FencedRDTSC() + PauseCycles);
33} 25}
34#else 26#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() { 27static void TPAUSE() {
46 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 28 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
47 // For reference: 29 // For reference:
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 227c431bc..3655b8478 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/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..52494e0d9 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -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) {
@@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
291 return Common::Input::NfcState::Success; 291 return Common::Input::NfcState::Success;
292}; 292};
293 293
294Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
295 auto handle = GetHandle(identifier);
296 if (handle == nullptr) {
297 return Common::Input::NfcState::Unknown;
298 }
299 return TranslateDriverResult(handle->StartNfcPolling());
300};
301
302Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
303 auto handle = GetHandle(identifier);
304 if (handle == nullptr) {
305 return Common::Input::NfcState::Unknown;
306 }
307 return TranslateDriverResult(handle->StopNfcPolling());
308};
309
310Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
311 std::vector<u8>& out_data) {
312 auto handle = GetHandle(identifier);
313 if (handle == nullptr) {
314 return Common::Input::NfcState::Unknown;
315 }
316 return TranslateDriverResult(handle->ReadAmiiboData(out_data));
317}
318
294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, 319Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
295 const std::vector<u8>& data) { 320 const std::vector<u8>& data) {
296 auto handle = GetHandle(identifier); 321 auto handle = GetHandle(identifier);
297 if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { 322 if (handle == nullptr) {
298 return Common::Input::NfcState::WriteFailed; 323 return Common::Input::NfcState::Unknown;
299 } 324 }
300 return Common::Input::NfcState::Success; 325 return TranslateDriverResult(handle->WriteNfcData(data));
326};
327
328Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
329 const Common::Input::MifareRequest& request,
330 Common::Input::MifareRequest& data) {
331 auto handle = GetHandle(identifier);
332 if (handle == nullptr) {
333 return Common::Input::NfcState::Unknown;
334 }
335
336 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
337 std::vector<Joycon::MifareReadChunk> read_request{};
338 for (const auto& request_data : request.data) {
339 if (request_data.command == 0) {
340 continue;
341 }
342 Joycon::MifareReadChunk chunk = {
343 .command = command,
344 .sector_key = {},
345 .sector = request_data.sector,
346 };
347 memcpy(chunk.sector_key.data(), request_data.key.data(),
348 sizeof(Joycon::MifareReadChunk::sector_key));
349 read_request.emplace_back(chunk);
350 }
351
352 std::vector<Joycon::MifareReadData> read_data(read_request.size());
353 const auto result = handle->ReadMifareData(read_request, read_data);
354 if (result == Joycon::DriverResult::Success) {
355 for (std::size_t i = 0; i < read_request.size(); i++) {
356 data.data[i] = {
357 .command = static_cast<u8>(command),
358 .sector = read_data[i].sector,
359 .key = {},
360 .data = read_data[i].data,
361 };
362 }
363 }
364 return TranslateDriverResult(result);
365};
366
367Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
368 const Common::Input::MifareRequest& request) {
369 auto handle = GetHandle(identifier);
370 if (handle == nullptr) {
371 return Common::Input::NfcState::Unknown;
372 }
373
374 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
375 std::vector<Joycon::MifareWriteChunk> write_request{};
376 for (const auto& request_data : request.data) {
377 if (request_data.command == 0) {
378 continue;
379 }
380 Joycon::MifareWriteChunk chunk = {
381 .command = command,
382 .sector_key = {},
383 .sector = request_data.sector,
384 .data = {},
385 };
386 memcpy(chunk.sector_key.data(), request_data.key.data(),
387 sizeof(Joycon::MifareReadChunk::sector_key));
388 memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
389 write_request.emplace_back(chunk);
390 }
391
392 return TranslateDriverResult(handle->WriteMifareData(write_request));
301}; 393};
302 394
303Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, 395Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
403} 495}
404 496
405void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 497void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
406 const std::vector<u8>& amiibo_data) { 498 const Joycon::TagInfo& tag_info) {
407 const auto identifier = GetIdentifier(port, type); 499 const auto identifier = GetIdentifier(port, type);
408 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved 500 const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
409 : Common::Input::NfcState::NewAmiibo; 501 : Common::Input::NfcState::NewAmiibo;
410 SetNfc(identifier, {nfc_state, amiibo_data}); 502
503 const Common::Input::NfcStatus nfc_status{
504 .state = nfc_state,
505 .uuid_length = tag_info.uuid_length,
506 .protocol = tag_info.protocol,
507 .tag_type = tag_info.tag_type,
508 .uuid = tag_info.uuid,
509 };
510
511 SetNfc(identifier, nfc_status);
411} 512}
412 513
413void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 514void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
@@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
726 return "Unknown Switch Controller"; 827 return "Unknown Switch Controller";
727 } 828 }
728} 829}
830
831Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const {
832 switch (result) {
833 case Joycon::DriverResult::Success:
834 return Common::Input::NfcState::Success;
835 case Joycon::DriverResult::Disabled:
836 return Common::Input::NfcState::WrongDeviceState;
837 case Joycon::DriverResult::NotSupported:
838 return Common::Input::NfcState::NotSupported;
839 default:
840 return Common::Input::NfcState::Unknown;
841 }
842}
843
729} // namespace InputCommon 844} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index e3f0ad78f..4c323d7d6 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,6 +15,7 @@ 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 DriverResult;
20enum class IrsResolution; 21enum class IrsResolution;
@@ -39,9 +40,18 @@ public:
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, 40 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override; 41 Common::Input::CameraFormat camera_format) override;
41 42
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 44 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
45 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
46 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
47 std::vector<u8>& out_data) override;
48 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
44 const std::vector<u8>& data) override; 49 const std::vector<u8>& data) override;
50 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
51 const Common::Input::MifareRequest& request,
52 Common::Input::MifareRequest& out_data) override;
53 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
54 const Common::Input::MifareRequest& request) override;
45 55
46 Common::Input::DriverResult SetPollingMode( 56 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; 57 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
@@ -82,7 +92,7 @@ private:
82 const Joycon::MotionData& value); 92 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data); 93 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 94 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
85 const std::vector<u8>& amiibo_data); 95 const Joycon::TagInfo& amiibo_data);
86 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 96 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
87 Joycon::IrsResolution format); 97 Joycon::IrsResolution format);
88 98
@@ -102,6 +112,8 @@ private:
102 /// Returns the name of the device in text format 112 /// Returns the name of the device in text format
103 std::string JoyconName(Joycon::ControllerType type) const; 113 std::string JoyconName(Joycon::ControllerType type) const;
104 114
115 Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const;
116
105 std::jthread scan_thread; 117 std::jthread scan_thread;
106 118
107 // Joycon types are split by type to ease supporting dualjoycon configurations 119 // 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..ec984a647 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.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/logging/log.h" 4#include "common/logging/log.h"
5#include "common/scope_exit.h"
5#include "common/swap.h" 6#include "common/swap.h"
6#include "common/thread.h" 7#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h" 8#include "input_common/helpers/joycon_driver.h"
@@ -71,6 +72,7 @@ DriverResult JoyconDriver::InitializeDevice() {
71 nfc_enabled = false; 72 nfc_enabled = false;
72 passive_enabled = false; 73 passive_enabled = false;
73 irs_enabled = false; 74 irs_enabled = false;
75 input_only_device = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; 76 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833; 77 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; 78 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
@@ -85,16 +87,23 @@ DriverResult JoyconDriver::InitializeDevice() {
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); 87 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86 88
87 // Get fixed joycon info 89 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version); 90 if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) {
89 generic_protocol->SetLowPowerMode(false); 91 // If this command fails the device doesn't accept configuration commands
90 generic_protocol->GetColor(color); 92 input_only_device = true;
91 if (handle_device_type == ControllerType::Pro) {
92 // Some 3rd party controllers aren't pro controllers
93 generic_protocol->GetControllerType(device_type);
94 } else {
95 device_type = handle_device_type;
96 } 93 }
97 generic_protocol->GetSerialNumber(serial_number); 94
95 if (!input_only_device) {
96 generic_protocol->SetLowPowerMode(false);
97 generic_protocol->GetColor(color);
98 if (handle_device_type == ControllerType::Pro) {
99 // Some 3rd party controllers aren't pro controllers
100 generic_protocol->GetControllerType(device_type);
101 } else {
102 device_type = handle_device_type;
103 }
104 generic_protocol->GetSerialNumber(serial_number);
105 }
106
98 supported_features = GetSupportedFeatures(); 107 supported_features = GetSupportedFeatures();
99 108
100 // Get Calibration data 109 // Get Calibration data
@@ -112,7 +121,7 @@ DriverResult JoyconDriver::InitializeDevice() {
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, 121 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration); 122 right_stick_calibration, motion_calibration);
114 123
115 // Start pooling for data 124 // Start polling for data
116 is_connected = true; 125 is_connected = true;
117 if (!input_thread_running) { 126 if (!input_thread_running) {
118 input_thread = 127 input_thread =
@@ -208,7 +217,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); 217 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 } 218 }
210 219
211 if (nfc_protocol->IsEnabled()) { 220 if (nfc_protocol->IsPolling()) {
212 if (amiibo_detected) { 221 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) { 222 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({}); 223 joycon_poller->UpdateAmiibo({});
@@ -218,10 +227,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
218 } 227 }
219 228
220 if (!amiibo_detected) { 229 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C); 230 Joycon::TagInfo tag_info;
222 const auto result = nfc_protocol->ScanAmiibo(data); 231 const auto result = nfc_protocol->GetTagInfo(tag_info);
223 if (result == DriverResult::Success) { 232 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data); 233 joycon_poller->UpdateAmiibo(tag_info);
225 amiibo_detected = true; 234 amiibo_detected = true;
226 } 235 }
227 } 236 }
@@ -247,6 +256,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
247} 256}
248 257
249DriverResult JoyconDriver::SetPollingMode() { 258DriverResult JoyconDriver::SetPollingMode() {
259 SCOPE_EXIT({ disable_input_thread = false; });
250 disable_input_thread = true; 260 disable_input_thread = true;
251 261
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); 262 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
@@ -259,6 +269,10 @@ DriverResult JoyconDriver::SetPollingMode() {
259 generic_protocol->EnableImu(false); 269 generic_protocol->EnableImu(false);
260 } 270 }
261 271
272 if (input_only_device) {
273 return DriverResult::NotSupported;
274 }
275
262 if (irs_protocol->IsEnabled()) { 276 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs(); 277 irs_protocol->DisableIrs();
264 } 278 }
@@ -276,24 +290,21 @@ DriverResult JoyconDriver::SetPollingMode() {
276 if (irs_enabled && supported_features.irs) { 290 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs(); 291 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) { 292 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result; 293 return result;
281 } 294 }
282 irs_protocol->DisableIrs(); 295 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS"); 296 LOG_ERROR(Input, "Error enabling IRS");
297 return result;
284 } 298 }
285 299
286 if (nfc_enabled && supported_features.nfc) { 300 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc(); 301 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) { 302 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result; 303 return result;
294 } 304 }
295 nfc_protocol->DisableNfc(); 305 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC"); 306 LOG_ERROR(Input, "Error enabling NFC");
307 return result;
297 } 308 }
298 309
299 if (hidbus_enabled && supported_features.hidbus) { 310 if (hidbus_enabled && supported_features.hidbus) {
@@ -303,18 +314,17 @@ DriverResult JoyconDriver::SetPollingMode() {
303 } 314 }
304 if (result == DriverResult::Success) { 315 if (result == DriverResult::Success) {
305 ring_connected = true; 316 ring_connected = true;
306 disable_input_thread = false;
307 return result; 317 return result;
308 } 318 }
309 ring_connected = false; 319 ring_connected = false;
310 ring_protocol->DisableRingCon(); 320 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon"); 321 LOG_ERROR(Input, "Error enabling Ringcon");
322 return result;
312 } 323 }
313 324
314 if (passive_enabled && supported_features.passive) { 325 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode(); 326 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) { 327 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result; 328 return result;
319 } 329 }
320 LOG_ERROR(Input, "Error enabling passive mode"); 330 LOG_ERROR(Input, "Error enabling passive mode");
@@ -328,7 +338,6 @@ DriverResult JoyconDriver::SetPollingMode() {
328 // Switch calls this function after enabling active mode 338 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed(); 339 generic_protocol->TriggersElapsed();
330 340
331 disable_input_thread = false;
332 return result; 341 return result;
333} 342}
334 343
@@ -339,6 +348,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
339 .vibration = true, 348 .vibration = true,
340 }; 349 };
341 350
351 if (input_only_device) {
352 return features;
353 }
354
342 if (device_type == ControllerType::Right) { 355 if (device_type == ControllerType::Right) {
343 features.nfc = true; 356 features.nfc = true;
344 features.irs = true; 357 features.irs = true;
@@ -492,9 +505,68 @@ DriverResult JoyconDriver::SetRingConMode() {
492 return result; 505 return result;
493} 506}
494 507
495DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { 508DriverResult JoyconDriver::StartNfcPolling() {
509 std::scoped_lock lock{mutex};
510
511 if (!supported_features.nfc) {
512 return DriverResult::NotSupported;
513 }
514 if (!nfc_protocol->IsEnabled()) {
515 return DriverResult::Disabled;
516 }
517
518 disable_input_thread = true;
519 const auto result = nfc_protocol->StartNFCPollingMode();
520 disable_input_thread = false;
521
522 return result;
523}
524
525DriverResult JoyconDriver::StopNfcPolling() {
526 std::scoped_lock lock{mutex};
527
528 if (!supported_features.nfc) {
529 return DriverResult::NotSupported;
530 }
531 if (!nfc_protocol->IsEnabled()) {
532 return DriverResult::Disabled;
533 }
534
535 disable_input_thread = true;
536 const auto result = nfc_protocol->StopNFCPollingMode();
537 disable_input_thread = false;
538
539 if (amiibo_detected) {
540 amiibo_detected = false;
541 joycon_poller->UpdateAmiibo({});
542 }
543
544 return result;
545}
546
547DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
496 std::scoped_lock lock{mutex}; 548 std::scoped_lock lock{mutex};
549
550 if (!supported_features.nfc) {
551 return DriverResult::NotSupported;
552 }
553 if (!nfc_protocol->IsEnabled()) {
554 return DriverResult::Disabled;
555 }
556 if (!amiibo_detected) {
557 return DriverResult::ErrorWritingData;
558 }
559
560 out_data.resize(0x21C);
497 disable_input_thread = true; 561 disable_input_thread = true;
562 const auto result = nfc_protocol->ReadAmiibo(out_data);
563 disable_input_thread = false;
564
565 return result;
566}
567
568DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
569 std::scoped_lock lock{mutex};
498 570
499 if (!supported_features.nfc) { 571 if (!supported_features.nfc) {
500 return DriverResult::NotSupported; 572 return DriverResult::NotSupported;
@@ -506,9 +578,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
506 return DriverResult::ErrorWritingData; 578 return DriverResult::ErrorWritingData;
507 } 579 }
508 580
581 disable_input_thread = true;
509 const auto result = nfc_protocol->WriteAmiibo(data); 582 const auto result = nfc_protocol->WriteAmiibo(data);
583 disable_input_thread = false;
584
585 return result;
586}
587
588DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
589 std::span<MifareReadData> out_data) {
590 std::scoped_lock lock{mutex};
591
592 if (!supported_features.nfc) {
593 return DriverResult::NotSupported;
594 }
595 if (!nfc_protocol->IsEnabled()) {
596 return DriverResult::Disabled;
597 }
598 if (!amiibo_detected) {
599 return DriverResult::ErrorWritingData;
600 }
601
602 disable_input_thread = true;
603 const auto result = nfc_protocol->ReadMifare(data, out_data);
604 disable_input_thread = false;
605
606 return result;
607}
608
609DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
610 std::scoped_lock lock{mutex};
510 611
612 if (!supported_features.nfc) {
613 return DriverResult::NotSupported;
614 }
615 if (!nfc_protocol->IsEnabled()) {
616 return DriverResult::Disabled;
617 }
618 if (!amiibo_detected) {
619 return DriverResult::ErrorWritingData;
620 }
621
622 disable_input_thread = true;
623 const auto result = nfc_protocol->WriteMifare(data);
511 disable_input_thread = false; 624 disable_input_thread = false;
625
512 return result; 626 return result;
513} 627}
514 628
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index e9b2fccbb..45b32d2f8 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -49,7 +49,13 @@ public:
49 DriverResult SetIrMode(); 49 DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 51 DriverResult SetRingConMode();
52 DriverResult StartNfcPolling();
53 DriverResult StopNfcPolling();
54 DriverResult ReadAmiiboData(std::vector<u8>& out_data);
52 DriverResult WriteNfcData(std::span<const u8> data); 55 DriverResult WriteNfcData(std::span<const u8> data);
56 DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
57 std::span<MifareReadData> out_data);
58 DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
53 59
54 void SetCallbacks(const JoyconCallbacks& callbacks); 60 void SetCallbacks(const JoyconCallbacks& callbacks);
55 61
@@ -114,6 +120,7 @@ private:
114 // Hardware configuration 120 // Hardware configuration
115 u8 leds{}; 121 u8 leds{};
116 ReportMode mode{}; 122 ReportMode mode{};
123 bool input_only_device{};
117 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time 124 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
118 bool hidbus_enabled{}; // External device support 125 bool hidbus_enabled{}; // External device support
119 bool irs_enabled{}; // Infrared camera input 126 bool irs_enabled{}; // Infrared camera input
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 51669261a..88f4cec1c 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -73,7 +73,7 @@ DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, 73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) { 74 SubCommandResponse& output) {
75 constexpr int timeout_mili = 66; 75 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 15; 76 constexpr int MaxTries = 3;
77 int tries = 0; 77 int tries = 0;
78 78
79 do { 79 do {
@@ -113,9 +113,7 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
113 return result; 113 return result;
114 } 114 }
115 115
116 result = GetSubCommandResponse(sc, output); 116 return GetSubCommandResponse(sc, output);
117
118 return DriverResult::Success;
119} 117}
120 118
121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 119DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
@@ -158,7 +156,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
158 156
159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { 157DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5; 158 constexpr std::size_t HeaderSize = 5;
161 constexpr std::size_t MaxTries = 10; 159 constexpr std::size_t MaxTries = 5;
162 std::size_t tries = 0; 160 std::size_t tries = 0;
163 SubCommandResponse response{}; 161 SubCommandResponse response{};
164 std::array<u8, sizeof(ReadSpiPacket)> buffer{}; 162 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 5007b0e18..e0e431156 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 {
@@ -592,6 +606,14 @@ struct NFCWriteCommandData {
592static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); 606static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
593#pragma pack(pop) 607#pragma pack(pop)
594 608
609struct MifareCommandData {
610 u8 unknown1;
611 u8 unknown2;
612 u8 number_of_short_bytes;
613 MifareUUID uid;
614};
615static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size");
616
595struct NFCPollingCommandData { 617struct NFCPollingCommandData {
596 u8 enable_mifare; 618 u8 enable_mifare;
597 u8 unknown_1; 619 u8 unknown_1;
@@ -629,6 +651,41 @@ struct NFCWritePackage {
629 std::array<NFCDataChunk, 4> data_chunks; 651 std::array<NFCDataChunk, 4> data_chunks;
630}; 652};
631 653
654struct MifareReadChunk {
655 MifareCmd command;
656 std::array<u8, 0x6> sector_key;
657 u8 sector;
658};
659
660struct MifareWriteChunk {
661 MifareCmd command;
662 std::array<u8, 0x6> sector_key;
663 u8 sector;
664 std::array<u8, 0x10> data;
665};
666
667struct MifareReadData {
668 u8 sector;
669 std::array<u8, 0x10> data;
670};
671
672struct MifareReadPackage {
673 MifareCommandData command_data;
674 std::array<MifareReadChunk, 0x10> data_chunks;
675};
676
677struct MifareWritePackage {
678 MifareCommandData command_data;
679 std::array<MifareWriteChunk, 0x10> data_chunks;
680};
681
682struct TagInfo {
683 u8 uuid_length;
684 u8 protocol;
685 u8 tag_type;
686 std::array<u8, 10> uuid;
687};
688
632struct IrsConfigure { 689struct IrsConfigure {
633 MCUCommand command; 690 MCUCommand command;
634 MCUSubCommand sub_command; 691 MCUSubCommand sub_command;
@@ -744,7 +801,7 @@ struct JoyconCallbacks {
744 std::function<void(int, f32)> on_stick_data; 801 std::function<void(int, f32)> on_stick_data;
745 std::function<void(int, const MotionData&)> on_motion_data; 802 std::function<void(int, const MotionData&)> on_motion_data;
746 std::function<void(f32)> on_ring_data; 803 std::function<void(f32)> on_ring_data;
747 std::function<void(const std::vector<u8>&)> on_amiibo_data; 804 std::function<void(const Joycon::TagInfo&)> on_amiibo_data;
748 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; 805 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
749}; 806};
750 807
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index f7058c4a7..261f46255 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() {
40 if (result == DriverResult::Success) { 40 if (result == DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready); 41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 } 42 }
43 if (result == DriverResult::Success) {
44 MCUCommandResponse output{};
45 result = SendStopPollingRequest(output);
46 }
47 if (result == DriverResult::Success) {
48 result = WaitUntilNfcIs(NFCStatus::Ready);
49 }
50 if (result == DriverResult::Success) {
51 is_enabled = true;
52 }
43 53
44 return result; 54 return result;
45} 55}
@@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() {
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() { 72DriverResult 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 DriverResult result{DriverResult::Success};
65 76
66 if (result == DriverResult::Success) { 77 if (result == 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 == DriverResult::Success) {
71 result = WaitUntilNfcIs(NFCStatus::Ready); 82 result = WaitUntilNfcIs(NFCStatus::Polling);
72 } 83 }
73 if (result == DriverResult::Success) { 84 if (result == DriverResult::Success) {
85 is_polling = true;
86 }
87
88 return result;
89}
90
91DriverResult NfcProtocol::StopNFCPollingMode() {
92 LOG_DEBUG(Input, "Stop NFC polling Mode");
93 ScopedSetBlocking sb(this);
94 DriverResult result{DriverResult::Success};
95
96 if (result == 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 == DriverResult::Success) {
78 result = WaitUntilNfcIs(NFCStatus::Polling); 101 result = WaitUntilNfcIs(NFCStatus::WriteReady);
79 } 102 }
80 if (result == DriverResult::Success) { 103 if (result == 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) { 110DriverResult 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 DriverResult::Delayed;
90 } 113 }
@@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
100 } 123 }
101 124
102 if (result == DriverResult::Success) { 125 if (result == 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
150DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
151 LOG_DEBUG(Input, "Scan for amiibos");
152 ScopedSetBlocking sb(this);
153 DriverResult result{DriverResult::Success};
154 TagFoundData tag_data{};
155
156 if (result == DriverResult::Success) {
157 result = IsTagInRange(tag_data, 7);
158 }
159
160 if (result == DriverResult::Success) {
108 result = GetAmiiboData(data); 161 result = GetAmiiboData(data);
109 } 162 }
110 163
@@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
154 return result; 207 return result;
155} 208}
156 209
210DriverResult 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 DriverResult result{DriverResult::Success};
215 TagFoundData tag_data{};
216 MifareUUID tag_uuid{};
217
218 if (result == DriverResult::Success) {
219 result = IsTagInRange(tag_data, 7);
220 }
221 if (result == 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 == DriverResult::Success) {
226 MCUCommandResponse output{};
227 result = SendStopPollingRequest(output);
228 }
229 if (result == DriverResult::Success) {
230 result = WaitUntilNfcIs(NFCStatus::Ready);
231 }
232 if (result == DriverResult::Success) {
233 MCUCommandResponse output{};
234 result = SendStartPollingRequest(output, true);
235 }
236 if (result == DriverResult::Success) {
237 result = WaitUntilNfcIs(NFCStatus::WriteReady);
238 }
239 return result;
240}
241
242DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) {
243 LOG_DEBUG(Input, "Write mifare");
244 ScopedSetBlocking sb(this);
245 DriverResult result{DriverResult::Success};
246 TagFoundData tag_data{};
247 MifareUUID tag_uuid{};
248
249 if (result == DriverResult::Success) {
250 result = IsTagInRange(tag_data, 7);
251 }
252 if (result == DriverResult::Success) {
253 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
254 result = WriteMifareData(tag_uuid, write_request);
255 }
256 if (result == DriverResult::Success) {
257 MCUCommandResponse output{};
258 result = SendStopPollingRequest(output);
259 }
260 if (result == DriverResult::Success) {
261 result = WaitUntilNfcIs(NFCStatus::Ready);
262 }
263 if (result == DriverResult::Success) {
264 MCUCommandResponse output{};
265 result = SendStartPollingRequest(output, true);
266 }
267 if (result == DriverResult::Success) {
268 result = WaitUntilNfcIs(NFCStatus::WriteReady);
269 }
270 return result;
271}
272
157bool NfcProtocol::HasAmiibo() { 273bool NfcProtocol::HasAmiibo() {
158 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 274 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
159 return true; 275 return true;
@@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
341 return result; 457 return result;
342} 458}
343 459
460DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
461 std::span<const MifareReadChunk> read_request,
462 std::span<MifareReadData> out_data) {
463 constexpr std::size_t timeout_limit = 60;
464 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
465 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
466 std::span<const u8> buffer(nfc_buffer_data);
467 DriverResult result = DriverResult::Success;
468 MCUCommandResponse output{};
469 u8 block_id = 1;
470 u8 package_index = 0;
471 std::size_t tries = 0;
472 std::size_t current_position = 0;
473
474 LOG_INFO(Input, "Reading Mifare data");
475
476 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
477 while (current_position < buffer.size() && tries++ < timeout_limit) {
478 const std::size_t next_position =
479 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
480 const std::size_t block_size = next_position - current_position;
481 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
482
483 SendReadDataMifareRequest(output, block_id, is_last_packet,
484 buffer.subspan(current_position, block_size));
485
486 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
487
488 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
489 return DriverResult::ErrorReadingData;
490 }
491
492 // Increase position when data is confirmed by the joycon
493 if (output.mcu_report == MCUReport::NFCState &&
494 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
495 output.mcu_data[3] == block_id) {
496 block_id++;
497 current_position = next_position;
498 }
499 }
500
501 if (result != DriverResult::Success) {
502 return result;
503 }
504
505 // Wait for reply and save the output data
506 while (tries++ < timeout_limit) {
507 result = SendNextPackageRequest(output, package_index);
508 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
509
510 if (result != DriverResult::Success) {
511 return result;
512 }
513
514 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
515 return DriverResult::ErrorReadingData;
516 }
517
518 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
519 constexpr std::size_t DATA_LENGHT = 0x10 + 1;
520 constexpr std::size_t DATA_START = 11;
521 const u8 number_of_elements = output.mcu_data[10];
522 for (std::size_t i = 0; i < number_of_elements; i++) {
523 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)];
524 memcpy(out_data[i].data.data(),
525 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT),
526 sizeof(MifareReadData::data));
527 }
528 package_index++;
529 continue;
530 }
531
532 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
533 LOG_INFO(Input, "Finished reading mifare");
534 break;
535 }
536 }
537
538 return result;
539}
540
541DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
542 std::span<const MifareWriteChunk> write_request) {
543 constexpr std::size_t timeout_limit = 60;
544 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
545 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
546 std::span<const u8> buffer(nfc_buffer_data);
547 DriverResult result = DriverResult::Success;
548 MCUCommandResponse output{};
549 u8 block_id = 1;
550 u8 package_index = 0;
551 std::size_t tries = 0;
552 std::size_t current_position = 0;
553
554 LOG_INFO(Input, "Writing Mifare data");
555
556 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
557 while (current_position < buffer.size() && tries++ < timeout_limit) {
558 const std::size_t next_position =
559 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
560 const std::size_t block_size = next_position - current_position;
561 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
562
563 SendReadDataMifareRequest(output, block_id, is_last_packet,
564 buffer.subspan(current_position, block_size));
565
566 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
567
568 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
569 return DriverResult::ErrorReadingData;
570 }
571
572 // Increase position when data is confirmed by the joycon
573 if (output.mcu_report == MCUReport::NFCState &&
574 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
575 output.mcu_data[3] == block_id) {
576 block_id++;
577 current_position = next_position;
578 }
579 }
580
581 if (result != DriverResult::Success) {
582 return result;
583 }
584
585 // Wait for reply and ignore the output data
586 while (tries++ < timeout_limit) {
587 result = SendNextPackageRequest(output, package_index);
588 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
589
590 if (result != DriverResult::Success) {
591 return result;
592 }
593
594 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
595 return DriverResult::ErrorReadingData;
596 }
597
598 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
599 package_index++;
600 continue;
601 }
602
603 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
604 LOG_INFO(Input, "Finished writing mifare");
605 break;
606 }
607 }
608
609 return result;
610}
611
344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, 612DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) { 613 bool is_second_attempt) {
346 NFCRequestState request{ 614 NFCRequestState request{
@@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
477 output); 745 output);
478} 746}
479 747
748DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
749 bool is_last_packet, std::span<const u8> data) {
750 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
751 NFCRequestState request{
752 .command_argument = NFCCommand::Mifare,
753 .block_id = block_id,
754 .packet_id = {},
755 .packet_flag =
756 is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
757 .data_length = static_cast<u8>(data_size),
758 .raw_data = {},
759 .crc = {},
760 };
761 memcpy(request.raw_data.data(), data.data(), data_size);
762
763 std::array<u8, sizeof(NFCRequestState)> request_data{};
764 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
765 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
766 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
767 output);
768}
769
480std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { 770std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
481 const std::size_t header_size = 771 const std::size_t header_size =
482 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); 772 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
@@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag
498 return serialized_data; 788 return serialized_data;
499} 789}
500 790
791std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const {
792 const std::size_t header_size = sizeof(MifareCommandData);
793 std::vector<u8> serialized_data(header_size);
794 std::size_t start_index = 0;
795
796 memcpy(serialized_data.data(), &package, header_size);
797 start_index += header_size;
798
799 for (const auto& data_chunk : package.data_chunks) {
800 const std::size_t chunk_size = sizeof(MifareReadChunk);
801 if (data_chunk.command == MifareCmd::None) {
802 continue;
803 }
804 serialized_data.resize(start_index + chunk_size);
805 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
806 start_index += chunk_size;
807 }
808
809 return serialized_data;
810}
811
812std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const {
813 const std::size_t header_size = sizeof(MifareCommandData);
814 std::vector<u8> serialized_data(header_size);
815 std::size_t start_index = 0;
816
817 memcpy(serialized_data.data(), &package, header_size);
818 start_index += header_size;
819
820 for (const auto& data_chunk : package.data_chunks) {
821 const std::size_t chunk_size = sizeof(MifareWriteChunk);
822 if (data_chunk.command == MifareCmd::None) {
823 continue;
824 }
825 serialized_data.resize(start_index + chunk_size);
826 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
827 start_index += chunk_size;
828 }
829
830 return serialized_data;
831}
832
501NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, 833NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
502 std::span<const u8> data) const { 834 std::span<const u8> data) const {
503 return { 835 return {
@@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
527 }; 859 };
528} 860}
529 861
862MifareReadPackage NfcProtocol::MakeMifareReadPackage(
863 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const {
864 MifareReadPackage package{
865 .command_data{
866 .unknown1 = 0xd0,
867 .unknown2 = 0x07,
868 .number_of_short_bytes = static_cast<u8>(
869 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2),
870 .uid = tag_uuid,
871 },
872 .data_chunks = {},
873 };
874
875 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
876 package.data_chunks[i] = read_request[i];
877 }
878
879 return package;
880}
881
882MifareWritePackage NfcProtocol::MakeMifareWritePackage(
883 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const {
884 MifareWritePackage package{
885 .command_data{
886 .unknown1 = 0xd0,
887 .unknown2 = 0x07,
888 .number_of_short_bytes = static_cast<u8>(
889 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2),
890 .uid = tag_uuid,
891 },
892 .data_chunks = {},
893 };
894
895 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
896 package.data_chunks[i] = read_request[i];
897 }
898
899 return package;
900}
901
530NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { 902NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
531 constexpr u8 NFC_PAGE_SIZE = 4; 903 constexpr u8 NFC_PAGE_SIZE = 4;
532 904
@@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {
606 return is_enabled; 978 return is_enabled;
607} 979}
608 980
981bool NfcProtocol::IsPolling() const {
982 return is_polling;
983}
984
609} // namespace InputCommon::Joycon 985} // 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..0be95e40e 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -25,14 +25,25 @@ public:
25 25
26 DriverResult StartNFCPollingMode(); 26 DriverResult StartNFCPollingMode();
27 27
28 DriverResult ScanAmiibo(std::vector<u8>& data); 28 DriverResult StopNFCPollingMode();
29
30 DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
31
32 DriverResult ReadAmiibo(std::vector<u8>& data);
29 33
30 DriverResult WriteAmiibo(std::span<const u8> data); 34 DriverResult WriteAmiibo(std::span<const u8> data);
31 35
36 DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
37 std::span<MifareReadData> out_data);
38
39 DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
40
32 bool HasAmiibo(); 41 bool HasAmiibo();
33 42
34 bool IsEnabled() const; 43 bool IsEnabled() const;
35 44
45 bool IsPolling() const;
46
36private: 47private:
37 // Number of times the function will be delayed until it outputs valid data 48 // Number of times the function will be delayed until it outputs valid data
38 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; 49 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
@@ -51,6 +62,13 @@ private:
51 62
52 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); 63 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
53 64
65 DriverResult GetMifareData(const MifareUUID& tag_uuid,
66 std::span<const MifareReadChunk> read_request,
67 std::span<MifareReadData> out_data);
68
69 DriverResult WriteMifareData(const MifareUUID& tag_uuid,
70 std::span<const MifareWriteChunk> write_request);
71
54 DriverResult SendStartPollingRequest(MCUCommandResponse& output, 72 DriverResult SendStartPollingRequest(MCUCommandResponse& output,
55 bool is_second_attempt = false); 73 bool is_second_attempt = false);
56 74
@@ -65,17 +83,31 @@ private:
65 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 83 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
66 bool is_last_packet, std::span<const u8> data); 84 bool is_last_packet, std::span<const u8> data);
67 85
86 DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
87 bool is_last_packet, std::span<const u8> data);
88
68 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; 89 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
69 90
91 std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const;
92
93 std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const;
94
70 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; 95 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
71 96
72 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; 97 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
73 98
99 MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid,
100 std::span<const MifareReadChunk> read_request) const;
101
102 MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid,
103 std::span<const MifareWriteChunk> read_request) const;
104
74 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 105 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
75 106
76 TagUUID GetTagUUID(std::span<const u8> data) const; 107 TagUUID GetTagUUID(std::span<const u8> data) const;
77 108
78 bool is_enabled{}; 109 bool is_enabled{};
110 bool is_polling{};
79 std::size_t update_counter{}; 111 std::size_t update_counter{};
80}; 112};
81 113
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/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/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 9bafd8cc0..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;
@@ -719,9 +719,15 @@ void BufferCache<P>::BindHostVertexBuffers() {
719 bool any_valid{false}; 719 bool any_valid{false};
720 auto& flags = maxwell3d->dirty.flags; 720 auto& flags = maxwell3d->dirty.flags;
721 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 721 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
722 const Binding& binding = channel_state->vertex_buffers[index];
723 Buffer& buffer = slot_buffers[binding.buffer_id];
724 TouchBuffer(buffer, binding.buffer_id);
725 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
722 if (!flags[Dirty::VertexBuffer0 + index]) { 726 if (!flags[Dirty::VertexBuffer0 + index]) {
723 continue; 727 continue;
724 } 728 }
729 flags[Dirty::VertexBuffer0 + index] = false;
730
725 host_bindings.min_index = std::min(host_bindings.min_index, index); 731 host_bindings.min_index = std::min(host_bindings.min_index, index);
726 host_bindings.max_index = std::max(host_bindings.max_index, index); 732 host_bindings.max_index = std::max(host_bindings.max_index, index);
727 any_valid = true; 733 any_valid = true;
@@ -735,9 +741,6 @@ void BufferCache<P>::BindHostVertexBuffers() {
735 const Binding& binding = channel_state->vertex_buffers[index]; 741 const Binding& binding = channel_state->vertex_buffers[index];
736 Buffer& buffer = slot_buffers[binding.buffer_id]; 742 Buffer& buffer = slot_buffers[binding.buffer_id];
737 743
738 TouchBuffer(buffer, binding.buffer_id);
739 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
740
741 const u32 stride = maxwell3d->regs.vertex_streams[index].stride; 744 const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
742 const u32 offset = buffer.Offset(binding.cpu_addr); 745 const u32 offset = buffer.Offset(binding.cpu_addr);
743 746
@@ -1276,7 +1279,7 @@ template <class P>
1276typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr, 1279typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr,
1277 u32 wanted_size) { 1280 u32 wanted_size) {
1278 static constexpr int STREAM_LEAP_THRESHOLD = 16; 1281 static constexpr int STREAM_LEAP_THRESHOLD = 16;
1279 std::vector<BufferId> overlap_ids; 1282 boost::container::small_vector<BufferId, 16> overlap_ids;
1280 VAddr begin = cpu_addr; 1283 VAddr begin = cpu_addr;
1281 VAddr end = cpu_addr + wanted_size; 1284 VAddr end = cpu_addr + wanted_size;
1282 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/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 0e94c521a..f34090791 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.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/settings.h"
4#include "video_core/dirty_flags.h" 5#include "video_core/dirty_flags.h"
5#include "video_core/engines/draw_manager.h" 6#include "video_core/engines/draw_manager.h"
6#include "video_core/rasterizer_interface.h" 7#include "video_core/rasterizer_interface.h"
@@ -195,8 +196,12 @@ void DrawManager::DrawTexture() {
195 if (lower_left) { 196 if (lower_left) {
196 draw_texture_state.dst_y0 -= dst_height; 197 draw_texture_state.dst_y0 -= dst_height;
197 } 198 }
198 draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width; 199 draw_texture_state.dst_x1 =
199 draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height; 200 draw_texture_state.dst_x0 +
201 static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_width)));
202 draw_texture_state.dst_y1 =
203 draw_texture_state.dst_y0 +
204 static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_height)));
200 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f; 205 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
201 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f; 206 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
202 draw_texture_state.src_x1 = 207 draw_texture_state.src_x1 =
@@ -207,7 +212,6 @@ void DrawManager::DrawTexture() {
207 draw_texture_state.src_y0; 212 draw_texture_state.src_y0;
208 draw_texture_state.src_sampler = regs.draw_texture.src_sampler; 213 draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
209 draw_texture_state.src_texture = regs.draw_texture.src_texture; 214 draw_texture_state.src_texture = regs.draw_texture.src_texture;
210
211 maxwell3d->rasterizer->DrawTexture(); 215 maxwell3d->rasterizer->DrawTexture();
212} 216}
213 217
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ebe5536de..bc1eb41e7 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,32 @@ 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.ReadBlockUnsafe(
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.ReadBlockUnsafe(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.ReadBlockUnsafe(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 memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
155 regs.line_length_in); 157 regs.line_length_in);
156 } 158 }
157 } 159 }
@@ -171,7 +173,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
171 src_operand.address = regs.offset_in; 173 src_operand.address = regs.offset_in;
172 174
173 DMA::BufferOperand dst_operand; 175 DMA::BufferOperand dst_operand;
174 dst_operand.pitch = regs.pitch_out; 176 u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out));
177 dst_operand.pitch = abs_pitch_out;
175 dst_operand.width = regs.line_length_in; 178 dst_operand.width = regs.line_length_in;
176 dst_operand.height = regs.line_count; 179 dst_operand.height = regs.line_count;
177 dst_operand.address = regs.offset_out; 180 dst_operand.address = regs.offset_out;
@@ -218,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
218 const size_t src_size = 221 const size_t src_size =
219 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 222 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
220 223
221 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 224 const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count;
222 read_buffer.resize_destructive(src_size); 225 read_buffer.resize_destructive(src_size);
223 write_buffer.resize_destructive(dst_size); 226 write_buffer.resize_destructive(dst_size);
224 227
@@ -227,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
227 230
228 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 231 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, 232 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
230 regs.pitch_out); 233 abs_pitch_out);
231 234
232 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); 235 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
233} 236}
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/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 67356c679..660f7c9ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -373,7 +373,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
373 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 373 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
374 }; 374 };
375 // 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
376 boost::container::small_vector<VkBufferCopy, 3> vk_copies(copies.size()); 376 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
377 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); 377 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
378 scheduler.RequestOutsideRenderPassOperationContext(); 378 scheduler.RequestOutsideRenderPassOperationContext();
379 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) {
@@ -528,15 +528,15 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
528 buffer_handles.push_back(handle); 528 buffer_handles.push_back(handle);
529 } 529 }
530 if (device.IsExtExtendedDynamicStateSupported()) { 530 if (device.IsExtExtendedDynamicStateSupported()) {
531 scheduler.Record([bindings = bindings, 531 scheduler.Record([bindings = std::move(bindings),
532 buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 532 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
533 cmdbuf.BindVertexBuffers2EXT( 533 cmdbuf.BindVertexBuffers2EXT(
534 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), 534 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
535 bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); 535 bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data());
536 }); 536 });
537 } else { 537 } else {
538 scheduler.Record([bindings = bindings, 538 scheduler.Record([bindings = std::move(bindings),
539 buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 539 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
540 cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, 540 cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index,
541 buffer_handles.data(), bindings.offsets.data()); 541 buffer_handles.data(), bindings.offsets.data());
542 }); 542 });
@@ -573,12 +573,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
573 for (u32 index = 0; index < bindings.buffers.size(); ++index) { 573 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
574 buffer_handles.push_back(bindings.buffers[index]->Handle()); 574 buffer_handles.push_back(bindings.buffers[index]->Handle());
575 } 575 }
576 scheduler.Record( 576 scheduler.Record([bindings = std::move(bindings),
577 [bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 577 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
578 cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), 578 cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()),
579 buffer_handles.data(), bindings.offsets.data(), 579 buffer_handles.data(), bindings.offsets.data(),
580 bindings.sizes.data()); 580 bindings.sizes.data());
581 }); 581 });
582} 582}
583 583
584void BufferCacheRuntime::ReserveNullBuffer() { 584void BufferCacheRuntime::ReserveNullBuffer() {
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..9f316113c 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;
@@ -705,10 +711,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
705std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 711std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
706 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 712 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
707 PipelineStatistics* statistics, bool build_in_parallel) try { 713 PipelineStatistics* statistics, bool build_in_parallel) try {
708 // TODO: Remove this when Intel fixes their shader compiler. 714 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()); 715 LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash());
713 return nullptr; 716 return nullptr;
714 } 717 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 28985b6fe..ce6acc30c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -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 }
@@ -866,8 +866,8 @@ void TextureCacheRuntime::BarrierFeedbackLoop() {
866 866
867void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, 867void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
868 std::span<const VideoCommon::ImageCopy> copies) { 868 std::span<const VideoCommon::ImageCopy> copies) {
869 std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); 869 boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size());
870 std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); 870 boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size());
871 const VkImageAspectFlags src_aspect_mask = src.AspectMask(); 871 const VkImageAspectFlags src_aspect_mask = src.AspectMask();
872 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); 872 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask();
873 873
@@ -1156,7 +1156,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
1156 1156
1157void TextureCacheRuntime::CopyImage(Image& dst, Image& src, 1157void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
1158 std::span<const VideoCommon::ImageCopy> copies) { 1158 std::span<const VideoCommon::ImageCopy> copies) {
1159 std::vector<VkImageCopy> vk_copies(copies.size()); 1159 boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size());
1160 const VkImageAspectFlags aspect_mask = dst.AspectMask(); 1160 const VkImageAspectFlags aspect_mask = dst.AspectMask();
1161 ASSERT(aspect_mask == src.AspectMask()); 1161 ASSERT(aspect_mask == src.AspectMask());
1162 1162
@@ -1331,7 +1331,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
1331 ScaleDown(true); 1331 ScaleDown(true);
1332 } 1332 }
1333 scheduler->RequestOutsideRenderPassOperationContext(); 1333 scheduler->RequestOutsideRenderPassOperationContext();
1334 std::vector vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); 1334 auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
1335 const VkBuffer src_buffer = buffer; 1335 const VkBuffer src_buffer = buffer;
1336 const VkImage vk_image = *original_image; 1336 const VkImage vk_image = *original_image;
1337 const VkImageAspectFlags vk_aspect_mask = aspect_mask; 1337 const VkImageAspectFlags vk_aspect_mask = aspect_mask;
@@ -1366,8 +1366,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1366 if (is_rescaled) { 1366 if (is_rescaled) {
1367 ScaleDown(); 1367 ScaleDown();
1368 } 1368 }
1369 boost::container::small_vector<VkBuffer, 1> buffers_vector{}; 1369 boost::container::small_vector<VkBuffer, 8> buffers_vector{};
1370 boost::container::small_vector<std::vector<VkBufferImageCopy>, 1> vk_copies; 1370 boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8>
1371 vk_copies;
1371 for (size_t index = 0; index < buffers_span.size(); index++) { 1372 for (size_t index = 0; index < buffers_span.size(); index++) {
1372 buffers_vector.emplace_back(buffers_span[index]); 1373 buffers_vector.emplace_back(buffers_span[index]);
1373 vk_copies.emplace_back( 1374 vk_copies.emplace_back(
@@ -1855,7 +1856,7 @@ Framebuffer::~Framebuffer() = default;
1855void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, 1856void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1856 std::span<ImageView*, NUM_RT> color_buffers, 1857 std::span<ImageView*, NUM_RT> color_buffers,
1857 ImageView* depth_buffer, bool is_rescaled) { 1858 ImageView* depth_buffer, bool is_rescaled) {
1858 std::vector<VkImageView> attachments; 1859 boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
1859 RenderPassKey renderpass_key{}; 1860 RenderPassKey renderpass_key{};
1860 s32 num_layers = 1; 1861 s32 num_layers = 1;
1861 1862
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/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..d3f03a995 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,7 +593,7 @@ 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) {
@@ -1097,7 +1101,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
1097 const bool native_bgr = runtime.HasNativeBgr(); 1101 const bool native_bgr = runtime.HasNativeBgr();
1098 const bool flexible_formats = True(options & RelaxedOptions::Format); 1102 const bool flexible_formats = True(options & RelaxedOptions::Format);
1099 ImageId image_id{}; 1103 ImageId image_id{};
1100 boost::container::small_vector<ImageId, 1> image_ids; 1104 boost::container::small_vector<ImageId, 8> image_ids;
1101 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1105 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1102 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1106 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1103 return false; 1107 return false;
@@ -1618,7 +1622,7 @@ ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr)
1618 } 1622 }
1619 } 1623 }
1620 ImageId image_id{}; 1624 ImageId image_id{};
1621 boost::container::small_vector<ImageId, 1> image_ids; 1625 boost::container::small_vector<ImageId, 8> image_ids;
1622 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1626 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1623 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1627 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1624 return false; 1628 return false;
@@ -1678,7 +1682,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
1678} 1682}
1679 1683
1680template <class P> 1684template <class P>
1681ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { 1685ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
1682 const auto& regs = maxwell3d->regs; 1686 const auto& regs = maxwell3d->regs;
1683 if (index >= regs.rt_control.count) { 1687 if (index >= regs.rt_control.count) {
1684 return ImageViewId{}; 1688 return ImageViewId{};
@@ -1692,11 +1696,11 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
1692 return ImageViewId{}; 1696 return ImageViewId{};
1693 } 1697 }
1694 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); 1698 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode);
1695 return FindRenderTargetView(info, gpu_addr, is_clear); 1699 return FindRenderTargetView(info, gpu_addr);
1696} 1700}
1697 1701
1698template <class P> 1702template <class P>
1699ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { 1703ImageViewId TextureCache<P>::FindDepthBuffer() {
1700 const auto& regs = maxwell3d->regs; 1704 const auto& regs = maxwell3d->regs;
1701 if (!regs.zeta_enable) { 1705 if (!regs.zeta_enable) {
1702 return ImageViewId{}; 1706 return ImageViewId{};
@@ -1706,18 +1710,16 @@ ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
1706 return ImageViewId{}; 1710 return ImageViewId{};
1707 } 1711 }
1708 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); 1712 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
1709 return FindRenderTargetView(info, gpu_addr, is_clear); 1713 return FindRenderTargetView(info, gpu_addr);
1710} 1714}
1711 1715
1712template <class P> 1716template <class P>
1713ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 1717ImageViewId 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{}; 1718 ImageId image_id{};
1717 bool delete_state = has_deleted_images; 1719 bool delete_state = has_deleted_images;
1718 do { 1720 do {
1719 has_deleted_images = false; 1721 has_deleted_images = false;
1720 image_id = FindOrInsertImage(info, gpu_addr, options); 1722 image_id = FindOrInsertImage(info, gpu_addr);
1721 delete_state |= has_deleted_images; 1723 delete_state |= has_deleted_images;
1722 } while (has_deleted_images); 1724 } while (has_deleted_images);
1723 has_deleted_images = delete_state; 1725 has_deleted_images = delete_state;
@@ -1940,7 +1942,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1940 image.map_view_id = map_id; 1942 image.map_view_id = map_id;
1941 return; 1943 return;
1942 } 1944 }
1943 std::vector<ImageViewId> sparse_maps{}; 1945 boost::container::small_vector<ImageViewId, 16> sparse_maps;
1944 ForEachSparseSegment( 1946 ForEachSparseSegment(
1945 image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { 1947 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); 1948 auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id);
@@ -2215,7 +2217,7 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept {
2215 2217
2216template <class P> 2218template <class P>
2217void TextureCache<P>::SynchronizeAliases(ImageId image_id) { 2219void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
2218 boost::container::small_vector<const AliasedImage*, 1> aliased_images; 2220 boost::container::small_vector<const AliasedImage*, 8> aliased_images;
2219 Image& image = slot_images[image_id]; 2221 Image& image = slot_images[image_id];
2220 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); 2222 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);
2221 bool any_modified = True(image.flags & ImageFlagBits::GpuModified); 2223 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..f781cb7a0 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -329,13 +329,13 @@ template <u32 GOB_EXTENT>
329 329
330[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D( 330[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D(
331 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { 331 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
332 const std::vector<u32> slice_offsets = CalculateSliceOffsets(new_info); 332 const auto slice_offsets = CalculateSliceOffsets(new_info);
333 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr); 333 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr);
334 const auto it = std::ranges::find(slice_offsets, diff); 334 const auto it = std::ranges::find(slice_offsets, diff);
335 if (it == slice_offsets.end()) { 335 if (it == slice_offsets.end()) {
336 return std::nullopt; 336 return std::nullopt;
337 } 337 }
338 const std::vector subresources = CalculateSliceSubresources(new_info); 338 const auto subresources = CalculateSliceSubresources(new_info);
339 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)]; 339 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)];
340 const ImageInfo& info = overlap.info; 340 const ImageInfo& info = overlap.info;
341 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) { 341 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) {
@@ -655,9 +655,9 @@ LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {
655 return sizes; 655 return sizes;
656} 656}
657 657
658std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { 658boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info) {
659 ASSERT(info.type == ImageType::e3D); 659 ASSERT(info.type == ImageType::e3D);
660 std::vector<u32> offsets; 660 boost::container::small_vector<u32, 16> offsets;
661 offsets.reserve(NumSlices(info)); 661 offsets.reserve(NumSlices(info));
662 662
663 const LevelInfo level_info = MakeLevelInfo(info); 663 const LevelInfo level_info = MakeLevelInfo(info);
@@ -679,9 +679,10 @@ std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) {
679 return offsets; 679 return offsets;
680} 680}
681 681
682std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info) { 682boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources(
683 const ImageInfo& info) {
683 ASSERT(info.type == ImageType::e3D); 684 ASSERT(info.type == ImageType::e3D);
684 std::vector<SubresourceBase> subresources; 685 boost::container::small_vector<SubresourceBase, 16> subresources;
685 subresources.reserve(NumSlices(info)); 686 subresources.reserve(NumSlices(info));
686 for (s32 level = 0; level < info.resources.levels; ++level) { 687 for (s32 level = 0; level < info.resources.levels; ++level) {
687 const s32 depth = AdjustMipSize(info.size.depth, level); 688 const s32 depth = AdjustMipSize(info.size.depth, level);
@@ -723,8 +724,10 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {
723 } 724 }
724} 725}
725 726
726std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, 727boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(const ImageInfo& dst,
727 SubresourceBase base, u32 up_scale, u32 down_shift) { 728 const ImageInfo& src,
729 SubresourceBase base,
730 u32 up_scale, u32 down_shift) {
728 ASSERT(dst.resources.levels >= src.resources.levels); 731 ASSERT(dst.resources.levels >= src.resources.levels);
729 732
730 const bool is_dst_3d = dst.type == ImageType::e3D; 733 const bool is_dst_3d = dst.type == ImageType::e3D;
@@ -733,7 +736,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
733 ASSERT(src.resources.levels == 1); 736 ASSERT(src.resources.levels == 1);
734 } 737 }
735 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D}; 738 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D};
736 std::vector<ImageCopy> copies; 739 boost::container::small_vector<ImageCopy, 16> copies;
737 copies.reserve(src.resources.levels); 740 copies.reserve(src.resources.levels);
738 for (s32 level = 0; level < src.resources.levels; ++level) { 741 for (s32 level = 0; level < src.resources.levels; ++level) {
739 ImageCopy& copy = copies.emplace_back(); 742 ImageCopy& copy = copies.emplace_back();
@@ -770,9 +773,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
770 return copies; 773 return copies;
771} 774}
772 775
773std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale, 776boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(const ImageInfo& src,
774 u32 down_shift) { 777 u32 up_scale,
775 std::vector<ImageCopy> copies; 778 u32 down_shift) {
779 boost::container::small_vector<ImageCopy, 16> copies;
776 copies.reserve(src.resources.levels); 780 copies.reserve(src.resources.levels);
777 const bool is_3d = src.type == ImageType::e3D; 781 const bool is_3d = src.type == ImageType::e3D;
778 for (s32 level = 0; level < src.resources.levels; ++level) { 782 for (s32 level = 0; level < src.resources.levels; ++level) {
@@ -824,9 +828,11 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
824 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value(); 828 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value();
825} 829}
826 830
827std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 831boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
828 const ImageInfo& info, std::span<const u8> input, 832 GPUVAddr gpu_addr,
829 std::span<u8> output) { 833 const ImageInfo& info,
834 std::span<const u8> input,
835 std::span<u8> output) {
830 const size_t guest_size_bytes = input.size_bytes(); 836 const size_t guest_size_bytes = input.size_bytes();
831 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 837 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
832 const Extent3D size = info.size; 838 const Extent3D size = info.size;
@@ -861,7 +867,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
861 info.tile_width_spacing); 867 info.tile_width_spacing);
862 size_t guest_offset = 0; 868 size_t guest_offset = 0;
863 u32 host_offset = 0; 869 u32 host_offset = 0;
864 std::vector<BufferImageCopy> copies(num_levels); 870 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
865 871
866 for (s32 level = 0; level < num_levels; ++level) { 872 for (s32 level = 0; level < num_levels; ++level) {
867 const Extent3D level_size = AdjustMipSize(size, level); 873 const Extent3D level_size = AdjustMipSize(size, level);
@@ -978,7 +984,7 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
978 } 984 }
979} 985}
980 986
981std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { 987boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) {
982 const Extent3D size = info.size; 988 const Extent3D size = info.size;
983 const u32 bytes_per_block = BytesPerBlock(info.format); 989 const u32 bytes_per_block = BytesPerBlock(info.format);
984 if (info.type == ImageType::Linear) { 990 if (info.type == ImageType::Linear) {
@@ -1006,7 +1012,7 @@ std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) {
1006 1012
1007 u32 host_offset = 0; 1013 u32 host_offset = 0;
1008 1014
1009 std::vector<BufferImageCopy> copies(num_levels); 1015 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
1010 for (s32 level = 0; level < num_levels; ++level) { 1016 for (s32 level = 0; level < num_levels; ++level) {
1011 const Extent3D level_size = AdjustMipSize(size, level); 1017 const Extent3D level_size = AdjustMipSize(size, level);
1012 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); 1018 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);
@@ -1042,10 +1048,10 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) {
1042 return AdjustMipBlockSize(num_tiles, level_info.block, level); 1048 return AdjustMipBlockSize(num_tiles, level_info.block, level);
1043} 1049}
1044 1050
1045std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { 1051boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) {
1046 const Extent2D tile_size = DefaultBlockSize(info.format); 1052 const Extent2D tile_size = DefaultBlockSize(info.format);
1047 if (info.type == ImageType::Linear) { 1053 if (info.type == ImageType::Linear) {
1048 return std::vector{SwizzleParameters{ 1054 return {SwizzleParameters{
1049 .num_tiles = AdjustTileSize(info.size, tile_size), 1055 .num_tiles = AdjustTileSize(info.size, tile_size),
1050 .block = {}, 1056 .block = {},
1051 .buffer_offset = 0, 1057 .buffer_offset = 0,
@@ -1057,7 +1063,7 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {
1057 const s32 num_levels = info.resources.levels; 1063 const s32 num_levels = info.resources.levels;
1058 1064
1059 u32 guest_offset = 0; 1065 u32 guest_offset = 0;
1060 std::vector<SwizzleParameters> params(num_levels); 1066 boost::container::small_vector<SwizzleParameters, 16> params(num_levels);
1061 for (s32 level = 0; level < num_levels; ++level) { 1067 for (s32 level = 0; level < num_levels; ++level) {
1062 const Extent3D level_size = AdjustMipSize(size, level); 1068 const Extent3D level_size = AdjustMipSize(size, level);
1063 const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); 1069 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/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_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 31226084f..e4ca65b58 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -318,6 +318,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
318std::vector<const char*> ExtensionListForVulkan( 318std::vector<const char*> ExtensionListForVulkan(
319 const std::set<std::string, std::less<>>& extensions) { 319 const std::set<std::string, std::less<>>& extensions) {
320 std::vector<const char*> output; 320 std::vector<const char*> output;
321 output.reserve(extensions.size());
321 for (const auto& extension : extensions) { 322 for (const auto& extension : extensions) {
322 output.push_back(extension.c_str()); 323 output.push_back(extension.c_str());
323 } 324 }
@@ -564,6 +565,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
564 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); 565 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
565 cant_blit_msaa = true; 566 cant_blit_msaa = true;
566 } 567 }
568 has_broken_compute =
569 CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
570 !Settings::values.enable_compute_pipelines.GetValue();
567 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { 571 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) {
568 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); 572 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
569 must_emulate_bgr565 = true; 573 must_emulate_bgr565 = true;
@@ -807,9 +811,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
807 811
808 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); 812 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
809 FOR_EACH_VK_EXTENSION(EXTENSION); 813 FOR_EACH_VK_EXTENSION(EXTENSION);
810#ifdef _WIN32
811 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
812#endif
813 814
814#undef FEATURE_EXTENSION 815#undef FEATURE_EXTENSION
815#undef EXTENSION 816#undef EXTENSION
@@ -828,11 +829,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
828 829
829 FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION); 830 FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION);
830 FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION); 831 FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION);
831#ifdef _WIN32
832 FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION);
833#else
834 FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION);
835#endif
836 832
837 if (requires_swapchain) { 833 if (requires_swapchain) {
838 CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 834 CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 9936f5658..b84af3dfb 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -10,6 +10,7 @@
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
@@ -70,7 +71,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
70 EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ 71 EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
71 EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ 72 EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
72 EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ 73 EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
73 EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \
74 EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ 74 EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
75 EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \ 75 EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \
76 EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \ 76 EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \
@@ -82,9 +82,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
82 EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ 82 EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
83 EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) 83 EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle)
84 84
85#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \
86 EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32)
87
88// Define extensions which must be supported. 85// Define extensions which must be supported.
89#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ 86#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
90 EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \ 87 EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
@@ -92,12 +89,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
92 EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \ 89 EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \
93 EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) 90 EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)
94 91
95#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \
96 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
97
98#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \
99 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
100
101// Define extensions where the absence of the extension may result in a degraded experience. 92// Define extensions where the absence of the extension may result in a degraded experience.
102#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \ 93#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \
103 EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \ 94 EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \
@@ -535,6 +526,11 @@ public:
535 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue(); 526 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue();
536 } 527 }
537 528
529 /// @returns True if compute pipelines can cause crashing.
530 bool HasBrokenCompute() const {
531 return has_broken_compute;
532 }
533
538 /// Returns true when the device does not properly support cube compatibility. 534 /// Returns true when the device does not properly support cube compatibility.
539 bool HasBrokenCubeImageCompability() const { 535 bool HasBrokenCubeImageCompability() const {
540 return has_broken_cube_compatibility; 536 return has_broken_cube_compatibility;
@@ -596,6 +592,22 @@ public:
596 return supports_conditional_barriers; 592 return supports_conditional_barriers;
597 } 593 }
598 594
595 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
596 u32 driver_version) {
597 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
598 const u32 major = VK_API_VERSION_MAJOR(driver_version);
599 const u32 minor = VK_API_VERSION_MINOR(driver_version);
600 const u32 patch = VK_API_VERSION_PATCH(driver_version);
601 if (major == 0 && minor == 405 && patch < 286) {
602 LOG_WARNING(
603 Render_Vulkan,
604 "Intel proprietary drivers 0.405.0 until 0.405.286 have broken compute");
605 return true;
606 }
607 }
608 return false;
609 }
610
599private: 611private:
600 /// Checks if the physical device is suitable and configures the object state 612 /// Checks if the physical device is suitable and configures the object state
601 /// with all necessary info about its properties. 613 /// with all necessary info about its properties.
@@ -644,7 +656,6 @@ private:
644 FOR_EACH_VK_FEATURE_1_3(FEATURE); 656 FOR_EACH_VK_FEATURE_1_3(FEATURE);
645 FOR_EACH_VK_FEATURE_EXT(FEATURE); 657 FOR_EACH_VK_FEATURE_EXT(FEATURE);
646 FOR_EACH_VK_EXTENSION(EXTENSION); 658 FOR_EACH_VK_EXTENSION(EXTENSION);
647 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
648 659
649#undef EXTENSION 660#undef EXTENSION
650#undef FEATURE 661#undef FEATURE
@@ -691,6 +702,7 @@ private:
691 bool is_integrated{}; ///< Is GPU an iGPU. 702 bool is_integrated{}; ///< Is GPU an iGPU.
692 bool is_virtual{}; ///< Is GPU a virtual GPU. 703 bool is_virtual{}; ///< Is GPU a virtual GPU.
693 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 704 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
705 bool has_broken_compute{}; ///< Compute shaders can cause crashes
694 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit 706 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
695 bool has_renderdoc{}; ///< Has RenderDoc attached 707 bool has_renderdoc{}; ///< Has RenderDoc attached
696 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 708 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 84d9ca796..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
@@ -210,6 +213,8 @@ add_executable(yuzu
210 util/url_request_interceptor.h 213 util/url_request_interceptor.h
211 util/util.cpp 214 util/util.cpp
212 util/util.h 215 util/util.h
216 vk_device_info.cpp
217 vk_device_info.h
213 compatdb.cpp 218 compatdb.cpp
214 compatdb.h 219 compatdb.h
215 yuzu.qrc 220 yuzu.qrc
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..87ab88cfa 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);
@@ -1063,6 +1077,10 @@ void Config::SavePlayerValue(std::size_t player_index) {
1063 QString::fromStdString(player.motions[i]), 1077 QString::fromStdString(player.motions[i]),
1064 QString::fromStdString(default_param)); 1078 QString::fromStdString(default_param));
1065 } 1079 }
1080
1081 if (player_index == 0) {
1082 SaveMousePanningValues();
1083 }
1066} 1084}
1067 1085
1068void Config::SaveDebugValues() { 1086void Config::SaveDebugValues() {
@@ -1099,6 +1117,16 @@ void Config::SaveTouchscreenValues() {
1099 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); 1117 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
1100} 1118}
1101 1119
1120void Config::SaveMousePanningValues() {
1121 // Don't overwrite values.mouse_panning
1122 WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity);
1123 WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity);
1124 WriteBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight);
1125 WriteBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight);
1126 WriteBasicSetting(Settings::values.mouse_panning_decay_strength);
1127 WriteBasicSetting(Settings::values.mouse_panning_min_decay);
1128}
1129
1102void Config::SaveMotionTouchValues() { 1130void Config::SaveMotionTouchValues() {
1103 WriteBasicSetting(Settings::values.touch_device); 1131 WriteBasicSetting(Settings::values.touch_device);
1104 WriteBasicSetting(Settings::values.touch_from_button_map_index); 1132 WriteBasicSetting(Settings::values.touch_from_button_map_index);
@@ -1185,6 +1213,7 @@ void Config::SaveControlValues() {
1185 SaveDebugValues(); 1213 SaveDebugValues();
1186 SaveMouseValues(); 1214 SaveMouseValues();
1187 SaveTouchscreenValues(); 1215 SaveTouchscreenValues();
1216 SaveMousePanningValues();
1188 SaveMotionTouchValues(); 1217 SaveMotionTouchValues();
1189 SaveHidbusValues(); 1218 SaveHidbusValues();
1190 SaveIrCameraValues(); 1219 SaveIrCameraValues();
@@ -1199,7 +1228,6 @@ void Config::SaveControlValues() {
1199 WriteBasicSetting(Settings::values.random_amiibo_id); 1228 WriteBasicSetting(Settings::values.random_amiibo_id);
1200 WriteBasicSetting(Settings::values.keyboard_enabled); 1229 WriteBasicSetting(Settings::values.keyboard_enabled);
1201 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1230 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1202 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
1203 WriteBasicSetting(Settings::values.controller_navigation); 1231 WriteBasicSetting(Settings::values.controller_navigation);
1204 1232
1205 WriteBasicSetting(Settings::values.tas_enable); 1233 WriteBasicSetting(Settings::values.tas_enable);
@@ -1417,6 +1445,7 @@ void Config::SaveRendererValues() {
1417 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 1445 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1418 WriteGlobalSetting(Settings::values.enable_compute_pipelines); 1446 WriteGlobalSetting(Settings::values.enable_compute_pipelines);
1419 WriteGlobalSetting(Settings::values.use_video_framerate); 1447 WriteGlobalSetting(Settings::values.use_video_framerate);
1448 WriteGlobalSetting(Settings::values.barrier_feedback_loops);
1420 WriteGlobalSetting(Settings::values.bg_red); 1449 WriteGlobalSetting(Settings::values.bg_red);
1421 WriteGlobalSetting(Settings::values.bg_green); 1450 WriteGlobalSetting(Settings::values.bg_green);
1422 WriteGlobalSetting(Settings::values.bg_blue); 1451 WriteGlobalSetting(Settings::values.bg_blue);
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_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 8e76a819a..bdf83ebfe 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,6 +6,7 @@
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "ui_configure.h" 8#include "ui_configure.h"
9#include "vk_device_info.h"
9#include "yuzu/configuration/config.h" 10#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_audio.h" 11#include "yuzu/configuration/configure_audio.h"
11#include "yuzu/configuration/configure_cpu.h" 12#include "yuzu/configuration/configure_cpu.h"
@@ -28,6 +29,7 @@
28 29
29ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, 30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
30 InputCommon::InputSubsystem* input_subsystem, 31 InputCommon::InputSubsystem* input_subsystem,
32 std::vector<VkDeviceInfo::Record>& vk_device_records,
31 Core::System& system_, bool enable_web_config) 33 Core::System& system_, bool enable_web_config)
32 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
33 registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, 35 registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_,
@@ -38,7 +40,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
38 general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, 40 general_tab{std::make_unique<ConfigureGeneral>(system_, this)},
39 graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, 41 graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)},
40 graphics_tab{std::make_unique<ConfigureGraphics>( 42 graphics_tab{std::make_unique<ConfigureGraphics>(
41 system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this)}, 43 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
44 this)},
42 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, 45 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
43 input_tab{std::make_unique<ConfigureInput>(system_, this)}, 46 input_tab{std::make_unique<ConfigureInput>(system_, this)},
44 network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, 47 network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index a086a07c4..2a08b7fee 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -4,7 +4,9 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <vector>
7#include <QDialog> 8#include <QDialog>
9#include "yuzu/vk_device_info.h"
8 10
9namespace Core { 11namespace Core {
10class System; 12class System;
@@ -40,8 +42,9 @@ class ConfigureDialog : public QDialog {
40 42
41public: 43public:
42 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, 44 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
43 InputCommon::InputSubsystem* input_subsystem, Core::System& system_, 45 InputCommon::InputSubsystem* input_subsystem,
44 bool enable_web_config = true); 46 std::vector<VkDeviceInfo::Record>& vk_device_records,
47 Core::System& system_, bool enable_web_config = true);
45 ~ConfigureDialog() override; 48 ~ConfigureDialog() override;
46 49
47 void ApplyConfiguration(); 50 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 431585216..a4965524a 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -1,10 +1,6 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4// Include this early to include Vulkan headers how we want to
5#include "video_core/vulkan_common/vulkan_device.h"
6#include "video_core/vulkan_common/vulkan_wrapper.h"
7
8#include <algorithm> 4#include <algorithm>
9#include <functional> 5#include <functional>
10#include <iosfwd> 6#include <iosfwd>
@@ -34,13 +30,11 @@
34#include "common/settings.h" 30#include "common/settings.h"
35#include "core/core.h" 31#include "core/core.h"
36#include "ui_configure_graphics.h" 32#include "ui_configure_graphics.h"
37#include "video_core/vulkan_common/vulkan_instance.h"
38#include "video_core/vulkan_common/vulkan_library.h"
39#include "video_core/vulkan_common/vulkan_surface.h"
40#include "yuzu/configuration/configuration_shared.h" 33#include "yuzu/configuration/configuration_shared.h"
41#include "yuzu/configuration/configure_graphics.h" 34#include "yuzu/configuration/configure_graphics.h"
42#include "yuzu/qt_common.h" 35#include "yuzu/qt_common.h"
43#include "yuzu/uisettings.h" 36#include "yuzu/uisettings.h"
37#include "yuzu/vk_device_info.h"
44 38
45static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, 39static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
46 VK_PRESENT_MODE_FIFO_KHR}; 40 VK_PRESENT_MODE_FIFO_KHR};
@@ -77,9 +71,10 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
77} 71}
78 72
79ConfigureGraphics::ConfigureGraphics(const Core::System& system_, 73ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
74 std::vector<VkDeviceInfo::Record>& records_,
80 const std::function<void()>& expose_compute_option_, 75 const std::function<void()>& expose_compute_option_,
81 QWidget* parent) 76 QWidget* parent)
82 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, 77 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_},
83 expose_compute_option{expose_compute_option_}, system{system_} { 78 expose_compute_option{expose_compute_option_}, system{system_} {
84 vulkan_device = Settings::values.vulkan_device.GetValue(); 79 vulkan_device = Settings::values.vulkan_device.GetValue();
85 RetrieveVulkanDevices(); 80 RetrieveVulkanDevices();
@@ -504,47 +499,19 @@ void ConfigureGraphics::UpdateAPILayout() {
504 } 499 }
505} 500}
506 501
507void ConfigureGraphics::RetrieveVulkanDevices() try { 502void ConfigureGraphics::RetrieveVulkanDevices() {
508 if (UISettings::values.has_broken_vulkan) {
509 return;
510 }
511
512 using namespace Vulkan;
513
514 auto* window = this->window()->windowHandle();
515 auto wsi = QtCommon::GetWindowSystemInfo(window);
516
517 vk::InstanceDispatch dld;
518 const auto library = OpenLibrary();
519 const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
520 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
521 vk::SurfaceKHR surface = CreateSurface(instance, wsi);
522
523 vulkan_devices.clear(); 503 vulkan_devices.clear();
524 vulkan_devices.reserve(physical_devices.size()); 504 vulkan_devices.reserve(records.size());
525 device_present_modes.clear(); 505 device_present_modes.clear();
526 device_present_modes.reserve(physical_devices.size()); 506 device_present_modes.reserve(records.size());
527 for (const VkPhysicalDevice device : physical_devices) { 507 for (const auto& record : records) {
528 const auto physical_device = vk::PhysicalDevice(device, dld); 508 vulkan_devices.push_back(QString::fromStdString(record.name));
529 const std::string name = physical_device.GetProperties().deviceName; 509 device_present_modes.push_back(record.vsync_support);
530 const std::vector<VkPresentModeKHR> present_modes = 510
531 physical_device.GetSurfacePresentModesKHR(*surface); 511 if (record.has_broken_compute) {
532 vulkan_devices.push_back(QString::fromStdString(name));
533 device_present_modes.push_back(present_modes);
534
535 VkPhysicalDeviceDriverProperties driver_properties{};
536 driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
537 driver_properties.pNext = nullptr;
538 VkPhysicalDeviceProperties2 properties{};
539 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
540 properties.pNext = &driver_properties;
541 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
542 if (driver_properties.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
543 expose_compute_option(); 512 expose_compute_option();
544 } 513 }
545 } 514 }
546} catch (const Vulkan::vk::Exception& exception) {
547 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
548} 515}
549 516
550Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 517Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 364b1cac2..be9310b74 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -12,6 +12,7 @@
12#include <qobjectdefs.h> 12#include <qobjectdefs.h>
13#include <vulkan/vulkan_core.h> 13#include <vulkan/vulkan_core.h>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "vk_device_info.h"
15 16
16class QEvent; 17class QEvent;
17class QObject; 18class QObject;
@@ -39,6 +40,7 @@ class ConfigureGraphics : public QWidget {
39 40
40public: 41public:
41 explicit ConfigureGraphics(const Core::System& system_, 42 explicit ConfigureGraphics(const Core::System& system_,
43 std::vector<VkDeviceInfo::Record>& records,
42 const std::function<void()>& expose_compute_option_, 44 const std::function<void()>& expose_compute_option_,
43 QWidget* parent = nullptr); 45 QWidget* parent = nullptr);
44 ~ConfigureGraphics() override; 46 ~ConfigureGraphics() override;
@@ -77,6 +79,7 @@ private:
77 ConfigurationShared::CheckState use_disk_shader_cache; 79 ConfigurationShared::CheckState use_disk_shader_cache;
78 ConfigurationShared::CheckState use_asynchronous_gpu_emulation; 80 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
79 81
82 std::vector<VkDeviceInfo::Record>& records;
80 std::vector<QString> vulkan_devices; 83 std::vector<QString> vulkan_devices;
81 std::vector<std::vector<VkPresentModeKHR>> device_present_modes; 84 std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
82 std::vector<VkPresentModeKHR> 85 std::vector<VkPresentModeKHR>
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_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 7ac162586..eb96e6068 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <utility> 8#include <utility>
9#include <vector>
9 10
10#include <fmt/format.h> 11#include <fmt/format.h>
11 12
@@ -34,8 +35,10 @@
34#include "yuzu/configuration/configure_system.h" 35#include "yuzu/configuration/configure_system.h"
35#include "yuzu/uisettings.h" 36#include "yuzu/uisettings.h"
36#include "yuzu/util/util.h" 37#include "yuzu/util/util.h"
38#include "yuzu/vk_device_info.h"
37 39
38ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, 40ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
41 std::vector<VkDeviceInfo::Record>& vk_device_records,
39 Core::System& system_) 42 Core::System& system_)
40 : QDialog(parent), 43 : QDialog(parent),
41 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { 44 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} {
@@ -50,7 +53,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 general_tab = std::make_unique<ConfigureGeneral>(system_, this); 53 general_tab = std::make_unique<ConfigureGeneral>(system_, this);
51 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); 54 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this);
52 graphics_tab = std::make_unique<ConfigureGraphics>( 55 graphics_tab = std::make_unique<ConfigureGraphics>(
53 system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); 56 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this);
54 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); 57 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
55 system_tab = std::make_unique<ConfigureSystem>(system_, this); 58 system_tab = std::make_unique<ConfigureSystem>(system_, this);
56 59
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 85752f1fa..7ec1ded06 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -5,11 +5,13 @@
5 5
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <vector>
8 9
9#include <QDialog> 10#include <QDialog>
10#include <QList> 11#include <QList>
11 12
12#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs_types.h"
14#include "vk_device_info.h"
13#include "yuzu/configuration/config.h" 15#include "yuzu/configuration/config.h"
14 16
15namespace Core { 17namespace Core {
@@ -45,6 +47,7 @@ class ConfigurePerGame : public QDialog {
45public: 47public:
46 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 48 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
47 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, 49 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
50 std::vector<VkDeviceInfo::Record>& vk_device_records,
48 Core::System& system_); 51 Core::System& system_);
49 ~ConfigurePerGame() override; 52 ~ConfigurePerGame() override;
50 53
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 013715b44..2133f7343 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -147,6 +147,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
147#include "yuzu/startup_checks.h" 147#include "yuzu/startup_checks.h"
148#include "yuzu/uisettings.h" 148#include "yuzu/uisettings.h"
149#include "yuzu/util/clickable_label.h" 149#include "yuzu/util/clickable_label.h"
150#include "yuzu/vk_device_info.h"
150 151
151#ifdef YUZU_DBGHELP 152#ifdef YUZU_DBGHELP
152#include "yuzu/mini_dump.h" 153#include "yuzu/mini_dump.h"
@@ -440,10 +441,20 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
440 441
441 renderer_status_button->setDisabled(true); 442 renderer_status_button->setDisabled(true);
442 renderer_status_button->setChecked(false); 443 renderer_status_button->setChecked(false);
444 } else {
445 VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle());
443 } 446 }
444 447
445#if defined(HAVE_SDL2) && !defined(_WIN32) 448#if defined(HAVE_SDL2) && !defined(_WIN32)
446 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
447 // SDL disables the screen saver by default, and setting the hint 458 // SDL disables the screen saver by default, and setting the hint
448 // 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
449 // for now. 460 // for now.
@@ -1620,45 +1631,6 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
1620} 1631}
1621 1632
1622#ifdef __unix__ 1633#ifdef __unix__
1623static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1624 if (!QDBusConnection::sessionBus().isConnected()) {
1625 return {};
1626 }
1627 // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
1628 QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"),
1629 QString::fromLatin1("/org/freedesktop/portal/desktop"),
1630 QString::fromLatin1("org.freedesktop.portal.Inhibit"));
1631 if (!xdp.isValid()) {
1632 LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
1633 return {};
1634 }
1635 QVariantMap options = {};
1636 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
1637 //: computer from sleeping
1638 options.insert(QString::fromLatin1("reason"),
1639 QCoreApplication::translate("GMainWindow", "yuzu is running a game"));
1640 // 0x4: Suspend lock; 0x8: Idle lock
1641 QDBusReply<QDBusObjectPath> reply =
1642 xdp.call(QString::fromLatin1("Inhibit"),
1643 QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
1644
1645 if (reply.isValid()) {
1646 return reply.value();
1647 }
1648 LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
1649 reply.error().message().toStdString());
1650 return {};
1651}
1652
1653static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
1654 if (!QDBusConnection::sessionBus().isConnected()) {
1655 return;
1656 }
1657 QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
1658 QString::fromLatin1("org.freedesktop.portal.Request"));
1659 unlocker.call(QString::fromLatin1("Close"));
1660}
1661
1662std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0}; 1634std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
1663 1635
1664void GMainWindow::SetupSigInterrupts() { 1636void GMainWindow::SetupSigInterrupts() {
@@ -1711,12 +1683,6 @@ void GMainWindow::PreventOSSleep() {
1711 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1683 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1712#elif defined(HAVE_SDL2) 1684#elif defined(HAVE_SDL2)
1713 SDL_DisableScreenSaver(); 1685 SDL_DisableScreenSaver();
1714#ifdef __unix__
1715 auto reply = HoldWakeLockLinux(winId());
1716 if (reply) {
1717 wake_lock = std::move(reply.value());
1718 }
1719#endif
1720#endif 1686#endif
1721} 1687}
1722 1688
@@ -1725,11 +1691,6 @@ void GMainWindow::AllowOSSleep() {
1725 SetThreadExecutionState(ES_CONTINUOUS); 1691 SetThreadExecutionState(ES_CONTINUOUS);
1726#elif defined(HAVE_SDL2) 1692#elif defined(HAVE_SDL2)
1727 SDL_EnableScreenSaver(); 1693 SDL_EnableScreenSaver();
1728#ifdef __unix__
1729 if (!wake_lock.path().isEmpty()) {
1730 ReleaseWakeLockLinux(wake_lock);
1731 }
1732#endif
1733#endif 1694#endif
1734} 1695}
1735 1696
@@ -3494,7 +3455,8 @@ void GMainWindow::OnConfigure() {
3494 const auto old_language_index = Settings::values.language_index.GetValue(); 3455 const auto old_language_index = Settings::values.language_index.GetValue();
3495 3456
3496 Settings::SetConfiguringGlobal(true); 3457 Settings::SetConfiguringGlobal(true);
3497 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system, 3458 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
3459 vk_device_records, *system,
3498 !multiplayer_state->IsHostingPublicRoom()); 3460 !multiplayer_state->IsHostingPublicRoom());
3499 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, 3461 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
3500 &GMainWindow::OnLanguageChanged); 3462 &GMainWindow::OnLanguageChanged);
@@ -3765,7 +3727,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
3765 const auto v_file = Core::GetGameFileFromPath(vfs, file_name); 3727 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
3766 3728
3767 Settings::SetConfiguringGlobal(false); 3729 Settings::SetConfiguringGlobal(false);
3768 ConfigurePerGame dialog(this, title_id, file_name, *system); 3730 ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *system);
3769 dialog.LoadFromFile(v_file); 3731 dialog.LoadFromFile(v_file);
3770 const auto result = dialog.exec(); 3732 const auto result = dialog.exec();
3771 3733
@@ -3836,7 +3798,7 @@ void GMainWindow::OnLoadAmiibo() {
3836 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3798 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3837 3799
3838 // Remove amiibo if one is connected 3800 // Remove amiibo if one is connected
3839 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3801 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3840 virtual_amiibo->CloseAmiibo(); 3802 virtual_amiibo->CloseAmiibo();
3841 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3803 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3842 return; 3804 return;
@@ -3864,7 +3826,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
3864 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3826 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3865 const QString title = tr("Error loading Amiibo data"); 3827 const QString title = tr("Error loading Amiibo data");
3866 // Remove amiibo if one is connected 3828 // Remove amiibo if one is connected
3867 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3829 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3868 virtual_amiibo->CloseAmiibo(); 3830 virtual_amiibo->CloseAmiibo();
3869 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3831 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3870 return; 3832 return;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6bb70972f..2cfb96257 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -118,6 +118,10 @@ enum class ReinitializeKeyBehavior {
118 Warning, 118 Warning,
119}; 119};
120 120
121namespace VkDeviceInfo {
122class Record;
123}
124
121class GMainWindow : public QMainWindow { 125class GMainWindow : public QMainWindow {
122 Q_OBJECT 126 Q_OBJECT
123 127
@@ -418,6 +422,8 @@ private:
418 422
419 GameListPlaceholder* game_list_placeholder; 423 GameListPlaceholder* game_list_placeholder;
420 424
425 std::vector<VkDeviceInfo::Record> vk_device_records;
426
421 // Status bar elements 427 // Status bar elements
422 QLabel* message_label = nullptr; 428 QLabel* message_label = nullptr;
423 QLabel* shader_building_label = nullptr; 429 QLabel* shader_building_label = nullptr;
@@ -498,8 +504,6 @@ private:
498#ifdef __unix__ 504#ifdef __unix__
499 QSocketNotifier* sig_interrupt_notifier; 505 QSocketNotifier* sig_interrupt_notifier;
500 static std::array<int, 3> sig_interrupt_fds; 506 static std::array<int, 3> sig_interrupt_fds;
501
502 QDBusObjectPath wake_lock{};
503#endif 507#endif
504 508
505protected: 509protected:
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
new file mode 100644
index 000000000..7c26a3dc7
--- /dev/null
+++ b/src/yuzu/vk_device_info.cpp
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <utility>
5#include <vector>
6#include "common/dynamic_library.h"
7#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_device.h"
9#include "video_core/vulkan_common/vulkan_instance.h"
10#include "video_core/vulkan_common/vulkan_library.h"
11#include "video_core/vulkan_common/vulkan_surface.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13#include "vulkan/vulkan_core.h"
14#include "yuzu/qt_common.h"
15#include "yuzu/vk_device_info.h"
16
17class QWindow;
18
19namespace VkDeviceInfo {
20Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_,
21 bool has_broken_compute_)
22 : name{name_}, vsync_support{vsync_modes_}, has_broken_compute{has_broken_compute_} {}
23
24Record::~Record() = default;
25
26void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
27 using namespace Vulkan;
28
29 auto wsi = QtCommon::GetWindowSystemInfo(window);
30
31 vk::InstanceDispatch dld;
32 const auto library = OpenLibrary();
33 const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
34 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
35 vk::SurfaceKHR surface = CreateSurface(instance, wsi);
36
37 records.clear();
38 records.reserve(physical_devices.size());
39 for (const VkPhysicalDevice device : physical_devices) {
40 const auto physical_device = vk::PhysicalDevice(device, dld);
41 const std::string name = physical_device.GetProperties().deviceName;
42 const std::vector<VkPresentModeKHR> present_modes =
43 physical_device.GetSurfacePresentModesKHR(*surface);
44
45 VkPhysicalDeviceDriverProperties driver_properties{};
46 driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
47 driver_properties.pNext = nullptr;
48 VkPhysicalDeviceProperties2 properties{};
49 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
50 properties.pNext = &driver_properties;
51 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
52
53 bool has_broken_compute{Vulkan::Device::CheckBrokenCompute(
54 driver_properties.driverID, properties.properties.driverVersion)};
55
56 records.push_back(VkDeviceInfo::Record(name, present_modes, has_broken_compute));
57 }
58} catch (const Vulkan::vk::Exception& exception) {
59 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
60}
61} // namespace VkDeviceInfo
diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h
new file mode 100644
index 000000000..bda8262f4
--- /dev/null
+++ b/src/yuzu/vk_device_info.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <iterator>
8#include <memory>
9#include <string>
10#include <string_view>
11#include <vector>
12#include "common/common_types.h"
13#include "vulkan/vulkan_core.h"
14
15class QWindow;
16
17namespace Settings {
18enum class VSyncMode : u32;
19}
20// #include "common/settings.h"
21
22namespace VkDeviceInfo {
23// Short class to record Vulkan driver information for configuration purposes
24class Record {
25public:
26 explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
27 bool has_broken_compute);
28 ~Record();
29
30 const std::string name;
31 const std::vector<VkPresentModeKHR> vsync_support;
32 const bool has_broken_compute;
33};
34
35void PopulateRecords(std::vector<Record>& records, QWindow* window);
36} // namespace VkDeviceInfo
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