summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts4
-rw-r--r--src/android/app/src/main/AndroidManifest.xml5
-rw-r--r--src/android/gradle.properties3
-rw-r--r--src/audio_core/sink/sink_stream.cpp34
-rw-r--r--src/audio_core/sink/sink_stream.h3
-rw-r--r--src/common/input.h2
-rw-r--r--src/common/ring_buffer.h2
-rw-r--r--src/common/scratch_buffer.h46
-rw-r--r--src/common/settings.h10
-rw-r--r--src/common/telemetry.cpp1
-rw-r--r--src/common/x64/cpu_detect.cpp1
-rw-r--r--src/common/x64/cpu_detect.h1
-rw-r--r--src/common/x64/cpu_wait.cpp52
-rw-r--r--src/core/hle/service/audio/audin_u.cpp16
-rw-r--r--src/core/hle/service/audio/audout_u.cpp20
-rw-r--r--src/core/hle/service/audio/audren_u.cpp23
-rw-r--r--src/core/hle/service/audio/hwopus.cpp9
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp25
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h5
-rw-r--r--src/core/hle/service/nvnflinger/parcel.h24
-rw-r--r--src/input_common/drivers/joycon.cpp35
-rw-r--r--src/input_common/drivers/joycon.h3
-rw-r--r--src/input_common/drivers/mouse.cpp86
-rw-r--r--src/input_common/drivers/mouse.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp129
-rw-r--r--src/input_common/helpers/joycon_driver.h48
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp53
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h15
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp104
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h44
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp52
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h41
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp70
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.h22
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h17
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp231
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h66
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp45
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.h14
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp5
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.h8
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp7
-rw-r--r--src/video_core/host1x/codecs/codec.cpp2
-rw-r--r--src/video_core/host1x/codecs/h264.cpp14
-rw-r--r--src/video_core/host1x/codecs/h264.h12
-rw-r--r--src/video_core/host1x/codecs/vp8.cpp2
-rw-r--r--src/video_core/host1x/codecs/vp8.h7
-rw-r--r--src/video_core/host1x/codecs/vp9.cpp1
-rw-r--r--src/video_core/host1x/codecs/vp9.h8
-rw-r--r--src/video_core/host1x/codecs/vp9_types.h1
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp3
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp20
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp18
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp4
-rw-r--r--src/video_core/surface.cpp22
-rw-r--r--src/video_core/surface.h2
-rw-r--r--src/video_core/texture_cache/decode_bc.cpp129
-rw-r--r--src/video_core/texture_cache/decode_bc.h (renamed from src/video_core/texture_cache/decode_bc4.h)6
-rw-r--r--src/video_core/texture_cache/decode_bc4.cpp96
-rw-r--r--src/video_core/texture_cache/texture_cache.h25
-rw-r--r--src/video_core/texture_cache/util.cpp24
-rw-r--r--src/video_core/textures/bcn.cpp1
-rw-r--r--src/video_core/textures/bcn.h9
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.cpp40
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h35
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp64
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp17
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h11
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_player.ui15
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.cpp49
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.ui36
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp3
-rw-r--r--src/yuzu/qt_common.cpp9
-rw-r--r--src/yuzu/vk_device_info.cpp5
85 files changed, 1243 insertions, 934 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index bab4f4d0f..9a47e2bd8 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -26,7 +26,7 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
26android { 26android {
27 namespace = "org.yuzu.yuzu_emu" 27 namespace = "org.yuzu.yuzu_emu"
28 28
29 compileSdkVersion = "android-33" 29 compileSdkVersion = "android-34"
30 ndkVersion = "25.2.9519653" 30 ndkVersion = "25.2.9519653"
31 31
32 buildFeatures { 32 buildFeatures {
@@ -51,7 +51,7 @@ android {
51 // TODO If this is ever modified, change application_id in strings.xml 51 // TODO If this is ever modified, change application_id in strings.xml
52 applicationId = "org.yuzu.yuzu_emu" 52 applicationId = "org.yuzu.yuzu_emu"
53 minSdk = 30 53 minSdk = 30
54 targetSdk = 33 54 targetSdk = 34
55 versionName = getGitVersion() 55 versionName = getGitVersion()
56 56
57 // If you want to use autoVersion for the versionCode, create a property in local.properties 57 // If you want to use autoVersion for the versionCode, create a property in local.properties
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index e31ad69e2..51d949d65 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
13 13
14 <uses-permission android:name="android.permission.INTERNET" /> 14 <uses-permission android:name="android.permission.INTERNET" />
15 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 15 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
16 <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
16 <uses-permission android:name="android.permission.NFC" /> 17 <uses-permission android:name="android.permission.NFC" />
17 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 18 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
18 19
@@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
69 android:resource="@xml/nfc_tech_filter" /> 70 android:resource="@xml/nfc_tech_filter" />
70 </activity> 71 </activity>
71 72
72 <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/> 73 <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
74 <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
75 </service>
73 76
74 <provider 77 <provider
75 android:name=".features.DocumentProvider" 78 android:name=".features.DocumentProvider"
diff --git a/src/android/gradle.properties b/src/android/gradle.properties
index e2f278f33..4fca1b576 100644
--- a/src/android/gradle.properties
+++ b/src/android/gradle.properties
@@ -15,3 +15,6 @@ android.useAndroidX=true
15kotlin.code.style=official 15kotlin.code.style=official
16kotlin.parallel.tasks.in.project=true 16kotlin.parallel.tasks.in.project=true
17android.defaults.buildfeatures.buildconfig=true 17android.defaults.buildfeatures.buildconfig=true
18
19# Android Gradle plugin 8.0.2
20android.suppressUnsupportedCompileSdk=34
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 404dcd0e9..6081352a2 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -12,6 +12,7 @@
12#include "audio_core/sink/sink_stream.h" 12#include "audio_core/sink/sink_stream.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/fixed_point.h" 14#include "common/fixed_point.h"
15#include "common/scope_exit.h"
15#include "common/settings.h" 16#include "common/settings.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/core_timing.h" 18#include "core/core_timing.h"
@@ -19,9 +20,12 @@
19namespace AudioCore::Sink { 20namespace AudioCore::Sink {
20 21
21void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { 22void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
22 if (type == StreamType::In) { 23 SCOPE_EXIT({
23 queue.enqueue(buffer); 24 queue.enqueue(buffer);
24 queued_buffers++; 25 ++queued_buffers;
26 });
27
28 if (type == StreamType::In) {
25 return; 29 return;
26 } 30 }
27 31
@@ -66,16 +70,17 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
66 static_cast<s16>(std::clamp(right_sample, min, max)); 70 static_cast<s16>(std::clamp(right_sample, min, max));
67 } 71 }
68 72
69 samples = samples.subspan(0, samples.size() / system_channels * device_channels); 73 samples_buffer.Push(samples.subspan(0, samples.size() / system_channels * device_channels));
74 return;
75 }
70 76
71 } else if (system_channels == 2 && device_channels == 6) { 77 if (system_channels == 2 && device_channels == 6) {
72 // We need moar samples! Not all games will provide 6 channel audio. 78 // We need moar samples! Not all games will provide 6 channel audio.
73 // TODO: Implement some upmixing here. Currently just passthrough, with other 79 // TODO: Implement some upmixing here. Currently just passthrough, with other
74 // channels left as silence. 80 // channels left as silence.
75 auto new_size = samples.size() / system_channels * device_channels; 81 std::vector<s16> new_samples(samples.size() / system_channels * device_channels);
76 tmp_samples.resize_destructive(new_size);
77 82
78 for (u32 read_index = 0, write_index = 0; read_index < new_size; 83 for (u32 read_index = 0, write_index = 0; read_index < samples.size();
79 read_index += system_channels, write_index += device_channels) { 84 read_index += system_channels, write_index += device_channels) {
80 const auto left_sample{static_cast<s16>(std::clamp( 85 const auto left_sample{static_cast<s16>(std::clamp(
81 static_cast<s32>( 86 static_cast<s32>(
@@ -83,7 +88,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
83 volume), 88 volume),
84 min, max))}; 89 min, max))};
85 90
86 tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; 91 new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
87 92
88 const auto right_sample{static_cast<s16>(std::clamp( 93 const auto right_sample{static_cast<s16>(std::clamp(
89 static_cast<s32>( 94 static_cast<s32>(
@@ -91,20 +96,21 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
91 volume), 96 volume),
92 min, max))}; 97 min, max))};
93 98
94 tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; 99 new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
95 } 100 }
96 samples = std::span<s16>(tmp_samples);
97 101
98 } else if (volume != 1.0f) { 102 samples_buffer.Push(new_samples);
99 for (u32 i = 0; i < samples.size(); i++) { 103 return;
104 }
105
106 if (volume != 1.0f) {
107 for (u32 i = 0; i < samples.size(); ++i) {
100 samples[i] = static_cast<s16>( 108 samples[i] = static_cast<s16>(
101 std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); 109 std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max));
102 } 110 }
103 } 111 }
104 112
105 samples_buffer.Push(samples); 113 samples_buffer.Push(samples);
106 queue.enqueue(buffer);
107 queued_buffers++;
108} 114}
109 115
110std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) { 116std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 98d72ace1..6a4996ca3 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -16,7 +16,6 @@
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"
20#include "common/thread.h" 19#include "common/thread.h"
21 20
22namespace Core { 21namespace Core {
@@ -256,8 +255,6 @@ private:
256 /// Signalled when ring buffer entries are consumed 255 /// Signalled when ring buffer entries are consumed
257 std::condition_variable_any release_cv; 256 std::condition_variable_any release_cv;
258 std::mutex release_mutex; 257 std::mutex release_mutex;
259 /// Temporary buffer for appending samples when upmixing
260 Common::ScratchBuffer<s16> tmp_samples{};
261}; 258};
262 259
263using SinkStreamPtr = std::unique_ptr<SinkStream>; 260using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/common/input.h b/src/common/input.h
index ea30770ae..2c4ccea22 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -75,8 +75,10 @@ enum class DriverResult {
75 ErrorWritingData, 75 ErrorWritingData,
76 NoDeviceDetected, 76 NoDeviceDetected,
77 InvalidHandle, 77 InvalidHandle,
78 InvalidParameters,
78 NotSupported, 79 NotSupported,
79 Disabled, 80 Disabled,
81 Delayed,
80 Unknown, 82 Unknown,
81}; 83};
82 84
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 416680d44..5c961b202 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -54,7 +54,7 @@ public:
54 return push_count; 54 return push_count;
55 } 55 }
56 56
57 std::size_t Push(const std::span<T> input) { 57 std::size_t Push(std::span<const T> input) {
58 return Push(input.data(), input.size()); 58 return Push(input.data(), input.size());
59 } 59 }
60 60
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
index 6fe907953..d5961b020 100644
--- a/src/common/scratch_buffer.h
+++ b/src/common/scratch_buffer.h
@@ -5,7 +5,6 @@
5 5
6#include <iterator> 6#include <iterator>
7 7
8#include "common/concepts.h"
9#include "common/make_unique_for_overwrite.h" 8#include "common/make_unique_for_overwrite.h"
10 9
11namespace Common { 10namespace Common {
@@ -19,15 +18,22 @@ namespace Common {
19template <typename T> 18template <typename T>
20class ScratchBuffer { 19class ScratchBuffer {
21public: 20public:
22 using iterator = T*;
23 using const_iterator = const T*;
24 using value_type = T;
25 using element_type = T; 21 using element_type = T;
26 using iterator_category = std::contiguous_iterator_tag; 22 using value_type = T;
23 using size_type = size_t;
24 using difference_type = std::ptrdiff_t;
25 using pointer = T*;
26 using const_pointer = const T*;
27 using reference = T&;
28 using const_reference = const T&;
29 using iterator = pointer;
30 using const_iterator = const_pointer;
31 using iterator_category = std::random_access_iterator_tag;
32 using iterator_concept = std::contiguous_iterator_tag;
27 33
28 ScratchBuffer() = default; 34 ScratchBuffer() = default;
29 35
30 explicit ScratchBuffer(size_t initial_capacity) 36 explicit ScratchBuffer(size_type initial_capacity)
31 : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, 37 : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
32 buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} 38 buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
33 39
@@ -39,7 +45,7 @@ public:
39 45
40 /// This will only grow the buffer's capacity if size is greater than the current capacity. 46 /// This will only grow the buffer's capacity if size is greater than the current capacity.
41 /// The previously held data will remain intact. 47 /// The previously held data will remain intact.
42 void resize(size_t size) { 48 void resize(size_type size) {
43 if (size > buffer_capacity) { 49 if (size > buffer_capacity) {
44 auto new_buffer = Common::make_unique_for_overwrite<T[]>(size); 50 auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
45 std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get()); 51 std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get());
@@ -51,7 +57,7 @@ public:
51 57
52 /// This will only grow the buffer's capacity if size is greater than the current capacity. 58 /// This will only grow the buffer's capacity if size is greater than the current capacity.
53 /// The previously held data will be destroyed if a reallocation occurs. 59 /// The previously held data will be destroyed if a reallocation occurs.
54 void resize_destructive(size_t size) { 60 void resize_destructive(size_type size) {
55 if (size > buffer_capacity) { 61 if (size > buffer_capacity) {
56 buffer_capacity = size; 62 buffer_capacity = size;
57 buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity); 63 buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
@@ -59,43 +65,43 @@ public:
59 last_requested_size = size; 65 last_requested_size = size;
60 } 66 }
61 67
62 [[nodiscard]] T* data() noexcept { 68 [[nodiscard]] pointer data() noexcept {
63 return buffer.get(); 69 return buffer.get();
64 } 70 }
65 71
66 [[nodiscard]] const T* data() const noexcept { 72 [[nodiscard]] const_pointer data() const noexcept {
67 return buffer.get(); 73 return buffer.get();
68 } 74 }
69 75
70 [[nodiscard]] T* begin() noexcept { 76 [[nodiscard]] iterator begin() noexcept {
71 return data(); 77 return data();
72 } 78 }
73 79
74 [[nodiscard]] const T* begin() const noexcept { 80 [[nodiscard]] const_iterator begin() const noexcept {
75 return data(); 81 return data();
76 } 82 }
77 83
78 [[nodiscard]] T* end() noexcept { 84 [[nodiscard]] iterator end() noexcept {
79 return data() + last_requested_size; 85 return data() + last_requested_size;
80 } 86 }
81 87
82 [[nodiscard]] const T* end() const noexcept { 88 [[nodiscard]] const_iterator end() const noexcept {
83 return data() + last_requested_size; 89 return data() + last_requested_size;
84 } 90 }
85 91
86 [[nodiscard]] T& operator[](size_t i) { 92 [[nodiscard]] reference operator[](size_type i) {
87 return buffer[i]; 93 return buffer[i];
88 } 94 }
89 95
90 [[nodiscard]] const T& operator[](size_t i) const { 96 [[nodiscard]] const_reference operator[](size_type i) const {
91 return buffer[i]; 97 return buffer[i];
92 } 98 }
93 99
94 [[nodiscard]] size_t size() const noexcept { 100 [[nodiscard]] size_type size() const noexcept {
95 return last_requested_size; 101 return last_requested_size;
96 } 102 }
97 103
98 [[nodiscard]] size_t capacity() const noexcept { 104 [[nodiscard]] size_type capacity() const noexcept {
99 return buffer_capacity; 105 return buffer_capacity;
100 } 106 }
101 107
@@ -106,8 +112,8 @@ public:
106 } 112 }
107 113
108private: 114private:
109 size_t last_requested_size{}; 115 size_type last_requested_size{};
110 size_t buffer_capacity{}; 116 size_type buffer_capacity{};
111 std::unique_ptr<T[]> buffer{}; 117 std::unique_ptr<T[]> buffer{};
112}; 118};
113 119
diff --git a/src/common/settings.h b/src/common/settings.h
index ae5ed93d8..59e96e74f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -527,12 +527,10 @@ struct Values {
527 Setting<bool> mouse_panning{false, "mouse_panning"}; 527 Setting<bool> mouse_panning{false, "mouse_panning"};
528 Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; 528 Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"};
529 Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; 529 Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"};
530 Setting<u8, true> mouse_panning_deadzone_x_counterweight{ 530 Setting<u8, true> mouse_panning_deadzone_counterweight{20, 0, 100,
531 0, 0, 100, "mouse_panning_deadzone_x_counterweight"}; 531 "mouse_panning_deadzone_counterweight"};
532 Setting<u8, true> mouse_panning_deadzone_y_counterweight{ 532 Setting<u8, true> mouse_panning_decay_strength{18, 0, 100, "mouse_panning_decay_strength"};
533 0, 0, 100, "mouse_panning_deadzone_y_counterweight"}; 533 Setting<u8, true> mouse_panning_min_decay{6, 0, 100, "mouse_panning_min_decay"};
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"};
536 534
537 Setting<bool> mouse_enabled{false, "mouse_enabled"}; 535 Setting<bool> mouse_enabled{false, "mouse_enabled"};
538 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 536 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 91352912d..929ed67e4 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -93,6 +93,7 @@ void AppendCPUInfo(FieldCollection& fc) {
93 add_field("CPU_Extension_x64_GFNI", caps.gfni); 93 add_field("CPU_Extension_x64_GFNI", caps.gfni);
94 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); 94 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
95 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); 95 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
96 add_field("CPU_Extension_x64_MONITORX", caps.monitorx);
96 add_field("CPU_Extension_x64_MOVBE", caps.movbe); 97 add_field("CPU_Extension_x64_MOVBE", caps.movbe);
97 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); 98 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
98 add_field("CPU_Extension_x64_POPCNT", caps.popcnt); 99 add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index c998b1197..780120a5b 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -168,6 +168,7 @@ static CPUCaps Detect() {
168 __cpuid(cpu_id, 0x80000001); 168 __cpuid(cpu_id, 0x80000001);
169 caps.lzcnt = Common::Bit<5>(cpu_id[2]); 169 caps.lzcnt = Common::Bit<5>(cpu_id[2]);
170 caps.fma4 = Common::Bit<16>(cpu_id[2]); 170 caps.fma4 = Common::Bit<16>(cpu_id[2]);
171 caps.monitorx = Common::Bit<29>(cpu_id[2]);
171 } 172 }
172 173
173 if (max_ex_fn >= 0x80000007) { 174 if (max_ex_fn >= 0x80000007) {
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 8253944d6..756459417 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -63,6 +63,7 @@ struct CPUCaps {
63 bool gfni : 1; 63 bool gfni : 1;
64 bool invariant_tsc : 1; 64 bool invariant_tsc : 1;
65 bool lzcnt : 1; 65 bool lzcnt : 1;
66 bool monitorx : 1;
66 bool movbe : 1; 67 bool movbe : 1;
67 bool pclmulqdq : 1; 68 bool pclmulqdq : 1;
68 bool popcnt : 1; 69 bool popcnt : 1;
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp
index c53dd4945..41d385f59 100644
--- a/src/common/x64/cpu_wait.cpp
+++ b/src/common/x64/cpu_wait.cpp
@@ -13,36 +13,60 @@
13 13
14namespace Common::X64 { 14namespace Common::X64 {
15 15
16namespace {
17
18// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
19// For reference:
20// At 1 GHz, 100K cycles is 100us
21// At 2 GHz, 100K cycles is 50us
22// At 4 GHz, 100K cycles is 25us
23constexpr auto PauseCycles = 100'000U;
24
25} // Anonymous namespace
26
16#ifdef _MSC_VER 27#ifdef _MSC_VER
17__forceinline static void TPAUSE() { 28__forceinline static void TPAUSE() {
18 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 29 static constexpr auto RequestC02State = 0U;
19 // For reference: 30 _tpause(RequestC02State, FencedRDTSC() + PauseCycles);
20 // At 1 GHz, 100K cycles is 100us 31}
21 // At 2 GHz, 100K cycles is 50us 32
22 // At 4 GHz, 100K cycles is 25us 33__forceinline static void MWAITX() {
23 static constexpr auto PauseCycles = 100'000; 34 static constexpr auto EnableWaitTimeFlag = 1U << 1;
24 _tpause(0, FencedRDTSC() + PauseCycles); 35 static constexpr auto RequestC1State = 0U;
36
37 // monitor_var should be aligned to a cache line.
38 alignas(64) u64 monitor_var{};
39 _mm_monitorx(&monitor_var, 0, 0);
40 _mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
25} 41}
26#else 42#else
27static void TPAUSE() { 43static void TPAUSE() {
28 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 44 static constexpr auto RequestC02State = 0U;
29 // For reference:
30 // At 1 GHz, 100K cycles is 100us
31 // At 2 GHz, 100K cycles is 50us
32 // At 4 GHz, 100K cycles is 25us
33 static constexpr auto PauseCycles = 100'000;
34 const auto tsc = FencedRDTSC() + PauseCycles; 45 const auto tsc = FencedRDTSC() + PauseCycles;
35 const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF); 46 const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
36 const auto edx = static_cast<u32>(tsc >> 32); 47 const auto edx = static_cast<u32>(tsc >> 32);
37 asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax)); 48 asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
49}
50
51static void MWAITX() {
52 static constexpr auto EnableWaitTimeFlag = 1U << 1;
53 static constexpr auto RequestC1State = 0U;
54
55 // monitor_var should be aligned to a cache line.
56 alignas(64) u64 monitor_var{};
57 asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
58 asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
38} 59}
39#endif 60#endif
40 61
41void MicroSleep() { 62void MicroSleep() {
42 static const bool has_waitpkg = GetCPUCaps().waitpkg; 63 static const bool has_waitpkg = GetCPUCaps().waitpkg;
64 static const bool has_monitorx = GetCPUCaps().monitorx;
43 65
44 if (has_waitpkg) { 66 if (has_waitpkg) {
45 TPAUSE(); 67 TPAUSE();
68 } else if (has_monitorx) {
69 MWAITX();
46 } else { 70 } else {
47 std::this_thread::yield(); 71 std::this_thread::yield();
48 } 72 }
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index c8d574993..526a39130 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -5,7 +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/scratch_buffer.h"
9#include "common/string_util.h" 9#include "common/string_util.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
@@ -124,12 +124,15 @@ private:
124 124
125 void GetReleasedAudioInBuffer(HLERequestContext& ctx) { 125 void GetReleasedAudioInBuffer(HLERequestContext& ctx) {
126 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 126 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
127 tmp_buffer.resize_destructive(write_buffer_size); 127 released_buffer.resize_destructive(write_buffer_size);
128 tmp_buffer[0] = 0; 128 released_buffer[0] = 0;
129 129
130 const auto count = impl->GetReleasedBuffers(tmp_buffer); 130 const auto count = impl->GetReleasedBuffers(released_buffer);
131 131
132 ctx.WriteBuffer(tmp_buffer); 132 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
133 impl->GetSystem().GetSessionId(), count);
134
135 ctx.WriteBuffer(released_buffer);
133 136
134 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
135 rb.Push(ResultSuccess); 138 rb.Push(ResultSuccess);
@@ -155,7 +158,6 @@ private:
155 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); 158 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
156 159
157 IPC::ResponseBuilder rb{ctx, 3}; 160 IPC::ResponseBuilder rb{ctx, 3};
158
159 rb.Push(ResultSuccess); 161 rb.Push(ResultSuccess);
160 rb.Push(buffer_count); 162 rb.Push(buffer_count);
161 } 163 }
@@ -195,7 +197,7 @@ private:
195 KernelHelpers::ServiceContext service_context; 197 KernelHelpers::ServiceContext service_context;
196 Kernel::KEvent* event; 198 Kernel::KEvent* event;
197 std::shared_ptr<AudioCore::AudioIn::In> impl; 199 std::shared_ptr<AudioCore::AudioIn::In> impl;
198 Common::ScratchBuffer<u64> tmp_buffer; 200 Common::ScratchBuffer<u64> released_buffer;
199}; 201};
200 202
201AudInU::AudInU(Core::System& system_) 203AudInU::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 032c8c11f..23f84a29f 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -9,6 +9,7 @@
9#include "audio_core/renderer/audio_device.h" 9#include "audio_core/renderer/audio_device.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scratch_buffer.h"
12#include "common/string_util.h" 13#include "common/string_util.h"
13#include "common/swap.h" 14#include "common/swap.h"
14#include "core/core.h" 15#include "core/core.h"
@@ -102,8 +103,8 @@ private:
102 AudioOutBuffer buffer{}; 103 AudioOutBuffer buffer{};
103 std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); 104 std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
104 105
105 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; 106 LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}",
106 LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); 107 impl->GetSystem().GetSessionId(), tag);
107 108
108 auto result = impl->AppendBuffer(buffer, tag); 109 auto result = impl->AppendBuffer(buffer, tag);
109 110
@@ -123,12 +124,15 @@ private:
123 124
124 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { 125 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) {
125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 126 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 tmp_buffer.resize_destructive(write_buffer_size); 127 released_buffer.resize_destructive(write_buffer_size);
127 tmp_buffer[0] = 0; 128 released_buffer[0] = 0;
128 129
129 const auto count = impl->GetReleasedBuffers(tmp_buffer); 130 const auto count = impl->GetReleasedBuffers(released_buffer);
130 131
131 ctx.WriteBuffer(tmp_buffer); 132 ctx.WriteBuffer(released_buffer);
133
134 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
135 impl->GetSystem().GetSessionId(), count);
132 136
133 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
134 rb.Push(ResultSuccess); 138 rb.Push(ResultSuccess);
@@ -154,7 +158,6 @@ private:
154 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); 158 LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
155 159
156 IPC::ResponseBuilder rb{ctx, 3}; 160 IPC::ResponseBuilder rb{ctx, 3};
157
158 rb.Push(ResultSuccess); 161 rb.Push(ResultSuccess);
159 rb.Push(buffer_count); 162 rb.Push(buffer_count);
160 } 163 }
@@ -165,7 +168,6 @@ private:
165 LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); 168 LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
166 169
167 IPC::ResponseBuilder rb{ctx, 4}; 170 IPC::ResponseBuilder rb{ctx, 4};
168
169 rb.Push(ResultSuccess); 171 rb.Push(ResultSuccess);
170 rb.Push(samples_played); 172 rb.Push(samples_played);
171 } 173 }
@@ -205,7 +207,7 @@ private:
205 KernelHelpers::ServiceContext service_context; 207 KernelHelpers::ServiceContext service_context;
206 Kernel::KEvent* event; 208 Kernel::KEvent* event;
207 std::shared_ptr<AudioCore::AudioOut::Out> impl; 209 std::shared_ptr<AudioCore::AudioOut::Out> impl;
208 Common::ScratchBuffer<u64> tmp_buffer; 210 Common::ScratchBuffer<u64> released_buffer;
209}; 211};
210 212
211AudOutU::AudOutU(Core::System& system_) 213AudOutU::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 12845c23a..003870176 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -15,6 +15,7 @@
15#include "common/common_funcs.h" 15#include "common/common_funcs.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/polyfill_ranges.h" 17#include "common/polyfill_ranges.h"
18#include "common/scratch_buffer.h"
18#include "common/string_util.h" 19#include "common/string_util.h"
19#include "core/core.h" 20#include "core/core.h"
20#include "core/hle/kernel/k_event.h" 21#include "core/hle/kernel/k_event.h"
@@ -119,23 +120,23 @@ private:
119 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; 120 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
120 if (is_buffer_b) { 121 if (is_buffer_b) {
121 const auto buffersB{ctx.BufferDescriptorB()}; 122 const auto buffersB{ctx.BufferDescriptorB()};
122 tmp_output.resize_destructive(buffersB[0].Size()); 123 output_buffer.resize_destructive(buffersB[0].Size());
123 tmp_performance.resize_destructive(buffersB[1].Size()); 124 performance_buffer.resize_destructive(buffersB[1].Size());
124 } else { 125 } else {
125 const auto buffersC{ctx.BufferDescriptorC()}; 126 const auto buffersC{ctx.BufferDescriptorC()};
126 tmp_output.resize_destructive(buffersC[0].Size()); 127 output_buffer.resize_destructive(buffersC[0].Size());
127 tmp_performance.resize_destructive(buffersC[1].Size()); 128 performance_buffer.resize_destructive(buffersC[1].Size());
128 } 129 }
129 130
130 auto result = impl->RequestUpdate(input, tmp_performance, tmp_output); 131 auto result = impl->RequestUpdate(input, performance_buffer, output_buffer);
131 132
132 if (result.IsSuccess()) { 133 if (result.IsSuccess()) {
133 if (is_buffer_b) { 134 if (is_buffer_b) {
134 ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0); 135 ctx.WriteBufferB(output_buffer.data(), output_buffer.size(), 0);
135 ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1); 136 ctx.WriteBufferB(performance_buffer.data(), performance_buffer.size(), 1);
136 } else { 137 } else {
137 ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0); 138 ctx.WriteBufferC(output_buffer.data(), output_buffer.size(), 0);
138 ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1); 139 ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1);
139 } 140 }
140 } else { 141 } else {
141 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); 142 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
@@ -233,8 +234,8 @@ private:
233 Kernel::KEvent* rendered_event; 234 Kernel::KEvent* rendered_event;
234 Manager& manager; 235 Manager& manager;
235 std::unique_ptr<Renderer> impl; 236 std::unique_ptr<Renderer> impl;
236 Common::ScratchBuffer<u8> tmp_output; 237 Common::ScratchBuffer<u8> output_buffer;
237 Common::ScratchBuffer<u8> tmp_performance; 238 Common::ScratchBuffer<u8> performance_buffer;
238}; 239};
239 240
240class IAudioDevice final : public ServiceFramework<IAudioDevice> { 241class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index c835f6cb7..fa77007f3 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -11,6 +11,7 @@
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/scratch_buffer.h"
14#include "core/hle/service/audio/hwopus.h" 15#include "core/hle/service/audio/hwopus.h"
15#include "core/hle/service/ipc_helpers.h" 16#include "core/hle/service/ipc_helpers.h"
16 17
@@ -68,13 +69,13 @@ private:
68 ExtraBehavior extra_behavior) { 69 ExtraBehavior extra_behavior) {
69 u32 consumed = 0; 70 u32 consumed = 0;
70 u32 sample_count = 0; 71 u32 sample_count = 0;
71 tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); 72 samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>());
72 73
73 if (extra_behavior == ExtraBehavior::ResetContext) { 74 if (extra_behavior == ExtraBehavior::ResetContext) {
74 ResetDecoderContext(); 75 ResetDecoderContext();
75 } 76 }
76 77
77 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) { 78 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
78 LOG_ERROR(Audio, "Failed to decode opus data"); 79 LOG_ERROR(Audio, "Failed to decode opus data");
79 IPC::ResponseBuilder rb{ctx, 2}; 80 IPC::ResponseBuilder rb{ctx, 2};
80 // TODO(ogniK): Use correct error code 81 // TODO(ogniK): Use correct error code
@@ -90,7 +91,7 @@ private:
90 if (performance) { 91 if (performance) {
91 rb.Push<u64>(*performance); 92 rb.Push<u64>(*performance);
92 } 93 }
93 ctx.WriteBuffer(tmp_samples); 94 ctx.WriteBuffer(samples);
94 } 95 }
95 96
96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, 97 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
@@ -154,7 +155,7 @@ private:
154 OpusDecoderPtr decoder; 155 OpusDecoderPtr decoder;
155 u32 sample_rate; 156 u32 sample_rate;
156 u32 channel_count; 157 u32 channel_count;
157 Common::ScratchBuffer<opus_int16> tmp_samples; 158 Common::ScratchBuffer<opus_int16> samples;
158}; 159};
159 160
160class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index 348207e25..c8a880e84 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -2,7 +2,6 @@
2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later 3// SPDX-License-Identifier: GPL-3.0-or-later
4 4
5#include <cinttypes>
6#include "common/logging/log.h" 5#include "common/logging/log.h"
7#include "core/core.h" 6#include "core/core.h"
8#include "core/hle/kernel/k_event.h" 7#include "core/hle/kernel/k_event.h"
@@ -63,12 +62,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) {
63 } 62 }
64 63
65 // Check device 64 // Check device
66 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); 65 output_buffer.resize_destructive(ctx.GetWriteBufferSize(0));
67 const auto input_buffer = ctx.ReadBuffer(0); 66 const auto input_buffer = ctx.ReadBuffer(0);
68 67
69 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output); 68 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
70 if (command.is_out != 0) { 69 if (command.is_out != 0) {
71 ctx.WriteBuffer(tmp_output); 70 ctx.WriteBuffer(output_buffer);
72 } 71 }
73 72
74 IPC::ResponseBuilder rb{ctx, 3}; 73 IPC::ResponseBuilder rb{ctx, 3};
@@ -90,12 +89,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) {
90 89
91 const auto input_buffer = ctx.ReadBuffer(0); 90 const auto input_buffer = ctx.ReadBuffer(0);
92 const auto input_inlined_buffer = ctx.ReadBuffer(1); 91 const auto input_inlined_buffer = ctx.ReadBuffer(1);
93 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); 92 output_buffer.resize_destructive(ctx.GetWriteBufferSize(0));
94 93
95 const auto nv_result = 94 const auto nv_result =
96 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output); 95 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
97 if (command.is_out != 0) { 96 if (command.is_out != 0) {
98 ctx.WriteBuffer(tmp_output); 97 ctx.WriteBuffer(output_buffer);
99 } 98 }
100 99
101 IPC::ResponseBuilder rb{ctx, 3}; 100 IPC::ResponseBuilder rb{ctx, 3};
@@ -116,12 +115,14 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) {
116 } 115 }
117 116
118 const auto input_buffer = ctx.ReadBuffer(0); 117 const auto input_buffer = ctx.ReadBuffer(0);
119 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); 118 output_buffer.resize_destructive(ctx.GetWriteBufferSize(0));
120 tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1)); 119 inline_output_buffer.resize_destructive(ctx.GetWriteBufferSize(1));
121 const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline); 120
121 const auto nv_result =
122 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, inline_output_buffer);
122 if (command.is_out != 0) { 123 if (command.is_out != 0) {
123 ctx.WriteBuffer(tmp_output, 0); 124 ctx.WriteBuffer(output_buffer, 0);
124 ctx.WriteBuffer(tmp_output_inline, 1); 125 ctx.WriteBuffer(inline_output_buffer, 1);
125 } 126 }
126 127
127 IPC::ResponseBuilder rb{ctx, 3}; 128 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 4b593ff90..6e98115dc 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
7#include "common/scratch_buffer.h" 8#include "common/scratch_buffer.h"
8#include "core/hle/service/nvdrv/nvdrv.h" 9#include "core/hle/service/nvdrv/nvdrv.h"
9#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
@@ -34,8 +35,8 @@ private:
34 35
35 u64 pid{}; 36 u64 pid{};
36 bool is_initialized{}; 37 bool is_initialized{};
37 Common::ScratchBuffer<u8> tmp_output; 38 Common::ScratchBuffer<u8> output_buffer;
38 Common::ScratchBuffer<u8> tmp_output_inline; 39 Common::ScratchBuffer<u8> inline_output_buffer;
39}; 40};
40 41
41} // namespace Service::Nvidia 42} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h
index 23ba315a0..e2c9bbd50 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
9#include <boost/container/small_vector.hpp> 10#include <boost/container/small_vector.hpp>
10 11
11#include "common/alignment.h" 12#include "common/alignment.h"
@@ -148,9 +149,9 @@ public:
148 this->WriteImpl(0U, m_object_buffer); 149 this->WriteImpl(0U, m_object_buffer);
149 } 150 }
150 151
151 std::vector<u8> Serialize() const { 152 std::span<u8> Serialize() {
152 std::vector<u8> output_buffer(sizeof(ParcelHeader) + m_data_buffer.size() + 153 m_output_buffer.resize(sizeof(ParcelHeader) + m_data_buffer.size() +
153 m_object_buffer.size()); 154 m_object_buffer.size());
154 155
155 ParcelHeader header{}; 156 ParcelHeader header{};
156 header.data_size = static_cast<u32>(m_data_buffer.size()); 157 header.data_size = static_cast<u32>(m_data_buffer.size());
@@ -158,17 +159,17 @@ public:
158 header.objects_size = static_cast<u32>(m_object_buffer.size()); 159 header.objects_size = static_cast<u32>(m_object_buffer.size());
159 header.objects_offset = header.data_offset + header.data_size; 160 header.objects_offset = header.data_offset + header.data_size;
160 161
161 std::memcpy(output_buffer.data(), &header, sizeof(header)); 162 std::memcpy(m_output_buffer.data(), &header, sizeof(ParcelHeader));
162 std::ranges::copy(m_data_buffer, output_buffer.data() + header.data_offset); 163 std::ranges::copy(m_data_buffer, m_output_buffer.data() + header.data_offset);
163 std::ranges::copy(m_object_buffer, output_buffer.data() + header.objects_offset); 164 std::ranges::copy(m_object_buffer, m_output_buffer.data() + header.objects_offset);
164 165
165 return output_buffer; 166 return m_output_buffer;
166 } 167 }
167 168
168private: 169private:
169 template <typename T> 170 template <typename T, size_t BufferSize>
170 requires(std::is_trivially_copyable_v<T>) 171 requires(std::is_trivially_copyable_v<T>)
171 void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) { 172 void WriteImpl(const T& val, boost::container::small_vector<u8, BufferSize>& buffer) {
172 const size_t aligned_size = Common::AlignUp(sizeof(T), 4); 173 const size_t aligned_size = Common::AlignUp(sizeof(T), 4);
173 const size_t old_size = buffer.size(); 174 const size_t old_size = buffer.size();
174 buffer.resize(old_size + aligned_size); 175 buffer.resize(old_size + aligned_size);
@@ -177,8 +178,9 @@ private:
177 } 178 }
178 179
179private: 180private:
180 boost::container::small_vector<u8, 0x200> m_data_buffer; 181 boost::container::small_vector<u8, 0x1B0> m_data_buffer;
181 boost::container::small_vector<u8, 0x200> m_object_buffer; 182 boost::container::small_vector<u8, 0x40> m_object_buffer;
183 boost::container::small_vector<u8, 0x200> m_output_buffer;
182}; 184};
183 185
184} // namespace Service::android 186} // namespace Service::android
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 52494e0d9..0aca5a3a3 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -102,12 +102,12 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
102 Joycon::SerialNumber serial_number{}; 102 Joycon::SerialNumber serial_number{};
103 103
104 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); 104 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
105 if (result != Joycon::DriverResult::Success) { 105 if (result != Common::Input::DriverResult::Success) {
106 return false; 106 return false;
107 } 107 }
108 108
109 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number); 109 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
110 if (result2 != Joycon::DriverResult::Success) { 110 if (result2 != Common::Input::DriverResult::Success) {
111 return false; 111 return false;
112 } 112 }
113 113
@@ -171,10 +171,10 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
171 LOG_WARNING(Input, "No free handles available"); 171 LOG_WARNING(Input, "No free handles available");
172 return; 172 return;
173 } 173 }
174 if (result == Joycon::DriverResult::Success) { 174 if (result == Common::Input::DriverResult::Success) {
175 result = handle->RequestDeviceAccess(device_info); 175 result = handle->RequestDeviceAccess(device_info);
176 } 176 }
177 if (result == Joycon::DriverResult::Success) { 177 if (result == Common::Input::DriverResult::Success) {
178 LOG_WARNING(Input, "Initialize device"); 178 LOG_WARNING(Input, "Initialize device");
179 179
180 const std::size_t port = handle->GetDevicePort(); 180 const std::size_t port = handle->GetDevicePort();
@@ -273,8 +273,7 @@ Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
273 led_config += led_status.led_3 ? 4 : 0; 273 led_config += led_status.led_3 ? 4 : 0;
274 led_config += led_status.led_4 ? 8 : 0; 274 led_config += led_status.led_4 ? 8 : 0;
275 275
276 return static_cast<Common::Input::DriverResult>( 276 return handle->SetLedConfig(static_cast<u8>(led_config));
277 handle->SetLedConfig(static_cast<u8>(led_config)));
278} 277}
279 278
280Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier, 279Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
@@ -283,8 +282,8 @@ Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identi
283 if (handle == nullptr) { 282 if (handle == nullptr) {
284 return Common::Input::DriverResult::InvalidHandle; 283 return Common::Input::DriverResult::InvalidHandle;
285 } 284 }
286 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig( 285 return handle->SetIrsConfig(Joycon::IrsMode::ImageTransfer,
287 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format))); 286 static_cast<Joycon::IrsResolution>(camera_format));
288}; 287};
289 288
290Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { 289Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
@@ -351,7 +350,7 @@ Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
351 350
352 std::vector<Joycon::MifareReadData> read_data(read_request.size()); 351 std::vector<Joycon::MifareReadData> read_data(read_request.size());
353 const auto result = handle->ReadMifareData(read_request, read_data); 352 const auto result = handle->ReadMifareData(read_request, read_data);
354 if (result == Joycon::DriverResult::Success) { 353 if (result == Common::Input::DriverResult::Success) {
355 for (std::size_t i = 0; i < read_request.size(); i++) { 354 for (std::size_t i = 0; i < read_request.size(); i++) {
356 data.data[i] = { 355 data.data[i] = {
357 .command = static_cast<u8>(command), 356 .command = static_cast<u8>(command),
@@ -402,15 +401,15 @@ Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identif
402 401
403 switch (polling_mode) { 402 switch (polling_mode) {
404 case Common::Input::PollingMode::Active: 403 case Common::Input::PollingMode::Active:
405 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); 404 return handle->SetActiveMode();
406 case Common::Input::PollingMode::Passive: 405 case Common::Input::PollingMode::Passive:
407 return static_cast<Common::Input::DriverResult>(handle->SetPassiveMode()); 406 return handle->SetPassiveMode();
408 case Common::Input::PollingMode::IR: 407 case Common::Input::PollingMode::IR:
409 return static_cast<Common::Input::DriverResult>(handle->SetIrMode()); 408 return handle->SetIrMode();
410 case Common::Input::PollingMode::NFC: 409 case Common::Input::PollingMode::NFC:
411 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); 410 return handle->SetNfcMode();
412 case Common::Input::PollingMode::Ring: 411 case Common::Input::PollingMode::Ring:
413 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); 412 return handle->SetRingConMode();
414 default: 413 default:
415 return Common::Input::DriverResult::NotSupported; 414 return Common::Input::DriverResult::NotSupported;
416 } 415 }
@@ -828,13 +827,13 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
828 } 827 }
829} 828}
830 829
831Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const { 830Common::Input::NfcState Joycons::TranslateDriverResult(Common::Input::DriverResult result) const {
832 switch (result) { 831 switch (result) {
833 case Joycon::DriverResult::Success: 832 case Common::Input::DriverResult::Success:
834 return Common::Input::NfcState::Success; 833 return Common::Input::NfcState::Success;
835 case Joycon::DriverResult::Disabled: 834 case Common::Input::DriverResult::Disabled:
836 return Common::Input::NfcState::WrongDeviceState; 835 return Common::Input::NfcState::WrongDeviceState;
837 case Joycon::DriverResult::NotSupported: 836 case Common::Input::DriverResult::NotSupported:
838 return Common::Input::NfcState::NotSupported; 837 return Common::Input::NfcState::NotSupported;
839 default: 838 default:
840 return Common::Input::NfcState::Unknown; 839 return Common::Input::NfcState::Unknown;
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index 4c323d7d6..112e970e1 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -17,7 +17,6 @@ struct Color;
17struct MotionData; 17struct MotionData;
18struct TagInfo; 18struct TagInfo;
19enum class ControllerType : u8; 19enum class ControllerType : u8;
20enum class DriverResult;
21enum class IrsResolution; 20enum class IrsResolution;
22class JoyconDriver; 21class JoyconDriver;
23} // namespace InputCommon::Joycon 22} // namespace InputCommon::Joycon
@@ -112,7 +111,7 @@ private:
112 /// Returns the name of the device in text format 111 /// Returns the name of the device in text format
113 std::string JoyconName(Joycon::ControllerType type) const; 112 std::string JoyconName(Joycon::ControllerType type) const;
114 113
115 Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const; 114 Common::Input::NfcState TranslateDriverResult(Common::Input::DriverResult result) const;
116 115
117 std::jthread scan_thread; 116 std::jthread scan_thread;
118 117
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index f07cf8a0e..dac29c78f 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -12,9 +12,13 @@
12 12
13namespace InputCommon { 13namespace InputCommon {
14constexpr int update_time = 10; 14constexpr int update_time = 10;
15constexpr float default_stick_sensitivity = 0.0044f; 15constexpr float default_panning_sensitivity = 0.0010f;
16constexpr float default_motion_sensitivity = 0.0003f; 16constexpr float default_stick_sensitivity = 0.0006f;
17constexpr float default_deadzone_counterweight = 0.01f;
18constexpr float default_motion_panning_sensitivity = 2.5f;
19constexpr float default_motion_sensitivity = 0.416f;
17constexpr float maximum_rotation_speed = 2.0f; 20constexpr float maximum_rotation_speed = 2.0f;
21constexpr float maximum_stick_range = 1.5f;
18constexpr int mouse_axis_x = 0; 22constexpr int mouse_axis_x = 0;
19constexpr int mouse_axis_y = 1; 23constexpr int mouse_axis_y = 1;
20constexpr int wheel_axis_x = 2; 24constexpr int wheel_axis_x = 2;
@@ -81,7 +85,7 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
81} 85}
82 86
83void Mouse::UpdateStickInput() { 87void Mouse::UpdateStickInput() {
84 if (!Settings::values.mouse_panning) { 88 if (!IsMousePanningEnabled()) {
85 return; 89 return;
86 } 90 }
87 91
@@ -89,26 +93,13 @@ void Mouse::UpdateStickInput() {
89 93
90 // Prevent input from exceeding the max range (1.0f) too much, 94 // Prevent input from exceeding the max range (1.0f) too much,
91 // but allow some room to make it easier to sustain 95 // but allow some room to make it easier to sustain
92 if (length > 1.2f) { 96 if (length > maximum_stick_range) {
93 last_mouse_change /= length; 97 last_mouse_change /= length;
94 last_mouse_change *= 1.2f; 98 last_mouse_change *= maximum_stick_range;
95 } 99 }
96 100
97 auto mouse_change = last_mouse_change; 101 SetAxis(identifier, mouse_axis_x, last_mouse_change.x);
98 102 SetAxis(identifier, mouse_axis_y, -last_mouse_change.y);
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 103
113 // Decay input over time 104 // Decay input over time
114 const float clamped_length = std::min(1.0f, length); 105 const float clamped_length = std::min(1.0f, length);
@@ -120,14 +111,13 @@ void Mouse::UpdateStickInput() {
120} 111}
121 112
122void Mouse::UpdateMotionInput() { 113void Mouse::UpdateMotionInput() {
123 // This may need its own sensitivity instead of using the average 114 const float sensitivity =
124 const float sensitivity = (Settings::values.mouse_panning_x_sensitivity.GetValue() + 115 IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity;
125 Settings::values.mouse_panning_y_sensitivity.GetValue()) /
126 2.0f * default_motion_sensitivity;
127 116
128 const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x + 117 const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x +
129 last_motion_change.y * last_motion_change.y); 118 last_motion_change.y * last_motion_change.y);
130 119
120 // Clamp rotation speed
131 if (rotation_velocity > maximum_rotation_speed / sensitivity) { 121 if (rotation_velocity > maximum_rotation_speed / sensitivity) {
132 const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity; 122 const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity;
133 last_motion_change.x = last_motion_change.x * multiplier; 123 last_motion_change.x = last_motion_change.x * multiplier;
@@ -144,7 +134,7 @@ void Mouse::UpdateMotionInput() {
144 .delta_timestamp = update_time * 1000, 134 .delta_timestamp = update_time * 1000,
145 }; 135 };
146 136
147 if (Settings::values.mouse_panning) { 137 if (IsMousePanningEnabled()) {
148 last_motion_change.x = 0; 138 last_motion_change.x = 0;
149 last_motion_change.y = 0; 139 last_motion_change.y = 0;
150 } 140 }
@@ -154,33 +144,42 @@ void Mouse::UpdateMotionInput() {
154} 144}
155 145
156void Mouse::Move(int x, int y, int center_x, int center_y) { 146void Mouse::Move(int x, int y, int center_x, int center_y) {
157 if (Settings::values.mouse_panning) { 147 if (IsMousePanningEnabled()) {
158 const auto mouse_change = 148 const auto mouse_change =
159 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); 149 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
160 const float x_sensitivity = 150 const float x_sensitivity =
161 Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; 151 Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity;
162 const float y_sensitivity = 152 const float y_sensitivity =
163 Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; 153 Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity;
154 const float deadzone_counterweight =
155 Settings::values.mouse_panning_deadzone_counterweight.GetValue() *
156 default_deadzone_counterweight;
157
158 last_motion_change += {-mouse_change.y * x_sensitivity, -mouse_change.x * y_sensitivity, 0};
159 last_mouse_change.x += mouse_change.x * x_sensitivity;
160 last_mouse_change.y += mouse_change.y * y_sensitivity;
164 161
165 last_motion_change += {-mouse_change.y, -mouse_change.x, 0}; 162 // Bind the mouse change to [0 <= deadzone_counterweight <= 1.0]
166 last_mouse_change.x += mouse_change.x * x_sensitivity * 0.09f; 163 if (last_mouse_change.Length() < deadzone_counterweight) {
167 last_mouse_change.y += mouse_change.y * y_sensitivity * 0.09f; 164 last_mouse_change /= last_mouse_change.Length();
165 last_mouse_change *= deadzone_counterweight;
166 }
168 167
169 return; 168 return;
170 } 169 }
171 170
172 if (button_pressed) { 171 if (button_pressed) {
173 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; 172 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
174 const float x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue(); 173 const float x_sensitivity =
175 const float y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue(); 174 Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
176 SetAxis(identifier, mouse_axis_x, 175 const float y_sensitivity =
177 static_cast<float>(mouse_move.x) * x_sensitivity * 0.0012f); 176 Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
178 SetAxis(identifier, mouse_axis_y, 177 SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
179 static_cast<float>(-mouse_move.y) * y_sensitivity * 0.0012f); 178 SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
180 179
181 last_motion_change = { 180 last_motion_change = {
182 static_cast<float>(-mouse_move.y) / 50.0f, 181 static_cast<float>(-mouse_move.y) * x_sensitivity,
183 static_cast<float>(-mouse_move.x) / 50.0f, 182 static_cast<float>(-mouse_move.x) * y_sensitivity,
184 last_motion_change.z, 183 last_motion_change.z,
185 }; 184 };
186 } 185 }
@@ -220,7 +219,7 @@ void Mouse::ReleaseButton(MouseButton button) {
220 SetButton(real_mouse_identifier, static_cast<int>(button), false); 219 SetButton(real_mouse_identifier, static_cast<int>(button), false);
221 SetButton(touch_identifier, static_cast<int>(button), false); 220 SetButton(touch_identifier, static_cast<int>(button), false);
222 221
223 if (!Settings::values.mouse_panning) { 222 if (!IsMousePanningEnabled()) {
224 SetAxis(identifier, mouse_axis_x, 0); 223 SetAxis(identifier, mouse_axis_x, 0);
225 SetAxis(identifier, mouse_axis_y, 0); 224 SetAxis(identifier, mouse_axis_y, 0);
226 } 225 }
@@ -234,7 +233,7 @@ void Mouse::ReleaseButton(MouseButton button) {
234void Mouse::MouseWheelChange(int x, int y) { 233void Mouse::MouseWheelChange(int x, int y) {
235 wheel_position.x += x; 234 wheel_position.x += x;
236 wheel_position.y += y; 235 wheel_position.y += y;
237 last_motion_change.z += static_cast<f32>(y) / 100.0f; 236 last_motion_change.z += static_cast<f32>(y);
238 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); 237 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
239 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); 238 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
240} 239}
@@ -244,6 +243,11 @@ void Mouse::ReleaseAllButtons() {
244 button_pressed = false; 243 button_pressed = false;
245} 244}
246 245
246bool Mouse::IsMousePanningEnabled() {
247 // Disable mouse panning when a real mouse is connected
248 return Settings::values.mouse_panning && !Settings::values.mouse_enabled;
249}
250
247std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { 251std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
248 std::vector<Common::ParamPackage> devices; 252 std::vector<Common::ParamPackage> devices;
249 devices.emplace_back(Common::ParamPackage{ 253 devices.emplace_back(Common::ParamPackage{
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 0e8edcce1..2b93a40b9 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -99,6 +99,8 @@ private:
99 void UpdateStickInput(); 99 void UpdateStickInput();
100 void UpdateMotionInput(); 100 void UpdateMotionInput();
101 101
102 bool IsMousePanningEnabled();
103
102 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; 104 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
103 105
104 Common::Vec2<int> mouse_origin; 106 Common::Vec2<int> mouse_origin;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index ec984a647..cf51f3481 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "common/scope_exit.h" 6#include "common/scope_exit.h"
6#include "common/swap.h" 7#include "common/swap.h"
@@ -28,13 +29,13 @@ void JoyconDriver::Stop() {
28 input_thread = {}; 29 input_thread = {};
29} 30}
30 31
31DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) { 32Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
32 std::scoped_lock lock{mutex}; 33 std::scoped_lock lock{mutex};
33 34
34 handle_device_type = ControllerType::None; 35 handle_device_type = ControllerType::None;
35 GetDeviceType(device_info, handle_device_type); 36 GetDeviceType(device_info, handle_device_type);
36 if (handle_device_type == ControllerType::None) { 37 if (handle_device_type == ControllerType::None) {
37 return DriverResult::UnsupportedControllerType; 38 return Common::Input::DriverResult::UnsupportedControllerType;
38 } 39 }
39 40
40 hidapi_handle->handle = 41 hidapi_handle->handle =
@@ -43,15 +44,15 @@ DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info)
43 if (!hidapi_handle->handle) { 44 if (!hidapi_handle->handle) {
44 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", 45 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
45 device_info->vendor_id, device_info->product_id); 46 device_info->vendor_id, device_info->product_id);
46 return DriverResult::HandleInUse; 47 return Common::Input::DriverResult::HandleInUse;
47 } 48 }
48 SDL_hid_set_nonblocking(hidapi_handle->handle, 1); 49 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
49 return DriverResult::Success; 50 return Common::Input::DriverResult::Success;
50} 51}
51 52
52DriverResult JoyconDriver::InitializeDevice() { 53Common::Input::DriverResult JoyconDriver::InitializeDevice() {
53 if (!hidapi_handle->handle) { 54 if (!hidapi_handle->handle) {
54 return DriverResult::InvalidHandle; 55 return Common::Input::DriverResult::InvalidHandle;
55 } 56 }
56 std::scoped_lock lock{mutex}; 57 std::scoped_lock lock{mutex};
57 disable_input_thread = true; 58 disable_input_thread = true;
@@ -87,7 +88,7 @@ DriverResult JoyconDriver::InitializeDevice() {
87 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); 88 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
88 89
89 // Get fixed joycon info 90 // Get fixed joycon info
90 if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) { 91 if (generic_protocol->GetVersionNumber(version) != Common::Input::DriverResult::Success) {
91 // If this command fails the device doesn't accept configuration commands 92 // If this command fails the device doesn't accept configuration commands
92 input_only_device = true; 93 input_only_device = true;
93 } 94 }
@@ -129,7 +130,7 @@ DriverResult JoyconDriver::InitializeDevice() {
129 } 130 }
130 131
131 disable_input_thread = false; 132 disable_input_thread = false;
132 return DriverResult::Success; 133 return Common::Input::DriverResult::Success;
133} 134}
134 135
135void JoyconDriver::InputThread(std::stop_token stop_token) { 136void JoyconDriver::InputThread(std::stop_token stop_token) {
@@ -229,7 +230,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
229 if (!amiibo_detected) { 230 if (!amiibo_detected) {
230 Joycon::TagInfo tag_info; 231 Joycon::TagInfo tag_info;
231 const auto result = nfc_protocol->GetTagInfo(tag_info); 232 const auto result = nfc_protocol->GetTagInfo(tag_info);
232 if (result == DriverResult::Success) { 233 if (result == Common::Input::DriverResult::Success) {
233 joycon_poller->UpdateAmiibo(tag_info); 234 joycon_poller->UpdateAmiibo(tag_info);
234 amiibo_detected = true; 235 amiibo_detected = true;
235 } 236 }
@@ -255,7 +256,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
255 } 256 }
256} 257}
257 258
258DriverResult JoyconDriver::SetPollingMode() { 259Common::Input::DriverResult JoyconDriver::SetPollingMode() {
259 SCOPE_EXIT({ disable_input_thread = false; }); 260 SCOPE_EXIT({ disable_input_thread = false; });
260 disable_input_thread = true; 261 disable_input_thread = true;
261 262
@@ -270,7 +271,7 @@ DriverResult JoyconDriver::SetPollingMode() {
270 } 271 }
271 272
272 if (input_only_device) { 273 if (input_only_device) {
273 return DriverResult::NotSupported; 274 return Common::Input::DriverResult::NotSupported;
274 } 275 }
275 276
276 if (irs_protocol->IsEnabled()) { 277 if (irs_protocol->IsEnabled()) {
@@ -289,7 +290,7 @@ DriverResult JoyconDriver::SetPollingMode() {
289 290
290 if (irs_enabled && supported_features.irs) { 291 if (irs_enabled && supported_features.irs) {
291 auto result = irs_protocol->EnableIrs(); 292 auto result = irs_protocol->EnableIrs();
292 if (result == DriverResult::Success) { 293 if (result == Common::Input::DriverResult::Success) {
293 return result; 294 return result;
294 } 295 }
295 irs_protocol->DisableIrs(); 296 irs_protocol->DisableIrs();
@@ -299,7 +300,7 @@ DriverResult JoyconDriver::SetPollingMode() {
299 300
300 if (nfc_enabled && supported_features.nfc) { 301 if (nfc_enabled && supported_features.nfc) {
301 auto result = nfc_protocol->EnableNfc(); 302 auto result = nfc_protocol->EnableNfc();
302 if (result == DriverResult::Success) { 303 if (result == Common::Input::DriverResult::Success) {
303 return result; 304 return result;
304 } 305 }
305 nfc_protocol->DisableNfc(); 306 nfc_protocol->DisableNfc();
@@ -309,10 +310,10 @@ DriverResult JoyconDriver::SetPollingMode() {
309 310
310 if (hidbus_enabled && supported_features.hidbus) { 311 if (hidbus_enabled && supported_features.hidbus) {
311 auto result = ring_protocol->EnableRingCon(); 312 auto result = ring_protocol->EnableRingCon();
312 if (result == DriverResult::Success) { 313 if (result == Common::Input::DriverResult::Success) {
313 result = ring_protocol->StartRingconPolling(); 314 result = ring_protocol->StartRingconPolling();
314 } 315 }
315 if (result == DriverResult::Success) { 316 if (result == Common::Input::DriverResult::Success) {
316 ring_connected = true; 317 ring_connected = true;
317 return result; 318 return result;
318 } 319 }
@@ -324,7 +325,7 @@ DriverResult JoyconDriver::SetPollingMode() {
324 325
325 if (passive_enabled && supported_features.passive) { 326 if (passive_enabled && supported_features.passive) {
326 const auto result = generic_protocol->EnablePassiveMode(); 327 const auto result = generic_protocol->EnablePassiveMode();
327 if (result == DriverResult::Success) { 328 if (result == Common::Input::DriverResult::Success) {
328 return result; 329 return result;
329 } 330 }
330 LOG_ERROR(Input, "Error enabling passive mode"); 331 LOG_ERROR(Input, "Error enabling passive mode");
@@ -332,7 +333,7 @@ DriverResult JoyconDriver::SetPollingMode() {
332 333
333 // Default Mode 334 // Default Mode
334 const auto result = generic_protocol->EnableActiveMode(); 335 const auto result = generic_protocol->EnableActiveMode();
335 if (result != DriverResult::Success) { 336 if (result != Common::Input::DriverResult::Success) {
336 LOG_ERROR(Input, "Error enabling active mode"); 337 LOG_ERROR(Input, "Error enabling active mode");
337 } 338 }
338 // Switch calls this function after enabling active mode 339 // Switch calls this function after enabling active mode
@@ -396,26 +397,26 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
396 return true; 397 return true;
397} 398}
398 399
399DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { 400Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
400 std::scoped_lock lock{mutex}; 401 std::scoped_lock lock{mutex};
401 if (disable_input_thread) { 402 if (disable_input_thread) {
402 return DriverResult::HandleInUse; 403 return Common::Input::DriverResult::HandleInUse;
403 } 404 }
404 return rumble_protocol->SendVibration(vibration); 405 return rumble_protocol->SendVibration(vibration);
405} 406}
406 407
407DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { 408Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
408 std::scoped_lock lock{mutex}; 409 std::scoped_lock lock{mutex};
409 if (disable_input_thread) { 410 if (disable_input_thread) {
410 return DriverResult::HandleInUse; 411 return Common::Input::DriverResult::HandleInUse;
411 } 412 }
412 return generic_protocol->SetLedPattern(led_pattern); 413 return generic_protocol->SetLedPattern(led_pattern);
413} 414}
414 415
415DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { 416Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
416 std::scoped_lock lock{mutex}; 417 std::scoped_lock lock{mutex};
417 if (disable_input_thread) { 418 if (disable_input_thread) {
418 return DriverResult::HandleInUse; 419 return Common::Input::DriverResult::HandleInUse;
419 } 420 }
420 disable_input_thread = true; 421 disable_input_thread = true;
421 const auto result = irs_protocol->SetIrsConfig(mode_, format_); 422 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
@@ -423,7 +424,7 @@ DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
423 return result; 424 return result;
424} 425}
425 426
426DriverResult JoyconDriver::SetPassiveMode() { 427Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
427 std::scoped_lock lock{mutex}; 428 std::scoped_lock lock{mutex};
428 motion_enabled = false; 429 motion_enabled = false;
429 hidbus_enabled = false; 430 hidbus_enabled = false;
@@ -433,7 +434,7 @@ DriverResult JoyconDriver::SetPassiveMode() {
433 return SetPollingMode(); 434 return SetPollingMode();
434} 435}
435 436
436DriverResult JoyconDriver::SetActiveMode() { 437Common::Input::DriverResult JoyconDriver::SetActiveMode() {
437 if (is_ring_disabled_by_irs) { 438 if (is_ring_disabled_by_irs) {
438 is_ring_disabled_by_irs = false; 439 is_ring_disabled_by_irs = false;
439 SetActiveMode(); 440 SetActiveMode();
@@ -449,11 +450,11 @@ DriverResult JoyconDriver::SetActiveMode() {
449 return SetPollingMode(); 450 return SetPollingMode();
450} 451}
451 452
452DriverResult JoyconDriver::SetIrMode() { 453Common::Input::DriverResult JoyconDriver::SetIrMode() {
453 std::scoped_lock lock{mutex}; 454 std::scoped_lock lock{mutex};
454 455
455 if (!supported_features.irs) { 456 if (!supported_features.irs) {
456 return DriverResult::NotSupported; 457 return Common::Input::DriverResult::NotSupported;
457 } 458 }
458 459
459 if (ring_connected) { 460 if (ring_connected) {
@@ -468,11 +469,11 @@ DriverResult JoyconDriver::SetIrMode() {
468 return SetPollingMode(); 469 return SetPollingMode();
469} 470}
470 471
471DriverResult JoyconDriver::SetNfcMode() { 472Common::Input::DriverResult JoyconDriver::SetNfcMode() {
472 std::scoped_lock lock{mutex}; 473 std::scoped_lock lock{mutex};
473 474
474 if (!supported_features.nfc) { 475 if (!supported_features.nfc) {
475 return DriverResult::NotSupported; 476 return Common::Input::DriverResult::NotSupported;
476 } 477 }
477 478
478 motion_enabled = true; 479 motion_enabled = true;
@@ -483,11 +484,11 @@ DriverResult JoyconDriver::SetNfcMode() {
483 return SetPollingMode(); 484 return SetPollingMode();
484} 485}
485 486
486DriverResult JoyconDriver::SetRingConMode() { 487Common::Input::DriverResult JoyconDriver::SetRingConMode() {
487 std::scoped_lock lock{mutex}; 488 std::scoped_lock lock{mutex};
488 489
489 if (!supported_features.hidbus) { 490 if (!supported_features.hidbus) {
490 return DriverResult::NotSupported; 491 return Common::Input::DriverResult::NotSupported;
491 } 492 }
492 493
493 motion_enabled = true; 494 motion_enabled = true;
@@ -499,20 +500,20 @@ DriverResult JoyconDriver::SetRingConMode() {
499 const auto result = SetPollingMode(); 500 const auto result = SetPollingMode();
500 501
501 if (!ring_connected) { 502 if (!ring_connected) {
502 return DriverResult::NoDeviceDetected; 503 return Common::Input::DriverResult::NoDeviceDetected;
503 } 504 }
504 505
505 return result; 506 return result;
506} 507}
507 508
508DriverResult JoyconDriver::StartNfcPolling() { 509Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
509 std::scoped_lock lock{mutex}; 510 std::scoped_lock lock{mutex};
510 511
511 if (!supported_features.nfc) { 512 if (!supported_features.nfc) {
512 return DriverResult::NotSupported; 513 return Common::Input::DriverResult::NotSupported;
513 } 514 }
514 if (!nfc_protocol->IsEnabled()) { 515 if (!nfc_protocol->IsEnabled()) {
515 return DriverResult::Disabled; 516 return Common::Input::DriverResult::Disabled;
516 } 517 }
517 518
518 disable_input_thread = true; 519 disable_input_thread = true;
@@ -522,14 +523,14 @@ DriverResult JoyconDriver::StartNfcPolling() {
522 return result; 523 return result;
523} 524}
524 525
525DriverResult JoyconDriver::StopNfcPolling() { 526Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
526 std::scoped_lock lock{mutex}; 527 std::scoped_lock lock{mutex};
527 528
528 if (!supported_features.nfc) { 529 if (!supported_features.nfc) {
529 return DriverResult::NotSupported; 530 return Common::Input::DriverResult::NotSupported;
530 } 531 }
531 if (!nfc_protocol->IsEnabled()) { 532 if (!nfc_protocol->IsEnabled()) {
532 return DriverResult::Disabled; 533 return Common::Input::DriverResult::Disabled;
533 } 534 }
534 535
535 disable_input_thread = true; 536 disable_input_thread = true;
@@ -544,17 +545,17 @@ DriverResult JoyconDriver::StopNfcPolling() {
544 return result; 545 return result;
545} 546}
546 547
547DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) { 548Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
548 std::scoped_lock lock{mutex}; 549 std::scoped_lock lock{mutex};
549 550
550 if (!supported_features.nfc) { 551 if (!supported_features.nfc) {
551 return DriverResult::NotSupported; 552 return Common::Input::DriverResult::NotSupported;
552 } 553 }
553 if (!nfc_protocol->IsEnabled()) { 554 if (!nfc_protocol->IsEnabled()) {
554 return DriverResult::Disabled; 555 return Common::Input::DriverResult::Disabled;
555 } 556 }
556 if (!amiibo_detected) { 557 if (!amiibo_detected) {
557 return DriverResult::ErrorWritingData; 558 return Common::Input::DriverResult::ErrorWritingData;
558 } 559 }
559 560
560 out_data.resize(0x21C); 561 out_data.resize(0x21C);
@@ -565,17 +566,17 @@ DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
565 return result; 566 return result;
566} 567}
567 568
568DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { 569Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
569 std::scoped_lock lock{mutex}; 570 std::scoped_lock lock{mutex};
570 571
571 if (!supported_features.nfc) { 572 if (!supported_features.nfc) {
572 return DriverResult::NotSupported; 573 return Common::Input::DriverResult::NotSupported;
573 } 574 }
574 if (!nfc_protocol->IsEnabled()) { 575 if (!nfc_protocol->IsEnabled()) {
575 return DriverResult::Disabled; 576 return Common::Input::DriverResult::Disabled;
576 } 577 }
577 if (!amiibo_detected) { 578 if (!amiibo_detected) {
578 return DriverResult::ErrorWritingData; 579 return Common::Input::DriverResult::ErrorWritingData;
579 } 580 }
580 581
581 disable_input_thread = true; 582 disable_input_thread = true;
@@ -585,18 +586,18 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
585 return result; 586 return result;
586} 587}
587 588
588DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data, 589Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
589 std::span<MifareReadData> out_data) { 590 std::span<MifareReadData> out_data) {
590 std::scoped_lock lock{mutex}; 591 std::scoped_lock lock{mutex};
591 592
592 if (!supported_features.nfc) { 593 if (!supported_features.nfc) {
593 return DriverResult::NotSupported; 594 return Common::Input::DriverResult::NotSupported;
594 } 595 }
595 if (!nfc_protocol->IsEnabled()) { 596 if (!nfc_protocol->IsEnabled()) {
596 return DriverResult::Disabled; 597 return Common::Input::DriverResult::Disabled;
597 } 598 }
598 if (!amiibo_detected) { 599 if (!amiibo_detected) {
599 return DriverResult::ErrorWritingData; 600 return Common::Input::DriverResult::ErrorWritingData;
600 } 601 }
601 602
602 disable_input_thread = true; 603 disable_input_thread = true;
@@ -606,17 +607,17 @@ DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
606 return result; 607 return result;
607} 608}
608 609
609DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) { 610Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
610 std::scoped_lock lock{mutex}; 611 std::scoped_lock lock{mutex};
611 612
612 if (!supported_features.nfc) { 613 if (!supported_features.nfc) {
613 return DriverResult::NotSupported; 614 return Common::Input::DriverResult::NotSupported;
614 } 615 }
615 if (!nfc_protocol->IsEnabled()) { 616 if (!nfc_protocol->IsEnabled()) {
616 return DriverResult::Disabled; 617 return Common::Input::DriverResult::Disabled;
617 } 618 }
618 if (!amiibo_detected) { 619 if (!amiibo_detected) {
619 return DriverResult::ErrorWritingData; 620 return Common::Input::DriverResult::ErrorWritingData;
620 } 621 }
621 622
622 disable_input_thread = true; 623 disable_input_thread = true;
@@ -675,8 +676,8 @@ void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
675 joycon_poller->SetCallbacks(callbacks); 676 joycon_poller->SetCallbacks(callbacks);
676} 677}
677 678
678DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, 679Common::Input::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
679 ControllerType& controller_type) { 680 ControllerType& controller_type) {
680 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{ 681 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{
681 std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, 682 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
682 {0x2007, ControllerType::Right}, 683 {0x2007, ControllerType::Right},
@@ -686,25 +687,25 @@ DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
686 687
687 controller_type = ControllerType::None; 688 controller_type = ControllerType::None;
688 if (device_info->vendor_id != nintendo_vendor_id) { 689 if (device_info->vendor_id != nintendo_vendor_id) {
689 return DriverResult::UnsupportedControllerType; 690 return Common::Input::DriverResult::UnsupportedControllerType;
690 } 691 }
691 692
692 for (const auto& [product_id, type] : supported_devices) { 693 for (const auto& [product_id, type] : supported_devices) {
693 if (device_info->product_id == static_cast<u16>(product_id)) { 694 if (device_info->product_id == static_cast<u16>(product_id)) {
694 controller_type = type; 695 controller_type = type;
695 return Joycon::DriverResult::Success; 696 return Common::Input::DriverResult::Success;
696 } 697 }
697 } 698 }
698 return Joycon::DriverResult::UnsupportedControllerType; 699 return Common::Input::DriverResult::UnsupportedControllerType;
699} 700}
700 701
701DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info, 702Common::Input::DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
702 SerialNumber& serial_number) { 703 SerialNumber& serial_number) {
703 if (device_info->serial_number == nullptr) { 704 if (device_info->serial_number == nullptr) {
704 return DriverResult::Unknown; 705 return Common::Input::DriverResult::Unknown;
705 } 706 }
706 std::memcpy(&serial_number, device_info->serial_number, 15); 707 std::memcpy(&serial_number, device_info->serial_number, 15);
707 return Joycon::DriverResult::Success; 708 return Common::Input::DriverResult::Success;
708} 709}
709 710
710} // namespace InputCommon::Joycon 711} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index 45b32d2f8..335e12cc3 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -11,6 +11,10 @@
11 11
12#include "input_common/helpers/joycon_protocol/joycon_types.h" 12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13 13
14namespace Common::Input {
15enum class DriverResult;
16}
17
14namespace InputCommon::Joycon { 18namespace InputCommon::Joycon {
15class CalibrationProtocol; 19class CalibrationProtocol;
16class GenericProtocol; 20class GenericProtocol;
@@ -26,8 +30,8 @@ public:
26 30
27 ~JoyconDriver(); 31 ~JoyconDriver();
28 32
29 DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info); 33 Common::Input::DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
30 DriverResult InitializeDevice(); 34 Common::Input::DriverResult InitializeDevice();
31 void Stop(); 35 void Stop();
32 36
33 bool IsConnected() const; 37 bool IsConnected() const;
@@ -41,31 +45,31 @@ public:
41 SerialNumber GetSerialNumber() const; 45 SerialNumber GetSerialNumber() const;
42 SerialNumber GetHandleSerialNumber() const; 46 SerialNumber GetHandleSerialNumber() const;
43 47
44 DriverResult SetVibration(const VibrationValue& vibration); 48 Common::Input::DriverResult SetVibration(const VibrationValue& vibration);
45 DriverResult SetLedConfig(u8 led_pattern); 49 Common::Input::DriverResult SetLedConfig(u8 led_pattern);
46 DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_); 50 Common::Input::DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
47 DriverResult SetPassiveMode(); 51 Common::Input::DriverResult SetPassiveMode();
48 DriverResult SetActiveMode(); 52 Common::Input::DriverResult SetActiveMode();
49 DriverResult SetIrMode(); 53 Common::Input::DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 54 Common::Input::DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 55 Common::Input::DriverResult SetRingConMode();
52 DriverResult StartNfcPolling(); 56 Common::Input::DriverResult StartNfcPolling();
53 DriverResult StopNfcPolling(); 57 Common::Input::DriverResult StopNfcPolling();
54 DriverResult ReadAmiiboData(std::vector<u8>& out_data); 58 Common::Input::DriverResult ReadAmiiboData(std::vector<u8>& out_data);
55 DriverResult WriteNfcData(std::span<const u8> data); 59 Common::Input::DriverResult WriteNfcData(std::span<const u8> data);
56 DriverResult ReadMifareData(std::span<const MifareReadChunk> request, 60 Common::Input::DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
57 std::span<MifareReadData> out_data); 61 std::span<MifareReadData> out_data);
58 DriverResult WriteMifareData(std::span<const MifareWriteChunk> request); 62 Common::Input::DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
59 63
60 void SetCallbacks(const JoyconCallbacks& callbacks); 64 void SetCallbacks(const JoyconCallbacks& callbacks);
61 65
62 // Returns device type from hidapi handle 66 // Returns device type from hidapi handle
63 static DriverResult GetDeviceType(SDL_hid_device_info* device_info, 67 static Common::Input::DriverResult GetDeviceType(SDL_hid_device_info* device_info,
64 ControllerType& controller_type); 68 ControllerType& controller_type);
65 69
66 // Returns serial number from hidapi handle 70 // Returns serial number from hidapi handle
67 static DriverResult GetSerialNumber(SDL_hid_device_info* device_info, 71 static Common::Input::DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
68 SerialNumber& serial_number); 72 SerialNumber& serial_number);
69 73
70private: 74private:
71 struct SupportedFeatures { 75 struct SupportedFeatures {
@@ -84,7 +88,7 @@ private:
84 void OnNewData(std::span<u8> buffer); 88 void OnNewData(std::span<u8> buffer);
85 89
86 /// Updates device configuration to enable or disable features 90 /// Updates device configuration to enable or disable features
87 DriverResult SetPollingMode(); 91 Common::Input::DriverResult SetPollingMode();
88 92
89 /// Returns true if input thread is valid and doesn't need to be stopped 93 /// Returns true if input thread is valid and doesn't need to be stopped
90 bool IsInputThreadValid() const; 94 bool IsInputThreadValid() const;
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
index d8f040f75..1300ecaf5 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.cpp
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cstring> 4#include <cstring>
5 5
6#include "common/input.h"
6#include "input_common/helpers/joycon_protocol/calibration.h" 7#include "input_common/helpers/joycon_protocol/calibration.h"
7#include "input_common/helpers/joycon_protocol/joycon_types.h" 8#include "input_common/helpers/joycon_protocol/joycon_types.h"
8 9
@@ -11,28 +12,29 @@ namespace InputCommon::Joycon {
11CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) 12CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
12 : JoyconCommonProtocol(std::move(handle)) {} 13 : JoyconCommonProtocol(std::move(handle)) {}
13 14
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { 15Common::Input::DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(
16 JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this); 17 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 18 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{}; 19 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false; 20 bool has_user_calibration = false;
19 calibration = {}; 21 calibration = {};
20 22
21 if (result == DriverResult::Success) { 23 if (result == Common::Input::DriverResult::Success) {
22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration); 24 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
23 } 25 }
24 26
25 // Read User defined calibration 27 // Read User defined calibration
26 if (result == DriverResult::Success && has_user_calibration) { 28 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration); 29 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
28 } 30 }
29 31
30 // Read Factory calibration 32 // Read Factory calibration
31 if (result == DriverResult::Success && !has_user_calibration) { 33 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration); 34 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 } 35 }
34 36
35 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); 38 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); 39 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); 40 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
@@ -47,28 +49,29 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration
47 return result; 49 return result;
48} 50}
49 51
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { 52Common::Input::DriverResult CalibrationProtocol::GetRightJoyStickCalibration(
53 JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this); 54 ScopedSetBlocking sb(this);
52 DriverResult result{DriverResult::Success}; 55 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{}; 56 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false; 57 bool has_user_calibration = false;
55 calibration = {}; 58 calibration = {};
56 59
57 if (result == DriverResult::Success) { 60 if (result == Common::Input::DriverResult::Success) {
58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration); 61 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
59 } 62 }
60 63
61 // Read User defined calibration 64 // Read User defined calibration
62 if (result == DriverResult::Success && has_user_calibration) { 65 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration); 66 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
64 } 67 }
65 68
66 // Read Factory calibration 69 // Read Factory calibration
67 if (result == DriverResult::Success && !has_user_calibration) { 70 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration); 71 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
69 } 72 }
70 73
71 if (result == DriverResult::Success) { 74 if (result == Common::Input::DriverResult::Success) {
72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); 75 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); 76 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); 77 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
@@ -83,28 +86,28 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio
83 return result; 86 return result;
84} 87}
85 88
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { 89Common::Input::DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this); 90 ScopedSetBlocking sb(this);
88 DriverResult result{DriverResult::Success}; 91 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
89 ImuSpiCalibration spi_calibration{}; 92 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false; 93 bool has_user_calibration = false;
91 calibration = {}; 94 calibration = {};
92 95
93 if (result == DriverResult::Success) { 96 if (result == Common::Input::DriverResult::Success) {
94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration); 97 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
95 } 98 }
96 99
97 // Read User defined calibration 100 // Read User defined calibration
98 if (result == DriverResult::Success && has_user_calibration) { 101 if (result == Common::Input::DriverResult::Success && has_user_calibration) {
99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration); 102 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 } 103 }
101 104
102 // Read Factory calibration 105 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) { 106 if (result == Common::Input::DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration); 107 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
105 } 108 }
106 109
107 if (result == DriverResult::Success) { 110 if (result == Common::Input::DriverResult::Success) {
108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0]; 111 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1]; 112 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2]; 113 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
@@ -127,8 +130,8 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati
127 return result; 130 return result;
128} 131}
129 132
130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, 133Common::Input::DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
131 s16 current_value) { 134 s16 current_value) {
132 constexpr s16 DefaultRingRange{800}; 135 constexpr s16 DefaultRingRange{800};
133 136
134 // TODO: Get default calibration form ring itself 137 // TODO: Get default calibration form ring itself
@@ -144,15 +147,15 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio
144 .max_value = ring_data_max, 147 .max_value = ring_data_max,
145 .min_value = ring_data_min, 148 .min_value = ring_data_min,
146 }; 149 };
147 return DriverResult::Success; 150 return Common::Input::DriverResult::Success;
148} 151}
149 152
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address, 153Common::Input::DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) { 154 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{}; 155 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)}; 156 const Common::Input::DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false; 157 has_user_calibration = false;
155 if (result == DriverResult::Success) { 158 if (result == Common::Input::DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 && 159 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1; 160 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 } 161 }
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
index c6fd0f729..82d94b366 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.h
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -12,8 +12,11 @@
12 12
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14 14
15namespace InputCommon::Joycon { 15namespace Common::Input {
16enum class DriverResult; 16enum class DriverResult;
17}
18
19namespace InputCommon::Joycon {
17struct JoyStickCalibration; 20struct JoyStickCalibration;
18struct IMUCalibration; 21struct IMUCalibration;
19struct JoyconHandle; 22struct JoyconHandle;
@@ -31,30 +34,30 @@ public:
31 * @param is_factory_calibration if true factory values will be returned 34 * @param is_factory_calibration if true factory values will be returned
32 * @returns JoyStickCalibration of the left joystick 35 * @returns JoyStickCalibration of the left joystick
33 */ 36 */
34 DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration); 37 Common::Input::DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
35 38
36 /** 39 /**
37 * Sends a request to obtain the right stick calibration from memory 40 * Sends a request to obtain the right stick calibration from memory
38 * @param is_factory_calibration if true factory values will be returned 41 * @param is_factory_calibration if true factory values will be returned
39 * @returns JoyStickCalibration of the right joystick 42 * @returns JoyStickCalibration of the right joystick
40 */ 43 */
41 DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration); 44 Common::Input::DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
42 45
43 /** 46 /**
44 * Sends a request to obtain the motion calibration from memory 47 * Sends a request to obtain the motion calibration from memory
45 * @returns ImuCalibration of the motion sensor 48 * @returns ImuCalibration of the motion sensor
46 */ 49 */
47 DriverResult GetImuCalibration(MotionCalibration& calibration); 50 Common::Input::DriverResult GetImuCalibration(MotionCalibration& calibration);
48 51
49 /** 52 /**
50 * Calculates on run time the proper calibration of the ring controller 53 * Calculates on run time the proper calibration of the ring controller
51 * @returns RingCalibration of the ring sensor 54 * @returns RingCalibration of the ring sensor
52 */ 55 */
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); 56 Common::Input::DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54 57
55private: 58private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration 59 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration); 60 Common::Input::DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58 61
59 /// Converts a raw calibration block to an u16 value containing the x axis value 62 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const; 63 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 88f4cec1c..a6eecf980 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/common_protocol.h" 6#include "input_common/helpers/joycon_protocol/common_protocol.h"
6 7
@@ -21,10 +22,10 @@ void JoyconCommonProtocol::SetNonBlocking() {
21 SDL_hid_set_nonblocking(hidapi_handle->handle, 1); 22 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
22} 23}
23 24
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { 25Common::Input::DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type); 26 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26 27
27 if (result == DriverResult::Success) { 28 if (result == Common::Input::DriverResult::Success) {
28 // Fallback to 3rd party pro controllers 29 // Fallback to 3rd party pro controllers
29 if (controller_type == ControllerType::None) { 30 if (controller_type == ControllerType::None) {
30 controller_type = ControllerType::Pro; 31 controller_type = ControllerType::Pro;
@@ -34,12 +35,13 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type
34 return result; 35 return result;
35} 36}
36 37
37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { 38Common::Input::DriverResult JoyconCommonProtocol::CheckDeviceAccess(
39 SDL_hid_device_info* device_info) {
38 ControllerType controller_type{ControllerType::None}; 40 ControllerType controller_type{ControllerType::None};
39 const auto result = GetDeviceType(controller_type); 41 const auto result = GetDeviceType(controller_type);
40 42
41 if (result != DriverResult::Success || controller_type == ControllerType::None) { 43 if (result != Common::Input::DriverResult::Success || controller_type == ControllerType::None) {
42 return DriverResult::UnsupportedControllerType; 44 return Common::Input::DriverResult::UnsupportedControllerType;
43 } 45 }
44 46
45 hidapi_handle->handle = 47 hidapi_handle->handle =
@@ -48,32 +50,32 @@ DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device
48 if (!hidapi_handle->handle) { 50 if (!hidapi_handle->handle) {
49 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", 51 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
50 device_info->vendor_id, device_info->product_id); 52 device_info->vendor_id, device_info->product_id);
51 return DriverResult::HandleInUse; 53 return Common::Input::DriverResult::HandleInUse;
52 } 54 }
53 55
54 SetNonBlocking(); 56 SetNonBlocking();
55 return DriverResult::Success; 57 return Common::Input::DriverResult::Success;
56} 58}
57 59
58DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) { 60Common::Input::DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
59 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)}; 61 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); 62 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
61} 63}
62 64
63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) { 65Common::Input::DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); 66 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
65 67
66 if (result == -1) { 68 if (result == -1) {
67 return DriverResult::ErrorWritingData; 69 return Common::Input::DriverResult::ErrorWritingData;
68 } 70 }
69 71
70 return DriverResult::Success; 72 return Common::Input::DriverResult::Success;
71} 73}
72 74
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, 75Common::Input::DriverResult JoyconCommonProtocol::GetSubCommandResponse(
74 SubCommandResponse& output) { 76 SubCommand sc, SubCommandResponse& output) {
75 constexpr int timeout_mili = 66; 77 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 3; 78 constexpr int MaxTries = 10;
77 int tries = 0; 79 int tries = 0;
78 80
79 do { 81 do {
@@ -84,16 +86,17 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
84 LOG_ERROR(Input, "No response from joycon"); 86 LOG_ERROR(Input, "No response from joycon");
85 } 87 }
86 if (tries++ > MaxTries) { 88 if (tries++ > MaxTries) {
87 return DriverResult::Timeout; 89 return Common::Input::DriverResult::Timeout;
88 } 90 }
89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY && 91 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
90 output.sub_command != sc); 92 output.sub_command != sc);
91 93
92 return DriverResult::Success; 94 return Common::Input::DriverResult::Success;
93} 95}
94 96
95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, 97Common::Input::DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc,
96 SubCommandResponse& output) { 98 std::span<const u8> buffer,
99 SubCommandResponse& output) {
97 SubCommandPacket packet{ 100 SubCommandPacket packet{
98 .output_report = OutputReport::RUMBLE_AND_SUBCMD, 101 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
99 .packet_counter = GetCounter(), 102 .packet_counter = GetCounter(),
@@ -102,26 +105,28 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
102 }; 105 };
103 106
104 if (buffer.size() > packet.command_data.size()) { 107 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters; 108 return Common::Input::DriverResult::InvalidParameters;
106 } 109 }
107 110
108 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 111 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109 112
110 auto result = SendData(packet); 113 auto result = SendData(packet);
111 114
112 if (result != DriverResult::Success) { 115 if (result != Common::Input::DriverResult::Success) {
113 return result; 116 return result;
114 } 117 }
115 118
116 return GetSubCommandResponse(sc, output); 119 return GetSubCommandResponse(sc, output);
117} 120}
118 121
119DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 122Common::Input::DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc,
123 std::span<const u8> buffer) {
120 SubCommandResponse output{}; 124 SubCommandResponse output{};
121 return SendSubCommand(sc, buffer, output); 125 return SendSubCommand(sc, buffer, output);
122} 126}
123 127
124DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { 128Common::Input::DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc,
129 std::span<const u8> buffer) {
125 SubCommandPacket packet{ 130 SubCommandPacket packet{
126 .output_report = OutputReport::MCU_DATA, 131 .output_report = OutputReport::MCU_DATA,
127 .packet_counter = GetCounter(), 132 .packet_counter = GetCounter(),
@@ -130,7 +135,7 @@ DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const
130 }; 135 };
131 136
132 if (buffer.size() > packet.command_data.size()) { 137 if (buffer.size() > packet.command_data.size()) {
133 return DriverResult::InvalidParameters; 138 return Common::Input::DriverResult::InvalidParameters;
134 } 139 }
135 140
136 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 141 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
@@ -138,7 +143,7 @@ DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const
138 return SendData(packet); 143 return SendData(packet);
139} 144}
140 145
141DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { 146Common::Input::DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
142 VibrationPacket packet{ 147 VibrationPacket packet{
143 .output_report = OutputReport::RUMBLE_ONLY, 148 .output_report = OutputReport::RUMBLE_ONLY,
144 .packet_counter = GetCounter(), 149 .packet_counter = GetCounter(),
@@ -146,7 +151,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
146 }; 151 };
147 152
148 if (buffer.size() > packet.vibration_data.size()) { 153 if (buffer.size() > packet.vibration_data.size()) {
149 return DriverResult::InvalidParameters; 154 return Common::Input::DriverResult::InvalidParameters;
150 } 155 }
151 156
152 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size()); 157 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
@@ -154,7 +159,8 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
154 return SendData(packet); 159 return SendData(packet);
155} 160}
156 161
157DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { 162Common::Input::DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr,
163 std::span<u8> output) {
158 constexpr std::size_t HeaderSize = 5; 164 constexpr std::size_t HeaderSize = 5;
159 constexpr std::size_t MaxTries = 5; 165 constexpr std::size_t MaxTries = 5;
160 std::size_t tries = 0; 166 std::size_t tries = 0;
@@ -168,36 +174,36 @@ DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> out
168 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket)); 174 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
169 do { 175 do {
170 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response); 176 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
171 if (result != DriverResult::Success) { 177 if (result != Common::Input::DriverResult::Success) {
172 return result; 178 return result;
173 } 179 }
174 180
175 if (tries++ > MaxTries) { 181 if (tries++ > MaxTries) {
176 return DriverResult::Timeout; 182 return Common::Input::DriverResult::Timeout;
177 } 183 }
178 } while (response.spi_address != addr); 184 } while (response.spi_address != addr);
179 185
180 if (response.command_data.size() < packet_data.size + HeaderSize) { 186 if (response.command_data.size() < packet_data.size + HeaderSize) {
181 return DriverResult::WrongReply; 187 return Common::Input::DriverResult::WrongReply;
182 } 188 }
183 189
184 // Remove header from output 190 // Remove header from output
185 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size); 191 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
186 return DriverResult::Success; 192 return Common::Input::DriverResult::Success;
187} 193}
188 194
189DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { 195Common::Input::DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
190 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)}; 196 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
191 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); 197 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
192 198
193 if (result != DriverResult::Success) { 199 if (result != Common::Input::DriverResult::Success) {
194 LOG_ERROR(Input, "Failed with error {}", result); 200 LOG_ERROR(Input, "Failed with error {}", result);
195 } 201 }
196 202
197 return result; 203 return result;
198} 204}
199 205
200DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) { 206Common::Input::DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
201 LOG_DEBUG(Input, "ConfigureMCU"); 207 LOG_DEBUG(Input, "ConfigureMCU");
202 std::array<u8, sizeof(MCUConfig)> config_buffer; 208 std::array<u8, sizeof(MCUConfig)> config_buffer;
203 memcpy(config_buffer.data(), &config, sizeof(MCUConfig)); 209 memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
@@ -205,15 +211,15 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
205 211
206 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); 212 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
207 213
208 if (result != DriverResult::Success) { 214 if (result != Common::Input::DriverResult::Success) {
209 LOG_ERROR(Input, "Failed with error {}", result); 215 LOG_ERROR(Input, "Failed with error {}", result);
210 } 216 }
211 217
212 return result; 218 return result;
213} 219}
214 220
215DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode, 221Common::Input::DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
216 MCUCommandResponse& output) { 222 MCUCommandResponse& output) {
217 constexpr int TimeoutMili = 200; 223 constexpr int TimeoutMili = 200;
218 constexpr int MaxTries = 9; 224 constexpr int MaxTries = 9;
219 int tries = 0; 225 int tries = 0;
@@ -226,17 +232,18 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
226 LOG_ERROR(Input, "No response from joycon attempt {}", tries); 232 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
227 } 233 }
228 if (tries++ > MaxTries) { 234 if (tries++ > MaxTries) {
229 return DriverResult::Timeout; 235 return Common::Input::DriverResult::Timeout;
230 } 236 }
231 } while (output.input_report.report_mode != report_mode || 237 } while (output.input_report.report_mode != report_mode ||
232 output.mcu_report == MCUReport::EmptyAwaitingCmd); 238 output.mcu_report == MCUReport::EmptyAwaitingCmd);
233 239
234 return DriverResult::Success; 240 return Common::Input::DriverResult::Success;
235} 241}
236 242
237DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCommand sc, 243Common::Input::DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode,
238 std::span<const u8> buffer, 244 MCUSubCommand sc,
239 MCUCommandResponse& output) { 245 std::span<const u8> buffer,
246 MCUCommandResponse& output) {
240 SubCommandPacket packet{ 247 SubCommandPacket packet{
241 .output_report = OutputReport::MCU_DATA, 248 .output_report = OutputReport::MCU_DATA,
242 .packet_counter = GetCounter(), 249 .packet_counter = GetCounter(),
@@ -245,23 +252,24 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCom
245 }; 252 };
246 253
247 if (buffer.size() > packet.command_data.size()) { 254 if (buffer.size() > packet.command_data.size()) {
248 return DriverResult::InvalidParameters; 255 return Common::Input::DriverResult::InvalidParameters;
249 } 256 }
250 257
251 memcpy(packet.command_data.data(), buffer.data(), buffer.size()); 258 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
252 259
253 auto result = SendData(packet); 260 auto result = SendData(packet);
254 261
255 if (result != DriverResult::Success) { 262 if (result != Common::Input::DriverResult::Success) {
256 return result; 263 return result;
257 } 264 }
258 265
259 result = GetMCUDataResponse(report_mode, output); 266 result = GetMCUDataResponse(report_mode, output);
260 267
261 return DriverResult::Success; 268 return Common::Input::DriverResult::Success;
262} 269}
263 270
264DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { 271Common::Input::DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode,
272 MCUMode mode) {
265 MCUCommandResponse output{}; 273 MCUCommandResponse output{};
266 constexpr std::size_t MaxTries{16}; 274 constexpr std::size_t MaxTries{16};
267 std::size_t tries{}; 275 std::size_t tries{};
@@ -269,17 +277,17 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
269 do { 277 do {
270 const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output); 278 const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output);
271 279
272 if (result != DriverResult::Success) { 280 if (result != Common::Input::DriverResult::Success) {
273 return result; 281 return result;
274 } 282 }
275 283
276 if (tries++ > MaxTries) { 284 if (tries++ > MaxTries) {
277 return DriverResult::WrongReply; 285 return Common::Input::DriverResult::WrongReply;
278 } 286 }
279 } while (output.mcu_report != MCUReport::StateReport || 287 } while (output.mcu_report != MCUReport::StateReport ||
280 output.mcu_data[6] != static_cast<u8>(mode)); 288 output.mcu_data[6] != static_cast<u8>(mode));
281 289
282 return DriverResult::Success; 290 return Common::Input::DriverResult::Success;
283} 291}
284 292
285// crc-8-ccitt / polynomial 0x07 look up table 293// crc-8-ccitt / polynomial 0x07 look up table
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 411ec018a..dd667ca2b 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -38,30 +38,30 @@ public:
38 * Sends a request to obtain the joycon type from device 38 * Sends a request to obtain the joycon type from device
39 * @returns controller type of the joycon 39 * @returns controller type of the joycon
40 */ 40 */
41 DriverResult GetDeviceType(ControllerType& controller_type); 41 Common::Input::DriverResult GetDeviceType(ControllerType& controller_type);
42 42
43 /** 43 /**
44 * Verifies and sets the joycon_handle if device is valid 44 * Verifies and sets the joycon_handle if device is valid
45 * @param device info from the driver 45 * @param device info from the driver
46 * @returns success if the device is valid 46 * @returns success if the device is valid
47 */ 47 */
48 DriverResult CheckDeviceAccess(SDL_hid_device_info* device); 48 Common::Input::DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
49 49
50 /** 50 /**
51 * Sends a request to set the polling mode of the joycon 51 * Sends a request to set the polling mode of the joycon
52 * @param report_mode polling mode to be set 52 * @param report_mode polling mode to be set
53 */ 53 */
54 DriverResult SetReportMode(Joycon::ReportMode report_mode); 54 Common::Input::DriverResult SetReportMode(Joycon::ReportMode report_mode);
55 55
56 /** 56 /**
57 * Sends data to the joycon device 57 * Sends data to the joycon device
58 * @param buffer data to be send 58 * @param buffer data to be send
59 */ 59 */
60 DriverResult SendRawData(std::span<const u8> buffer); 60 Common::Input::DriverResult SendRawData(std::span<const u8> buffer);
61 61
62 template <typename Output> 62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output> 63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) { 64 Common::Input::DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer; 65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output)); 66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer); 67 return SendRawData(buffer);
@@ -72,7 +72,8 @@ public:
72 * @param sub_command type of data to be returned 72 * @param sub_command type of data to be returned
73 * @returns a buffer containing the response 73 * @returns a buffer containing the response
74 */ 74 */
75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output); 75 Common::Input::DriverResult GetSubCommandResponse(SubCommand sub_command,
76 SubCommandResponse& output);
76 77
77 /** 78 /**
78 * Sends a sub command to the device and waits for it's reply 79 * Sends a sub command to the device and waits for it's reply
@@ -80,35 +81,35 @@ public:
80 * @param buffer data to be send 81 * @param buffer data to be send
81 * @returns output buffer containing the response 82 * @returns output buffer containing the response
82 */ 83 */
83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, 84 Common::Input::DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output); 85 SubCommandResponse& output);
85 86
86 /** 87 /**
87 * Sends a sub command to the device and waits for it's reply and ignores the output 88 * Sends a sub command to the device and waits for it's reply and ignores the output
88 * @param sc sub command to be send 89 * @param sc sub command to be send
89 * @param buffer data to be send 90 * @param buffer data to be send
90 */ 91 */
91 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer); 92 Common::Input::DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
92 93
93 /** 94 /**
94 * Sends a mcu command to the device 95 * Sends a mcu command to the device
95 * @param sc sub command to be send 96 * @param sc sub command to be send
96 * @param buffer data to be send 97 * @param buffer data to be send
97 */ 98 */
98 DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer); 99 Common::Input::DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
99 100
100 /** 101 /**
101 * Sends vibration data to the joycon 102 * Sends vibration data to the joycon
102 * @param buffer data to be send 103 * @param buffer data to be send
103 */ 104 */
104 DriverResult SendVibrationReport(std::span<const u8> buffer); 105 Common::Input::DriverResult SendVibrationReport(std::span<const u8> buffer);
105 106
106 /** 107 /**
107 * Reads the SPI memory stored on the joycon 108 * Reads the SPI memory stored on the joycon
108 * @param Initial address location 109 * @param Initial address location
109 * @returns output buffer containing the response 110 * @returns output buffer containing the response
110 */ 111 */
111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output); 112 Common::Input::DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112 113
113 /** 114 /**
114 * Reads the SPI memory stored on the joycon 115 * Reads the SPI memory stored on the joycon
@@ -117,37 +118,38 @@ public:
117 */ 118 */
118 template <typename Output> 119 template <typename Output>
119 requires std::is_trivially_copyable_v<Output> 120 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) { 121 Common::Input::DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer; 122 std::array<u8, sizeof(Output)> buffer;
122 output = {}; 123 output = {};
123 124
124 const auto result = ReadRawSPI(addr, buffer); 125 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) { 126 if (result != Common::Input::DriverResult::Success) {
126 return result; 127 return result;
127 } 128 }
128 129
129 std::memcpy(&output, buffer.data(), sizeof(Output)); 130 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success; 131 return Common::Input::DriverResult::Success;
131 } 132 }
132 133
133 /** 134 /**
134 * Enables MCU chip on the joycon 135 * Enables MCU chip on the joycon
135 * @param enable if true the chip will be enabled 136 * @param enable if true the chip will be enabled
136 */ 137 */
137 DriverResult EnableMCU(bool enable); 138 Common::Input::DriverResult EnableMCU(bool enable);
138 139
139 /** 140 /**
140 * Configures the MCU to the corresponding mode 141 * Configures the MCU to the corresponding mode
141 * @param MCUConfig configuration 142 * @param MCUConfig configuration
142 */ 143 */
143 DriverResult ConfigureMCU(const MCUConfig& config); 144 Common::Input::DriverResult ConfigureMCU(const MCUConfig& config);
144 145
145 /** 146 /**
146 * Waits until there's MCU data available. On timeout returns error 147 * Waits until there's MCU data available. On timeout returns error
147 * @param report mode of the expected reply 148 * @param report mode of the expected reply
148 * @returns a buffer containing the response 149 * @returns a buffer containing the response
149 */ 150 */
150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output); 151 Common::Input::DriverResult GetMCUDataResponse(ReportMode report_mode_,
152 MCUCommandResponse& output);
151 153
152 /** 154 /**
153 * Sends data to the MCU chip and waits for it's reply 155 * Sends data to the MCU chip and waits for it's reply
@@ -156,15 +158,15 @@ public:
156 * @param buffer data to be send 158 * @param buffer data to be send
157 * @returns output buffer containing the response 159 * @returns output buffer containing the response
158 */ 160 */
159 DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc, std::span<const u8> buffer, 161 Common::Input::DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc,
160 MCUCommandResponse& output); 162 std::span<const u8> buffer, MCUCommandResponse& output);
161 163
162 /** 164 /**
163 * Wait's until the MCU chip is on the specified mode 165 * Wait's until the MCU chip is on the specified mode
164 * @param report mode of the expected reply 166 * @param report mode of the expected reply
165 * @param MCUMode configuration 167 * @param MCUMode configuration
166 */ 168 */
167 DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode); 169 Common::Input::DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
168 170
169 /** 171 /**
170 * Calculates the checksum from the MCU data 172 * Calculates the checksum from the MCU data
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 548a4b9e3..e9a056448 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/generic_functions.h" 6#include "input_common/helpers/joycon_protocol/generic_functions.h"
6 7
@@ -9,73 +10,74 @@ namespace InputCommon::Joycon {
9GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) 10GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
11 12
12DriverResult GenericProtocol::EnablePassiveMode() { 13Common::Input::DriverResult GenericProtocol::EnablePassiveMode() {
13 ScopedSetBlocking sb(this); 14 ScopedSetBlocking sb(this);
14 return SetReportMode(ReportMode::SIMPLE_HID_MODE); 15 return SetReportMode(ReportMode::SIMPLE_HID_MODE);
15} 16}
16 17
17DriverResult GenericProtocol::EnableActiveMode() { 18Common::Input::DriverResult GenericProtocol::EnableActiveMode() {
18 ScopedSetBlocking sb(this); 19 ScopedSetBlocking sb(this);
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ); 20 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20} 21}
21 22
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) { 23Common::Input::DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this); 24 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; 25 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer); 26 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26} 27}
27 28
28DriverResult GenericProtocol::TriggersElapsed() { 29Common::Input::DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this); 30 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {}); 31 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31} 32}
32 33
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { 34Common::Input::DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this); 35 ScopedSetBlocking sb(this);
35 SubCommandResponse output{}; 36 SubCommandResponse output{};
36 37
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); 38 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38 39
39 device_info = {}; 40 device_info = {};
40 if (result == DriverResult::Success) { 41 if (result == Common::Input::DriverResult::Success) {
41 device_info = output.device_info; 42 device_info = output.device_info;
42 } 43 }
43 44
44 return result; 45 return result;
45} 46}
46 47
47DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) { 48Common::Input::DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
48 return GetDeviceType(controller_type); 49 return GetDeviceType(controller_type);
49} 50}
50 51
51DriverResult GenericProtocol::EnableImu(bool enable) { 52Common::Input::DriverResult GenericProtocol::EnableImu(bool enable) {
52 ScopedSetBlocking sb(this); 53 ScopedSetBlocking sb(this);
53 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; 54 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
54 return SendSubCommand(SubCommand::ENABLE_IMU, buffer); 55 return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
55} 56}
56 57
57DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, 58Common::Input::DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen,
58 AccelerometerSensitivity asen, 59 GyroPerformance gfrec,
59 AccelerometerPerformance afrec) { 60 AccelerometerSensitivity asen,
61 AccelerometerPerformance afrec) {
60 ScopedSetBlocking sb(this); 62 ScopedSetBlocking sb(this);
61 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), 63 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
62 static_cast<u8>(gfrec), static_cast<u8>(afrec)}; 64 static_cast<u8>(gfrec), static_cast<u8>(afrec)};
63 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer); 65 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
64} 66}
65 67
66DriverResult GenericProtocol::GetBattery(u32& battery_level) { 68Common::Input::DriverResult GenericProtocol::GetBattery(u32& battery_level) {
67 // This function is meant to request the high resolution battery status 69 // This function is meant to request the high resolution battery status
68 battery_level = 0; 70 battery_level = 0;
69 return DriverResult::NotSupported; 71 return Common::Input::DriverResult::NotSupported;
70} 72}
71 73
72DriverResult GenericProtocol::GetColor(Color& color) { 74Common::Input::DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this); 75 ScopedSetBlocking sb(this);
74 std::array<u8, 12> buffer{}; 76 std::array<u8, 12> buffer{};
75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer); 77 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76 78
77 color = {}; 79 color = {};
78 if (result == DriverResult::Success) { 80 if (result == Common::Input::DriverResult::Success) {
79 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]); 81 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]);
80 color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]); 82 color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
81 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]); 83 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
@@ -85,26 +87,26 @@ DriverResult GenericProtocol::GetColor(Color& color) {
85 return result; 87 return result;
86} 88}
87 89
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { 90Common::Input::DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this); 91 ScopedSetBlocking sb(this);
90 std::array<u8, 16> buffer{}; 92 std::array<u8, 16> buffer{};
91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer); 93 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92 94
93 serial_number = {}; 95 serial_number = {};
94 if (result == DriverResult::Success) { 96 if (result == Common::Input::DriverResult::Success) {
95 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber)); 97 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
96 } 98 }
97 99
98 return result; 100 return result;
99} 101}
100 102
101DriverResult GenericProtocol::GetTemperature(u32& temperature) { 103Common::Input::DriverResult GenericProtocol::GetTemperature(u32& temperature) {
102 // Not all devices have temperature sensor 104 // Not all devices have temperature sensor
103 temperature = 25; 105 temperature = 25;
104 return DriverResult::NotSupported; 106 return Common::Input::DriverResult::NotSupported;
105} 107}
106 108
107DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { 109Common::Input::DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
108 DeviceInfo device_info{}; 110 DeviceInfo device_info{};
109 111
110 const auto result = GetDeviceInfo(device_info); 112 const auto result = GetDeviceInfo(device_info);
@@ -113,23 +115,23 @@ DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
113 return result; 115 return result;
114} 116}
115 117
116DriverResult GenericProtocol::SetHomeLight() { 118Common::Input::DriverResult GenericProtocol::SetHomeLight() {
117 ScopedSetBlocking sb(this); 119 ScopedSetBlocking sb(this);
118 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00}; 120 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
119 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer); 121 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
120} 122}
121 123
122DriverResult GenericProtocol::SetLedBusy() { 124Common::Input::DriverResult GenericProtocol::SetLedBusy() {
123 return DriverResult::NotSupported; 125 return Common::Input::DriverResult::NotSupported;
124} 126}
125 127
126DriverResult GenericProtocol::SetLedPattern(u8 leds) { 128Common::Input::DriverResult GenericProtocol::SetLedPattern(u8 leds) {
127 ScopedSetBlocking sb(this); 129 ScopedSetBlocking sb(this);
128 const std::array<u8, 1> buffer{leds}; 130 const std::array<u8, 1> buffer{leds};
129 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer); 131 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
130} 132}
131 133
132DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) { 134Common::Input::DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
133 return SetLedPattern(static_cast<u8>(leds << 4)); 135 return SetLedPattern(static_cast<u8>(leds << 4));
134} 136}
135 137
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
index 424831e81..90fcd17f6 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.h
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -11,6 +11,10 @@
11#include "input_common/helpers/joycon_protocol/common_protocol.h" 11#include "input_common/helpers/joycon_protocol/common_protocol.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h" 12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13 13
14namespace Common::Input {
15enum class DriverResult;
16}
17
14namespace InputCommon::Joycon { 18namespace InputCommon::Joycon {
15 19
16/// Joycon driver functions that easily implemented 20/// Joycon driver functions that easily implemented
@@ -20,34 +24,34 @@ public:
20 24
21 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital 25 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
22 /// data instead of analog. Motion will be disabled 26 /// data instead of analog. Motion will be disabled
23 DriverResult EnablePassiveMode(); 27 Common::Input::DriverResult EnablePassiveMode();
24 28
25 /// Enables active mode. This mode will return the current status every 5-15ms 29 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode(); 30 Common::Input::DriverResult EnableActiveMode();
27 31
28 /// Enables or disables the low power mode 32 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable); 33 Common::Input::DriverResult SetLowPowerMode(bool enable);
30 34
31 /// Unknown function used by the switch 35 /// Unknown function used by the switch
32 DriverResult TriggersElapsed(); 36 Common::Input::DriverResult TriggersElapsed();
33 37
34 /** 38 /**
35 * Sends a request to obtain the joycon firmware and mac from handle 39 * Sends a request to obtain the joycon firmware and mac from handle
36 * @returns controller device info 40 * @returns controller device info
37 */ 41 */
38 DriverResult GetDeviceInfo(DeviceInfo& controller_type); 42 Common::Input::DriverResult GetDeviceInfo(DeviceInfo& controller_type);
39 43
40 /** 44 /**
41 * Sends a request to obtain the joycon type from handle 45 * Sends a request to obtain the joycon type from handle
42 * @returns controller type of the joycon 46 * @returns controller type of the joycon
43 */ 47 */
44 DriverResult GetControllerType(ControllerType& controller_type); 48 Common::Input::DriverResult GetControllerType(ControllerType& controller_type);
45 49
46 /** 50 /**
47 * Enables motion input 51 * Enables motion input
48 * @param enable if true motion data will be enabled 52 * @param enable if true motion data will be enabled
49 */ 53 */
50 DriverResult EnableImu(bool enable); 54 Common::Input::DriverResult EnableImu(bool enable);
51 55
52 /** 56 /**
53 * Configures the motion sensor with the specified parameters 57 * Configures the motion sensor with the specified parameters
@@ -56,59 +60,60 @@ public:
56 * @param asen accelerometer sensitivity in G force 60 * @param asen accelerometer sensitivity in G force
57 * @param afrec accelerometer frequency in hertz 61 * @param afrec accelerometer frequency in hertz
58 */ 62 */
59 DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, 63 Common::Input::DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
60 AccelerometerSensitivity asen, AccelerometerPerformance afrec); 64 AccelerometerSensitivity asen,
65 AccelerometerPerformance afrec);
61 66
62 /** 67 /**
63 * Request battery level from the device 68 * Request battery level from the device
64 * @returns battery level 69 * @returns battery level
65 */ 70 */
66 DriverResult GetBattery(u32& battery_level); 71 Common::Input::DriverResult GetBattery(u32& battery_level);
67 72
68 /** 73 /**
69 * Request joycon colors from the device 74 * Request joycon colors from the device
70 * @returns colors of the body and buttons 75 * @returns colors of the body and buttons
71 */ 76 */
72 DriverResult GetColor(Color& color); 77 Common::Input::DriverResult GetColor(Color& color);
73 78
74 /** 79 /**
75 * Request joycon serial number from the device 80 * Request joycon serial number from the device
76 * @returns 16 byte serial number 81 * @returns 16 byte serial number
77 */ 82 */
78 DriverResult GetSerialNumber(SerialNumber& serial_number); 83 Common::Input::DriverResult GetSerialNumber(SerialNumber& serial_number);
79 84
80 /** 85 /**
81 * Request joycon serial number from the device 86 * Request joycon serial number from the device
82 * @returns 16 byte serial number 87 * @returns 16 byte serial number
83 */ 88 */
84 DriverResult GetTemperature(u32& temperature); 89 Common::Input::DriverResult GetTemperature(u32& temperature);
85 90
86 /** 91 /**
87 * Request joycon serial number from the device 92 * Request joycon serial number from the device
88 * @returns 16 byte serial number 93 * @returns 16 byte serial number
89 */ 94 */
90 DriverResult GetVersionNumber(FirmwareVersion& version); 95 Common::Input::DriverResult GetVersionNumber(FirmwareVersion& version);
91 96
92 /** 97 /**
93 * Sets home led behaviour 98 * Sets home led behaviour
94 */ 99 */
95 DriverResult SetHomeLight(); 100 Common::Input::DriverResult SetHomeLight();
96 101
97 /** 102 /**
98 * Sets home led into a slow breathing state 103 * Sets home led into a slow breathing state
99 */ 104 */
100 DriverResult SetLedBusy(); 105 Common::Input::DriverResult SetLedBusy();
101 106
102 /** 107 /**
103 * Sets the 4 player leds on the joycon on a solid state 108 * Sets the 4 player leds on the joycon on a solid state
104 * @params bit flag containing the led state 109 * @params bit flag containing the led state
105 */ 110 */
106 DriverResult SetLedPattern(u8 leds); 111 Common::Input::DriverResult SetLedPattern(u8 leds);
107 112
108 /** 113 /**
109 * Sets the 4 player leds on the joycon on a blinking state 114 * Sets the 4 player leds on the joycon on a blinking state
110 * @returns bit flag containing the led state 115 * @returns bit flag containing the led state
111 */ 116 */
112 DriverResult SetLedBlinkPattern(u8 leds); 117 Common::Input::DriverResult SetLedBlinkPattern(u8 leds);
113}; 118};
114} // namespace InputCommon::Joycon 119} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
index 731fd5981..68b0589e3 100644
--- a/src/input_common/helpers/joycon_protocol/irs.cpp
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <thread> 4#include "common/input.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h" 6#include "input_common/helpers/joycon_protocol/irs.h"
7 7
@@ -10,21 +10,21 @@ namespace InputCommon::Joycon {
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) 10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
12 12
13DriverResult IrsProtocol::EnableIrs() { 13Common::Input::DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS"); 14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 17
18 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); 19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 } 20 }
21 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
22 result = EnableMCU(true); 22 result = EnableMCU(true);
23 } 23 }
24 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); 25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 } 26 }
27 if (result == DriverResult::Success) { 27 if (result == Common::Input::DriverResult::Success) {
28 const MCUConfig config{ 28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU, 29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode, 30 .sub_command = MCUSubCommand::SetMCUMode,
@@ -34,16 +34,16 @@ DriverResult IrsProtocol::EnableIrs() {
34 34
35 result = ConfigureMCU(config); 35 result = ConfigureMCU(config);
36 } 36 }
37 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR); 38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 } 39 }
40 if (result == DriverResult::Success) { 40 if (result == Common::Input::DriverResult::Success) {
41 result = ConfigureIrs(); 41 result = ConfigureIrs();
42 } 42 }
43 if (result == DriverResult::Success) { 43 if (result == Common::Input::DriverResult::Success) {
44 result = WriteRegistersStep1(); 44 result = WriteRegistersStep1();
45 } 45 }
46 if (result == DriverResult::Success) { 46 if (result == Common::Input::DriverResult::Success) {
47 result = WriteRegistersStep2(); 47 result = WriteRegistersStep2();
48 } 48 }
49 49
@@ -52,12 +52,12 @@ DriverResult IrsProtocol::EnableIrs() {
52 return result; 52 return result;
53} 53}
54 54
55DriverResult IrsProtocol::DisableIrs() { 55Common::Input::DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS"); 56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this); 57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success}; 58 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
59 59
60 if (result == DriverResult::Success) { 60 if (result == Common::Input::DriverResult::Success) {
61 result = EnableMCU(false); 61 result = EnableMCU(false);
62 } 62 }
63 63
@@ -66,7 +66,7 @@ DriverResult IrsProtocol::DisableIrs() {
66 return result; 66 return result;
67} 67}
68 68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) { 69Common::Input::DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode; 70 irs_mode = mode;
71 switch (format) { 71 switch (format) {
72 case IrsResolution::Size320x240: 72 case IrsResolution::Size320x240:
@@ -103,10 +103,10 @@ DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
103 return EnableIrs(); 103 return EnableIrs();
104 } 104 }
105 105
106 return DriverResult::Success; 106 return Common::Input::DriverResult::Success;
107} 107}
108 108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) { 109Common::Input::DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment = 110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1)); 111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112 112
@@ -129,7 +129,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
129 return RequestFrame(packet_fragment); 129 return RequestFrame(packet_fragment);
130} 130}
131 131
132DriverResult IrsProtocol::ConfigureIrs() { 132Common::Input::DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS"); 133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28; 134 constexpr std::size_t max_tries = 28;
135 SubCommandResponse output{}; 135 SubCommandResponse output{};
@@ -152,20 +152,20 @@ DriverResult IrsProtocol::ConfigureIrs() {
152 do { 152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154 154
155 if (result != DriverResult::Success) { 155 if (result != Common::Input::DriverResult::Success) {
156 return result; 156 return result;
157 } 157 }
158 if (tries++ >= max_tries) { 158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply; 159 return Common::Input::DriverResult::WrongReply;
160 } 160 }
161 } while (output.command_data[0] != 0x0b); 161 } while (output.command_data[0] != 0x0b);
162 162
163 return DriverResult::Success; 163 return Common::Input::DriverResult::Success;
164} 164}
165 165
166DriverResult IrsProtocol::WriteRegistersStep1() { 166Common::Input::DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1"); 167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success}; 168 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
169 constexpr std::size_t max_tries = 28; 169 constexpr std::size_t max_tries = 28;
170 SubCommandResponse output{}; 170 SubCommandResponse output{};
171 std::size_t tries = 0; 171 std::size_t tries = 0;
@@ -197,7 +197,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); 197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF; 198 mcu_request[37] = 0xFF;
199 199
200 if (result != DriverResult::Success) { 200 if (result != Common::Input::DriverResult::Success) {
201 return result; 201 return result;
202 } 202 }
203 203
@@ -205,26 +205,26 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206 206
207 // First time we need to set the report mode 207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) { 208 if (result == Common::Input::DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); 209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 } 210 }
211 if (result == DriverResult::Success && tries == 0) { 211 if (result == Common::Input::DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); 212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 } 213 }
214 214
215 if (result != DriverResult::Success) { 215 if (result != Common::Input::DriverResult::Success) {
216 return result; 216 return result;
217 } 217 }
218 if (tries++ >= max_tries) { 218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply; 219 return Common::Input::DriverResult::WrongReply;
220 } 220 }
221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) && 221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23); 222 output.command_data[0] != 0x23);
223 223
224 return DriverResult::Success; 224 return Common::Input::DriverResult::Success;
225} 225}
226 226
227DriverResult IrsProtocol::WriteRegistersStep2() { 227Common::Input::DriverResult IrsProtocol::WriteRegistersStep2() {
228 LOG_DEBUG(Input, "WriteRegistersStep2"); 228 LOG_DEBUG(Input, "WriteRegistersStep2");
229 constexpr std::size_t max_tries = 28; 229 constexpr std::size_t max_tries = 28;
230 SubCommandResponse output{}; 230 SubCommandResponse output{};
@@ -255,18 +255,18 @@ DriverResult IrsProtocol::WriteRegistersStep2() {
255 do { 255 do {
256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); 256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
257 257
258 if (result != DriverResult::Success) { 258 if (result != Common::Input::DriverResult::Success) {
259 return result; 259 return result;
260 } 260 }
261 if (tries++ >= max_tries) { 261 if (tries++ >= max_tries) {
262 return DriverResult::WrongReply; 262 return Common::Input::DriverResult::WrongReply;
263 } 263 }
264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23); 264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
265 265
266 return DriverResult::Success; 266 return Common::Input::DriverResult::Success;
267} 267}
268 268
269DriverResult IrsProtocol::RequestFrame(u8 frame) { 269Common::Input::DriverResult IrsProtocol::RequestFrame(u8 frame) {
270 std::array<u8, 38> mcu_request{}; 270 std::array<u8, 38> mcu_request{};
271 mcu_request[3] = frame; 271 mcu_request[3] = frame;
272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); 272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
@@ -274,7 +274,7 @@ DriverResult IrsProtocol::RequestFrame(u8 frame) {
274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); 274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
275} 275}
276 276
277DriverResult IrsProtocol::ResendFrame(u8 frame) { 277Common::Input::DriverResult IrsProtocol::ResendFrame(u8 frame) {
278 std::array<u8, 38> mcu_request{}; 278 std::array<u8, 38> mcu_request{};
279 mcu_request[1] = 0x1; 279 mcu_request[1] = 0x1;
280 mcu_request[2] = frame; 280 mcu_request[2] = frame;
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h
index 76dfa02ea..714cbb6b2 100644
--- a/src/input_common/helpers/joycon_protocol/irs.h
+++ b/src/input_common/helpers/joycon_protocol/irs.h
@@ -13,19 +13,23 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class IrsProtocol final : private JoyconCommonProtocol { 22class IrsProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableIrs(); 26 Common::Input::DriverResult EnableIrs();
23 27
24 DriverResult DisableIrs(); 28 Common::Input::DriverResult DisableIrs();
25 29
26 DriverResult SetIrsConfig(IrsMode mode, IrsResolution format); 30 Common::Input::DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
27 31
28 DriverResult RequestImage(std::span<u8> buffer); 32 Common::Input::DriverResult RequestImage(std::span<u8> buffer);
29 33
30 std::vector<u8> GetImage() const; 34 std::vector<u8> GetImage() const;
31 35
@@ -34,13 +38,13 @@ public:
34 bool IsEnabled() const; 38 bool IsEnabled() const;
35 39
36private: 40private:
37 DriverResult ConfigureIrs(); 41 Common::Input::DriverResult ConfigureIrs();
38 42
39 DriverResult WriteRegistersStep1(); 43 Common::Input::DriverResult WriteRegistersStep1();
40 DriverResult WriteRegistersStep2(); 44 Common::Input::DriverResult WriteRegistersStep2();
41 45
42 DriverResult RequestFrame(u8 frame); 46 Common::Input::DriverResult RequestFrame(u8 frame);
43 DriverResult ResendFrame(u8 frame); 47 Common::Input::DriverResult ResendFrame(u8 frame);
44 48
45 IrsMode irs_mode{IrsMode::ImageTransfer}; 49 IrsMode irs_mode{IrsMode::ImageTransfer};
46 IrsResolution resolution{IrsResolution::Size40x30}; 50 IrsResolution resolution{IrsResolution::Size40x30};
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index e0e431156..77a43c67a 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -402,23 +402,6 @@ enum class ExternalDeviceId : u16 {
402 Starlink = 0x2800, 402 Starlink = 0x2800,
403}; 403};
404 404
405enum class DriverResult {
406 Success,
407 WrongReply,
408 Timeout,
409 InvalidParameters,
410 UnsupportedControllerType,
411 HandleInUse,
412 ErrorReadingData,
413 ErrorWritingData,
414 NoDeviceDetected,
415 InvalidHandle,
416 NotSupported,
417 Disabled,
418 Delayed,
419 Unknown,
420};
421
422struct MotionSensorCalibration { 405struct MotionSensorCalibration {
423 s16 offset; 406 s16 offset;
424 s16 scale; 407 s16 scale;
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 261f46255..09953394b 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <thread> 4#include "common/input.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h" 6#include "input_common/helpers/joycon_protocol/nfc.h"
7 7
@@ -10,21 +10,21 @@ namespace InputCommon::Joycon {
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) 10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
12 12
13DriverResult NfcProtocol::EnableNfc() { 13Common::Input::DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC"); 14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
17 17
18 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); 19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 } 20 }
21 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
22 result = EnableMCU(true); 22 result = EnableMCU(true);
23 } 23 }
24 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); 25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 } 26 }
27 if (result == DriverResult::Success) { 27 if (result == Common::Input::DriverResult::Success) {
28 const MCUConfig config{ 28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU, 29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode, 30 .sub_command = MCUSubCommand::SetMCUMode,
@@ -34,32 +34,32 @@ DriverResult NfcProtocol::EnableNfc() {
34 34
35 result = ConfigureMCU(config); 35 result = ConfigureMCU(config);
36 } 36 }
37 if (result == DriverResult::Success) { 37 if (result == Common::Input::DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); 38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
39 } 39 }
40 if (result == DriverResult::Success) { 40 if (result == Common::Input::DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready); 41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 } 42 }
43 if (result == DriverResult::Success) { 43 if (result == Common::Input::DriverResult::Success) {
44 MCUCommandResponse output{}; 44 MCUCommandResponse output{};
45 result = SendStopPollingRequest(output); 45 result = SendStopPollingRequest(output);
46 } 46 }
47 if (result == DriverResult::Success) { 47 if (result == Common::Input::DriverResult::Success) {
48 result = WaitUntilNfcIs(NFCStatus::Ready); 48 result = WaitUntilNfcIs(NFCStatus::Ready);
49 } 49 }
50 if (result == DriverResult::Success) { 50 if (result == Common::Input::DriverResult::Success) {
51 is_enabled = true; 51 is_enabled = true;
52 } 52 }
53 53
54 return result; 54 return result;
55} 55}
56 56
57DriverResult NfcProtocol::DisableNfc() { 57Common::Input::DriverResult NfcProtocol::DisableNfc() {
58 LOG_DEBUG(Input, "Disable NFC"); 58 LOG_DEBUG(Input, "Disable NFC");
59 ScopedSetBlocking sb(this); 59 ScopedSetBlocking sb(this);
60 DriverResult result{DriverResult::Success}; 60 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
61 61
62 if (result == DriverResult::Success) { 62 if (result == Common::Input::DriverResult::Success) {
63 result = EnableMCU(false); 63 result = EnableMCU(false);
64 } 64 }
65 65
@@ -69,60 +69,60 @@ DriverResult NfcProtocol::DisableNfc() {
69 return result; 69 return result;
70} 70}
71 71
72DriverResult NfcProtocol::StartNFCPollingMode() { 72Common::Input::DriverResult NfcProtocol::StartNFCPollingMode() {
73 LOG_DEBUG(Input, "Start NFC polling Mode"); 73 LOG_DEBUG(Input, "Start NFC polling Mode");
74 ScopedSetBlocking sb(this); 74 ScopedSetBlocking sb(this);
75 DriverResult result{DriverResult::Success}; 75 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
76 76
77 if (result == DriverResult::Success) { 77 if (result == Common::Input::DriverResult::Success) {
78 MCUCommandResponse output{}; 78 MCUCommandResponse output{};
79 result = SendStartPollingRequest(output); 79 result = SendStartPollingRequest(output);
80 } 80 }
81 if (result == DriverResult::Success) { 81 if (result == Common::Input::DriverResult::Success) {
82 result = WaitUntilNfcIs(NFCStatus::Polling); 82 result = WaitUntilNfcIs(NFCStatus::Polling);
83 } 83 }
84 if (result == DriverResult::Success) { 84 if (result == Common::Input::DriverResult::Success) {
85 is_polling = true; 85 is_polling = true;
86 } 86 }
87 87
88 return result; 88 return result;
89} 89}
90 90
91DriverResult NfcProtocol::StopNFCPollingMode() { 91Common::Input::DriverResult NfcProtocol::StopNFCPollingMode() {
92 LOG_DEBUG(Input, "Stop NFC polling Mode"); 92 LOG_DEBUG(Input, "Stop NFC polling Mode");
93 ScopedSetBlocking sb(this); 93 ScopedSetBlocking sb(this);
94 DriverResult result{DriverResult::Success}; 94 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
95 95
96 if (result == DriverResult::Success) { 96 if (result == Common::Input::DriverResult::Success) {
97 MCUCommandResponse output{}; 97 MCUCommandResponse output{};
98 result = SendStopPollingRequest(output); 98 result = SendStopPollingRequest(output);
99 } 99 }
100 if (result == DriverResult::Success) { 100 if (result == Common::Input::DriverResult::Success) {
101 result = WaitUntilNfcIs(NFCStatus::WriteReady); 101 result = WaitUntilNfcIs(NFCStatus::WriteReady);
102 } 102 }
103 if (result == DriverResult::Success) { 103 if (result == Common::Input::DriverResult::Success) {
104 is_polling = false; 104 is_polling = false;
105 } 105 }
106 106
107 return result; 107 return result;
108} 108}
109 109
110DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) { 110Common::Input::DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
111 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 111 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
112 return DriverResult::Delayed; 112 return Common::Input::DriverResult::Delayed;
113 } 113 }
114 update_counter = 0; 114 update_counter = 0;
115 115
116 LOG_DEBUG(Input, "Scan for amiibos"); 116 LOG_DEBUG(Input, "Scan for amiibos");
117 ScopedSetBlocking sb(this); 117 ScopedSetBlocking sb(this);
118 DriverResult result{DriverResult::Success}; 118 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
119 TagFoundData tag_data{}; 119 TagFoundData tag_data{};
120 120
121 if (result == DriverResult::Success) { 121 if (result == Common::Input::DriverResult::Success) {
122 result = IsTagInRange(tag_data); 122 result = IsTagInRange(tag_data);
123 } 123 }
124 124
125 if (result == DriverResult::Success) { 125 if (result == Common::Input::DriverResult::Success) {
126 tag_info = { 126 tag_info = {
127 .uuid_length = tag_data.uuid_size, 127 .uuid_length = tag_data.uuid_size,
128 .protocol = 1, 128 .protocol = 1,
@@ -147,59 +147,59 @@ DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
147 return result; 147 return result;
148} 148}
149 149
150DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) { 150Common::Input::DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
151 LOG_DEBUG(Input, "Scan for amiibos"); 151 LOG_DEBUG(Input, "Scan for amiibos");
152 ScopedSetBlocking sb(this); 152 ScopedSetBlocking sb(this);
153 DriverResult result{DriverResult::Success}; 153 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
154 TagFoundData tag_data{}; 154 TagFoundData tag_data{};
155 155
156 if (result == DriverResult::Success) { 156 if (result == Common::Input::DriverResult::Success) {
157 result = IsTagInRange(tag_data, 7); 157 result = IsTagInRange(tag_data, 7);
158 } 158 }
159 159
160 if (result == DriverResult::Success) { 160 if (result == Common::Input::DriverResult::Success) {
161 result = GetAmiiboData(data); 161 result = GetAmiiboData(data);
162 } 162 }
163 163
164 return result; 164 return result;
165} 165}
166 166
167DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) { 167Common::Input::DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
168 LOG_DEBUG(Input, "Write amiibo"); 168 LOG_DEBUG(Input, "Write amiibo");
169 ScopedSetBlocking sb(this); 169 ScopedSetBlocking sb(this);
170 DriverResult result{DriverResult::Success}; 170 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
171 TagUUID tag_uuid = GetTagUUID(data); 171 TagUUID tag_uuid = GetTagUUID(data);
172 TagFoundData tag_data{}; 172 TagFoundData tag_data{};
173 173
174 if (result == DriverResult::Success) { 174 if (result == Common::Input::DriverResult::Success) {
175 result = IsTagInRange(tag_data, 7); 175 result = IsTagInRange(tag_data, 7);
176 } 176 }
177 if (result == DriverResult::Success) { 177 if (result == Common::Input::DriverResult::Success) {
178 if (tag_data.uuid != tag_uuid) { 178 if (tag_data.uuid != tag_uuid) {
179 result = DriverResult::InvalidParameters; 179 result = Common::Input::DriverResult::InvalidParameters;
180 } 180 }
181 } 181 }
182 if (result == DriverResult::Success) { 182 if (result == Common::Input::DriverResult::Success) {
183 MCUCommandResponse output{}; 183 MCUCommandResponse output{};
184 result = SendStopPollingRequest(output); 184 result = SendStopPollingRequest(output);
185 } 185 }
186 if (result == DriverResult::Success) { 186 if (result == Common::Input::DriverResult::Success) {
187 result = WaitUntilNfcIs(NFCStatus::Ready); 187 result = WaitUntilNfcIs(NFCStatus::Ready);
188 } 188 }
189 if (result == DriverResult::Success) { 189 if (result == Common::Input::DriverResult::Success) {
190 MCUCommandResponse output{}; 190 MCUCommandResponse output{};
191 result = SendStartPollingRequest(output, true); 191 result = SendStartPollingRequest(output, true);
192 } 192 }
193 if (result == DriverResult::Success) { 193 if (result == Common::Input::DriverResult::Success) {
194 result = WaitUntilNfcIs(NFCStatus::WriteReady); 194 result = WaitUntilNfcIs(NFCStatus::WriteReady);
195 } 195 }
196 if (result == DriverResult::Success) { 196 if (result == Common::Input::DriverResult::Success) {
197 result = WriteAmiiboData(tag_uuid, data); 197 result = WriteAmiiboData(tag_uuid, data);
198 } 198 }
199 if (result == DriverResult::Success) { 199 if (result == Common::Input::DriverResult::Success) {
200 result = WaitUntilNfcIs(NFCStatus::WriteDone); 200 result = WaitUntilNfcIs(NFCStatus::WriteDone);
201 } 201 }
202 if (result == DriverResult::Success) { 202 if (result == Common::Input::DriverResult::Success) {
203 MCUCommandResponse output{}; 203 MCUCommandResponse output{};
204 result = SendStopPollingRequest(output); 204 result = SendStopPollingRequest(output);
205 } 205 }
@@ -207,64 +207,65 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
207 return result; 207 return result;
208} 208}
209 209
210DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request, 210Common::Input::DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request,
211 std::span<MifareReadData> out_data) { 211 std::span<MifareReadData> out_data) {
212 LOG_DEBUG(Input, "Read mifare"); 212 LOG_DEBUG(Input, "Read mifare");
213 ScopedSetBlocking sb(this); 213 ScopedSetBlocking sb(this);
214 DriverResult result{DriverResult::Success}; 214 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
215 TagFoundData tag_data{}; 215 TagFoundData tag_data{};
216 MifareUUID tag_uuid{}; 216 MifareUUID tag_uuid{};
217 217
218 if (result == DriverResult::Success) { 218 if (result == Common::Input::DriverResult::Success) {
219 result = IsTagInRange(tag_data, 7); 219 result = IsTagInRange(tag_data, 7);
220 } 220 }
221 if (result == DriverResult::Success) { 221 if (result == Common::Input::DriverResult::Success) {
222 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); 222 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
223 result = GetMifareData(tag_uuid, read_request, out_data); 223 result = GetMifareData(tag_uuid, read_request, out_data);
224 } 224 }
225 if (result == DriverResult::Success) { 225 if (result == Common::Input::DriverResult::Success) {
226 MCUCommandResponse output{}; 226 MCUCommandResponse output{};
227 result = SendStopPollingRequest(output); 227 result = SendStopPollingRequest(output);
228 } 228 }
229 if (result == DriverResult::Success) { 229 if (result == Common::Input::DriverResult::Success) {
230 result = WaitUntilNfcIs(NFCStatus::Ready); 230 result = WaitUntilNfcIs(NFCStatus::Ready);
231 } 231 }
232 if (result == DriverResult::Success) { 232 if (result == Common::Input::DriverResult::Success) {
233 MCUCommandResponse output{}; 233 MCUCommandResponse output{};
234 result = SendStartPollingRequest(output, true); 234 result = SendStartPollingRequest(output, true);
235 } 235 }
236 if (result == DriverResult::Success) { 236 if (result == Common::Input::DriverResult::Success) {
237 result = WaitUntilNfcIs(NFCStatus::WriteReady); 237 result = WaitUntilNfcIs(NFCStatus::WriteReady);
238 } 238 }
239 return result; 239 return result;
240} 240}
241 241
242DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) { 242Common::Input::DriverResult NfcProtocol::WriteMifare(
243 std::span<const MifareWriteChunk> write_request) {
243 LOG_DEBUG(Input, "Write mifare"); 244 LOG_DEBUG(Input, "Write mifare");
244 ScopedSetBlocking sb(this); 245 ScopedSetBlocking sb(this);
245 DriverResult result{DriverResult::Success}; 246 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
246 TagFoundData tag_data{}; 247 TagFoundData tag_data{};
247 MifareUUID tag_uuid{}; 248 MifareUUID tag_uuid{};
248 249
249 if (result == DriverResult::Success) { 250 if (result == Common::Input::DriverResult::Success) {
250 result = IsTagInRange(tag_data, 7); 251 result = IsTagInRange(tag_data, 7);
251 } 252 }
252 if (result == DriverResult::Success) { 253 if (result == Common::Input::DriverResult::Success) {
253 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); 254 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
254 result = WriteMifareData(tag_uuid, write_request); 255 result = WriteMifareData(tag_uuid, write_request);
255 } 256 }
256 if (result == DriverResult::Success) { 257 if (result == Common::Input::DriverResult::Success) {
257 MCUCommandResponse output{}; 258 MCUCommandResponse output{};
258 result = SendStopPollingRequest(output); 259 result = SendStopPollingRequest(output);
259 } 260 }
260 if (result == DriverResult::Success) { 261 if (result == Common::Input::DriverResult::Success) {
261 result = WaitUntilNfcIs(NFCStatus::Ready); 262 result = WaitUntilNfcIs(NFCStatus::Ready);
262 } 263 }
263 if (result == DriverResult::Success) { 264 if (result == Common::Input::DriverResult::Success) {
264 MCUCommandResponse output{}; 265 MCUCommandResponse output{};
265 result = SendStartPollingRequest(output, true); 266 result = SendStartPollingRequest(output, true);
266 } 267 }
267 if (result == DriverResult::Success) { 268 if (result == Common::Input::DriverResult::Success) {
268 result = WaitUntilNfcIs(NFCStatus::WriteReady); 269 result = WaitUntilNfcIs(NFCStatus::WriteReady);
269 } 270 }
270 return result; 271 return result;
@@ -277,17 +278,17 @@ bool NfcProtocol::HasAmiibo() {
277 update_counter = 0; 278 update_counter = 0;
278 279
279 ScopedSetBlocking sb(this); 280 ScopedSetBlocking sb(this);
280 DriverResult result{DriverResult::Success}; 281 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
281 TagFoundData tag_data{}; 282 TagFoundData tag_data{};
282 283
283 if (result == DriverResult::Success) { 284 if (result == Common::Input::DriverResult::Success) {
284 result = IsTagInRange(tag_data, 7); 285 result = IsTagInRange(tag_data, 7);
285 } 286 }
286 287
287 return result == DriverResult::Success; 288 return result == Common::Input::DriverResult::Success;
288} 289}
289 290
290DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) { 291Common::Input::DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
291 constexpr std::size_t timeout_limit = 10; 292 constexpr std::size_t timeout_limit = 10;
292 MCUCommandResponse output{}; 293 MCUCommandResponse output{};
293 std::size_t tries = 0; 294 std::size_t tries = 0;
@@ -295,30 +296,31 @@ DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
295 do { 296 do {
296 auto result = SendNextPackageRequest(output, {}); 297 auto result = SendNextPackageRequest(output, {});
297 298
298 if (result != DriverResult::Success) { 299 if (result != Common::Input::DriverResult::Success) {
299 return result; 300 return result;
300 } 301 }
301 if (tries++ > timeout_limit) { 302 if (tries++ > timeout_limit) {
302 return DriverResult::Timeout; 303 return Common::Input::DriverResult::Timeout;
303 } 304 }
304 } while (output.mcu_report != MCUReport::NFCState || 305 } while (output.mcu_report != MCUReport::NFCState ||
305 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 306 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
306 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status)); 307 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
307 308
308 return DriverResult::Success; 309 return Common::Input::DriverResult::Success;
309} 310}
310 311
311DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) { 312Common::Input::DriverResult NfcProtocol::IsTagInRange(TagFoundData& data,
313 std::size_t timeout_limit) {
312 MCUCommandResponse output{}; 314 MCUCommandResponse output{};
313 std::size_t tries = 0; 315 std::size_t tries = 0;
314 316
315 do { 317 do {
316 const auto result = SendNextPackageRequest(output, {}); 318 const auto result = SendNextPackageRequest(output, {});
317 if (result != DriverResult::Success) { 319 if (result != Common::Input::DriverResult::Success) {
318 return result; 320 return result;
319 } 321 }
320 if (tries++ > timeout_limit) { 322 if (tries++ > timeout_limit) {
321 return DriverResult::Timeout; 323 return Common::Input::DriverResult::Timeout;
322 } 324 }
323 } while (output.mcu_report != MCUReport::NFCState || 325 } while (output.mcu_report != MCUReport::NFCState ||
324 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 326 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
@@ -328,10 +330,10 @@ DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_l
328 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID))); 330 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
329 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); 331 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
330 332
331 return DriverResult::Success; 333 return Common::Input::DriverResult::Success;
332} 334}
333 335
334DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { 336Common::Input::DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
335 constexpr std::size_t timeout_limit = 60; 337 constexpr std::size_t timeout_limit = 60;
336 MCUCommandResponse output{}; 338 MCUCommandResponse output{};
337 std::size_t tries = 0; 339 std::size_t tries = 0;
@@ -340,7 +342,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
340 std::size_t ntag_buffer_pos = 0; 342 std::size_t ntag_buffer_pos = 0;
341 auto result = SendReadAmiiboRequest(output, NFCPages::Block135); 343 auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
342 344
343 if (result != DriverResult::Success) { 345 if (result != Common::Input::DriverResult::Success) {
344 return result; 346 return result;
345 } 347 }
346 348
@@ -349,14 +351,14 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
349 result = SendNextPackageRequest(output, package_index); 351 result = SendNextPackageRequest(output, package_index);
350 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 352 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
351 353
352 if (result != DriverResult::Success) { 354 if (result != Common::Input::DriverResult::Success) {
353 return result; 355 return result;
354 } 356 }
355 357
356 if ((output.mcu_report == MCUReport::NFCReadData || 358 if ((output.mcu_report == MCUReport::NFCReadData ||
357 output.mcu_report == MCUReport::NFCState) && 359 output.mcu_report == MCUReport::NFCState) &&
358 nfc_status == NFCStatus::TagLost) { 360 nfc_status == NFCStatus::TagLost) {
359 return DriverResult::ErrorReadingData; 361 return Common::Input::DriverResult::ErrorReadingData;
360 } 362 }
361 363
362 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { 364 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
@@ -375,14 +377,15 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
375 377
376 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 378 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
377 LOG_INFO(Input, "Finished reading amiibo"); 379 LOG_INFO(Input, "Finished reading amiibo");
378 return DriverResult::Success; 380 return Common::Input::DriverResult::Success;
379 } 381 }
380 } 382 }
381 383
382 return DriverResult::Timeout; 384 return Common::Input::DriverResult::Timeout;
383} 385}
384 386
385DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) { 387Common::Input::DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid,
388 std::span<const u8> data) {
386 constexpr std::size_t timeout_limit = 60; 389 constexpr std::size_t timeout_limit = 60;
387 const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data); 390 const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data);
388 const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data); 391 const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data);
@@ -397,7 +400,7 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
397 400
398 auto result = SendWriteAmiiboRequest(output, tag_uuid); 401 auto result = SendWriteAmiiboRequest(output, tag_uuid);
399 402
400 if (result != DriverResult::Success) { 403 if (result != Common::Input::DriverResult::Success) {
401 return result; 404 return result;
402 } 405 }
403 406
@@ -406,14 +409,14 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
406 result = SendNextPackageRequest(output, package_index); 409 result = SendNextPackageRequest(output, package_index);
407 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 410 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
408 411
409 if (result != DriverResult::Success) { 412 if (result != Common::Input::DriverResult::Success) {
410 return result; 413 return result;
411 } 414 }
412 415
413 if ((output.mcu_report == MCUReport::NFCReadData || 416 if ((output.mcu_report == MCUReport::NFCReadData ||
414 output.mcu_report == MCUReport::NFCState) && 417 output.mcu_report == MCUReport::NFCState) &&
415 nfc_status == NFCStatus::TagLost) { 418 nfc_status == NFCStatus::TagLost) {
416 return DriverResult::ErrorReadingData; 419 return Common::Input::DriverResult::ErrorReadingData;
417 } 420 }
418 421
419 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { 422 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
@@ -442,7 +445,7 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
442 if ((output.mcu_report == MCUReport::NFCReadData || 445 if ((output.mcu_report == MCUReport::NFCReadData ||
443 output.mcu_report == MCUReport::NFCState) && 446 output.mcu_report == MCUReport::NFCState) &&
444 nfc_status == NFCStatus::TagLost) { 447 nfc_status == NFCStatus::TagLost) {
445 return DriverResult::ErrorReadingData; 448 return Common::Input::DriverResult::ErrorReadingData;
446 } 449 }
447 450
448 // Increase position when data is confirmed by the joycon 451 // Increase position when data is confirmed by the joycon
@@ -457,14 +460,14 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
457 return result; 460 return result;
458} 461}
459 462
460DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid, 463Common::Input::DriverResult NfcProtocol::GetMifareData(
461 std::span<const MifareReadChunk> read_request, 464 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request,
462 std::span<MifareReadData> out_data) { 465 std::span<MifareReadData> out_data) {
463 constexpr std::size_t timeout_limit = 60; 466 constexpr std::size_t timeout_limit = 60;
464 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request); 467 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
465 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data); 468 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
466 std::span<const u8> buffer(nfc_buffer_data); 469 std::span<const u8> buffer(nfc_buffer_data);
467 DriverResult result = DriverResult::Success; 470 Common::Input::DriverResult result = Common::Input::DriverResult::Success;
468 MCUCommandResponse output{}; 471 MCUCommandResponse output{};
469 u8 block_id = 1; 472 u8 block_id = 1;
470 u8 package_index = 0; 473 u8 package_index = 0;
@@ -486,7 +489,7 @@ DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
486 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 489 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
487 490
488 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { 491 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
489 return DriverResult::ErrorReadingData; 492 return Common::Input::DriverResult::ErrorReadingData;
490 } 493 }
491 494
492 // Increase position when data is confirmed by the joycon 495 // Increase position when data is confirmed by the joycon
@@ -498,7 +501,7 @@ DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
498 } 501 }
499 } 502 }
500 503
501 if (result != DriverResult::Success) { 504 if (result != Common::Input::DriverResult::Success) {
502 return result; 505 return result;
503 } 506 }
504 507
@@ -507,12 +510,12 @@ DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
507 result = SendNextPackageRequest(output, package_index); 510 result = SendNextPackageRequest(output, package_index);
508 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 511 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
509 512
510 if (result != DriverResult::Success) { 513 if (result != Common::Input::DriverResult::Success) {
511 return result; 514 return result;
512 } 515 }
513 516
514 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { 517 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
515 return DriverResult::ErrorReadingData; 518 return Common::Input::DriverResult::ErrorReadingData;
516 } 519 }
517 520
518 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { 521 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
@@ -538,13 +541,13 @@ DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
538 return result; 541 return result;
539} 542}
540 543
541DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid, 544Common::Input::DriverResult NfcProtocol::WriteMifareData(
542 std::span<const MifareWriteChunk> write_request) { 545 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> write_request) {
543 constexpr std::size_t timeout_limit = 60; 546 constexpr std::size_t timeout_limit = 60;
544 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request); 547 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
545 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data); 548 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
546 std::span<const u8> buffer(nfc_buffer_data); 549 std::span<const u8> buffer(nfc_buffer_data);
547 DriverResult result = DriverResult::Success; 550 Common::Input::DriverResult result = Common::Input::DriverResult::Success;
548 MCUCommandResponse output{}; 551 MCUCommandResponse output{};
549 u8 block_id = 1; 552 u8 block_id = 1;
550 u8 package_index = 0; 553 u8 package_index = 0;
@@ -566,7 +569,7 @@ DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
566 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 569 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
567 570
568 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { 571 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
569 return DriverResult::ErrorReadingData; 572 return Common::Input::DriverResult::ErrorReadingData;
570 } 573 }
571 574
572 // Increase position when data is confirmed by the joycon 575 // Increase position when data is confirmed by the joycon
@@ -578,7 +581,7 @@ DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
578 } 581 }
579 } 582 }
580 583
581 if (result != DriverResult::Success) { 584 if (result != Common::Input::DriverResult::Success) {
582 return result; 585 return result;
583 } 586 }
584 587
@@ -587,12 +590,12 @@ DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
587 result = SendNextPackageRequest(output, package_index); 590 result = SendNextPackageRequest(output, package_index);
588 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 591 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
589 592
590 if (result != DriverResult::Success) { 593 if (result != Common::Input::DriverResult::Success) {
591 return result; 594 return result;
592 } 595 }
593 596
594 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { 597 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
595 return DriverResult::ErrorReadingData; 598 return Common::Input::DriverResult::ErrorReadingData;
596 } 599 }
597 600
598 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { 601 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
@@ -609,8 +612,8 @@ DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
609 return result; 612 return result;
610} 613}
611 614
612DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, 615Common::Input::DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
613 bool is_second_attempt) { 616 bool is_second_attempt) {
614 NFCRequestState request{ 617 NFCRequestState request{
615 .command_argument = NFCCommand::StartPolling, 618 .command_argument = NFCCommand::StartPolling,
616 .block_id = {}, 619 .block_id = {},
@@ -635,7 +638,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
635 output); 638 output);
636} 639}
637 640
638DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { 641Common::Input::DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
639 NFCRequestState request{ 642 NFCRequestState request{
640 .command_argument = NFCCommand::StopPolling, 643 .command_argument = NFCCommand::StopPolling,
641 .block_id = {}, 644 .block_id = {},
@@ -653,7 +656,8 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
653 output); 656 output);
654} 657}
655 658
656DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) { 659Common::Input::DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output,
660 u8 packet_id) {
657 NFCRequestState request{ 661 NFCRequestState request{
658 .command_argument = NFCCommand::StartWaitingRecieve, 662 .command_argument = NFCCommand::StartWaitingRecieve,
659 .block_id = {}, 663 .block_id = {},
@@ -671,7 +675,8 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8
671 output); 675 output);
672} 676}
673 677
674DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { 678Common::Input::DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output,
679 NFCPages ntag_pages) {
675 NFCRequestState request{ 680 NFCRequestState request{
676 .command_argument = NFCCommand::ReadNtag, 681 .command_argument = NFCCommand::ReadNtag,
677 .block_id = {}, 682 .block_id = {},
@@ -696,8 +701,8 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
696 output); 701 output);
697} 702}
698 703
699DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output, 704Common::Input::DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
700 const TagUUID& tag_uuid) { 705 const TagUUID& tag_uuid) {
701 NFCRequestState request{ 706 NFCRequestState request{
702 .command_argument = NFCCommand::ReadNtag, 707 .command_argument = NFCCommand::ReadNtag,
703 .block_id = {}, 708 .block_id = {},
@@ -722,9 +727,10 @@ DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
722 output); 727 output);
723} 728}
724 729
725DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 730Common::Input::DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
726 bool is_last_packet, 731 u8 block_id,
727 std::span<const u8> data) { 732 bool is_last_packet,
733 std::span<const u8> data) {
728 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); 734 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
729 NFCRequestState request{ 735 NFCRequestState request{
730 .command_argument = NFCCommand::WriteNtag, 736 .command_argument = NFCCommand::WriteNtag,
@@ -745,8 +751,9 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
745 output); 751 output);
746} 752}
747 753
748DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, 754Common::Input::DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output,
749 bool is_last_packet, std::span<const u8> data) { 755 u8 block_id, bool is_last_packet,
756 std::span<const u8> data) {
750 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); 757 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
751 NFCRequestState request{ 758 NFCRequestState request{
752 .command_argument = NFCCommand::Mifare, 759 .command_argument = NFCCommand::Mifare,
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index 0be95e40e..22db95170 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -13,30 +13,34 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class NfcProtocol final : private JoyconCommonProtocol { 22class NfcProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableNfc(); 26 Common::Input::DriverResult EnableNfc();
23 27
24 DriverResult DisableNfc(); 28 Common::Input::DriverResult DisableNfc();
25 29
26 DriverResult StartNFCPollingMode(); 30 Common::Input::DriverResult StartNFCPollingMode();
27 31
28 DriverResult StopNFCPollingMode(); 32 Common::Input::DriverResult StopNFCPollingMode();
29 33
30 DriverResult GetTagInfo(Joycon::TagInfo& tag_info); 34 Common::Input::DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
31 35
32 DriverResult ReadAmiibo(std::vector<u8>& data); 36 Common::Input::DriverResult ReadAmiibo(std::vector<u8>& data);
33 37
34 DriverResult WriteAmiibo(std::span<const u8> data); 38 Common::Input::DriverResult WriteAmiibo(std::span<const u8> data);
35 39
36 DriverResult ReadMifare(std::span<const MifareReadChunk> read_request, 40 Common::Input::DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
37 std::span<MifareReadData> out_data); 41 std::span<MifareReadData> out_data);
38 42
39 DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request); 43 Common::Input::DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
40 44
41 bool HasAmiibo(); 45 bool HasAmiibo();
42 46
@@ -54,37 +58,41 @@ private:
54 TagUUID uuid; 58 TagUUID uuid;
55 }; 59 };
56 60
57 DriverResult WaitUntilNfcIs(NFCStatus status); 61 Common::Input::DriverResult WaitUntilNfcIs(NFCStatus status);
58 62
59 DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1); 63 Common::Input::DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
60 64
61 DriverResult GetAmiiboData(std::vector<u8>& data); 65 Common::Input::DriverResult GetAmiiboData(std::vector<u8>& data);
62 66
63 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); 67 Common::Input::DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
64 68
65 DriverResult GetMifareData(const MifareUUID& tag_uuid, 69 Common::Input::DriverResult GetMifareData(const MifareUUID& tag_uuid,
66 std::span<const MifareReadChunk> read_request, 70 std::span<const MifareReadChunk> read_request,
67 std::span<MifareReadData> out_data); 71 std::span<MifareReadData> out_data);
68 72
69 DriverResult WriteMifareData(const MifareUUID& tag_uuid, 73 Common::Input::DriverResult WriteMifareData(const MifareUUID& tag_uuid,
70 std::span<const MifareWriteChunk> write_request); 74 std::span<const MifareWriteChunk> write_request);
71 75
72 DriverResult SendStartPollingRequest(MCUCommandResponse& output, 76 Common::Input::DriverResult SendStartPollingRequest(MCUCommandResponse& output,
73 bool is_second_attempt = false); 77 bool is_second_attempt = false);
74 78
75 DriverResult SendStopPollingRequest(MCUCommandResponse& output); 79 Common::Input::DriverResult SendStopPollingRequest(MCUCommandResponse& output);
76 80
77 DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id); 81 Common::Input::DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
78 82
79 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); 83 Common::Input::DriverResult SendReadAmiiboRequest(MCUCommandResponse& output,
84 NFCPages ntag_pages);
80 85
81 DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid); 86 Common::Input::DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output,
87 const TagUUID& tag_uuid);
82 88
83 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 89 Common::Input::DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
84 bool is_last_packet, std::span<const u8> data); 90 bool is_last_packet,
91 std::span<const u8> data);
85 92
86 DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, 93 Common::Input::DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
87 bool is_last_packet, std::span<const u8> data); 94 bool is_last_packet,
95 std::span<const u8> data);
88 96
89 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; 97 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
90 98
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
index 190cef812..96414fb67 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.cpp
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/input.h"
4#include "common/logging/log.h" 5#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/ringcon.h" 6#include "input_common/helpers/joycon_protocol/ringcon.h"
6 7
@@ -9,18 +10,18 @@ namespace InputCommon::Joycon {
9RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle) 10RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {} 11 : JoyconCommonProtocol(std::move(handle)) {}
11 12
12DriverResult RingConProtocol::EnableRingCon() { 13Common::Input::DriverResult RingConProtocol::EnableRingCon() {
13 LOG_DEBUG(Input, "Enable Ringcon"); 14 LOG_DEBUG(Input, "Enable Ringcon");
14 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
15 DriverResult result{DriverResult::Success}; 16 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
16 17
17 if (result == DriverResult::Success) { 18 if (result == Common::Input::DriverResult::Success) {
18 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); 19 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
19 } 20 }
20 if (result == DriverResult::Success) { 21 if (result == Common::Input::DriverResult::Success) {
21 result = EnableMCU(true); 22 result = EnableMCU(true);
22 } 23 }
23 if (result == DriverResult::Success) { 24 if (result == Common::Input::DriverResult::Success) {
24 const MCUConfig config{ 25 const MCUConfig config{
25 .command = MCUCommand::ConfigureMCU, 26 .command = MCUCommand::ConfigureMCU,
26 .sub_command = MCUSubCommand::SetDeviceMode, 27 .sub_command = MCUSubCommand::SetDeviceMode,
@@ -33,12 +34,12 @@ DriverResult RingConProtocol::EnableRingCon() {
33 return result; 34 return result;
34} 35}
35 36
36DriverResult RingConProtocol::DisableRingCon() { 37Common::Input::DriverResult RingConProtocol::DisableRingCon() {
37 LOG_DEBUG(Input, "Disable RingCon"); 38 LOG_DEBUG(Input, "Disable RingCon");
38 ScopedSetBlocking sb(this); 39 ScopedSetBlocking sb(this);
39 DriverResult result{DriverResult::Success}; 40 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
40 41
41 if (result == DriverResult::Success) { 42 if (result == Common::Input::DriverResult::Success) {
42 result = EnableMCU(false); 43 result = EnableMCU(false);
43 } 44 }
44 45
@@ -47,29 +48,29 @@ DriverResult RingConProtocol::DisableRingCon() {
47 return result; 48 return result;
48} 49}
49 50
50DriverResult RingConProtocol::StartRingconPolling() { 51Common::Input::DriverResult RingConProtocol::StartRingconPolling() {
51 LOG_DEBUG(Input, "Enable Ringcon"); 52 LOG_DEBUG(Input, "Enable Ringcon");
52 ScopedSetBlocking sb(this); 53 ScopedSetBlocking sb(this);
53 DriverResult result{DriverResult::Success}; 54 Common::Input::DriverResult result{Common::Input::DriverResult::Success};
54 bool is_connected = false; 55 bool is_connected = false;
55 56
56 if (result == DriverResult::Success) { 57 if (result == Common::Input::DriverResult::Success) {
57 result = IsRingConnected(is_connected); 58 result = IsRingConnected(is_connected);
58 } 59 }
59 if (result == DriverResult::Success && is_connected) { 60 if (result == Common::Input::DriverResult::Success && is_connected) {
60 LOG_INFO(Input, "Ringcon detected"); 61 LOG_INFO(Input, "Ringcon detected");
61 result = ConfigureRing(); 62 result = ConfigureRing();
62 } 63 }
63 if (result == DriverResult::Success) { 64 if (result == Common::Input::DriverResult::Success) {
64 is_enabled = true; 65 is_enabled = true;
65 } 66 }
66 67
67 return result; 68 return result;
68} 69}
69 70
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { 71Common::Input::DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected"); 72 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28; 73 constexpr std::size_t max_tries = 42;
73 SubCommandResponse output{}; 74 SubCommandResponse output{};
74 std::size_t tries = 0; 75 std::size_t tries = 0;
75 is_connected = false; 76 is_connected = false;
@@ -77,20 +78,21 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
77 do { 78 do {
78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output); 79 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
79 80
80 if (result != DriverResult::Success) { 81 if (result != Common::Input::DriverResult::Success &&
82 result != Common::Input::DriverResult::Timeout) {
81 return result; 83 return result;
82 } 84 }
83 85
84 if (tries++ >= max_tries) { 86 if (tries++ >= max_tries) {
85 return DriverResult::NoDeviceDetected; 87 return Common::Input::DriverResult::NoDeviceDetected;
86 } 88 }
87 } while (output.external_device_id != ExternalDeviceId::RingController); 89 } while (output.external_device_id != ExternalDeviceId::RingController);
88 90
89 is_connected = true; 91 is_connected = true;
90 return DriverResult::Success; 92 return Common::Input::DriverResult::Success;
91} 93}
92 94
93DriverResult RingConProtocol::ConfigureRing() { 95Common::Input::DriverResult RingConProtocol::ConfigureRing() {
94 LOG_DEBUG(Input, "ConfigureRing"); 96 LOG_DEBUG(Input, "ConfigureRing");
95 97
96 static constexpr std::array<u8, 37> ring_config{ 98 static constexpr std::array<u8, 37> ring_config{
@@ -98,9 +100,10 @@ DriverResult RingConProtocol::ConfigureRing() {
98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 100 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; 101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
100 102
101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config); 103 const Common::Input::DriverResult result =
104 SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
102 105
103 if (result != DriverResult::Success) { 106 if (result != Common::Input::DriverResult::Success) {
104 return result; 107 return result;
105 } 108 }
106 109
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h
index 6e858f3fc..9f0888de3 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.h
+++ b/src/input_common/helpers/joycon_protocol/ringcon.h
@@ -13,24 +13,28 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class RingConProtocol final : private JoyconCommonProtocol { 22class RingConProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableRingCon(); 26 Common::Input::DriverResult EnableRingCon();
23 27
24 DriverResult DisableRingCon(); 28 Common::Input::DriverResult DisableRingCon();
25 29
26 DriverResult StartRingconPolling(); 30 Common::Input::DriverResult StartRingconPolling();
27 31
28 bool IsEnabled() const; 32 bool IsEnabled() const;
29 33
30private: 34private:
31 DriverResult IsRingConnected(bool& is_connected); 35 Common::Input::DriverResult IsRingConnected(bool& is_connected);
32 36
33 DriverResult ConfigureRing(); 37 Common::Input::DriverResult ConfigureRing();
34 38
35 bool is_enabled{}; 39 bool is_enabled{};
36}; 40};
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
index 63b60c946..7647f505e 100644
--- a/src/input_common/helpers/joycon_protocol/rumble.cpp
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5#include <cmath> 5#include <cmath>
6 6
7#include "common/input.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "input_common/helpers/joycon_protocol/rumble.h" 9#include "input_common/helpers/joycon_protocol/rumble.h"
9 10
@@ -12,14 +13,14 @@ namespace InputCommon::Joycon {
12RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) 13RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
13 : JoyconCommonProtocol(std::move(handle)) {} 14 : JoyconCommonProtocol(std::move(handle)) {}
14 15
15DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { 16Common::Input::DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
16 LOG_DEBUG(Input, "Enable Rumble"); 17 LOG_DEBUG(Input, "Enable Rumble");
17 ScopedSetBlocking sb(this); 18 ScopedSetBlocking sb(this);
18 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; 19 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
19 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer); 20 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
20} 21}
21 22
22DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { 23Common::Input::DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
23 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{}; 24 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
24 25
25 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { 26 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h
index 6c12b7925..5e50e531a 100644
--- a/src/input_common/helpers/joycon_protocol/rumble.h
+++ b/src/input_common/helpers/joycon_protocol/rumble.h
@@ -13,15 +13,19 @@
13#include "input_common/helpers/joycon_protocol/common_protocol.h" 13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h" 14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15 15
16namespace Common::Input {
17enum class DriverResult;
18}
19
16namespace InputCommon::Joycon { 20namespace InputCommon::Joycon {
17 21
18class RumbleProtocol final : private JoyconCommonProtocol { 22class RumbleProtocol final : private JoyconCommonProtocol {
19public: 23public:
20 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle); 24 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
21 25
22 DriverResult EnableRumble(bool is_enabled); 26 Common::Input::DriverResult EnableRumble(bool is_enabled);
23 27
24 DriverResult SendVibration(const VibrationValue& vibration); 28 Common::Input::DriverResult SendVibration(const VibrationValue& vibration);
25 29
26private: 30private:
27 u16 EncodeHighFrequency(f32 frequency) const; 31 u16 EncodeHighFrequency(f32 frequency) const;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e9e6f278d..3b2fe01da 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -220,8 +220,8 @@ add_library(video_core STATIC
220 surface.h 220 surface.h
221 texture_cache/accelerated_swizzle.cpp 221 texture_cache/accelerated_swizzle.cpp
222 texture_cache/accelerated_swizzle.h 222 texture_cache/accelerated_swizzle.h
223 texture_cache/decode_bc4.cpp 223 texture_cache/decode_bc.cpp
224 texture_cache/decode_bc4.h 224 texture_cache/decode_bc.h
225 texture_cache/descriptor_table.h 225 texture_cache/descriptor_table.h
226 texture_cache/formatter.cpp 226 texture_cache/formatter.cpp
227 texture_cache/formatter.h 227 texture_cache/formatter.h
@@ -279,7 +279,7 @@ add_library(video_core STATIC
279create_target_directory_groups(video_core) 279create_target_directory_groups(video_core)
280 280
281target_link_libraries(video_core PUBLIC common core) 281target_link_libraries(video_core PUBLIC common core)
282target_link_libraries(video_core PUBLIC glad shader_recompiler stb) 282target_link_libraries(video_core PUBLIC glad shader_recompiler stb bc_decoder)
283 283
284if (YUZU_USE_BUNDLED_FFMPEG AND NOT (WIN32 OR ANDROID)) 284if (YUZU_USE_BUNDLED_FFMPEG AND NOT (WIN32 OR ANDROID))
285 add_dependencies(video_core ffmpeg-build) 285 add_dependencies(video_core ffmpeg-build)
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a290d6ea7..f8598fd98 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -174,8 +174,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
174 src_operand.address = regs.offset_in; 174 src_operand.address = regs.offset_in;
175 175
176 DMA::BufferOperand dst_operand; 176 DMA::BufferOperand dst_operand;
177 u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out)); 177 dst_operand.pitch = static_cast<u32>(std::abs(regs.pitch_out));
178 dst_operand.pitch = abs_pitch_out;
179 dst_operand.width = regs.line_length_in; 178 dst_operand.width = regs.line_length_in;
180 dst_operand.height = regs.line_count; 179 dst_operand.height = regs.line_count;
181 dst_operand.address = regs.offset_out; 180 dst_operand.address = regs.offset_out;
@@ -222,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
222 const size_t src_size = 221 const size_t src_size =
223 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);
224 223
225 const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count; 224 const size_t dst_size = dst_operand.pitch * regs.line_count;
226 read_buffer.resize_destructive(src_size); 225 read_buffer.resize_destructive(src_size);
227 write_buffer.resize_destructive(dst_size); 226 write_buffer.resize_destructive(dst_size);
228 227
@@ -231,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
231 230
232 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,
233 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,
234 abs_pitch_out); 233 dst_operand.pitch);
235 234
236 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); 235 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
237} 236}
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index cd6a3a9b8..da07a556f 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -290,7 +290,7 @@ void Codec::Decode() {
290 return vp9_decoder->GetFrameBytes(); 290 return vp9_decoder->GetFrameBytes();
291 default: 291 default:
292 ASSERT(false); 292 ASSERT(false);
293 return std::vector<u8>{}; 293 return std::span<const u8>{};
294 } 294 }
295 }(); 295 }();
296 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; 296 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index ce827eb6c..862904e39 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -29,15 +29,15 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
29 29
30H264::~H264() = default; 30H264::~H264() = default;
31 31
32const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 32std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
33 bool is_first_frame) { 33 bool is_first_frame) {
34 H264DecoderContext context; 34 H264DecoderContext context;
35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, 35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
36 sizeof(H264DecoderContext)); 36 sizeof(H264DecoderContext));
37 37
38 const s64 frame_number = context.h264_parameter_set.frame_number.Value(); 38 const s64 frame_number = context.h264_parameter_set.frame_number.Value();
39 if (!is_first_frame && frame_number != 0) { 39 if (!is_first_frame && frame_number != 0) {
40 frame.resize(context.stream_len); 40 frame.resize_destructive(context.stream_len);
41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); 41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
42 return frame; 42 return frame;
43 } 43 }
@@ -135,14 +135,14 @@ const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegist
135 for (s32 index = 0; index < 6; index++) { 135 for (s32 index = 0; index < 6; index++) {
136 writer.WriteBit(true); 136 writer.WriteBit(true);
137 std::span<const u8> matrix{context.weight_scale}; 137 std::span<const u8> matrix{context.weight_scale};
138 writer.WriteScalingList(matrix, index * 16, 16); 138 writer.WriteScalingList(scan, matrix, index * 16, 16);
139 } 139 }
140 140
141 if (context.h264_parameter_set.transform_8x8_mode_flag) { 141 if (context.h264_parameter_set.transform_8x8_mode_flag) {
142 for (s32 index = 0; index < 2; index++) { 142 for (s32 index = 0; index < 2; index++) {
143 writer.WriteBit(true); 143 writer.WriteBit(true);
144 std::span<const u8> matrix{context.weight_scale_8x8}; 144 std::span<const u8> matrix{context.weight_scale_8x8};
145 writer.WriteScalingList(matrix, index * 64, 64); 145 writer.WriteScalingList(scan, matrix, index * 64, 64);
146 } 146 }
147 } 147 }
148 148
@@ -188,8 +188,8 @@ void H264BitWriter::WriteBit(bool state) {
188 WriteBits(state ? 1 : 0, 1); 188 WriteBits(state ? 1 : 0, 1);
189} 189}
190 190
191void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { 191void H264BitWriter::WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list,
192 static Common::ScratchBuffer<u8> scan{}; 192 s32 start, s32 count) {
193 scan.resize_destructive(count); 193 scan.resize_destructive(count);
194 if (count == 16) { 194 if (count == 16) {
195 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); 195 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h
index 5cc86454e..d6b556322 100644
--- a/src/video_core/host1x/codecs/h264.h
+++ b/src/video_core/host1x/codecs/h264.h
@@ -5,9 +5,11 @@
5 5
6#include <span> 6#include <span>
7#include <vector> 7#include <vector>
8
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/scratch_buffer.h"
11#include "video_core/host1x/nvdec_common.h" 13#include "video_core/host1x/nvdec_common.h"
12 14
13namespace Tegra { 15namespace Tegra {
@@ -37,7 +39,8 @@ public:
37 39
38 /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification 40 /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
39 /// Writes the scaling matrices of the sream 41 /// Writes the scaling matrices of the sream
40 void WriteScalingList(std::span<const u8> list, s32 start, s32 count); 42 void WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list, s32 start,
43 s32 count);
41 44
42 /// Return the bitstream as a vector. 45 /// Return the bitstream as a vector.
43 [[nodiscard]] std::vector<u8>& GetByteArray(); 46 [[nodiscard]] std::vector<u8>& GetByteArray();
@@ -63,11 +66,12 @@ public:
63 ~H264(); 66 ~H264();
64 67
65 /// Compose the H264 frame for FFmpeg decoding 68 /// Compose the H264 frame for FFmpeg decoding
66 [[nodiscard]] const std::vector<u8>& ComposeFrame( 69 [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
67 const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); 70 bool is_first_frame = false);
68 71
69private: 72private:
70 std::vector<u8> frame; 73 Common::ScratchBuffer<u8> frame;
74 Common::ScratchBuffer<u8> scan;
71 Host1x::Host1x& host1x; 75 Host1x::Host1x& host1x;
72 76
73 struct H264ParameterSet { 77 struct H264ParameterSet {
diff --git a/src/video_core/host1x/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp
index 28fb12cb8..ee6392ff9 100644
--- a/src/video_core/host1x/codecs/vp8.cpp
+++ b/src/video_core/host1x/codecs/vp8.cpp
@@ -12,7 +12,7 @@ VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {}
12 12
13VP8::~VP8() = default; 13VP8::~VP8() = default;
14 14
15const std::vector<u8>& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { 15std::span<const u8> VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
16 VP8PictureInfo info; 16 VP8PictureInfo info;
17 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); 17 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo));
18 18
diff --git a/src/video_core/host1x/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h
index 5bf07ecab..7926b73f3 100644
--- a/src/video_core/host1x/codecs/vp8.h
+++ b/src/video_core/host1x/codecs/vp8.h
@@ -4,10 +4,11 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <vector> 7#include <span>
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/scratch_buffer.h"
11#include "video_core/host1x/nvdec_common.h" 12#include "video_core/host1x/nvdec_common.h"
12 13
13namespace Tegra { 14namespace Tegra {
@@ -24,11 +25,11 @@ public:
24 ~VP8(); 25 ~VP8();
25 26
26 /// Compose the VP8 frame for FFmpeg decoding 27 /// Compose the VP8 frame for FFmpeg decoding
27 [[nodiscard]] const std::vector<u8>& ComposeFrame( 28 [[nodiscard]] std::span<const u8> ComposeFrame(
28 const Host1x::NvdecCommon::NvdecRegisters& state); 29 const Host1x::NvdecCommon::NvdecRegisters& state);
29 30
30private: 31private:
31 std::vector<u8> frame; 32 Common::ScratchBuffer<u8> frame;
32 Host1x::Host1x& host1x; 33 Host1x::Host1x& host1x;
33 34
34 struct VP8PictureInfo { 35 struct VP8PictureInfo {
diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp
index cf40c9012..306c3d0e8 100644
--- a/src/video_core/host1x/codecs/vp9.cpp
+++ b/src/video_core/host1x/codecs/vp9.cpp
@@ -3,6 +3,7 @@
3 3
4#include <algorithm> // for std::copy 4#include <algorithm> // for std::copy
5#include <numeric> 5#include <numeric>
6
6#include "common/assert.h" 7#include "common/assert.h"
7#include "video_core/host1x/codecs/vp9.h" 8#include "video_core/host1x/codecs/vp9.h"
8#include "video_core/host1x/host1x.h" 9#include "video_core/host1x/host1x.h"
diff --git a/src/video_core/host1x/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h
index d4083e8d3..f1ed19508 100644
--- a/src/video_core/host1x/codecs/vp9.h
+++ b/src/video_core/host1x/codecs/vp9.h
@@ -4,9 +4,11 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/scratch_buffer.h"
10#include "common/stream.h" 12#include "common/stream.h"
11#include "video_core/host1x/codecs/vp9_types.h" 13#include "video_core/host1x/codecs/vp9_types.h"
12#include "video_core/host1x/nvdec_common.h" 14#include "video_core/host1x/nvdec_common.h"
@@ -128,8 +130,8 @@ public:
128 return !current_frame_info.show_frame; 130 return !current_frame_info.show_frame;
129 } 131 }
130 132
131 /// Returns a const reference to the composed frame data. 133 /// Returns a const span to the composed frame data.
132 [[nodiscard]] const std::vector<u8>& GetFrameBytes() const { 134 [[nodiscard]] std::span<const u8> GetFrameBytes() const {
133 return frame; 135 return frame;
134 } 136 }
135 137
@@ -181,7 +183,7 @@ private:
181 [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); 183 [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
182 184
183 Host1x::Host1x& host1x; 185 Host1x::Host1x& host1x;
184 std::vector<u8> frame; 186 Common::ScratchBuffer<u8> frame;
185 187
186 std::array<s8, 4> loop_filter_ref_deltas{}; 188 std::array<s8, 4> loop_filter_ref_deltas{};
187 std::array<s8, 2> loop_filter_mode_deltas{}; 189 std::array<s8, 2> loop_filter_mode_deltas{};
diff --git a/src/video_core/host1x/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h
index adad8ed7e..cc9b25690 100644
--- a/src/video_core/host1x/codecs/vp9_types.h
+++ b/src/video_core/host1x/codecs/vp9_types.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <vector> 7#include <vector>
8
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index cf2964a3f..28d4b15a0 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -495,6 +495,9 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
495 const Region2D& dst_region, const Region2D& src_region, 495 const Region2D& dst_region, const Region2D& src_region,
496 Tegra::Engines::Fermi2D::Filter filter, 496 Tegra::Engines::Fermi2D::Filter filter,
497 Tegra::Engines::Fermi2D::Operation operation) { 497 Tegra::Engines::Fermi2D::Operation operation) {
498 if (!device.IsExtShaderStencilExportSupported()) {
499 return;
500 }
498 ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point); 501 ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
499 ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy); 502 ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
500 const BlitImagePipelineKey key{ 503 const BlitImagePipelineKey key{
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 9a0b10568..a8540339d 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -259,6 +259,26 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
259 break; 259 break;
260 } 260 }
261 } 261 }
262 // Transcode on hardware that doesn't support BCn natively
263 if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
264 const bool is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
265 if (pixel_format == PixelFormat::BC4_SNORM) {
266 tuple.format = VK_FORMAT_R8_SNORM;
267 } else if (pixel_format == PixelFormat::BC4_UNORM) {
268 tuple.format = VK_FORMAT_R8_UNORM;
269 } else if (pixel_format == PixelFormat::BC5_SNORM) {
270 tuple.format = VK_FORMAT_R8G8_SNORM;
271 } else if (pixel_format == PixelFormat::BC5_UNORM) {
272 tuple.format = VK_FORMAT_R8G8_UNORM;
273 } else if (pixel_format == PixelFormat::BC6H_SFLOAT ||
274 pixel_format == PixelFormat::BC6H_UFLOAT) {
275 tuple.format = VK_FORMAT_R16G16B16A16_SFLOAT;
276 } else if (is_srgb) {
277 tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
278 } else {
279 tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
280 }
281 }
262 const bool attachable = (tuple.usage & Attachable) != 0; 282 const bool attachable = (tuple.usage & Attachable) != 0;
263 const bool storage = (tuple.usage & Storage) != 0; 283 const bool storage = (tuple.usage & Storage) != 0;
264 284
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ddf28ca28..454bb66a4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -12,6 +12,7 @@
12#include <fmt/format.h> 12#include <fmt/format.h>
13 13
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "common/polyfill_ranges.h"
15#include "common/scope_exit.h" 16#include "common/scope_exit.h"
16#include "common/settings.h" 17#include "common/settings.h"
17#include "common/telemetry.h" 18#include "common/telemetry.h"
@@ -65,6 +66,21 @@ std::string BuildCommaSeparatedExtensions(
65 return fmt::format("{}", fmt::join(available_extensions, ",")); 66 return fmt::format("{}", fmt::join(available_extensions, ","));
66} 67}
67 68
69DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) {
70 if (!Settings::values.renderer_debug) {
71 return DebugCallback{};
72 }
73 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
74 const auto it = std::ranges::find_if(*properties, [](const auto& prop) {
75 return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0;
76 });
77 if (it != properties->end()) {
78 return CreateDebugUtilsCallback(instance);
79 } else {
80 return CreateDebugReportCallback(instance);
81 }
82}
83
68} // Anonymous namespace 84} // Anonymous namespace
69 85
70Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 86Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
@@ -87,7 +103,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
87 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), 103 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
88 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 104 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
89 Settings::values.renderer_debug.GetValue())), 105 Settings::values.renderer_debug.GetValue())),
90 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 106 debug_callback(MakeDebugCallback(instance, dld)),
91 surface(CreateSurface(instance, render_window.GetWindowInfo())), 107 surface(CreateSurface(instance, render_window.GetWindowInfo())),
92 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), 108 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
93 scheduler(device, state_tracker), 109 scheduler(device, state_tracker),
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index b2e8cbd1b..ca22c0baa 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -5,6 +5,7 @@
5 5
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <variant>
8 9
9#include "common/dynamic_library.h" 10#include "common/dynamic_library.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
@@ -33,6 +34,8 @@ class GPU;
33 34
34namespace Vulkan { 35namespace Vulkan {
35 36
37using DebugCallback = std::variant<vk::DebugUtilsMessenger, vk::DebugReportCallback>;
38
36Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 39Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
37 VkSurfaceKHR surface); 40 VkSurfaceKHR surface);
38 41
@@ -71,7 +74,7 @@ private:
71 vk::InstanceDispatch dld; 74 vk::InstanceDispatch dld;
72 75
73 vk::Instance instance; 76 vk::Instance instance;
74 vk::DebugUtilsMessenger debug_callback; 77 DebugCallback debug_callback;
75 vk::SurfaceKHR surface; 78 vk::SurfaceKHR surface;
76 79
77 ScreenInfo screen_info; 80 ScreenInfo screen_info;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 660f7c9ff..51df18ec3 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -590,7 +590,8 @@ void BufferCacheRuntime::ReserveNullBuffer() {
590 .pNext = nullptr, 590 .pNext = nullptr,
591 .flags = 0, 591 .flags = 0,
592 .size = 4, 592 .size = 4,
593 .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, 593 .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
594 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
594 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 595 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
595 .queueFamilyIndexCount = 0, 596 .queueFamilyIndexCount = 0,
596 .pQueueFamilyIndices = nullptr, 597 .pQueueFamilyIndices = nullptr,
@@ -598,7 +599,6 @@ void BufferCacheRuntime::ReserveNullBuffer() {
598 if (device.IsExtTransformFeedbackSupported()) { 599 if (device.IsExtTransformFeedbackSupported()) {
599 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 600 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
600 } 601 }
601 create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
602 null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal); 602 null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
603 if (device.HasDebuggingToolAttached()) { 603 if (device.HasDebuggingToolAttached()) {
604 null_buffer.SetObjectNameEXT("Null buffer"); 604 null_buffer.SetObjectNameEXT("Null buffer");
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index c1595642e..ad35cacac 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -652,13 +652,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
652 .pNext = nullptr, 652 .pNext = nullptr,
653 .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE, 653 .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
654 }; 654 };
655 const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
655 VkPipelineViewportStateCreateInfo viewport_ci{ 656 VkPipelineViewportStateCreateInfo viewport_ci{
656 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 657 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
657 .pNext = nullptr, 658 .pNext = nullptr,
658 .flags = 0, 659 .flags = 0,
659 .viewportCount = Maxwell::NumViewports, 660 .viewportCount = num_viewports,
660 .pViewports = nullptr, 661 .pViewports = nullptr,
661 .scissorCount = Maxwell::NumViewports, 662 .scissorCount = num_viewports,
662 .pScissors = nullptr, 663 .pScissors = nullptr,
663 }; 664 };
664 if (device.IsNvViewportSwizzleSupported()) { 665 if (device.IsNvViewportSwizzleSupported()) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 9f316113c..d600c4e61 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -309,7 +309,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
309 .support_int16 = device.IsShaderInt16Supported(), 309 .support_int16 = device.IsShaderInt16Supported(),
310 .support_int64 = device.IsShaderInt64Supported(), 310 .support_int64 = device.IsShaderInt64Supported(),
311 .support_vertex_instance_id = false, 311 .support_vertex_instance_id = false,
312 .support_float_controls = true, 312 .support_float_controls = device.IsKhrShaderFloatControlsSupported(),
313 .support_separate_denorm_behavior = 313 .support_separate_denorm_behavior =
314 float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, 314 float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
315 .support_separate_rounding_mode = 315 .support_separate_rounding_mode =
@@ -325,12 +325,13 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
325 .support_fp64_signed_zero_nan_preserve = 325 .support_fp64_signed_zero_nan_preserve =
326 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, 326 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
327 .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), 327 .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
328 .support_vote = true, 328 .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
329 .support_viewport_index_layer_non_geometry = 329 .support_viewport_index_layer_non_geometry =
330 device.IsExtShaderViewportIndexLayerSupported(), 330 device.IsExtShaderViewportIndexLayerSupported(),
331 .support_viewport_mask = device.IsNvViewportArray2Supported(), 331 .support_viewport_mask = device.IsNvViewportArray2Supported(),
332 .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), 332 .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(),
333 .support_demote_to_helper_invocation = true, 333 .support_demote_to_helper_invocation =
334 device.IsExtShaderDemoteToHelperInvocationSupported(),
334 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), 335 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
335 .support_derivative_control = true, 336 .support_derivative_control = true,
336 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 337 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 84e3a30cc..f7c0d939a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -315,7 +315,14 @@ void RasterizerVulkan::Clear(u32 layer_count) {
315 FlushWork(); 315 FlushWork();
316 gpu_memory->FlushCaching(); 316 gpu_memory->FlushCaching();
317 317
318#if ANDROID
319 if (Settings::IsGPULevelHigh()) {
320 // This is problematic on Android, disable on GPU Normal.
321 query_cache.UpdateCounters();
322 }
323#else
318 query_cache.UpdateCounters(); 324 query_cache.UpdateCounters();
325#endif
319 326
320 auto& regs = maxwell3d->regs; 327 auto& regs = maxwell3d->regs;
321 const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || 328 const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
@@ -925,7 +932,7 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
925 } 932 }
926 const bool is_rescaling{texture_cache.IsRescaling()}; 933 const bool is_rescaling{texture_cache.IsRescaling()};
927 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 934 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
928 const std::array viewports{ 935 const std::array viewport_list{
929 GetViewportState(device, regs, 0, scale), GetViewportState(device, regs, 1, scale), 936 GetViewportState(device, regs, 0, scale), GetViewportState(device, regs, 1, scale),
930 GetViewportState(device, regs, 2, scale), GetViewportState(device, regs, 3, scale), 937 GetViewportState(device, regs, 2, scale), GetViewportState(device, regs, 3, scale),
931 GetViewportState(device, regs, 4, scale), GetViewportState(device, regs, 5, scale), 938 GetViewportState(device, regs, 4, scale), GetViewportState(device, regs, 5, scale),
@@ -935,7 +942,11 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
935 GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale), 942 GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale),
936 GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale), 943 GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale),
937 }; 944 };
938 scheduler.Record([viewports](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewports); }); 945 scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) {
946 const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
947 const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
948 cmdbuf.SetViewport(0, viewports);
949 });
939} 950}
940 951
941void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) { 952void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -948,7 +959,7 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
948 up_scale = Settings::values.resolution_info.up_scale; 959 up_scale = Settings::values.resolution_info.up_scale;
949 down_shift = Settings::values.resolution_info.down_shift; 960 down_shift = Settings::values.resolution_info.down_shift;
950 } 961 }
951 const std::array scissors{ 962 const std::array scissor_list{
952 GetScissorState(regs, 0, up_scale, down_shift), 963 GetScissorState(regs, 0, up_scale, down_shift),
953 GetScissorState(regs, 1, up_scale, down_shift), 964 GetScissorState(regs, 1, up_scale, down_shift),
954 GetScissorState(regs, 2, up_scale, down_shift), 965 GetScissorState(regs, 2, up_scale, down_shift),
@@ -966,7 +977,11 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
966 GetScissorState(regs, 14, up_scale, down_shift), 977 GetScissorState(regs, 14, up_scale, down_shift),
967 GetScissorState(regs, 15, up_scale, down_shift), 978 GetScissorState(regs, 15, up_scale, down_shift),
968 }; 979 };
969 scheduler.Record([scissors](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissors); }); 980 scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) {
981 const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
982 const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
983 cmdbuf.SetScissor(0, scissors);
984 });
970} 985}
971 986
972void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { 987void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 62b251a9b..ce92f66ab 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -38,18 +38,20 @@ size_t Region(size_t iterator) noexcept {
38StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, 38StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
39 Scheduler& scheduler_) 39 Scheduler& scheduler_)
40 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { 40 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {
41 const VkBufferCreateInfo stream_ci = { 41 VkBufferCreateInfo stream_ci = {
42 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 42 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
43 .pNext = nullptr, 43 .pNext = nullptr,
44 .flags = 0, 44 .flags = 0,
45 .size = STREAM_BUFFER_SIZE, 45 .size = STREAM_BUFFER_SIZE,
46 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | 46 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
47 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 47 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
48 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
49 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 48 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
50 .queueFamilyIndexCount = 0, 49 .queueFamilyIndexCount = 0,
51 .pQueueFamilyIndices = nullptr, 50 .pQueueFamilyIndices = nullptr,
52 }; 51 };
52 if (device.IsExtTransformFeedbackSupported()) {
53 stream_ci.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
54 }
53 stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream); 55 stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
54 if (device.HasDebuggingToolAttached()) { 56 if (device.HasDebuggingToolAttached()) {
55 stream_buffer.SetObjectNameEXT("Stream Buffer"); 57 stream_buffer.SetObjectNameEXT("Stream Buffer");
@@ -164,19 +166,21 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
164StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, 166StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
165 bool deferred) { 167 bool deferred) {
166 const u32 log2 = Common::Log2Ceil64(size); 168 const u32 log2 = Common::Log2Ceil64(size);
167 const VkBufferCreateInfo buffer_ci = { 169 VkBufferCreateInfo buffer_ci = {
168 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 170 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
169 .pNext = nullptr, 171 .pNext = nullptr,
170 .flags = 0, 172 .flags = 0,
171 .size = 1ULL << log2, 173 .size = 1ULL << log2,
172 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 174 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
173 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 175 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
174 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | 176 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
175 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
176 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 177 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
177 .queueFamilyIndexCount = 0, 178 .queueFamilyIndexCount = 0,
178 .pQueueFamilyIndices = nullptr, 179 .pQueueFamilyIndices = nullptr,
179 }; 180 };
181 if (device.IsExtTransformFeedbackSupported()) {
182 buffer_ci.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
183 }
180 vk::Buffer buffer = memory_allocator.CreateBuffer(buffer_ci, usage); 184 vk::Buffer buffer = memory_allocator.CreateBuffer(buffer_ci, usage);
181 if (device.HasDebuggingToolAttached()) { 185 if (device.HasDebuggingToolAttached()) {
182 ++buffer_index; 186 ++buffer_index;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index ce6acc30c..8385b5509 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1279,6 +1279,10 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1279 flags |= VideoCommon::ImageFlagBits::Converted; 1279 flags |= VideoCommon::ImageFlagBits::Converted;
1280 flags |= VideoCommon::ImageFlagBits::CostlyLoad; 1280 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1281 } 1281 }
1282 if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
1283 flags |= VideoCommon::ImageFlagBits::Converted;
1284 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1285 }
1282 if (runtime->device.HasDebuggingToolAttached()) { 1286 if (runtime->device.HasDebuggingToolAttached()) {
1283 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1287 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
1284 } 1288 }
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index cb51529e4..e16cd5e73 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -269,6 +269,28 @@ bool IsPixelFormatASTC(PixelFormat format) {
269 } 269 }
270} 270}
271 271
272bool IsPixelFormatBCn(PixelFormat format) {
273 switch (format) {
274 case PixelFormat::BC1_RGBA_UNORM:
275 case PixelFormat::BC2_UNORM:
276 case PixelFormat::BC3_UNORM:
277 case PixelFormat::BC4_UNORM:
278 case PixelFormat::BC4_SNORM:
279 case PixelFormat::BC5_UNORM:
280 case PixelFormat::BC5_SNORM:
281 case PixelFormat::BC1_RGBA_SRGB:
282 case PixelFormat::BC2_SRGB:
283 case PixelFormat::BC3_SRGB:
284 case PixelFormat::BC7_UNORM:
285 case PixelFormat::BC6H_UFLOAT:
286 case PixelFormat::BC6H_SFLOAT:
287 case PixelFormat::BC7_SRGB:
288 return true;
289 default:
290 return false;
291 }
292}
293
272bool IsPixelFormatSRGB(PixelFormat format) { 294bool IsPixelFormatSRGB(PixelFormat format) {
273 switch (format) { 295 switch (format) {
274 case PixelFormat::A8B8G8R8_SRGB: 296 case PixelFormat::A8B8G8R8_SRGB:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 0225d3287..9b9c4d9bc 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -501,6 +501,8 @@ SurfaceType GetFormatType(PixelFormat pixel_format);
501 501
502bool IsPixelFormatASTC(PixelFormat format); 502bool IsPixelFormatASTC(PixelFormat format);
503 503
504bool IsPixelFormatBCn(PixelFormat format);
505
504bool IsPixelFormatSRGB(PixelFormat format); 506bool IsPixelFormatSRGB(PixelFormat format);
505 507
506bool IsPixelFormatInteger(PixelFormat format); 508bool IsPixelFormatInteger(PixelFormat format);
diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp
new file mode 100644
index 000000000..3e26474a3
--- /dev/null
+++ b/src/video_core/texture_cache/decode_bc.cpp
@@ -0,0 +1,129 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <span>
7#include <bc_decoder.h>
8
9#include "common/common_types.h"
10#include "video_core/texture_cache/decode_bc.h"
11
12namespace VideoCommon {
13
14namespace {
15constexpr u32 BLOCK_SIZE = 4;
16
17using VideoCore::Surface::PixelFormat;
18
19constexpr bool IsSigned(PixelFormat pixel_format) {
20 switch (pixel_format) {
21 case PixelFormat::BC4_SNORM:
22 case PixelFormat::BC4_UNORM:
23 case PixelFormat::BC5_SNORM:
24 case PixelFormat::BC5_UNORM:
25 case PixelFormat::BC6H_SFLOAT:
26 case PixelFormat::BC6H_UFLOAT:
27 return true;
28 default:
29 return false;
30 }
31}
32
33constexpr u32 BlockSize(PixelFormat pixel_format) {
34 switch (pixel_format) {
35 case PixelFormat::BC1_RGBA_SRGB:
36 case PixelFormat::BC1_RGBA_UNORM:
37 case PixelFormat::BC4_SNORM:
38 case PixelFormat::BC4_UNORM:
39 return 8;
40 default:
41 return 16;
42 }
43}
44} // Anonymous namespace
45
46u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
47 switch (pixel_format) {
48 case PixelFormat::BC4_SNORM:
49 case PixelFormat::BC4_UNORM:
50 return 1;
51 case PixelFormat::BC5_SNORM:
52 case PixelFormat::BC5_UNORM:
53 return 2;
54 case PixelFormat::BC6H_SFLOAT:
55 case PixelFormat::BC6H_UFLOAT:
56 return 8;
57 default:
58 return 4;
59 }
60}
61
62template <auto decompress, PixelFormat pixel_format>
63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
64 bool is_signed = false) {
65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
66 const u32 block_width = std::min(extent.width, BLOCK_SIZE);
67 const u32 block_height = std::min(extent.height, BLOCK_SIZE);
68 const u32 pitch = extent.width * out_bpp;
69 size_t input_offset = 0;
70 size_t output_offset = 0;
71 for (u32 slice = 0; slice < extent.depth; ++slice) {
72 for (u32 y = 0; y < extent.height; y += block_height) {
73 size_t row_offset = 0;
74 for (u32 x = 0; x < extent.width;
75 x += block_width, row_offset += block_width * out_bpp) {
76 const u8* src = input.data() + input_offset;
77 u8* const dst = output.data() + output_offset + row_offset;
78 if constexpr (IsSigned(pixel_format)) {
79 decompress(src, dst, x, y, extent.width, extent.height, is_signed);
80 } else {
81 decompress(src, dst, x, y, extent.width, extent.height);
82 }
83 input_offset += BlockSize(pixel_format);
84 }
85 output_offset += block_height * pitch;
86 }
87 }
88}
89
90void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
91 VideoCore::Surface::PixelFormat pixel_format) {
92 switch (pixel_format) {
93 case PixelFormat::BC1_RGBA_UNORM:
94 case PixelFormat::BC1_RGBA_SRGB:
95 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent);
96 break;
97 case PixelFormat::BC2_UNORM:
98 case PixelFormat::BC2_SRGB:
99 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
100 break;
101 case PixelFormat::BC3_UNORM:
102 case PixelFormat::BC3_SRGB:
103 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
104 break;
105 case PixelFormat::BC4_SNORM:
106 case PixelFormat::BC4_UNORM:
107 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
108 input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
109 break;
110 case PixelFormat::BC5_SNORM:
111 case PixelFormat::BC5_UNORM:
112 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
113 input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
114 break;
115 case PixelFormat::BC6H_SFLOAT:
116 case PixelFormat::BC6H_UFLOAT:
117 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
118 input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
119 break;
120 case PixelFormat::BC7_SRGB:
121 case PixelFormat::BC7_UNORM:
122 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
123 break;
124 default:
125 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
126 }
127}
128
129} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/decode_bc4.h b/src/video_core/texture_cache/decode_bc.h
index ab2f735be..41d1ec0a3 100644
--- a/src/video_core/texture_cache/decode_bc4.h
+++ b/src/video_core/texture_cache/decode_bc.h
@@ -6,10 +6,14 @@
6#include <span> 6#include <span>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "video_core/surface.h"
9#include "video_core/texture_cache/types.h" 10#include "video_core/texture_cache/types.h"
10 11
11namespace VideoCommon { 12namespace VideoCommon {
12 13
13void DecompressBC4(std::span<const u8> data, Extent3D extent, std::span<u8> output); 14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
15
16void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
17 VideoCore::Surface::PixelFormat pixel_format);
14 18
15} // namespace VideoCommon 19} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/decode_bc4.cpp b/src/video_core/texture_cache/decode_bc4.cpp
deleted file mode 100644
index ef98afdca..000000000
--- a/src/video_core/texture_cache/decode_bc4.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <span>
7
8#include "common/assert.h"
9#include "common/common_types.h"
10#include "video_core/texture_cache/decode_bc4.h"
11#include "video_core/texture_cache/types.h"
12
13namespace VideoCommon {
14
15// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
16[[nodiscard]] constexpr u32 DecompressBlock(u64 bits, u32 x, u32 y) {
17 const u32 code_offset = 16 + 3 * (4 * y + x);
18 const u32 code = (bits >> code_offset) & 7;
19 const u32 red0 = (bits >> 0) & 0xff;
20 const u32 red1 = (bits >> 8) & 0xff;
21 if (red0 > red1) {
22 switch (code) {
23 case 0:
24 return red0;
25 case 1:
26 return red1;
27 case 2:
28 return (6 * red0 + 1 * red1) / 7;
29 case 3:
30 return (5 * red0 + 2 * red1) / 7;
31 case 4:
32 return (4 * red0 + 3 * red1) / 7;
33 case 5:
34 return (3 * red0 + 4 * red1) / 7;
35 case 6:
36 return (2 * red0 + 5 * red1) / 7;
37 case 7:
38 return (1 * red0 + 6 * red1) / 7;
39 }
40 } else {
41 switch (code) {
42 case 0:
43 return red0;
44 case 1:
45 return red1;
46 case 2:
47 return (4 * red0 + 1 * red1) / 5;
48 case 3:
49 return (3 * red0 + 2 * red1) / 5;
50 case 4:
51 return (2 * red0 + 3 * red1) / 5;
52 case 5:
53 return (1 * red0 + 4 * red1) / 5;
54 case 6:
55 return 0;
56 case 7:
57 return 0xff;
58 }
59 }
60 return 0;
61}
62
63void DecompressBC4(std::span<const u8> input, Extent3D extent, std::span<u8> output) {
64 UNIMPLEMENTED_IF_MSG(extent.width % 4 != 0, "Unaligned width={}", extent.width);
65 UNIMPLEMENTED_IF_MSG(extent.height % 4 != 0, "Unaligned height={}", extent.height);
66 static constexpr u32 BLOCK_SIZE = 4;
67 size_t input_offset = 0;
68 for (u32 slice = 0; slice < extent.depth; ++slice) {
69 for (u32 block_y = 0; block_y < extent.height / 4; ++block_y) {
70 for (u32 block_x = 0; block_x < extent.width / 4; ++block_x) {
71 u64 bits;
72 std::memcpy(&bits, &input[input_offset], sizeof(bits));
73 input_offset += sizeof(bits);
74
75 for (u32 y = 0; y < BLOCK_SIZE; ++y) {
76 for (u32 x = 0; x < BLOCK_SIZE; ++x) {
77 const u32 linear_z = slice;
78 const u32 linear_y = block_y * BLOCK_SIZE + y;
79 const u32 linear_x = block_x * BLOCK_SIZE + x;
80 const u32 offset_z = linear_z * extent.width * extent.height;
81 const u32 offset_y = linear_y * extent.width;
82 const u32 offset_x = linear_x;
83 const u32 output_offset = (offset_z + offset_y + offset_x) * 4ULL;
84 const u32 color = DecompressBlock(bits, x, y);
85 output[output_offset + 0] = static_cast<u8>(color);
86 output[output_offset + 1] = 0;
87 output[output_offset + 2] = 0;
88 output[output_offset + 3] = 0xff;
89 }
90 }
91 }
92 }
93 }
94}
95
96} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index d3f03a995..79f158db4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -598,14 +598,6 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
598 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 598 [&](ImageId id, Image&) { deleted_images.push_back(id); });
599 for (const ImageId id : deleted_images) { 599 for (const ImageId id : deleted_images) {
600 Image& image = slot_images[id]; 600 Image& image = slot_images[id];
601 if (True(image.flags & ImageFlagBits::CpuModified)) {
602 return;
603 }
604 image.flags |= ImageFlagBits::CpuModified;
605 if (True(image.flags & ImageFlagBits::Tracked)) {
606 UntrackImage(image, id);
607 }
608 /*
609 if (True(image.flags & ImageFlagBits::Remapped)) { 601 if (True(image.flags & ImageFlagBits::Remapped)) {
610 continue; 602 continue;
611 } 603 }
@@ -613,7 +605,6 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
613 if (True(image.flags & ImageFlagBits::Tracked)) { 605 if (True(image.flags & ImageFlagBits::Tracked)) {
614 UntrackImage(image, id); 606 UntrackImage(image, id);
615 } 607 }
616 */
617 } 608 }
618} 609}
619 610
@@ -874,11 +865,19 @@ void TextureCache<P>::PopAsyncFlushes() {
874template <class P> 865template <class P>
875ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { 866ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) {
876 const ImageInfo dst_info(operand); 867 const ImageInfo dst_info(operand);
877 const ImageId image_id = FindDMAImage(dst_info, operand.address); 868 const ImageId dst_id = FindDMAImage(dst_info, operand.address);
878 if (!image_id) { 869 if (!dst_id) {
870 return NULL_IMAGE_ID;
871 }
872 auto& image = slot_images[dst_id];
873 if (False(image.flags & ImageFlagBits::GpuModified)) {
874 // No need to waste time on an image that's synced with guest
875 return NULL_IMAGE_ID;
876 }
877 if (image.info.type == ImageType::e3D) {
878 // Don't accelerate 3D images.
879 return NULL_IMAGE_ID; 879 return NULL_IMAGE_ID;
880 } 880 }
881 auto& image = slot_images[image_id];
882 if (!is_upload && !image.info.dma_downloaded) { 881 if (!is_upload && !image.info.dma_downloaded) {
883 // Force a full sync. 882 // Force a full sync.
884 image.info.dma_downloaded = true; 883 image.info.dma_downloaded = true;
@@ -888,7 +887,7 @@ ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, boo
888 if (!base) { 887 if (!base) {
889 return NULL_IMAGE_ID; 888 return NULL_IMAGE_ID;
890 } 889 }
891 return image_id; 890 return dst_id;
892} 891}
893 892
894template <class P> 893template <class P>
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index f781cb7a0..9a618a57a 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -24,7 +24,7 @@
24#include "video_core/engines/maxwell_3d.h" 24#include "video_core/engines/maxwell_3d.h"
25#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
26#include "video_core/surface.h" 26#include "video_core/surface.h"
27#include "video_core/texture_cache/decode_bc4.h" 27#include "video_core/texture_cache/decode_bc.h"
28#include "video_core/texture_cache/format_lookup_table.h" 28#include "video_core/texture_cache/format_lookup_table.h"
29#include "video_core/texture_cache/formatter.h" 29#include "video_core/texture_cache/formatter.h"
30#include "video_core/texture_cache/samples_helper.h" 30#include "video_core/texture_cache/samples_helper.h"
@@ -61,8 +61,6 @@ using VideoCore::Surface::PixelFormatFromDepthFormat;
61using VideoCore::Surface::PixelFormatFromRenderTargetFormat; 61using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
62using VideoCore::Surface::SurfaceType; 62using VideoCore::Surface::SurfaceType;
63 63
64constexpr u32 CONVERTED_BYTES_PER_BLOCK = BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
65
66struct LevelInfo { 64struct LevelInfo {
67 Extent3D size; 65 Extent3D size;
68 Extent3D block; 66 Extent3D block;
@@ -612,7 +610,8 @@ u32 CalculateConvertedSizeBytes(const ImageInfo& info) noexcept {
612 } 610 }
613 return output_size; 611 return output_size;
614 } 612 }
615 return NumBlocksPerLayer(info, TILE_SIZE) * info.resources.layers * CONVERTED_BYTES_PER_BLOCK; 613 return NumBlocksPerLayer(info, TILE_SIZE) * info.resources.layers *
614 ConvertedBytesPerBlock(info.format);
616} 615}
617 616
618u32 CalculateLayerStride(const ImageInfo& info) noexcept { 617u32 CalculateLayerStride(const ImageInfo& info) noexcept {
@@ -945,7 +944,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
945 tile_size.height, output.subspan(output_offset)); 944 tile_size.height, output.subspan(output_offset));
946 945
947 output_offset += copy.image_extent.width * copy.image_extent.height * 946 output_offset += copy.image_extent.width * copy.image_extent.height *
948 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 947 copy.image_subresource.num_layers *
948 BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
949 } else if (astc) { 949 } else if (astc) {
950 // BC1 uses 0.5 bytes per texel 950 // BC1 uses 0.5 bytes per texel
951 // BC3 uses 1 byte per texel 951 // BC3 uses 1 byte per texel
@@ -956,7 +956,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
956 956
957 const u32 plane_dim = copy.image_extent.width * copy.image_extent.height; 957 const u32 plane_dim = copy.image_extent.width * copy.image_extent.height;
958 const u32 level_size = plane_dim * copy.image_extent.depth * 958 const u32 level_size = plane_dim * copy.image_extent.depth *
959 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 959 copy.image_subresource.num_layers *
960 BytesPerBlock(PixelFormat::A8B8G8R8_UNORM);
960 decode_scratch.resize_destructive(level_size); 961 decode_scratch.resize_destructive(level_size);
961 962
962 Tegra::Texture::ASTC::Decompress( 963 Tegra::Texture::ASTC::Decompress(
@@ -976,10 +977,15 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
976 bpp_div; 977 bpp_div;
977 output_offset += static_cast<u32>(copy.buffer_size); 978 output_offset += static_cast<u32>(copy.buffer_size);
978 } else { 979 } else {
979 DecompressBC4(input_offset, copy.image_extent, output.subspan(output_offset)); 980 const Extent3D image_extent{
980 981 .width = copy.image_extent.width,
982 .height = copy.image_extent.height * copy.image_subresource.num_layers,
983 .depth = copy.image_extent.depth,
984 };
985 DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
981 output_offset += copy.image_extent.width * copy.image_extent.height * 986 output_offset += copy.image_extent.width * copy.image_extent.height *
982 copy.image_subresource.num_layers * CONVERTED_BYTES_PER_BLOCK; 987 copy.image_subresource.num_layers *
988 ConvertedBytesPerBlock(info.format);
983 } 989 }
984 } 990 }
985} 991}
diff --git a/src/video_core/textures/bcn.cpp b/src/video_core/textures/bcn.cpp
index 671212a49..16ddbe320 100644
--- a/src/video_core/textures/bcn.cpp
+++ b/src/video_core/textures/bcn.cpp
@@ -3,7 +3,6 @@
3 3
4#include <stb_dxt.h> 4#include <stb_dxt.h>
5#include <string.h> 5#include <string.h>
6
7#include "common/alignment.h" 6#include "common/alignment.h"
8#include "video_core/textures/bcn.h" 7#include "video_core/textures/bcn.h"
9#include "video_core/textures/workers.h" 8#include "video_core/textures/workers.h"
diff --git a/src/video_core/textures/bcn.h b/src/video_core/textures/bcn.h
index 6464af885..d5d2a16c9 100644
--- a/src/video_core/textures/bcn.h
+++ b/src/video_core/textures/bcn.h
@@ -4,14 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include <span> 6#include <span>
7#include <stdint.h> 7
8#include "common/common_types.h"
8 9
9namespace Tegra::Texture::BCN { 10namespace Tegra::Texture::BCN {
10 11
11void CompressBC1(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 12void CompressBC1(std::span<const u8> data, u32 width, u32 height, u32 depth, std::span<u8> output);
12 std::span<uint8_t> output);
13 13
14void CompressBC3(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 14void CompressBC3(std::span<const u8> data, u32 width, u32 height, u32 depth, std::span<u8> output);
15 std::span<uint8_t> output);
16 15
17} // namespace Tegra::Texture::BCN 16} // namespace Tegra::Texture::BCN
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
index 9de484c29..67e8065a4 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -7,10 +7,10 @@
7 7
8namespace Vulkan { 8namespace Vulkan {
9namespace { 9namespace {
10VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, 10VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
11 VkDebugUtilsMessageTypeFlagsEXT type, 11 VkDebugUtilsMessageTypeFlagsEXT type,
12 const VkDebugUtilsMessengerCallbackDataEXT* data, 12 const VkDebugUtilsMessengerCallbackDataEXT* data,
13 [[maybe_unused]] void* user_data) { 13 [[maybe_unused]] void* user_data) {
14 // Skip logging known false-positive validation errors 14 // Skip logging known false-positive validation errors
15 switch (static_cast<u32>(data->messageIdNumber)) { 15 switch (static_cast<u32>(data->messageIdNumber)) {
16#ifdef ANDROID 16#ifdef ANDROID
@@ -62,9 +62,26 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
62 } 62 }
63 return VK_FALSE; 63 return VK_FALSE;
64} 64}
65
66VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
67 uint64_t object, size_t location, int32_t messageCode,
68 const char* pLayerPrefix, const char* pMessage, void* pUserData) {
69 const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags);
70 const std::string_view message{pMessage};
71 if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
72 LOG_CRITICAL(Render_Vulkan, "{}", message);
73 } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
74 LOG_WARNING(Render_Vulkan, "{}", message);
75 } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
76 LOG_INFO(Render_Vulkan, "{}", message);
77 } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
78 LOG_DEBUG(Render_Vulkan, "{}", message);
79 }
80 return VK_FALSE;
81}
65} // Anonymous namespace 82} // Anonymous namespace
66 83
67vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) { 84vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) {
68 return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{ 85 return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{
69 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 86 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
70 .pNext = nullptr, 87 .pNext = nullptr,
@@ -76,7 +93,18 @@ vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
76 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 93 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
77 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | 94 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
78 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, 95 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
79 .pfnUserCallback = Callback, 96 .pfnUserCallback = DebugUtilCallback,
97 .pUserData = nullptr,
98 });
99}
100
101vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) {
102 return instance.CreateDebugReportCallback({
103 .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
104 .pNext = nullptr,
105 .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
106 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
107 .pfnCallback = DebugReportCallback,
80 .pUserData = nullptr, 108 .pUserData = nullptr,
81 }); 109 });
82} 110}
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h
index 71b1f69ec..a8af7b406 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.h
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.h
@@ -7,6 +7,8 @@
7 7
8namespace Vulkan { 8namespace Vulkan {
9 9
10vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance); 10vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance);
11
12vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance);
11 13
12} // namespace Vulkan 14} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e4ca65b58..421e71e5a 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -349,7 +349,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
349 const bool is_s8gen2 = device_id == 0x43050a01; 349 const bool is_s8gen2 = device_id == 0x43050a01;
350 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; 350 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
351 351
352 if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) { 352 if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) {
353 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway"); 353 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
354 } else if (!is_suitable) { 354 } else if (!is_suitable) {
355 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER); 355 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
@@ -528,6 +528,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
528 } 528 }
529 529
530 sets_per_pool = 64; 530 sets_per_pool = 64;
531 if (extensions.extended_dynamic_state3 && is_amd_driver &&
532 properties.properties.driverVersion >= VK_MAKE_API_VERSION(0, 2, 0, 270)) {
533 LOG_WARNING(Render_Vulkan,
534 "AMD drivers after 23.5.2 have broken extendedDynamicState3ColorBlendEquation");
535 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
536 features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
537 dynamic_state3_blending = false;
538 }
531 if (is_amd_driver) { 539 if (is_amd_driver) {
532 // AMD drivers need a higher amount of Sets per Pool in certain circumstances like in XC2. 540 // AMD drivers need a higher amount of Sets per Pool in certain circumstances like in XC2.
533 sets_per_pool = 96; 541 sets_per_pool = 96;
@@ -905,6 +913,10 @@ bool Device::GetSuitability(bool requires_swapchain) {
905 properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; 913 properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
906 SetNext(next, properties.driver); 914 SetNext(next, properties.driver);
907 915
916 // Retrieve subgroup properties.
917 properties.subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
918 SetNext(next, properties.subgroup_properties);
919
908 // Retrieve relevant extension properties. 920 // Retrieve relevant extension properties.
909 if (extensions.shader_float_controls) { 921 if (extensions.shader_float_controls) {
910 properties.float_controls.sType = 922 properties.float_controls.sType =
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index b84af3dfb..1f17265d5 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -293,6 +293,11 @@ public:
293 return features.features.textureCompressionASTC_LDR; 293 return features.features.textureCompressionASTC_LDR;
294 } 294 }
295 295
296 /// Returns true if BCn is natively supported.
297 bool IsOptimalBcnSupported() const {
298 return features.features.textureCompressionBC;
299 }
300
296 /// Returns true if descriptor aliasing is natively supported. 301 /// Returns true if descriptor aliasing is natively supported.
297 bool IsDescriptorAliasingSupported() const { 302 bool IsDescriptorAliasingSupported() const {
298 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; 303 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
@@ -323,6 +328,11 @@ public:
323 return properties.subgroup_size_control.requiredSubgroupSizeStages & stage; 328 return properties.subgroup_size_control.requiredSubgroupSizeStages & stage;
324 } 329 }
325 330
331 /// Returns true if the device supports the provided subgroup feature.
332 bool IsSubgroupFeatureSupported(VkSubgroupFeatureFlagBits feature) const {
333 return properties.subgroup_properties.supportedOperations & feature;
334 }
335
326 /// Returns the maximum number of push descriptors. 336 /// Returns the maximum number of push descriptors.
327 u32 MaxPushDescriptors() const { 337 u32 MaxPushDescriptors() const {
328 return properties.push_descriptor.maxPushDescriptors; 338 return properties.push_descriptor.maxPushDescriptors;
@@ -388,6 +398,11 @@ public:
388 return extensions.swapchain_mutable_format; 398 return extensions.swapchain_mutable_format;
389 } 399 }
390 400
401 /// Returns true if VK_KHR_shader_float_controls is enabled.
402 bool IsKhrShaderFloatControlsSupported() const {
403 return extensions.shader_float_controls;
404 }
405
391 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 406 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
392 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 407 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
393 return extensions.workgroup_memory_explicit_layout; 408 return extensions.workgroup_memory_explicit_layout;
@@ -413,6 +428,11 @@ public:
413 return extensions.sampler_filter_minmax; 428 return extensions.sampler_filter_minmax;
414 } 429 }
415 430
431 /// Returns true if the device supports VK_EXT_shader_stencil_export.
432 bool IsExtShaderStencilExportSupported() const {
433 return extensions.shader_stencil_export;
434 }
435
416 /// Returns true if the device supports VK_EXT_depth_range_unrestricted. 436 /// Returns true if the device supports VK_EXT_depth_range_unrestricted.
417 bool IsExtDepthRangeUnrestrictedSupported() const { 437 bool IsExtDepthRangeUnrestrictedSupported() const {
418 return extensions.depth_range_unrestricted; 438 return extensions.depth_range_unrestricted;
@@ -482,9 +502,9 @@ public:
482 return extensions.vertex_input_dynamic_state; 502 return extensions.vertex_input_dynamic_state;
483 } 503 }
484 504
485 /// Returns true if the device supports VK_EXT_shader_stencil_export. 505 /// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation
486 bool IsExtShaderStencilExportSupported() const { 506 bool IsExtShaderDemoteToHelperInvocationSupported() const {
487 return extensions.shader_stencil_export; 507 return extensions.shader_demote_to_helper_invocation;
488 } 508 }
489 509
490 /// Returns true if the device supports VK_EXT_conservative_rasterization. 510 /// Returns true if the device supports VK_EXT_conservative_rasterization.
@@ -518,12 +538,12 @@ public:
518 if (extensions.spirv_1_4) { 538 if (extensions.spirv_1_4) {
519 return 0x00010400U; 539 return 0x00010400U;
520 } 540 }
521 return 0x00010000U; 541 return 0x00010300U;
522 } 542 }
523 543
524 /// Returns true when a known debugging tool is attached. 544 /// Returns true when a known debugging tool is attached.
525 bool HasDebuggingToolAttached() const { 545 bool HasDebuggingToolAttached() const {
526 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue(); 546 return has_renderdoc || has_nsight_graphics;
527 } 547 }
528 548
529 /// @returns True if compute pipelines can cause crashing. 549 /// @returns True if compute pipelines can cause crashing.
@@ -588,6 +608,10 @@ public:
588 return properties.properties.limits.maxVertexInputBindings; 608 return properties.properties.limits.maxVertexInputBindings;
589 } 609 }
590 610
611 u32 GetMaxViewports() const {
612 return properties.properties.limits.maxViewports;
613 }
614
591 bool SupportsConditionalBarriers() const { 615 bool SupportsConditionalBarriers() const {
592 return supports_conditional_barriers; 616 return supports_conditional_barriers;
593 } 617 }
@@ -680,6 +704,7 @@ private:
680 704
681 struct Properties { 705 struct Properties {
682 VkPhysicalDeviceDriverProperties driver{}; 706 VkPhysicalDeviceDriverProperties driver{};
707 VkPhysicalDeviceSubgroupProperties subgroup_properties{};
683 VkPhysicalDeviceFloatControlsProperties float_controls{}; 708 VkPhysicalDeviceFloatControlsProperties float_controls{};
684 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; 709 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{};
685 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; 710 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index b6d83e446..6a294c1da 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -19,11 +19,9 @@
19#include <windows.h> 19#include <windows.h>
20// ensure include order 20// ensure include order
21#include <vulkan/vulkan_win32.h> 21#include <vulkan/vulkan_win32.h>
22#elif defined(__APPLE__)
23#include <vulkan/vulkan_macos.h>
24#elif defined(__ANDROID__) 22#elif defined(__ANDROID__)
25#include <vulkan/vulkan_android.h> 23#include <vulkan/vulkan_android.h>
26#else 24#elif !defined(__APPLE__)
27#include <X11/Xlib.h> 25#include <X11/Xlib.h>
28#include <vulkan/vulkan_wayland.h> 26#include <vulkan/vulkan_wayland.h>
29#include <vulkan/vulkan_xlib.h> 27#include <vulkan/vulkan_xlib.h>
@@ -31,10 +29,34 @@
31 29
32namespace Vulkan { 30namespace Vulkan {
33namespace { 31namespace {
32
33[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
34 std::span<const char* const> extensions) {
35 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
36 if (!properties) {
37 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
38 return false;
39 }
40 for (const char* extension : extensions) {
41 const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
42 return std::strcmp(extension, prop.extensionName) == 0;
43 });
44 if (it == properties->end()) {
45 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
46 return false;
47 }
48 }
49 return true;
50}
51
34[[nodiscard]] std::vector<const char*> RequiredExtensions( 52[[nodiscard]] std::vector<const char*> RequiredExtensions(
35 Core::Frontend::WindowSystemType window_type, bool enable_validation) { 53 const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type,
54 bool enable_validation) {
36 std::vector<const char*> extensions; 55 std::vector<const char*> extensions;
37 extensions.reserve(6); 56 extensions.reserve(6);
57#ifdef __APPLE__
58 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
59#endif
38 switch (window_type) { 60 switch (window_type) {
39 case Core::Frontend::WindowSystemType::Headless: 61 case Core::Frontend::WindowSystemType::Headless:
40 break; 62 break;
@@ -44,7 +66,7 @@ namespace {
44 break; 66 break;
45#elif defined(__APPLE__) 67#elif defined(__APPLE__)
46 case Core::Frontend::WindowSystemType::Cocoa: 68 case Core::Frontend::WindowSystemType::Cocoa:
47 extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
48 break; 70 break;
49#elif defined(__ANDROID__) 71#elif defined(__ANDROID__)
50 case Core::Frontend::WindowSystemType::Android: 72 case Core::Frontend::WindowSystemType::Android:
@@ -66,35 +88,14 @@ namespace {
66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 88 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67 } 89 }
68 if (enable_validation) { 90 if (enable_validation) {
69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 91 const bool debug_utils =
92 AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME});
93 extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME
94 : VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
70 } 95 }
71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
72
73#ifdef __APPLE__
74 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
75#endif
76 return extensions; 96 return extensions;
77} 97}
78 98
79[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
80 std::span<const char* const> extensions) {
81 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
82 if (!properties) {
83 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
84 return false;
85 }
86 for (const char* extension : extensions) {
87 const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
88 return std::strcmp(extension, prop.extensionName) == 0;
89 });
90 if (it == properties->end()) {
91 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
92 return false;
93 }
94 }
95 return true;
96}
97
98[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) { 99[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
99 std::vector<const char*> layers; 100 std::vector<const char*> layers;
100 if (enable_validation) { 101 if (enable_validation) {
@@ -138,7 +139,8 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); 139 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 140 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
140 } 141 }
141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation); 142 const std::vector<const char*> extensions =
143 RequiredExtensions(dld, window_type, enable_validation);
142 if (!AreExtensionsSupported(dld, extensions)) { 144 if (!AreExtensionsSupported(dld, extensions)) {
143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 145 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
144 } 146 }
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
index c34599365..cfea4cd7b 100644
--- a/src/video_core/vulkan_common/vulkan_surface.cpp
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -11,11 +11,9 @@
11#include <windows.h> 11#include <windows.h>
12// ensure include order 12// ensure include order
13#include <vulkan/vulkan_win32.h> 13#include <vulkan/vulkan_win32.h>
14#elif defined(__APPLE__)
15#include <vulkan/vulkan_macos.h>
16#elif defined(__ANDROID__) 14#elif defined(__ANDROID__)
17#include <vulkan/vulkan_android.h> 15#include <vulkan/vulkan_android.h>
18#else 16#elif !defined(__APPLE__)
19#include <X11/Xlib.h> 17#include <X11/Xlib.h>
20#include <vulkan/vulkan_wayland.h> 18#include <vulkan/vulkan_wayland.h>
21#include <vulkan/vulkan_xlib.h> 19#include <vulkan/vulkan_xlib.h>
@@ -44,12 +42,13 @@ vk::SurfaceKHR CreateSurface(
44 } 42 }
45#elif defined(__APPLE__) 43#elif defined(__APPLE__)
46 if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { 44 if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
47 const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 45 const VkMetalSurfaceCreateInfoEXT macos_ci = {
48 nullptr, 0, window_info.render_surface}; 46 .pLayer = static_cast<const CAMetalLayer*>(window_info.render_surface),
49 const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>( 47 };
50 dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK")); 48 const auto vkCreateMetalSurfaceEXT = reinterpret_cast<PFN_vkCreateMetalSurfaceEXT>(
51 if (!vkCreateMacOSSurfaceMVK || 49 dld.vkGetInstanceProcAddr(*instance, "vkCreateMetalSurfaceEXT"));
52 vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { 50 if (!vkCreateMetalSurfaceEXT ||
51 vkCreateMetalSurfaceEXT(*instance, &macos_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
53 LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); 52 LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface");
54 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 53 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
55 } 54 }
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 28fcb21a0..2fa29793a 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -259,7 +259,9 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
259 // These functions may fail to load depending on the enabled extensions. 259 // These functions may fail to load depending on the enabled extensions.
260 // Don't return a failure on these. 260 // Don't return a failure on these.
261 X(vkCreateDebugUtilsMessengerEXT); 261 X(vkCreateDebugUtilsMessengerEXT);
262 X(vkCreateDebugReportCallbackEXT);
262 X(vkDestroyDebugUtilsMessengerEXT); 263 X(vkDestroyDebugUtilsMessengerEXT);
264 X(vkDestroyDebugReportCallbackEXT);
263 X(vkDestroySurfaceKHR); 265 X(vkDestroySurfaceKHR);
264 X(vkGetPhysicalDeviceFeatures2); 266 X(vkGetPhysicalDeviceFeatures2);
265 X(vkGetPhysicalDeviceProperties2); 267 X(vkGetPhysicalDeviceProperties2);
@@ -481,6 +483,11 @@ void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle,
481 dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr); 483 dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr);
482} 484}
483 485
486void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle,
487 const InstanceDispatch& dld) noexcept {
488 dld.vkDestroyDebugReportCallbackEXT(instance, handle, nullptr);
489}
490
484void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { 491void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
485 dld.vkDestroySurfaceKHR(instance, handle, nullptr); 492 dld.vkDestroySurfaceKHR(instance, handle, nullptr);
486} 493}
@@ -549,6 +556,13 @@ DebugUtilsMessenger Instance::CreateDebugUtilsMessenger(
549 return DebugUtilsMessenger(object, handle, *dld); 556 return DebugUtilsMessenger(object, handle, *dld);
550} 557}
551 558
559DebugReportCallback Instance::CreateDebugReportCallback(
560 const VkDebugReportCallbackCreateInfoEXT& create_info) const {
561 VkDebugReportCallbackEXT object;
562 Check(dld->vkCreateDebugReportCallbackEXT(handle, &create_info, nullptr, &object));
563 return DebugReportCallback(object, handle, *dld);
564}
565
552void Image::SetObjectNameEXT(const char* name) const { 566void Image::SetObjectNameEXT(const char* name) const {
553 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); 567 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name);
554} 568}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 44fce47a5..32bd75ad8 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -15,6 +15,8 @@
15#define VK_NO_PROTOTYPES 15#define VK_NO_PROTOTYPES
16#ifdef _WIN32 16#ifdef _WIN32
17#define VK_USE_PLATFORM_WIN32_KHR 17#define VK_USE_PLATFORM_WIN32_KHR
18#elif defined(__APPLE__)
19#define VK_USE_PLATFORM_METAL_EXT
18#endif 20#endif
19#include <vulkan/vulkan.h> 21#include <vulkan/vulkan.h>
20 22
@@ -164,8 +166,10 @@ struct InstanceDispatch {
164 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{}; 166 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
165 167
166 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{}; 168 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{};
169 PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT{};
167 PFN_vkCreateDevice vkCreateDevice{}; 170 PFN_vkCreateDevice vkCreateDevice{};
168 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{}; 171 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{};
172 PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT{};
169 PFN_vkDestroyDevice vkDestroyDevice{}; 173 PFN_vkDestroyDevice vkDestroyDevice{};
170 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{}; 174 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{};
171 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; 175 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
@@ -366,6 +370,7 @@ void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept;
366void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept; 370void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept;
367void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept; 371void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept;
368void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept; 372void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept;
373void Destroy(VkInstance, VkDebugReportCallbackEXT, const InstanceDispatch&) noexcept;
369void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept; 374void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept;
370 375
371VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept; 376VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept;
@@ -581,6 +586,7 @@ private:
581}; 586};
582 587
583using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; 588using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
589using DebugReportCallback = Handle<VkDebugReportCallbackEXT, VkInstance, InstanceDispatch>;
584using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; 590using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
585using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>; 591using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>;
586using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; 592using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
@@ -613,6 +619,11 @@ public:
613 DebugUtilsMessenger CreateDebugUtilsMessenger( 619 DebugUtilsMessenger CreateDebugUtilsMessenger(
614 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const; 620 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
615 621
622 /// Creates a debug report callback.
623 /// @throw Exception on creation failure.
624 DebugReportCallback CreateDebugReportCallback(
625 const VkDebugReportCallbackCreateInfoEXT& create_info) const;
626
616 /// Returns dispatch table. 627 /// Returns dispatch table.
617 const InstanceDispatch& Dispatch() const noexcept { 628 const InstanceDispatch& Dispatch() const noexcept {
618 return *dld; 629 return *dld;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 29467d380..195d3556c 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -503,8 +503,7 @@ void Config::ReadMousePanningValues() {
503 ReadBasicSetting(Settings::values.mouse_panning); 503 ReadBasicSetting(Settings::values.mouse_panning);
504 ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity); 504 ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity);
505 ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity); 505 ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity);
506 ReadBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight); 506 ReadBasicSetting(Settings::values.mouse_panning_deadzone_counterweight);
507 ReadBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight);
508 ReadBasicSetting(Settings::values.mouse_panning_decay_strength); 507 ReadBasicSetting(Settings::values.mouse_panning_decay_strength);
509 ReadBasicSetting(Settings::values.mouse_panning_min_decay); 508 ReadBasicSetting(Settings::values.mouse_panning_min_decay);
510} 509}
@@ -1122,8 +1121,7 @@ void Config::SaveMousePanningValues() {
1122 // Don't overwrite values.mouse_panning 1121 // Don't overwrite values.mouse_panning
1123 WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity); 1122 WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity);
1124 WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity); 1123 WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity);
1125 WriteBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight); 1124 WriteBasicSetting(Settings::values.mouse_panning_deadzone_counterweight);
1126 WriteBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight);
1127 WriteBasicSetting(Settings::values.mouse_panning_decay_strength); 1125 WriteBasicSetting(Settings::values.mouse_panning_decay_strength);
1128 WriteBasicSetting(Settings::values.mouse_panning_min_decay); 1126 WriteBasicSetting(Settings::values.mouse_panning_min_decay);
1129} 1127}
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 43f6c7b50..611a79477 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -3105,21 +3105,6 @@
3105 </property> 3105 </property>
3106 <item> 3106 <item>
3107 <widget class="QPushButton" name="mousePanningButton"> 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"> 3108 <property name="text">
3124 <string>Configure</string> 3109 <string>Configure</string>
3125 </property> 3110 </property>
diff --git a/src/yuzu/configuration/configure_mouse_panning.cpp b/src/yuzu/configuration/configure_mouse_panning.cpp
index f183d2740..e37c546b0 100644
--- a/src/yuzu/configuration/configure_mouse_panning.cpp
+++ b/src/yuzu/configuration/configure_mouse_panning.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 <QCloseEvent> 4#include <QCloseEvent>
5#include <QMessageBox>
5 6
6#include "common/settings.h" 7#include "common/settings.h"
7#include "ui_configure_mouse_panning.h" 8#include "ui_configure_mouse_panning.h"
@@ -27,31 +28,34 @@ void ConfigureMousePanning::SetConfiguration(float right_stick_deadzone, float r
27 ui->enable->setChecked(Settings::values.mouse_panning.GetValue()); 28 ui->enable->setChecked(Settings::values.mouse_panning.GetValue());
28 ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetValue()); 29 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->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetValue());
30 ui->deadzone_x_counterweight->setValue( 31 ui->deadzone_counterweight->setValue(
31 Settings::values.mouse_panning_deadzone_x_counterweight.GetValue()); 32 Settings::values.mouse_panning_deadzone_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()); 33 ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetValue());
35 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetValue()); 34 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetValue());
36 35
37 if (right_stick_deadzone > 0.0f || right_stick_range != 1.0f) { 36 if (right_stick_deadzone > 0.0f || right_stick_range != 1.0f) {
38 ui->warning_label->setText(QString::fromStdString( 37 const QString right_stick_deadzone_str =
39 "Mouse panning works better with a deadzone of 0% and a range of 100%.\n" 38 QString::fromStdString(std::to_string(static_cast<int>(right_stick_deadzone * 100.0f)));
40 "Current values are " + 39 const QString right_stick_range_str =
41 std::to_string(static_cast<int>(right_stick_deadzone * 100.0f)) + "% and " + 40 QString::fromStdString(std::to_string(static_cast<int>(right_stick_range * 100.0f)));
42 std::to_string(static_cast<int>(right_stick_range * 100.0f)) + "% respectively.")); 41
43 } else { 42 ui->warning_label->setText(
44 ui->warning_label->hide(); 43 tr("Mouse panning works better with a deadzone of 0% and a range of 100%.\nCurrent "
44 "values are %1% and %2% respectively.")
45 .arg(right_stick_deadzone_str, right_stick_range_str));
46 }
47
48 if (Settings::values.mouse_enabled) {
49 ui->warning_label->setText(
50 tr("Emulated mouse is enabled. This is incompatible with mouse panning."));
45 } 51 }
46} 52}
47 53
48void ConfigureMousePanning::SetDefaultConfiguration() { 54void ConfigureMousePanning::SetDefaultConfiguration() {
49 ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetDefault()); 55 ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetDefault());
50 ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetDefault()); 56 ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetDefault());
51 ui->deadzone_x_counterweight->setValue( 57 ui->deadzone_counterweight->setValue(
52 Settings::values.mouse_panning_deadzone_x_counterweight.GetDefault()); 58 Settings::values.mouse_panning_deadzone_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()); 59 ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetDefault());
56 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetDefault()); 60 ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetDefault());
57} 61}
@@ -68,12 +72,19 @@ void ConfigureMousePanning::ApplyConfiguration() {
68 Settings::values.mouse_panning = ui->enable->isChecked(); 72 Settings::values.mouse_panning = ui->enable->isChecked();
69 Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value()); 73 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()); 74 Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value());
71 Settings::values.mouse_panning_deadzone_x_counterweight = 75 Settings::values.mouse_panning_deadzone_counterweight =
72 static_cast<float>(ui->deadzone_x_counterweight->value()); 76 static_cast<float>(ui->deadzone_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()); 77 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()); 78 Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value());
77 79
80 if (Settings::values.mouse_enabled && Settings::values.mouse_panning) {
81 Settings::values.mouse_panning = false;
82 QMessageBox::critical(
83 this, tr("Emulated mouse is enabled"),
84 tr("Real mouse input and mouse panning are incompatible. Please disable the "
85 "emulated mouse in input advanced settings to allow mouse panning."));
86 return;
87 }
88
78 accept(); 89 accept();
79} 90}
diff --git a/src/yuzu/configuration/configure_mouse_panning.ui b/src/yuzu/configuration/configure_mouse_panning.ui
index 75795b727..84fb7ee80 100644
--- a/src/yuzu/configuration/configure_mouse_panning.ui
+++ b/src/yuzu/configuration/configure_mouse_panning.ui
@@ -9,10 +9,10 @@
9 <item> 9 <item>
10 <widget class="QCheckBox" name="enable"> 10 <widget class="QCheckBox" name="enable">
11 <property name="text"> 11 <property name="text">
12 <string>Enable</string> 12 <string>Enable mouse panning</string>
13 </property> 13 </property>
14 <property name="toolTip"> 14 <property name="toolTip">
15 <string>Can be toggled via a hotkey</string> 15 <string>Can be toggled via a hotkey. Default hotkey is Ctrl + F9</string>
16 </property> 16 </property>
17 </widget> 17 </widget>
18 </item> 18 </item>
@@ -89,40 +89,14 @@
89 </property> 89 </property>
90 <layout class="QGridLayout"> 90 <layout class="QGridLayout">
91 <item row="0" column="0"> 91 <item row="0" column="0">
92 <widget class="QLabel" name="deadzone_x_counterweight_label"> 92 <widget class="QLabel" name="deadzone_counterweight_label">
93 <property name="text"> 93 <property name="text">
94 <string>Horizontal</string> 94 <string>Deadzone</string>
95 </property> 95 </property>
96 </widget> 96 </widget>
97 </item> 97 </item>
98 <item row="0" column="1"> 98 <item row="0" column="1">
99 <widget class="QSpinBox" name="deadzone_x_counterweight"> 99 <widget class="QSpinBox" name="deadzone_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"> 100 <property name="alignment">
127 <set>Qt::AlignCenter</set> 101 <set>Qt::AlignCenter</set>
128 </property> 102 </property>
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 71afbc423..f83705544 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -305,6 +305,9 @@ void ConfigureRingController::EnableRingController() {
305 QMessageBox::warning(this, dialog_title, 305 QMessageBox::warning(this, dialog_title,
306 tr("The current mapped device doesn't have a ring attached")); 306 tr("The current mapped device doesn't have a ring attached"));
307 break; 307 break;
308 case Common::Input::DriverResult::InvalidHandle:
309 QMessageBox::warning(this, dialog_title, tr("The current mapped device is not connected"));
310 break;
308 default: 311 default:
309 QMessageBox::warning(this, dialog_title, 312 QMessageBox::warning(this, dialog_title,
310 tr("Unexpected driver result %1").arg(static_cast<int>(result))); 313 tr("Unexpected driver result %1").arg(static_cast<int>(result)));
diff --git a/src/yuzu/qt_common.cpp b/src/yuzu/qt_common.cpp
index 5d0fd7674..413402165 100644
--- a/src/yuzu/qt_common.cpp
+++ b/src/yuzu/qt_common.cpp
@@ -10,6 +10,8 @@
10 10
11#if !defined(WIN32) && !defined(__APPLE__) 11#if !defined(WIN32) && !defined(__APPLE__)
12#include <qpa/qplatformnativeinterface.h> 12#include <qpa/qplatformnativeinterface.h>
13#elif defined(__APPLE__)
14#include <objc/message.h>
13#endif 15#endif
14 16
15namespace QtCommon { 17namespace QtCommon {
@@ -37,9 +39,12 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
37 Core::Frontend::EmuWindow::WindowSystemInfo wsi; 39 Core::Frontend::EmuWindow::WindowSystemInfo wsi;
38 wsi.type = GetWindowSystemType(); 40 wsi.type = GetWindowSystemType();
39 41
42#if defined(WIN32)
40 // Our Win32 Qt external doesn't have the private API. 43 // Our Win32 Qt external doesn't have the private API.
41#if defined(WIN32) || defined(__APPLE__) 44 wsi.render_surface = reinterpret_cast<void*>(window->winId());
42 wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; 45#elif defined(__APPLE__)
46 wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)(
47 reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
43#else 48#else
44 QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); 49 QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
45 wsi.display_connection = pni->nativeResourceForWindow("display", window); 50 wsi.display_connection = pni->nativeResourceForWindow("display", window);
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
index 7c26a3dc7..e1a0e6a2a 100644
--- a/src/yuzu/vk_device_info.cpp
+++ b/src/yuzu/vk_device_info.cpp
@@ -26,7 +26,10 @@ Record::~Record() = default;
26void PopulateRecords(std::vector<Record>& records, QWindow* window) try { 26void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
27 using namespace Vulkan; 27 using namespace Vulkan;
28 28
29 auto wsi = QtCommon::GetWindowSystemInfo(window); 29 // Create a test window with a Vulkan surface type for checking present modes.
30 QWindow test_window(window);
31 test_window.setSurfaceType(QWindow::VulkanSurface);
32 auto wsi = QtCommon::GetWindowSystemInfo(&test_window);
30 33
31 vk::InstanceDispatch dld; 34 vk::InstanceDispatch dld;
32 const auto library = OpenLibrary(); 35 const auto library = OpenLibrary();