summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/address_space.h7
-rw-r--r--src/common/alignment.h18
-rw-r--r--src/common/atomic_helpers.h2
-rw-r--r--src/common/bit_util.h6
-rw-r--r--src/common/concepts.h6
-rw-r--r--src/common/div_ceil.h4
-rw-r--r--src/common/expected.h60
-rw-r--r--src/common/input.h2
-rw-r--r--src/common/intrusive_red_black_tree.h20
-rw-r--r--src/common/make_unique_for_overwrite.h8
-rw-r--r--src/common/polyfill_ranges.h8
-rw-r--r--src/common/polyfill_thread.h87
-rw-r--r--src/common/settings.h12
-rw-r--r--src/common/string_util.cpp2
-rw-r--r--src/common/string_util.h3
-rw-r--r--src/common/tree.h74
-rw-r--r--src/common/vector_math.h16
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hardware_properties.h20
-rw-r--r--src/core/hid/emulated_controller.cpp75
-rw-r--r--src/core/hid/emulated_controller.h6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp30
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_auto_object.h20
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp358
-rw-r--r--src/core/hle/kernel/k_capabilities.h295
-rw-r--r--src/core/hle/kernel/k_device_address_space.cpp150
-rw-r--r--src/core/hle/kernel/k_device_address_space.h60
-rw-r--r--src/core/hle/kernel/k_priority_queue.h54
-rw-r--r--src/core/hle/kernel/k_scoped_lock.h11
-rw-r--r--src/core/hle/kernel/k_thread.h8
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h6
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc_types.h19
-rw-r--r--src/core/hle/kernel/svc_version.h58
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp11
-rw-r--r--src/core/hle/service/glue/arp.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp8
-rw-r--r--src/core/hle/service/hid/controllers/npad.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h3
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h3
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h2
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h2
-rw-r--r--src/core/hle/service/jit/jit.cpp4
-rw-r--r--src/core/hle/service/ldn/ldn.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h22
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp35
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h22
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h12
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp4
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h4
-rw-r--r--src/core/hle/service/nvflinger/parcel.h87
-rw-r--r--src/core/hle/service/prepo/prepo.cpp8
-rw-r--r--src/core/hle/service/sockets/bsd.cpp15
-rw-r--r--src/core/hle/service/sockets/bsd.h23
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp2
-rw-r--r--src/core/hle/service/ssl/ssl.cpp8
-rw-r--r--src/core/hle/service/vi/vi.cpp4
-rw-r--r--src/core/internal_network/network.cpp4
-rw-r--r--src/core/internal_network/socket_proxy.cpp4
-rw-r--r--src/core/internal_network/socket_proxy.h5
-rw-r--r--src/core/internal_network/sockets.h9
-rw-r--r--src/core/reporter.cpp2
-rw-r--r--src/core/reporter.h4
-rw-r--r--src/input_common/drivers/joycon.cpp4
-rw-r--r--src/input_common/drivers/joycon.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp18
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp210
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h18
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp157
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h52
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp12
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp13
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h186
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp68
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h8
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp12
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp45
-rw-r--r--src/input_common/input_poller.cpp30
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp23
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp53
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp49
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp31
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h3
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h1
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp9
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h8
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc6
-rw-r--r--src/shader_recompiler/frontend/ir/value.h24
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp8
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp28
-rw-r--r--src/shader_recompiler/object_pool.h4
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt2
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.vert10
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h10
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp126
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp10
-rw-r--r--src/video_core/texture_cache/descriptor_table.h4
-rw-r--r--src/video_core/texture_cache/image_info.cpp4
-rw-r--r--src/video_core/texture_cache/samples_helper.h44
-rw-r--r--src/video_core/texture_cache/slot_vector.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp17
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp35
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h2
-rw-r--r--src/yuzu/configuration/input_profiles.cpp7
-rw-r--r--src/yuzu/multiplayer/lobby.cpp16
-rw-r--r--src/yuzu/multiplayer/lobby.h2
-rw-r--r--src/yuzu/multiplayer/lobby.ui7
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h13
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp48
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/yuzu.cpp55
154 files changed, 2689 insertions, 1093 deletions
diff --git a/src/common/address_space.h b/src/common/address_space.h
index 9222b2fdc..8683c23c3 100644
--- a/src/common/address_space.h
+++ b/src/common/address_space.h
@@ -12,7 +12,8 @@
12 12
13namespace Common { 13namespace Common {
14template <typename VaType, size_t AddressSpaceBits> 14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; 15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >=
16AddressSpaceBits;
16 17
17struct EmptyStruct {}; 18struct EmptyStruct {};
18 19
@@ -21,7 +22,7 @@ struct EmptyStruct {};
21 */ 22 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, 23template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
23 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> 24 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
24requires AddressSpaceValid<VaType, AddressSpaceBits> 25 requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap { 26class FlatAddressSpaceMap {
26public: 27public:
27 /// The maximum VA that this AS can technically reach 28 /// The maximum VA that this AS can technically reach
@@ -109,7 +110,7 @@ private:
109 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block 110 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
110 */ 111 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> 112template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits> 113 requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator 114class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { 115 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private: 116private:
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 7e897334b..fa715d497 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -10,7 +10,7 @@
10namespace Common { 10namespace Common {
11 11
12template <typename T> 12template <typename T>
13requires std::is_unsigned_v<T> 13 requires std::is_unsigned_v<T>
14[[nodiscard]] constexpr T AlignUp(T value, size_t size) { 14[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
15 auto mod{static_cast<T>(value % size)}; 15 auto mod{static_cast<T>(value % size)};
16 value -= mod; 16 value -= mod;
@@ -18,31 +18,31 @@ requires std::is_unsigned_v<T>
18} 18}
19 19
20template <typename T> 20template <typename T>
21requires std::is_unsigned_v<T> 21 requires std::is_unsigned_v<T>
22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { 22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); 23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
24} 24}
25 25
26template <typename T> 26template <typename T>
27requires std::is_unsigned_v<T> 27 requires std::is_unsigned_v<T>
28[[nodiscard]] constexpr T AlignDown(T value, size_t size) { 28[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
29 return static_cast<T>(value - value % size); 29 return static_cast<T>(value - value % size);
30} 30}
31 31
32template <typename T> 32template <typename T>
33requires std::is_unsigned_v<T> 33 requires std::is_unsigned_v<T>
34[[nodiscard]] constexpr bool Is4KBAligned(T value) { 34[[nodiscard]] constexpr bool Is4KBAligned(T value) {
35 return (value & 0xFFF) == 0; 35 return (value & 0xFFF) == 0;
36} 36}
37 37
38template <typename T> 38template <typename T>
39requires std::is_unsigned_v<T> 39 requires std::is_unsigned_v<T>
40[[nodiscard]] constexpr bool IsWordAligned(T value) { 40[[nodiscard]] constexpr bool IsWordAligned(T value) {
41 return (value & 0b11) == 0; 41 return (value & 0b11) == 0;
42} 42}
43 43
44template <typename T> 44template <typename T>
45requires std::is_integral_v<T> 45 requires std::is_integral_v<T>
46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { 46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
47 using U = typename std::make_unsigned_t<T>; 47 using U = typename std::make_unsigned_t<T>;
48 const U mask = static_cast<U>(alignment - 1); 48 const U mask = static_cast<U>(alignment - 1);
@@ -50,7 +50,7 @@ requires std::is_integral_v<T>
50} 50}
51 51
52template <typename T, typename U> 52template <typename T, typename U>
53requires std::is_integral_v<T> 53 requires std::is_integral_v<T>
54[[nodiscard]] constexpr T DivideUp(T x, U y) { 54[[nodiscard]] constexpr T DivideUp(T x, U y) {
55 return (x + (y - 1)) / y; 55 return (x + (y - 1)) / y;
56} 56}
@@ -73,11 +73,11 @@ public:
73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
74 74
75 [[nodiscard]] T* allocate(size_type n) { 75 [[nodiscard]] T* allocate(size_type n) {
76 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); 76 return static_cast<T*>(::operator new(n * sizeof(T), std::align_val_t{Align}));
77 } 77 }
78 78
79 void deallocate(T* p, size_type n) { 79 void deallocate(T* p, size_type n) {
80 ::operator delete (p, n * sizeof(T), std::align_val_t{Align}); 80 ::operator delete(p, n * sizeof(T), std::align_val_t{Align});
81 } 81 }
82 82
83 template <typename T2> 83 template <typename T2>
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index aef3b66a4..d997f10ba 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -75,7 +75,7 @@ extern "C" void AnnotateHappensAfter(const char*, int, void*);
75#if defined(AE_VCPP) || defined(AE_ICC) 75#if defined(AE_VCPP) || defined(AE_ICC)
76#define AE_FORCEINLINE __forceinline 76#define AE_FORCEINLINE __forceinline
77#elif defined(AE_GCC) 77#elif defined(AE_GCC)
78//#define AE_FORCEINLINE __attribute__((always_inline)) 78// #define AE_FORCEINLINE __attribute__((always_inline))
79#define AE_FORCEINLINE inline 79#define AE_FORCEINLINE inline
80#else 80#else
81#define AE_FORCEINLINE inline 81#define AE_FORCEINLINE inline
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index e4e6287f3..13368b439 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -45,19 +45,19 @@ template <typename T>
45} 45}
46 46
47template <typename T> 47template <typename T>
48requires std::is_unsigned_v<T> 48 requires std::is_unsigned_v<T>
49[[nodiscard]] constexpr bool IsPow2(T value) { 49[[nodiscard]] constexpr bool IsPow2(T value) {
50 return std::has_single_bit(value); 50 return std::has_single_bit(value);
51} 51}
52 52
53template <typename T> 53template <typename T>
54requires std::is_integral_v<T> 54 requires std::is_integral_v<T>
55[[nodiscard]] T NextPow2(T value) { 55[[nodiscard]] T NextPow2(T value) {
56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
57} 57}
58 58
59template <size_t bit_index, typename T> 59template <size_t bit_index, typename T>
60requires std::is_integral_v<T> 60 requires std::is_integral_v<T>
61[[nodiscard]] constexpr bool Bit(const T value) { 61[[nodiscard]] constexpr bool Bit(const T value) {
62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); 62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
63 return ((value >> bit_index) & T(1)) == T(1); 63 return ((value >> bit_index) & T(1)) == T(1);
diff --git a/src/common/concepts.h b/src/common/concepts.h
index a9acff3e7..61df1d32a 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -16,9 +16,9 @@ concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
16// is available on all supported platforms. 16// is available on all supported platforms.
17template <typename Derived, typename Base> 17template <typename Derived, typename Base>
18concept DerivedFrom = requires { 18concept DerivedFrom = requires {
19 std::is_base_of_v<Base, Derived>; 19 std::is_base_of_v<Base, Derived>;
20 std::is_convertible_v<const volatile Derived*, const volatile Base*>; 20 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
21}; 21 };
22 22
23// TODO: Replace with std::convertible_to when libc++ implements it. 23// TODO: Replace with std::convertible_to when libc++ implements it.
24template <typename From, typename To> 24template <typename From, typename To>
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index eebc279c2..c12477d42 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -10,14 +10,14 @@ namespace Common {
10 10
11/// Ceiled integer division. 11/// Ceiled integer division.
12template <typename N, typename D> 12template <typename N, typename D>
13requires std::is_integral_v<N> && std::is_unsigned_v<D> 13 requires std::is_integral_v<N> && std::is_unsigned_v<D>
14[[nodiscard]] constexpr N DivCeil(N number, D divisor) { 14[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); 15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
16} 16}
17 17
18/// Ceiled integer division with logarithmic divisor in base 2 18/// Ceiled integer division with logarithmic divisor in base 2
19template <typename N, typename D> 19template <typename N, typename D>
20requires std::is_integral_v<N> && std::is_unsigned_v<D> 20 requires std::is_integral_v<N> && std::is_unsigned_v<D>
21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { 21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); 22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
23} 23}
diff --git a/src/common/expected.h b/src/common/expected.h
index 6e6c86ee7..5fccfbcbd 100644
--- a/src/common/expected.h
+++ b/src/common/expected.h
@@ -64,7 +64,7 @@ struct no_init_t {
64 * Additionally, this requires E to be trivially destructible 64 * Additionally, this requires E to be trivially destructible
65 */ 65 */
66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> 66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
67requires std::is_trivially_destructible_v<E> 67 requires std::is_trivially_destructible_v<E>
68struct expected_storage_base { 68struct expected_storage_base {
69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
70 70
@@ -111,7 +111,7 @@ struct expected_storage_base {
111 * Additionally, this requires E to be trivially destructible 111 * Additionally, this requires E to be trivially destructible
112 */ 112 */
113template <typename T, typename E> 113template <typename T, typename E>
114requires std::is_trivially_destructible_v<E> 114 requires std::is_trivially_destructible_v<E>
115struct expected_storage_base<T, E, true> { 115struct expected_storage_base<T, E, true> {
116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
117 117
@@ -251,7 +251,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
251 * Additionally, this requires E to be trivially copy constructible 251 * Additionally, this requires E to be trivially copy constructible
252 */ 252 */
253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> 253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
254requires std::is_trivially_copy_constructible_v<E> 254 requires std::is_trivially_copy_constructible_v<E>
255struct expected_copy_base : expected_operations_base<T, E> { 255struct expected_copy_base : expected_operations_base<T, E> {
256 using expected_operations_base<T, E>::expected_operations_base; 256 using expected_operations_base<T, E>::expected_operations_base;
257}; 257};
@@ -261,7 +261,7 @@ struct expected_copy_base : expected_operations_base<T, E> {
261 * Additionally, this requires E to be trivially copy constructible 261 * Additionally, this requires E to be trivially copy constructible
262 */ 262 */
263template <typename T, typename E> 263template <typename T, typename E>
264requires std::is_trivially_copy_constructible_v<E> 264 requires std::is_trivially_copy_constructible_v<E>
265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { 265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
266 using expected_operations_base<T, E>::expected_operations_base; 266 using expected_operations_base<T, E>::expected_operations_base;
267 267
@@ -289,7 +289,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
289 * Additionally, this requires E to be trivially move constructible 289 * Additionally, this requires E to be trivially move constructible
290 */ 290 */
291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> 291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
292requires std::is_trivially_move_constructible_v<E> 292 requires std::is_trivially_move_constructible_v<E>
293struct expected_move_base : expected_copy_base<T, E> { 293struct expected_move_base : expected_copy_base<T, E> {
294 using expected_copy_base<T, E>::expected_copy_base; 294 using expected_copy_base<T, E>::expected_copy_base;
295}; 295};
@@ -299,7 +299,7 @@ struct expected_move_base : expected_copy_base<T, E> {
299 * Additionally, this requires E to be trivially move constructible 299 * Additionally, this requires E to be trivially move constructible
300 */ 300 */
301template <typename T, typename E> 301template <typename T, typename E>
302requires std::is_trivially_move_constructible_v<E> 302 requires std::is_trivially_move_constructible_v<E>
303struct expected_move_base<T, E, false> : expected_copy_base<T, E> { 303struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
304 using expected_copy_base<T, E>::expected_copy_base; 304 using expected_copy_base<T, E>::expected_copy_base;
305 305
@@ -330,9 +330,9 @@ template <typename T, typename E,
330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, 330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
331 std::is_trivially_copy_constructible<T>, 331 std::is_trivially_copy_constructible<T>,
332 std::is_trivially_destructible<T>>> 332 std::is_trivially_destructible<T>>>
333requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 333 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
334 std::is_trivially_copy_constructible<E>, 334 std::is_trivially_copy_constructible<E>,
335 std::is_trivially_destructible<E>> 335 std::is_trivially_destructible<E>>
336struct expected_copy_assign_base : expected_move_base<T, E> { 336struct expected_copy_assign_base : expected_move_base<T, E> {
337 using expected_move_base<T, E>::expected_move_base; 337 using expected_move_base<T, E>::expected_move_base;
338}; 338};
@@ -342,9 +342,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> {
342 * Additionally, this requires E to be trivially copy assignable 342 * Additionally, this requires E to be trivially copy assignable
343 */ 343 */
344template <typename T, typename E> 344template <typename T, typename E>
345requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 345 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
346 std::is_trivially_copy_constructible<E>, 346 std::is_trivially_copy_constructible<E>,
347 std::is_trivially_destructible<E>> 347 std::is_trivially_destructible<E>>
348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { 348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
349 using expected_move_base<T, E>::expected_move_base; 349 using expected_move_base<T, E>::expected_move_base;
350 350
@@ -371,9 +371,9 @@ template <typename T, typename E,
371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>, 371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
372 std::is_trivially_move_constructible<T>, 372 std::is_trivially_move_constructible<T>,
373 std::is_trivially_destructible<T>>> 373 std::is_trivially_destructible<T>>>
374requires std::conjunction_v<std::is_trivially_move_assignable<E>, 374 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
375 std::is_trivially_move_constructible<E>, 375 std::is_trivially_move_constructible<E>,
376 std::is_trivially_destructible<E>> 376 std::is_trivially_destructible<E>>
377struct expected_move_assign_base : expected_copy_assign_base<T, E> { 377struct expected_move_assign_base : expected_copy_assign_base<T, E> {
378 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 378 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
379}; 379};
@@ -383,9 +383,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> {
383 * Additionally, this requires E to be trivially move assignable 383 * Additionally, this requires E to be trivially move assignable
384 */ 384 */
385template <typename T, typename E> 385template <typename T, typename E>
386requires std::conjunction_v<std::is_trivially_move_assignable<E>, 386 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
387 std::is_trivially_move_constructible<E>, 387 std::is_trivially_move_constructible<E>,
388 std::is_trivially_destructible<E>> 388 std::is_trivially_destructible<E>>
389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { 389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
390 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 390 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
391 391
@@ -412,7 +412,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E>
412 */ 412 */
413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, 413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
414 bool EnableMove = std::is_move_constructible_v<T>> 414 bool EnableMove = std::is_move_constructible_v<T>>
415requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 415 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
416struct expected_delete_ctor_base { 416struct expected_delete_ctor_base {
417 expected_delete_ctor_base() = default; 417 expected_delete_ctor_base() = default;
418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -422,7 +422,7 @@ struct expected_delete_ctor_base {
422}; 422};
423 423
424template <typename T, typename E> 424template <typename T, typename E>
425requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 425 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
426struct expected_delete_ctor_base<T, E, true, false> { 426struct expected_delete_ctor_base<T, E, true, false> {
427 expected_delete_ctor_base() = default; 427 expected_delete_ctor_base() = default;
428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -432,7 +432,7 @@ struct expected_delete_ctor_base<T, E, true, false> {
432}; 432};
433 433
434template <typename T, typename E> 434template <typename T, typename E>
435requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 435 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
436struct expected_delete_ctor_base<T, E, false, true> { 436struct expected_delete_ctor_base<T, E, false, true> {
437 expected_delete_ctor_base() = default; 437 expected_delete_ctor_base() = default;
438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -442,7 +442,7 @@ struct expected_delete_ctor_base<T, E, false, true> {
442}; 442};
443 443
444template <typename T, typename E> 444template <typename T, typename E>
445requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 445 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
446struct expected_delete_ctor_base<T, E, false, false> { 446struct expected_delete_ctor_base<T, E, false, false> {
447 expected_delete_ctor_base() = default; 447 expected_delete_ctor_base() = default;
448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -460,8 +460,8 @@ template <
460 typename T, typename E, 460 typename T, typename E,
461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, 461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> 462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
463requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 463 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
464 std::is_copy_assignable<E>, std::is_move_assignable<E>> 464 std::is_copy_assignable<E>, std::is_move_assignable<E>>
465struct expected_delete_assign_base { 465struct expected_delete_assign_base {
466 expected_delete_assign_base() = default; 466 expected_delete_assign_base() = default;
467 expected_delete_assign_base(const expected_delete_assign_base&) = default; 467 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -471,8 +471,8 @@ struct expected_delete_assign_base {
471}; 471};
472 472
473template <typename T, typename E> 473template <typename T, typename E>
474requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 474 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
475 std::is_copy_assignable<E>, std::is_move_assignable<E>> 475 std::is_copy_assignable<E>, std::is_move_assignable<E>>
476struct expected_delete_assign_base<T, E, true, false> { 476struct expected_delete_assign_base<T, E, true, false> {
477 expected_delete_assign_base() = default; 477 expected_delete_assign_base() = default;
478 expected_delete_assign_base(const expected_delete_assign_base&) = default; 478 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -482,8 +482,8 @@ struct expected_delete_assign_base<T, E, true, false> {
482}; 482};
483 483
484template <typename T, typename E> 484template <typename T, typename E>
485requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 485 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
486 std::is_copy_assignable<E>, std::is_move_assignable<E>> 486 std::is_copy_assignable<E>, std::is_move_assignable<E>>
487struct expected_delete_assign_base<T, E, false, true> { 487struct expected_delete_assign_base<T, E, false, true> {
488 expected_delete_assign_base() = default; 488 expected_delete_assign_base() = default;
489 expected_delete_assign_base(const expected_delete_assign_base&) = default; 489 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -493,8 +493,8 @@ struct expected_delete_assign_base<T, E, false, true> {
493}; 493};
494 494
495template <typename T, typename E> 495template <typename T, typename E>
496requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 496 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
497 std::is_copy_assignable<E>, std::is_move_assignable<E>> 497 std::is_copy_assignable<E>, std::is_move_assignable<E>>
498struct expected_delete_assign_base<T, E, false, false> { 498struct expected_delete_assign_base<T, E, false, false> {
499 expected_delete_assign_base() = default; 499 expected_delete_assign_base() = default;
500 expected_delete_assign_base(const expected_delete_assign_base&) = default; 500 expected_delete_assign_base(const expected_delete_assign_base&) = default;
diff --git a/src/common/input.h b/src/common/input.h
index d61cd7ca8..b5748a6c8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -130,6 +130,8 @@ struct ButtonStatus {
130 bool inverted{}; 130 bool inverted{};
131 // Press once to activate, press again to release 131 // Press once to activate, press again to release
132 bool toggle{}; 132 bool toggle{};
133 // Spams the button when active
134 bool turbo{};
133 // Internal lock for the toggle status 135 // Internal lock for the toggle status
134 bool locked{}; 136 bool locked{};
135}; 137};
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 93046615e..5f6b34e82 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -242,19 +242,21 @@ public:
242 242
243template <typename T> 243template <typename T>
244concept HasRedBlackKeyType = requires { 244concept HasRedBlackKeyType = requires {
245 { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; 245 {
246}; 246 std::is_same<typename T::RedBlackKeyType, void>::value
247 } -> std::convertible_to<bool>;
248 };
247 249
248namespace impl { 250namespace impl {
249 251
250 template <typename T, typename Default> 252template <typename T, typename Default>
251 consteval auto* GetRedBlackKeyType() { 253consteval auto* GetRedBlackKeyType() {
252 if constexpr (HasRedBlackKeyType<T>) { 254 if constexpr (HasRedBlackKeyType<T>) {
253 return static_cast<typename T::RedBlackKeyType*>(nullptr); 255 return static_cast<typename T::RedBlackKeyType*>(nullptr);
254 } else { 256 } else {
255 return static_cast<Default*>(nullptr); 257 return static_cast<Default*>(nullptr);
256 }
257 } 258 }
259}
258 260
259} // namespace impl 261} // namespace impl
260 262
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h
index c7413cf51..17f81bba4 100644
--- a/src/common/make_unique_for_overwrite.h
+++ b/src/common/make_unique_for_overwrite.h
@@ -9,17 +9,19 @@
9namespace Common { 9namespace Common {
10 10
11template <class T> 11template <class T>
12requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { 12 requires(!std::is_array_v<T>)
13std::unique_ptr<T> make_unique_for_overwrite() {
13 return std::unique_ptr<T>(new T); 14 return std::unique_ptr<T>(new T);
14} 15}
15 16
16template <class T> 17template <class T>
17requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { 18 requires std::is_unbounded_array_v<T>
19std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
18 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); 20 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
19} 21}
20 22
21template <class T, class... Args> 23template <class T, class... Args>
22requires std::is_bounded_array_v<T> 24 requires std::is_bounded_array_v<T>
23void make_unique_for_overwrite(Args&&...) = delete; 25void make_unique_for_overwrite(Args&&...) = delete;
24 26
25} // namespace Common 27} // namespace Common
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
index ca44bfaef..512dbcbcb 100644
--- a/src/common/polyfill_ranges.h
+++ b/src/common/polyfill_ranges.h
@@ -18,9 +18,9 @@ namespace ranges {
18 18
19template <typename T> 19template <typename T>
20concept range = requires(T& t) { 20concept range = requires(T& t) {
21 begin(t); 21 begin(t);
22 end(t); 22 end(t);
23}; 23 };
24 24
25template <typename T> 25template <typename T>
26concept input_range = range<T>; 26concept input_range = range<T>;
@@ -421,7 +421,7 @@ struct generate_fn {
421 } 421 }
422 422
423 template <typename R, std::copy_constructible F> 423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R> 424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { 425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); 426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 } 427 }
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index b2c929d2f..b5ef055db 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -41,17 +41,18 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep,
41#include <chrono> 41#include <chrono>
42#include <condition_variable> 42#include <condition_variable>
43#include <functional> 43#include <functional>
44#include <list> 44#include <map>
45#include <memory> 45#include <memory>
46#include <mutex> 46#include <mutex>
47#include <optional> 47#include <optional>
48#include <thread> 48#include <thread>
49#include <type_traits> 49#include <type_traits>
50#include <utility>
50 51
51namespace std { 52namespace std {
52namespace polyfill { 53namespace polyfill {
53 54
54using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
55 56
56class stop_state { 57class stop_state {
57public: 58public:
@@ -59,61 +60,69 @@ public:
59 ~stop_state() = default; 60 ~stop_state() = default;
60 61
61 bool request_stop() { 62 bool request_stop() {
62 stop_state_callbacks callbacks; 63 unique_lock lk{m_lock};
63 64
64 { 65 if (m_stop_requested) {
65 scoped_lock lk{m_lock}; 66 // Already set, nothing to do.
67 return false;
68 }
66 69
67 if (m_stop_requested.load()) { 70 // Mark stop requested.
68 // Already set, nothing to do 71 m_stop_requested = true;
69 return false;
70 }
71 72
72 // Set as requested 73 while (!m_callbacks.empty()) {
73 m_stop_requested = true; 74 // Get an iterator to the first element.
75 const auto it = m_callbacks.begin();
74 76
75 // Copy callback list 77 // Move the callback function out of the map.
76 callbacks = m_callbacks; 78 function<void()> f;
77 } 79 swap(it->second, f);
80
81 // Erase the now-empty map element.
82 m_callbacks.erase(it);
78 83
79 for (auto callback : callbacks) { 84 // Run the callback.
80 callback(); 85 if (f) {
86 f();
87 }
81 } 88 }
82 89
83 return true; 90 return true;
84 } 91 }
85 92
86 bool stop_requested() const { 93 bool stop_requested() const {
87 return m_stop_requested.load(); 94 unique_lock lk{m_lock};
95 return m_stop_requested;
88 } 96 }
89 97
90 stop_state_callbacks::const_iterator insert_callback(function<void()> f) { 98 stop_state_callback insert_callback(function<void()> f) {
91 stop_state_callbacks::const_iterator ret{}; 99 unique_lock lk{m_lock};
92 bool should_run{};
93
94 {
95 scoped_lock lk{m_lock};
96 should_run = m_stop_requested.load();
97 m_callbacks.push_front(f);
98 ret = m_callbacks.begin();
99 }
100 100
101 if (should_run) { 101 if (m_stop_requested) {
102 f(); 102 // Stop already requested. Don't insert anything,
103 // just run the callback synchronously.
104 if (f) {
105 f();
106 }
107 return 0;
103 } 108 }
104 109
110 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f));
105 return ret; 113 return ret;
106 } 114 }
107 115
108 void remove_callback(stop_state_callbacks::const_iterator it) { 116 void remove_callback(stop_state_callback cb) {
109 scoped_lock lk{m_lock}; 117 unique_lock lk{m_lock};
110 m_callbacks.erase(it); 118 m_callbacks.erase(cb);
111 } 119 }
112 120
113private: 121private:
114 mutex m_lock; 122 mutable recursive_mutex m_lock;
115 atomic<bool> m_stop_requested; 123 map<stop_state_callback, function<void()>> m_callbacks;
116 stop_state_callbacks m_callbacks; 124 stop_state_callback m_next_callback{0};
125 bool m_stop_requested{false};
117}; 126};
118 127
119} // namespace polyfill 128} // namespace polyfill
@@ -204,7 +213,7 @@ public:
204 using callback_type = Callback; 213 using callback_type = Callback;
205 214
206 template <typename C> 215 template <typename C>
207 requires constructible_from<Callback, C> 216 requires constructible_from<Callback, C>
208 explicit stop_callback(const stop_token& st, 217 explicit stop_callback(const stop_token& st,
209 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 218 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
210 : m_stop_state(st.m_stop_state) { 219 : m_stop_state(st.m_stop_state) {
@@ -213,7 +222,7 @@ public:
213 } 222 }
214 } 223 }
215 template <typename C> 224 template <typename C>
216 requires constructible_from<Callback, C> 225 requires constructible_from<Callback, C>
217 explicit stop_callback(stop_token&& st, 226 explicit stop_callback(stop_token&& st,
218 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 227 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
219 : m_stop_state(move(st.m_stop_state)) { 228 : m_stop_state(move(st.m_stop_state)) {
@@ -223,7 +232,7 @@ public:
223 } 232 }
224 ~stop_callback() { 233 ~stop_callback() {
225 if (m_stop_state && m_callback) { 234 if (m_stop_state && m_callback) {
226 m_stop_state->remove_callback(*m_callback); 235 m_stop_state->remove_callback(m_callback);
227 } 236 }
228 } 237 }
229 238
@@ -234,7 +243,7 @@ public:
234 243
235private: 244private:
236 shared_ptr<polyfill::stop_state> m_stop_state; 245 shared_ptr<polyfill::stop_state> m_stop_state;
237 optional<polyfill::stop_state_callbacks::const_iterator> m_callback; 246 polyfill::stop_state_callback m_callback;
238}; 247};
239 248
240template <typename Callback> 249template <typename Callback>
diff --git a/src/common/settings.h b/src/common/settings.h
index 4b4da4da2..64db66f37 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -131,7 +131,8 @@ public:
131 * @param default_val Intial value of the setting, and default value of the setting 131 * @param default_val Intial value of the setting, and default value of the setting
132 * @param name Label for the setting 132 * @param name Label for the setting
133 */ 133 */
134 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) 134 explicit Setting(const Type& default_val, const std::string& name)
135 requires(!ranged)
135 : value{default_val}, default_value{default_val}, label{name} {} 136 : value{default_val}, default_value{default_val}, label{name} {}
136 virtual ~Setting() = default; 137 virtual ~Setting() = default;
137 138
@@ -144,7 +145,8 @@ public:
144 * @param name Label for the setting 145 * @param name Label for the setting
145 */ 146 */
146 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 147 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
147 const std::string& name) requires(ranged) 148 const std::string& name)
149 requires(ranged)
148 : value{default_val}, 150 : value{default_val},
149 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} 151 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
150 152
@@ -232,7 +234,8 @@ public:
232 * @param default_val Intial value of the setting, and default value of the setting 234 * @param default_val Intial value of the setting, and default value of the setting
233 * @param name Label for the setting 235 * @param name Label for the setting
234 */ 236 */
235 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) 237 explicit SwitchableSetting(const Type& default_val, const std::string& name)
238 requires(!ranged)
236 : Setting<Type>{default_val, name} {} 239 : Setting<Type>{default_val, name} {}
237 virtual ~SwitchableSetting() = default; 240 virtual ~SwitchableSetting() = default;
238 241
@@ -245,7 +248,8 @@ public:
245 * @param name Label for the setting 248 * @param name Label for the setting
246 */ 249 */
247 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, 250 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
248 const std::string& name) requires(ranged) 251 const std::string& name)
252 requires(ranged)
249 : Setting<Type, true>{default_val, min_val, max_val, name} {} 253 : Setting<Type, true>{default_val, min_val, max_val, name} {}
250 254
251 /** 255 /**
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index b26db4796..e0b6180c5 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -30,7 +30,7 @@ std::string ToUpper(std::string str) {
30 return str; 30 return str;
31} 31}
32 32
33std::string StringFromBuffer(const std::vector<u8>& data) { 33std::string StringFromBuffer(std::span<const u8> data) {
34 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); 34 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
35} 35}
36 36
diff --git a/src/common/string_util.h b/src/common/string_util.h
index ce18a33cf..f8aecc875 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <span>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -17,7 +18,7 @@ namespace Common {
17/// Make a string uppercase 18/// Make a string uppercase
18[[nodiscard]] std::string ToUpper(std::string str); 19[[nodiscard]] std::string ToUpper(std::string str);
19 20
20[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data); 21[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
21 22
22[[nodiscard]] std::string StripSpaces(const std::string& s); 23[[nodiscard]] std::string StripSpaces(const std::string& s);
23[[nodiscard]] std::string StripQuotes(const std::string& s); 24[[nodiscard]] std::string StripQuotes(const std::string& s);
diff --git a/src/common/tree.h b/src/common/tree.h
index f77859209..f4fc43de3 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -103,12 +103,12 @@ concept IsRBEntry = CheckRBEntry<T>::value;
103 103
104template <typename T> 104template <typename T>
105concept HasRBEntry = requires(T& t, const T& ct) { 105concept HasRBEntry = requires(T& t, const T& ct) {
106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; 106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; 107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
108}; 108 };
109 109
110template <typename T> 110template <typename T>
111requires HasRBEntry<T> 111 requires HasRBEntry<T>
112class RBHead { 112class RBHead {
113private: 113private:
114 T* m_rbh_root = nullptr; 114 T* m_rbh_root = nullptr;
@@ -130,90 +130,90 @@ public:
130}; 130};
131 131
132template <typename T> 132template <typename T>
133requires HasRBEntry<T> 133 requires HasRBEntry<T>
134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { 134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
135 return t->GetRBEntry(); 135 return t->GetRBEntry();
136} 136}
137template <typename T> 137template <typename T>
138requires HasRBEntry<T> 138 requires HasRBEntry<T>
139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { 139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
140 return t->GetRBEntry(); 140 return t->GetRBEntry();
141} 141}
142 142
143template <typename T> 143template <typename T>
144requires HasRBEntry<T> 144 requires HasRBEntry<T>
145[[nodiscard]] constexpr T* RB_LEFT(T* t) { 145[[nodiscard]] constexpr T* RB_LEFT(T* t) {
146 return RB_ENTRY(t).Left(); 146 return RB_ENTRY(t).Left();
147} 147}
148template <typename T> 148template <typename T>
149requires HasRBEntry<T> 149 requires HasRBEntry<T>
150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) { 150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
151 return RB_ENTRY(t).Left(); 151 return RB_ENTRY(t).Left();
152} 152}
153 153
154template <typename T> 154template <typename T>
155requires HasRBEntry<T> 155 requires HasRBEntry<T>
156[[nodiscard]] constexpr T* RB_RIGHT(T* t) { 156[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
157 return RB_ENTRY(t).Right(); 157 return RB_ENTRY(t).Right();
158} 158}
159template <typename T> 159template <typename T>
160requires HasRBEntry<T> 160 requires HasRBEntry<T>
161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { 161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
162 return RB_ENTRY(t).Right(); 162 return RB_ENTRY(t).Right();
163} 163}
164 164
165template <typename T> 165template <typename T>
166requires HasRBEntry<T> 166 requires HasRBEntry<T>
167[[nodiscard]] constexpr T* RB_PARENT(T* t) { 167[[nodiscard]] constexpr T* RB_PARENT(T* t) {
168 return RB_ENTRY(t).Parent(); 168 return RB_ENTRY(t).Parent();
169} 169}
170template <typename T> 170template <typename T>
171requires HasRBEntry<T> 171 requires HasRBEntry<T>
172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) { 172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
173 return RB_ENTRY(t).Parent(); 173 return RB_ENTRY(t).Parent();
174} 174}
175 175
176template <typename T> 176template <typename T>
177requires HasRBEntry<T> 177 requires HasRBEntry<T>
178constexpr void RB_SET_LEFT(T* t, T* e) { 178constexpr void RB_SET_LEFT(T* t, T* e) {
179 RB_ENTRY(t).SetLeft(e); 179 RB_ENTRY(t).SetLeft(e);
180} 180}
181template <typename T> 181template <typename T>
182requires HasRBEntry<T> 182 requires HasRBEntry<T>
183constexpr void RB_SET_RIGHT(T* t, T* e) { 183constexpr void RB_SET_RIGHT(T* t, T* e) {
184 RB_ENTRY(t).SetRight(e); 184 RB_ENTRY(t).SetRight(e);
185} 185}
186template <typename T> 186template <typename T>
187requires HasRBEntry<T> 187 requires HasRBEntry<T>
188constexpr void RB_SET_PARENT(T* t, T* e) { 188constexpr void RB_SET_PARENT(T* t, T* e) {
189 RB_ENTRY(t).SetParent(e); 189 RB_ENTRY(t).SetParent(e);
190} 190}
191 191
192template <typename T> 192template <typename T>
193requires HasRBEntry<T> 193 requires HasRBEntry<T>
194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { 194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
195 return RB_ENTRY(t).IsBlack(); 195 return RB_ENTRY(t).IsBlack();
196} 196}
197template <typename T> 197template <typename T>
198requires HasRBEntry<T> 198 requires HasRBEntry<T>
199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) { 199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
200 return RB_ENTRY(t).IsRed(); 200 return RB_ENTRY(t).IsRed();
201} 201}
202 202
203template <typename T> 203template <typename T>
204requires HasRBEntry<T> 204 requires HasRBEntry<T>
205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { 205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
206 return RB_ENTRY(t).Color(); 206 return RB_ENTRY(t).Color();
207} 207}
208 208
209template <typename T> 209template <typename T>
210requires HasRBEntry<T> 210 requires HasRBEntry<T>
211constexpr void RB_SET_COLOR(T* t, RBColor c) { 211constexpr void RB_SET_COLOR(T* t, RBColor c) {
212 RB_ENTRY(t).SetColor(c); 212 RB_ENTRY(t).SetColor(c);
213} 213}
214 214
215template <typename T> 215template <typename T>
216requires HasRBEntry<T> 216 requires HasRBEntry<T>
217constexpr void RB_SET(T* elm, T* parent) { 217constexpr void RB_SET(T* elm, T* parent) {
218 auto& rb_entry = RB_ENTRY(elm); 218 auto& rb_entry = RB_ENTRY(elm);
219 rb_entry.SetParent(parent); 219 rb_entry.SetParent(parent);
@@ -223,14 +223,14 @@ constexpr void RB_SET(T* elm, T* parent) {
223} 223}
224 224
225template <typename T> 225template <typename T>
226requires HasRBEntry<T> 226 requires HasRBEntry<T>
227constexpr void RB_SET_BLACKRED(T* black, T* red) { 227constexpr void RB_SET_BLACKRED(T* black, T* red) {
228 RB_SET_COLOR(black, RBColor::RB_BLACK); 228 RB_SET_COLOR(black, RBColor::RB_BLACK);
229 RB_SET_COLOR(red, RBColor::RB_RED); 229 RB_SET_COLOR(red, RBColor::RB_RED);
230} 230}
231 231
232template <typename T> 232template <typename T>
233requires HasRBEntry<T> 233 requires HasRBEntry<T>
234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { 234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
235 tmp = RB_RIGHT(elm); 235 tmp = RB_RIGHT(elm);
236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { 236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
@@ -252,7 +252,7 @@ constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
252} 252}
253 253
254template <typename T> 254template <typename T>
255requires HasRBEntry<T> 255 requires HasRBEntry<T>
256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { 256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
257 tmp = RB_LEFT(elm); 257 tmp = RB_LEFT(elm);
258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { 258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
@@ -274,7 +274,7 @@ constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
274} 274}
275 275
276template <typename T> 276template <typename T>
277requires HasRBEntry<T> 277 requires HasRBEntry<T>
278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { 278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
279 T* tmp; 279 T* tmp;
280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { 280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
@@ -358,7 +358,7 @@ constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
358} 358}
359 359
360template <typename T> 360template <typename T>
361requires HasRBEntry<T> 361 requires HasRBEntry<T>
362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { 362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
363 T* child = nullptr; 363 T* child = nullptr;
364 T* parent = nullptr; 364 T* parent = nullptr;
@@ -451,7 +451,7 @@ constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
451} 451}
452 452
453template <typename T> 453template <typename T>
454requires HasRBEntry<T> 454 requires HasRBEntry<T>
455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { 455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
456 T *parent = nullptr, *tmp = nullptr; 456 T *parent = nullptr, *tmp = nullptr;
457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { 457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
@@ -499,7 +499,7 @@ constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
499} 499}
500 500
501template <typename T, typename Compare> 501template <typename T, typename Compare>
502requires HasRBEntry<T> 502 requires HasRBEntry<T>
503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { 503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
504 T* parent = nullptr; 504 T* parent = nullptr;
505 T* tmp = head.Root(); 505 T* tmp = head.Root();
@@ -534,7 +534,7 @@ constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
534} 534}
535 535
536template <typename T, typename Compare> 536template <typename T, typename Compare>
537requires HasRBEntry<T> 537 requires HasRBEntry<T>
538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { 538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
539 T* tmp = head.Root(); 539 T* tmp = head.Root();
540 540
@@ -553,7 +553,7 @@ constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
553} 553}
554 554
555template <typename T, typename Compare> 555template <typename T, typename Compare>
556requires HasRBEntry<T> 556 requires HasRBEntry<T>
557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { 557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
558 T* tmp = head.Root(); 558 T* tmp = head.Root();
559 T* res = nullptr; 559 T* res = nullptr;
@@ -574,7 +574,7 @@ constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
574} 574}
575 575
576template <typename T, typename U, typename Compare> 576template <typename T, typename U, typename Compare>
577requires HasRBEntry<T> 577 requires HasRBEntry<T>
578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
579 T* tmp = head.Root(); 579 T* tmp = head.Root();
580 580
@@ -593,7 +593,7 @@ constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
593} 593}
594 594
595template <typename T, typename U, typename Compare> 595template <typename T, typename U, typename Compare>
596requires HasRBEntry<T> 596 requires HasRBEntry<T>
597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
598 T* tmp = head.Root(); 598 T* tmp = head.Root();
599 T* res = nullptr; 599 T* res = nullptr;
@@ -614,7 +614,7 @@ constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
614} 614}
615 615
616template <typename T, typename Compare> 616template <typename T, typename Compare>
617requires HasRBEntry<T> 617 requires HasRBEntry<T>
618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { 618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
619 T* tmp = head.Root(); 619 T* tmp = head.Root();
620 620
@@ -631,7 +631,7 @@ constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
631} 631}
632 632
633template <typename T, typename U, typename Compare> 633template <typename T, typename U, typename Compare>
634requires HasRBEntry<T> 634 requires HasRBEntry<T>
635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { 635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
636 T* tmp = head.Root(); 636 T* tmp = head.Root();
637 637
@@ -648,7 +648,7 @@ constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
648} 648}
649 649
650template <typename T> 650template <typename T>
651requires HasRBEntry<T> 651 requires HasRBEntry<T>
652constexpr T* RB_NEXT(T* elm) { 652constexpr T* RB_NEXT(T* elm) {
653 if (RB_RIGHT(elm)) { 653 if (RB_RIGHT(elm)) {
654 elm = RB_RIGHT(elm); 654 elm = RB_RIGHT(elm);
@@ -669,7 +669,7 @@ constexpr T* RB_NEXT(T* elm) {
669} 669}
670 670
671template <typename T> 671template <typename T>
672requires HasRBEntry<T> 672 requires HasRBEntry<T>
673constexpr T* RB_PREV(T* elm) { 673constexpr T* RB_PREV(T* elm) {
674 if (RB_LEFT(elm)) { 674 if (RB_LEFT(elm)) {
675 elm = RB_LEFT(elm); 675 elm = RB_LEFT(elm);
@@ -690,7 +690,7 @@ constexpr T* RB_PREV(T* elm) {
690} 690}
691 691
692template <typename T> 692template <typename T>
693requires HasRBEntry<T> 693 requires HasRBEntry<T>
694constexpr T* RB_MIN(RBHead<T>& head) { 694constexpr T* RB_MIN(RBHead<T>& head) {
695 T* tmp = head.Root(); 695 T* tmp = head.Root();
696 T* parent = nullptr; 696 T* parent = nullptr;
@@ -704,7 +704,7 @@ constexpr T* RB_MIN(RBHead<T>& head) {
704} 704}
705 705
706template <typename T> 706template <typename T>
707requires HasRBEntry<T> 707 requires HasRBEntry<T>
708constexpr T* RB_MAX(RBHead<T>& head) { 708constexpr T* RB_MAX(RBHead<T>& head) {
709 T* tmp = head.Root(); 709 T* tmp = head.Root();
710 T* parent = nullptr; 710 T* parent = nullptr;
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index e62eeea2e..0e2095c45 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -348,9 +348,7 @@ public:
348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
349// component names (x<->r) and permutations (xy<->yx) 349// component names (x<->r) and permutations (xy<->yx)
350#define _DEFINE_SWIZZLER2(a, b, name) \ 350#define _DEFINE_SWIZZLER2(a, b, name) \
351 [[nodiscard]] constexpr Vec2<T> name() const { \ 351 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
352 return Vec2<T>(a, b); \
353 }
354#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 352#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
355 _DEFINE_SWIZZLER2(a, b, a##b); \ 353 _DEFINE_SWIZZLER2(a, b, a##b); \
356 _DEFINE_SWIZZLER2(a, b, a2##b2); \ 354 _DEFINE_SWIZZLER2(a, b, a2##b2); \
@@ -543,9 +541,7 @@ public:
543// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 541// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
544// permutations (xy<->yx) 542// permutations (xy<->yx)
545#define _DEFINE_SWIZZLER2(a, b, name) \ 543#define _DEFINE_SWIZZLER2(a, b, name) \
546 [[nodiscard]] constexpr Vec2<T> name() const { \ 544 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
547 return Vec2<T>(a, b); \
548 }
549#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 545#define DEFINE_SWIZZLER2_COMP1(a, a2) \
550 _DEFINE_SWIZZLER2(a, a, a##a); \ 546 _DEFINE_SWIZZLER2(a, a, a##a); \
551 _DEFINE_SWIZZLER2(a, a, a2##a2) 547 _DEFINE_SWIZZLER2(a, a, a2##a2)
@@ -570,9 +566,7 @@ public:
570#undef _DEFINE_SWIZZLER2 566#undef _DEFINE_SWIZZLER2
571 567
572#define _DEFINE_SWIZZLER3(a, b, c, name) \ 568#define _DEFINE_SWIZZLER3(a, b, c, name) \
573 [[nodiscard]] constexpr Vec3<T> name() const { \ 569 [[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
574 return Vec3<T>(a, b, c); \
575 }
576#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 570#define DEFINE_SWIZZLER3_COMP1(a, a2) \
577 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ 571 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \
578 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) 572 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
@@ -641,8 +635,8 @@ template <typename T>
641 635
642// linear interpolation via float: 0.0=begin, 1.0=end 636// linear interpolation via float: 0.0=begin, 1.0=end
643template <typename X> 637template <typename X>
644[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) 638[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
645 Lerp(const X& begin, const X& end, const float t) { 639 const float t) {
646 return begin * (1.f - t) + end * t; 640 return begin * (1.f - t) + end * t;
647} 641}
648 642
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5afdeb5ff..112c61b80 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -182,6 +182,8 @@ add_library(core STATIC
182 hle/kernel/k_auto_object_container.cpp 182 hle/kernel/k_auto_object_container.cpp
183 hle/kernel/k_auto_object_container.h 183 hle/kernel/k_auto_object_container.h
184 hle/kernel/k_affinity_mask.h 184 hle/kernel/k_affinity_mask.h
185 hle/kernel/k_capabilities.cpp
186 hle/kernel/k_capabilities.h
185 hle/kernel/k_class_token.cpp 187 hle/kernel/k_class_token.cpp
186 hle/kernel/k_class_token.h 188 hle/kernel/k_class_token.h
187 hle/kernel/k_client_port.cpp 189 hle/kernel/k_client_port.cpp
@@ -193,6 +195,8 @@ add_library(core STATIC
193 hle/kernel/k_condition_variable.cpp 195 hle/kernel/k_condition_variable.cpp
194 hle/kernel/k_condition_variable.h 196 hle/kernel/k_condition_variable.h
195 hle/kernel/k_debug.h 197 hle/kernel/k_debug.h
198 hle/kernel/k_device_address_space.cpp
199 hle/kernel/k_device_address_space.h
196 hle/kernel/k_dynamic_page_manager.h 200 hle/kernel/k_dynamic_page_manager.h
197 hle/kernel/k_dynamic_resource_manager.h 201 hle/kernel/k_dynamic_resource_manager.h
198 hle/kernel/k_dynamic_slab_heap.h 202 hle/kernel/k_dynamic_slab_heap.h
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index 13cbdb734..45567b840 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -25,6 +25,26 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
26}; 26};
27 27
28static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
29
30static constexpr inline u64 VirtualCoreMask = [] {
31 u64 mask = 0;
32 for (size_t i = 0; i < NumVirtualCores; ++i) {
33 mask |= (UINT64_C(1) << i);
34 }
35 return mask;
36}();
37
38static constexpr inline u64 ConvertVirtualCoreMaskToPhysical(u64 v_core_mask) {
39 u64 p_core_mask = 0;
40 while (v_core_mask != 0) {
41 const u64 next = std::countr_zero(v_core_mask);
42 v_core_mask &= ~(static_cast<u64>(1) << next);
43 p_core_mask |= (static_cast<u64>(1) << VirtualToPhysicalCoreMap[next]);
44 }
45 return p_core_mask;
46}
47
28// Cortex-A57 supports 4 memory watchpoints 48// Cortex-A57 supports 4 memory watchpoints
29constexpr u64 NUM_WATCHPOINTS = 4; 49constexpr u64 NUM_WATCHPOINTS = 4;
30 50
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 0e06468da..631aa6ad2 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -12,6 +12,7 @@
12namespace Core::HID { 12namespace Core::HID {
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_TRIGGER_MAX = 0x7fff; 14constexpr s32 HID_TRIGGER_MAX = 0x7fff;
15constexpr u32 TURBO_BUTTON_DELAY = 4;
15// Use a common UUID for TAS and Virtual Gamepad 16// Use a common UUID for TAS and Virtual Gamepad
16constexpr Common::UUID TAS_UUID = 17constexpr Common::UUID TAS_UUID =
17 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 18 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -447,6 +448,7 @@ void EmulatedController::ReloadInput() {
447 }, 448 },
448 }); 449 });
449 } 450 }
451 turbo_button_state = 0;
450} 452}
451 453
452void EmulatedController::UnloadInput() { 454void EmulatedController::UnloadInput() {
@@ -687,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
687 } 689 }
688 690
689 current_status.toggle = new_status.toggle; 691 current_status.toggle = new_status.toggle;
692 current_status.turbo = new_status.turbo;
690 current_status.uuid = uuid; 693 current_status.uuid = uuid;
691 694
692 // Update button status with current 695 // Update button status with current
@@ -1548,7 +1551,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1548 if (is_configuring) { 1551 if (is_configuring) {
1549 return {}; 1552 return {};
1550 } 1553 }
1551 return controller.npad_button_state; 1554 return {controller.npad_button_state.raw & GetTurboButtonMask()};
1552} 1555}
1553 1556
1554DebugPadButton EmulatedController::GetDebugPadButtons() const { 1557DebugPadButton EmulatedController::GetDebugPadButtons() const {
@@ -1656,4 +1659,74 @@ void EmulatedController::DeleteCallback(int key) {
1656 } 1659 }
1657 callback_list.erase(iterator); 1660 callback_list.erase(iterator);
1658} 1661}
1662
1663void EmulatedController::TurboButtonUpdate() {
1664 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
1665}
1666
1667NpadButton EmulatedController::GetTurboButtonMask() const {
1668 // Apply no mask when disabled
1669 if (turbo_button_state < TURBO_BUTTON_DELAY) {
1670 return {NpadButton::All};
1671 }
1672
1673 NpadButtonState button_mask{};
1674 for (std::size_t index = 0; index < controller.button_values.size(); ++index) {
1675 if (!controller.button_values[index].turbo) {
1676 continue;
1677 }
1678
1679 switch (index) {
1680 case Settings::NativeButton::A:
1681 button_mask.a.Assign(1);
1682 break;
1683 case Settings::NativeButton::B:
1684 button_mask.b.Assign(1);
1685 break;
1686 case Settings::NativeButton::X:
1687 button_mask.x.Assign(1);
1688 break;
1689 case Settings::NativeButton::Y:
1690 button_mask.y.Assign(1);
1691 break;
1692 case Settings::NativeButton::L:
1693 button_mask.l.Assign(1);
1694 break;
1695 case Settings::NativeButton::R:
1696 button_mask.r.Assign(1);
1697 break;
1698 case Settings::NativeButton::ZL:
1699 button_mask.zl.Assign(1);
1700 break;
1701 case Settings::NativeButton::ZR:
1702 button_mask.zr.Assign(1);
1703 break;
1704 case Settings::NativeButton::DLeft:
1705 button_mask.left.Assign(1);
1706 break;
1707 case Settings::NativeButton::DUp:
1708 button_mask.up.Assign(1);
1709 break;
1710 case Settings::NativeButton::DRight:
1711 button_mask.right.Assign(1);
1712 break;
1713 case Settings::NativeButton::DDown:
1714 button_mask.down.Assign(1);
1715 break;
1716 case Settings::NativeButton::SL:
1717 button_mask.left_sl.Assign(1);
1718 button_mask.right_sl.Assign(1);
1719 break;
1720 case Settings::NativeButton::SR:
1721 button_mask.left_sr.Assign(1);
1722 button_mask.right_sr.Assign(1);
1723 break;
1724 default:
1725 break;
1726 }
1727 }
1728
1729 return static_cast<NpadButton>(~button_mask.raw);
1730}
1731
1659} // namespace Core::HID 1732} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 3ac77b2b5..b02bf35c4 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -411,6 +411,9 @@ public:
411 */ 411 */
412 void DeleteCallback(int key); 412 void DeleteCallback(int key);
413 413
414 /// Swaps the state of the turbo buttons
415 void TurboButtonUpdate();
416
414private: 417private:
415 /// creates input devices from params 418 /// creates input devices from params
416 void LoadDevices(); 419 void LoadDevices();
@@ -511,6 +514,8 @@ private:
511 */ 514 */
512 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 515 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
513 516
517 NpadButton GetTurboButtonMask() const;
518
514 const NpadIdType npad_id_type; 519 const NpadIdType npad_id_type;
515 NpadStyleIndex npad_type{NpadStyleIndex::None}; 520 NpadStyleIndex npad_type{NpadStyleIndex::None};
516 NpadStyleIndex original_npad_type{NpadStyleIndex::None}; 521 NpadStyleIndex original_npad_type{NpadStyleIndex::None};
@@ -520,6 +525,7 @@ private:
520 bool system_buttons_enabled{true}; 525 bool system_buttons_enabled{true};
521 f32 motion_sensitivity{0.01f}; 526 f32 motion_sensitivity{0.01f};
522 bool force_update_motion{false}; 527 bool force_update_motion{false};
528 u32 turbo_button_state{0};
523 529
524 // Temporary values to avoid doing changes while the controller is in configuring mode 530 // Temporary values to avoid doing changes while the controller is in configuring mode
525 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 531 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 738b6d0f1..494151eef 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -11,6 +11,7 @@
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/scratch_buffer.h"
14#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/hle_ipc.h" 16#include "core/hle/kernel/hle_ipc.h"
16#include "core/hle/kernel/k_auto_object.h" 17#include "core/hle/kernel/k_auto_object.h"
@@ -325,7 +326,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
325 return ResultSuccess; 326 return ResultSuccess;
326} 327}
327 328
328std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 329std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) const {
329 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 330 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
330 BufferDescriptorA()[buffer_index].Size()}; 331 BufferDescriptorA()[buffer_index].Size()};
331 if (is_buffer_a) { 332 if (is_buffer_a) {
@@ -345,6 +346,33 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
345 } 346 }
346} 347}
347 348
349std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
350 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a;
351 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x;
352
353 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
354 BufferDescriptorA()[buffer_index].Size()};
355 if (is_buffer_a) {
356 ASSERT_OR_EXECUTE_MSG(
357 BufferDescriptorA().size() > buffer_index, { return {}; },
358 "BufferDescriptorA invalid buffer_index {}", buffer_index);
359 auto& read_buffer = read_buffer_a[buffer_index];
360 read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size());
361 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(),
362 read_buffer.size());
363 return read_buffer;
364 } else {
365 ASSERT_OR_EXECUTE_MSG(
366 BufferDescriptorX().size() > buffer_index, { return {}; },
367 "BufferDescriptorX invalid buffer_index {}", buffer_index);
368 auto& read_buffer = read_buffer_x[buffer_index];
369 read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size());
370 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(),
371 read_buffer.size());
372 return read_buffer;
373 }
374}
375
348std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, 376std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
349 std::size_t buffer_index) const { 377 std::size_t buffer_index) const {
350 if (size == 0) { 378 if (size == 0) {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index e252b5f4b..5bf4f171b 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -7,6 +7,7 @@
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <optional> 9#include <optional>
10#include <span>
10#include <string> 11#include <string>
11#include <type_traits> 12#include <type_traits>
12#include <vector> 13#include <vector>
@@ -270,8 +271,11 @@ public:
270 return domain_message_header.has_value(); 271 return domain_message_header.has_value();
271 } 272 }
272 273
273 /// Helper function to read a buffer using the appropriate buffer descriptor 274 /// Helper function to get a span of a buffer using the appropriate buffer descriptor
274 [[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const; 275 [[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
276
277 /// Helper function to read a copy of a buffer using the appropriate buffer descriptor
278 [[nodiscard]] std::vector<u8> ReadBufferCopy(std::size_t buffer_index = 0) const;
275 279
276 /// Helper function to write a buffer using the appropriate buffer descriptor 280 /// Helper function to write a buffer using the appropriate buffer descriptor
277 std::size_t WriteBuffer(const void* buffer, std::size_t size, 281 std::size_t WriteBuffer(const void* buffer, std::size_t size,
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 7b363eb1e..571acf4b2 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 12#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_debug.h" 13#include "core/hle/kernel/k_debug.h"
14#include "core/hle/kernel/k_device_address_space.h"
14#include "core/hle/kernel/k_event.h" 15#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_event_info.h" 16#include "core/hle/kernel/k_event_info.h"
16#include "core/hle/kernel/k_memory_layout.h" 17#include "core/hle/kernel/k_memory_layout.h"
@@ -43,6 +44,7 @@ namespace Kernel::Init {
43 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ 44 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
44 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 45 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
45 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ 46 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
47 HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ##__VA_ARGS__) \
46 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 48 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
47 HANDLER(KThreadLocalPage, \ 49 HANDLER(KThreadLocalPage, \
48 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ 50 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 2827763d5..e8118c2b8 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -24,9 +24,7 @@ private:
24 friend class ::Kernel::KClassTokenGenerator; \ 24 friend class ::Kernel::KClassTokenGenerator; \
25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \ 25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
26 static constexpr inline const char* const TypeName = #CLASS; \ 26 static constexpr inline const char* const TypeName = #CLASS; \
27 static constexpr inline ClassTokenType ClassToken() { \ 27 static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken<CLASS>; } \
28 return ::Kernel::ClassToken<CLASS>; \
29 } \
30 \ 28 \
31public: \ 29public: \
32 YUZU_NON_COPYABLE(CLASS); \ 30 YUZU_NON_COPYABLE(CLASS); \
@@ -37,15 +35,9 @@ public:
37 constexpr ClassTokenType Token = ClassToken(); \ 35 constexpr ClassTokenType Token = ClassToken(); \
38 return TypeObj(TypeName, Token); \ 36 return TypeObj(TypeName, Token); \
39 } \ 37 } \
40 static constexpr const char* GetStaticTypeName() { \ 38 static constexpr const char* GetStaticTypeName() { return TypeName; } \
41 return TypeName; \ 39 virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); } \
42 } \ 40 virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); } \
43 virtual TypeObj GetTypeObj() ATTRIBUTE { \
44 return GetStaticTypeObj(); \
45 } \
46 virtual const char* GetTypeName() ATTRIBUTE { \
47 return GetStaticTypeName(); \
48 } \
49 \ 41 \
50private: \ 42private: \
51 constexpr bool operator!=(const TypeObj& rhs) 43 constexpr bool operator!=(const TypeObj& rhs)
@@ -245,8 +237,8 @@ public:
245 } 237 }
246 238
247 template <typename U> 239 template <typename U>
248 requires(std::derived_from<T, U> || 240 requires(std::derived_from<T, U> || std::derived_from<U, T>)
249 std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) { 241 constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
250 if constexpr (std::derived_from<U, T>) { 242 if constexpr (std::derived_from<U, T>) {
251 // Upcast. 243 // Upcast.
252 m_obj = rhs.m_obj; 244 m_obj = rhs.m_obj;
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
new file mode 100644
index 000000000..64f1d7371
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -0,0 +1,358 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hardware_properties.h"
5#include "core/hle/kernel/k_capabilities.h"
6#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_page_table.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_version.h"
11
12namespace Kernel {
13
14Result KCapabilities::InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table) {
15 // We're initializing an initial process.
16 m_svc_access_flags.reset();
17 m_irq_access_flags.reset();
18 m_debug_capabilities = 0;
19 m_handle_table_size = 0;
20 m_intended_kernel_version = 0;
21 m_program_type = 0;
22
23 // Initial processes may run on all cores.
24 constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
25 constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);
26
27 m_core_mask = VirtMask;
28 m_phys_core_mask = PhysMask;
29
30 // Initial processes may use any user priority they like.
31 m_priority_mask = ~0xFULL;
32
33 // Here, Nintendo sets the kernel version to the current kernel version.
34 // We will follow suit and set the version to the highest supported kernel version.
35 KernelVersion intended_kernel_version{};
36 intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
37 intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
38 m_intended_kernel_version = intended_kernel_version.raw;
39
40 // Parse the capabilities array.
41 R_RETURN(this->SetCapabilities(kern_caps, page_table));
42}
43
44Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
45 // We're initializing a user process.
46 m_svc_access_flags.reset();
47 m_irq_access_flags.reset();
48 m_debug_capabilities = 0;
49 m_handle_table_size = 0;
50 m_intended_kernel_version = 0;
51 m_program_type = 0;
52
53 // User processes must specify what cores/priorities they can use.
54 m_core_mask = 0;
55 m_priority_mask = 0;
56
57 // Parse the user capabilities array.
58 R_RETURN(this->SetCapabilities(user_caps, page_table));
59}
60
61Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
62 // We can't set core/priority if we've already set them.
63 R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
64 R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);
65
66 // Validate the core/priority.
67 CorePriority pack{cap};
68 const u32 min_core = pack.minimum_core_id;
69 const u32 max_core = pack.maximum_core_id;
70 const u32 max_prio = pack.lowest_thread_priority;
71 const u32 min_prio = pack.highest_thread_priority;
72
73 R_UNLESS(min_core <= max_core, ResultInvalidCombination);
74 R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
75 R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);
76
77 ASSERT(max_prio < Common::BitSize<u64>());
78
79 // Set core mask.
80 for (auto core_id = min_core; core_id <= max_core; core_id++) {
81 m_core_mask |= (1ULL << core_id);
82 }
83 ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);
84
85 // Set physical core mask.
86 m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);
87
88 // Set priority mask.
89 for (auto prio = min_prio; prio <= max_prio; prio++) {
90 m_priority_mask |= (1ULL << prio);
91 }
92
93 // We must have some core/priority we can use.
94 R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
95 R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);
96
97 // Processes must not have access to kernel thread priorities.
98 R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);
99
100 R_SUCCEED();
101}
102
103Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
104 // Validate the index.
105 SyscallMask pack{cap};
106 const u32 mask = pack.mask;
107 const u32 index = pack.index;
108
109 const u32 index_flag = (1U << index);
110 R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
111 set_svc |= index_flag;
112
113 // Set SVCs.
114 for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
115 const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
116 if (mask & (1U << i)) {
117 R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
118 }
119 }
120
121 R_SUCCEED();
122}
123
124Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
125 const auto range_pack = MapRange{cap};
126 const auto size_pack = MapRangeSize{size_cap};
127
128 // Get/validate address/size
129 const u64 phys_addr = range_pack.address.Value() * PageSize;
130
131 // Validate reserved bits are unused.
132 R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);
133
134 const size_t num_pages = size_pack.pages;
135 const size_t size = num_pages * PageSize;
136 R_UNLESS(num_pages != 0, ResultInvalidSize);
137 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
138 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
139
140 // Do the mapping.
141 [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
142 ? KMemoryPermission::UserRead
143 : KMemoryPermission::UserReadWrite;
144 if (MapRangeSize{size_cap}.normal) {
145 // R_RETURN(page_table->MapStatic(phys_addr, size, perm));
146 } else {
147 // R_RETURN(page_table->MapIo(phys_addr, size, perm));
148 }
149
150 UNIMPLEMENTED();
151 R_SUCCEED();
152}
153
154Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
155 // Get/validate address/size
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1;
158 const size_t size = num_pages * PageSize;
159 R_UNLESS(num_pages != 0, ResultInvalidSize);
160 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
161 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
162
163 // Do the mapping.
164 // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
165
166 UNIMPLEMENTED();
167 R_SUCCEED();
168}
169
170template <typename F>
171Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
172 // Define the allowed memory regions.
173 constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
174 KMemoryRegionType_None,
175 KMemoryRegionType_KernelTraceBuffer,
176 KMemoryRegionType_OnMemoryBootImage,
177 KMemoryRegionType_DTB,
178 };
179
180 // Extract regions/read only.
181 const MapRegion pack{cap};
182 const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
183 const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};
184
185 for (size_t i = 0; i < types.size(); i++) {
186 const auto type = types[i];
187 const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
188 switch (type) {
189 case RegionType::NoMapping:
190 break;
191 case RegionType::KernelTraceBuffer:
192 case RegionType::OnMemoryBootImage:
193 case RegionType::DTB:
194 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
195 break;
196 default:
197 R_THROW(ResultNotFound);
198 }
199 }
200
201 R_SUCCEED();
202}
203
204Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
205 // Map each region into the process's page table.
206 R_RETURN(ProcessMapRegionCapability(
207 cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
208 // R_RETURN(page_table->MapRegion(region_type, perm));
209 UNIMPLEMENTED();
210 R_SUCCEED();
211 }));
212}
213
214Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
215 // Check that each region has a physical backing store.
216 R_RETURN(ProcessMapRegionCapability(
217 cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
218 R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
219 region_type) != nullptr,
220 ResultOutOfRange);
221 R_SUCCEED();
222 }));
223}
224
225Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
226 // Extract interrupts.
227 const InterruptPair pack{cap};
228 const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};
229
230 for (size_t i = 0; i < ids.size(); i++) {
231 if (ids[i] != PaddingInterruptId) {
232 UNIMPLEMENTED();
233 // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
234 // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
235 }
236 }
237
238 R_SUCCEED();
239}
240
241Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
242 // Validate.
243 const ProgramType pack{cap};
244 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
245
246 m_program_type = pack.type;
247 R_SUCCEED();
248}
249
250Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
251 // Ensure we haven't set our version before.
252 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);
253
254 // Set, ensure that we set a valid version.
255 m_intended_kernel_version = cap;
256 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);
257
258 R_SUCCEED();
259}
260
261Result KCapabilities::SetHandleTableCapability(const u32 cap) {
262 // Validate.
263 const HandleTable pack{cap};
264 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
265
266 m_handle_table_size = pack.size;
267 R_SUCCEED();
268}
269
270Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
271 // Validate.
272 const DebugFlags pack{cap};
273 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
274
275 DebugFlags debug_capabilities{m_debug_capabilities};
276 debug_capabilities.allow_debug.Assign(pack.allow_debug);
277 debug_capabilities.force_debug.Assign(pack.force_debug);
278 m_debug_capabilities = debug_capabilities.raw;
279
280 R_SUCCEED();
281}
282
283Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
284 KPageTable* page_table) {
285 // Validate this is a capability we can act on.
286 const auto type = GetCapabilityType(cap);
287 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
288
289 // If the type is padding, we have no work to do.
290 R_SUCCEED_IF(type == CapabilityType::Padding);
291
292 // Check that we haven't already processed this capability.
293 const auto flag = GetCapabilityFlag(type);
294 R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
295 set_flags |= flag;
296
297 // Process the capability.
298 switch (type) {
299 case CapabilityType::CorePriority:
300 R_RETURN(this->SetCorePriorityCapability(cap));
301 case CapabilityType::SyscallMask:
302 R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
303 case CapabilityType::MapIoPage:
304 R_RETURN(this->MapIoPage_(cap, page_table));
305 case CapabilityType::MapRegion:
306 R_RETURN(this->MapRegion_(cap, page_table));
307 case CapabilityType::InterruptPair:
308 R_RETURN(this->SetInterruptPairCapability(cap));
309 case CapabilityType::ProgramType:
310 R_RETURN(this->SetProgramTypeCapability(cap));
311 case CapabilityType::KernelVersion:
312 R_RETURN(this->SetKernelVersionCapability(cap));
313 case CapabilityType::HandleTable:
314 R_RETURN(this->SetHandleTableCapability(cap));
315 case CapabilityType::DebugFlags:
316 R_RETURN(this->SetDebugFlagsCapability(cap));
317 default:
318 R_THROW(ResultInvalidArgument);
319 }
320}
321
322Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
323 u32 set_flags = 0, set_svc = 0;
324
325 for (size_t i = 0; i < caps.size(); i++) {
326 const u32 cap{caps[i]};
327
328 if (GetCapabilityType(cap) == CapabilityType::MapRange) {
329 // Check that the pair cap exists.
330 R_UNLESS((++i) < caps.size(), ResultInvalidCombination);
331
332 // Check the pair cap is a map range cap.
333 const u32 size_cap{caps[i]};
334 R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
335 ResultInvalidCombination);
336
337 // Map the range.
338 R_TRY(this->MapRange_(cap, size_cap, page_table));
339 } else {
340 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
341 }
342 }
343
344 R_SUCCEED();
345}
346
347Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
348 for (auto cap : caps) {
349 // Check the capability refers to a valid region.
350 if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
351 R_TRY(CheckMapRegion(kernel, cap));
352 }
353 }
354
355 R_SUCCEED();
356}
357
358} // namespace Kernel
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h
new file mode 100644
index 000000000..cd96f8d23
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.h
@@ -0,0 +1,295 @@
1
2// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include <bitset>
8#include <span>
9
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13#include "core/hle/kernel/svc_types.h"
14#include "core/hle/result.h"
15
16namespace Kernel {
17
18class KPageTable;
19class KernelCore;
20
21class KCapabilities {
22public:
23 constexpr explicit KCapabilities() = default;
24
25 Result InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table);
26 Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
27
28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
29
30 constexpr u64 GetCoreMask() const {
31 return m_core_mask;
32 }
33
34 constexpr u64 GetPhysicalCoreMask() const {
35 return m_phys_core_mask;
36 }
37
38 constexpr u64 GetPriorityMask() const {
39 return m_priority_mask;
40 }
41
42 constexpr s32 GetHandleTableSize() const {
43 return m_handle_table_size;
44 }
45
46 constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
47 return m_svc_access_flags;
48 }
49
50 constexpr bool IsPermittedSvc(u32 id) const {
51 return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
52 }
53
54 constexpr bool IsPermittedInterrupt(u32 id) const {
55 return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
56 }
57
58 constexpr bool IsPermittedDebug() const {
59 return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
60 }
61
62 constexpr bool CanForceDebug() const {
63 return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
64 }
65
66 constexpr u32 GetIntendedKernelMajorVersion() const {
67 return KernelVersion{m_intended_kernel_version}.major_version;
68 }
69
70 constexpr u32 GetIntendedKernelMinorVersion() const {
71 return KernelVersion{m_intended_kernel_version}.minor_version;
72 }
73
74private:
75 static constexpr size_t InterruptIdCount = 0x400;
76 using InterruptFlagSet = std::bitset<InterruptIdCount>;
77
78 enum class CapabilityType : u32 {
79 CorePriority = (1U << 3) - 1,
80 SyscallMask = (1U << 4) - 1,
81 MapRange = (1U << 6) - 1,
82 MapIoPage = (1U << 7) - 1,
83 MapRegion = (1U << 10) - 1,
84 InterruptPair = (1U << 11) - 1,
85 ProgramType = (1U << 13) - 1,
86 KernelVersion = (1U << 14) - 1,
87 HandleTable = (1U << 15) - 1,
88 DebugFlags = (1U << 16) - 1,
89
90 Invalid = 0U,
91 Padding = ~0U,
92 };
93
94 using RawCapabilityValue = u32;
95
96 static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
97 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
98 }
99
100 static constexpr u32 GetCapabilityFlag(CapabilityType type) {
101 return static_cast<u32>(type) + 1;
102 }
103
104 template <CapabilityType Type>
105 static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;
106
107 template <CapabilityType Type>
108 static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);
109
110 union CorePriority {
111 static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);
112
113 RawCapabilityValue raw;
114 BitField<0, 4, CapabilityType> id;
115 BitField<4, 6, u32> lowest_thread_priority;
116 BitField<10, 6, u32> highest_thread_priority;
117 BitField<16, 8, u32> minimum_core_id;
118 BitField<24, 8, u32> maximum_core_id;
119 };
120
121 union SyscallMask {
122 static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);
123
124 RawCapabilityValue raw;
125 BitField<0, 5, CapabilityType> id;
126 BitField<5, 24, u32> mask;
127 BitField<29, 3, u32> index;
128 };
129
130 // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
131 static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;
132
133 union MapRange {
134 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
135
136 RawCapabilityValue raw;
137 BitField<0, 7, CapabilityType> id;
138 BitField<7, 24, u32> address;
139 BitField<31, 1, u32> read_only;
140 };
141
142 union MapRangeSize {
143 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
144
145 RawCapabilityValue raw;
146 BitField<0, 7, CapabilityType> id;
147 BitField<7, 20, u32> pages;
148 BitField<27, 4, u32> reserved;
149 BitField<31, 1, u32> normal;
150 };
151
152 union MapIoPage {
153 static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);
154
155 RawCapabilityValue raw;
156 BitField<0, 8, CapabilityType> id;
157 BitField<8, 24, u32> address;
158 };
159
160 enum class RegionType : u32 {
161 NoMapping = 0,
162 KernelTraceBuffer = 1,
163 OnMemoryBootImage = 2,
164 DTB = 3,
165 };
166
167 union MapRegion {
168 static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);
169
170 RawCapabilityValue raw;
171 BitField<0, 11, CapabilityType> id;
172 BitField<11, 6, RegionType> region0;
173 BitField<17, 1, u32> read_only0;
174 BitField<18, 6, RegionType> region1;
175 BitField<24, 1, u32> read_only1;
176 BitField<25, 6, RegionType> region2;
177 BitField<31, 1, u32> read_only2;
178 };
179
180 union InterruptPair {
181 static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);
182
183 RawCapabilityValue raw;
184 BitField<0, 12, CapabilityType> id;
185 BitField<12, 10, u32> interrupt_id0;
186 BitField<22, 10, u32> interrupt_id1;
187 };
188
189 union ProgramType {
190 static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);
191
192 RawCapabilityValue raw;
193 BitField<0, 14, CapabilityType> id;
194 BitField<14, 3, u32> type;
195 BitField<17, 15, u32> reserved;
196 };
197
198 union KernelVersion {
199 static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);
200
201 RawCapabilityValue raw;
202 BitField<0, 15, CapabilityType> id;
203 BitField<15, 4, u32> major_version;
204 BitField<19, 13, u32> minor_version;
205 };
206
207 union HandleTable {
208 static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);
209
210 RawCapabilityValue raw;
211 BitField<0, 16, CapabilityType> id;
212 BitField<16, 10, u32> size;
213 BitField<26, 6, u32> reserved;
214 };
215
216 union DebugFlags {
217 static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);
218
219 RawCapabilityValue raw;
220 BitField<0, 17, CapabilityType> id;
221 BitField<17, 1, u32> allow_debug;
222 BitField<18, 1, u32> force_debug;
223 BitField<19, 13, u32> reserved;
224 };
225
226 static_assert(sizeof(CorePriority) == 4);
227 static_assert(sizeof(SyscallMask) == 4);
228 static_assert(sizeof(MapRange) == 4);
229 static_assert(sizeof(MapRangeSize) == 4);
230 static_assert(sizeof(MapIoPage) == 4);
231 static_assert(sizeof(MapRegion) == 4);
232 static_assert(sizeof(InterruptPair) == 4);
233 static_assert(sizeof(ProgramType) == 4);
234 static_assert(sizeof(KernelVersion) == 4);
235 static_assert(sizeof(HandleTable) == 4);
236 static_assert(sizeof(DebugFlags) == 4);
237
238 static constexpr u32 InitializeOnceFlags =
239 CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
240 CapabilityFlag<CapabilityType::KernelVersion> |
241 CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;
242
243 static const u32 PaddingInterruptId = 0x3FF;
244 static_assert(PaddingInterruptId < InterruptIdCount);
245
246private:
247 constexpr bool SetSvcAllowed(u32 id) {
248 if (id < m_svc_access_flags.size()) [[likely]] {
249 m_svc_access_flags[id] = true;
250 return true;
251 } else {
252 return false;
253 }
254 }
255
256 constexpr bool SetInterruptPermitted(u32 id) {
257 if (id < m_irq_access_flags.size()) [[likely]] {
258 m_irq_access_flags[id] = true;
259 return true;
260 } else {
261 return false;
262 }
263 }
264
265 Result SetCorePriorityCapability(const u32 cap);
266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
267 Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
268 Result MapIoPage_(const u32 cap, KPageTable* page_table);
269 Result MapRegion_(const u32 cap, KPageTable* page_table);
270 Result SetInterruptPairCapability(const u32 cap);
271 Result SetProgramTypeCapability(const u32 cap);
272 Result SetKernelVersionCapability(const u32 cap);
273 Result SetHandleTableCapability(const u32 cap);
274 Result SetDebugFlagsCapability(const u32 cap);
275
276 template <typename F>
277 static Result ProcessMapRegionCapability(const u32 cap, F f);
278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
279
280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
281 Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
282
283private:
284 Svc::SvcAccessFlagSet m_svc_access_flags{};
285 InterruptFlagSet m_irq_access_flags{};
286 u64 m_core_mask{};
287 u64 m_phys_core_mask{};
288 u64 m_priority_mask{};
289 u32 m_debug_capabilities{};
290 s32 m_handle_table_size{};
291 u32 m_intended_kernel_version{};
292 u32 m_program_type{};
293};
294
295} // namespace Kernel
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp
new file mode 100644
index 000000000..27659ea3b
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.cpp
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_device_address_space.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel_)
13 : KAutoObjectWithSlabHeapAndContainer(kernel_), m_lock(kernel_), m_is_initialized(false) {}
14KDeviceAddressSpace::~KDeviceAddressSpace() = default;
15
16void KDeviceAddressSpace::Initialize() {
17 // This just forwards to the device page table manager.
18 // KDevicePageTable::Initialize();
19}
20
21// Member functions.
22Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
23 // Initialize the device page table.
24 // R_TRY(m_table.Initialize(address, size));
25
26 // Set member variables.
27 m_space_address = address;
28 m_space_size = size;
29 m_is_initialized = true;
30
31 R_SUCCEED();
32}
33
34void KDeviceAddressSpace::Finalize() {
35 // Finalize the table.
36 // m_table.Finalize();
37}
38
39Result KDeviceAddressSpace::Attach(Svc::DeviceName device_name) {
40 // Lock the address space.
41 KScopedLightLock lk(m_lock);
42
43 // Attach.
44 // R_RETURN(m_table.Attach(device_name, m_space_address, m_space_size));
45 R_SUCCEED();
46}
47
48Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
49 // Lock the address space.
50 KScopedLightLock lk(m_lock);
51
52 // Detach.
53 // R_RETURN(m_table.Detach(device_name));
54 R_SUCCEED();
55}
56
57Result KDeviceAddressSpace::Map(KPageTable* page_table, VAddr process_address, size_t size,
58 u64 device_address, u32 option, bool is_aligned) {
59 // Check that the address falls within the space.
60 R_UNLESS((m_space_address <= device_address &&
61 device_address + size - 1 <= m_space_address + m_space_size - 1),
62 ResultInvalidCurrentMemory);
63
64 // Decode the option.
65 const Svc::MapDeviceAddressSpaceOption option_pack{option};
66 const auto device_perm = option_pack.permission.Value();
67 const auto flags = option_pack.flags.Value();
68 const auto reserved = option_pack.reserved.Value();
69
70 // Validate the option.
71 // TODO: It is likely that this check for flags == none is only on NX board.
72 R_UNLESS(flags == Svc::MapDeviceAddressSpaceFlag::None, ResultInvalidEnumValue);
73 R_UNLESS(reserved == 0, ResultInvalidEnumValue);
74
75 // Lock the address space.
76 KScopedLightLock lk(m_lock);
77
78 // Lock the page table to prevent concurrent device mapping operations.
79 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
80
81 // Lock the pages.
82 bool is_io{};
83 R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size,
84 ConvertToKMemoryPermission(device_perm),
85 is_aligned, true));
86
87 // Ensure that if we fail, we don't keep unmapped pages locked.
88 ON_RESULT_FAILURE {
89 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
90 };
91
92 // Check that the io status is allowable.
93 if (is_io) {
94 R_UNLESS(static_cast<u32>(flags & Svc::MapDeviceAddressSpaceFlag::NotIoRegister) == 0,
95 ResultInvalidCombination);
96 }
97
98 // Map the pages.
99 {
100 // Perform the mapping.
101 // R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm,
102 // is_aligned, is_io));
103
104 // Ensure that we unmap the pages if we fail to update the protections.
105 // NOTE: Nintendo does not check the result of this unmap call.
106 // ON_RESULT_FAILURE { m_table.Unmap(device_address, size); };
107
108 // Update the protections in accordance with how much we mapped.
109 // R_TRY(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size));
110 }
111
112 // We succeeded.
113 R_SUCCEED();
114}
115
116Result KDeviceAddressSpace::Unmap(KPageTable* page_table, VAddr process_address, size_t size,
117 u64 device_address) {
118 // Check that the address falls within the space.
119 R_UNLESS((m_space_address <= device_address &&
120 device_address + size - 1 <= m_space_address + m_space_size - 1),
121 ResultInvalidCurrentMemory);
122
123 // Lock the address space.
124 KScopedLightLock lk(m_lock);
125
126 // Lock the page table to prevent concurrent device mapping operations.
127 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
128
129 // Lock the pages.
130 R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));
131
132 // Unmap the pages.
133 {
134 // If we fail to unmap, we want to do a partial unlock.
135 // ON_RESULT_FAILURE {
136 // ASSERT(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size) ==
137 // ResultSuccess);
138 // };
139
140 // Perform the unmap.
141 // R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
142 }
143
144 // Unlock the pages.
145 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
146
147 R_SUCCEED();
148}
149
150} // namespace Kernel
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h
new file mode 100644
index 000000000..4709df995
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_page_table.h"
10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/result.h"
12
13namespace Kernel {
14
15class KDeviceAddressSpace final
16 : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
17 KERNEL_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
18
19public:
20 explicit KDeviceAddressSpace(KernelCore& kernel);
21 ~KDeviceAddressSpace();
22
23 Result Initialize(u64 address, u64 size);
24 void Finalize();
25
26 bool IsInitialized() const {
27 return m_is_initialized;
28 }
29 static void PostDestroy(uintptr_t arg) {}
30
31 Result Attach(Svc::DeviceName device_name);
32 Result Detach(Svc::DeviceName device_name);
33
34 Result MapByForce(KPageTable* page_table, VAddr process_address, size_t size,
35 u64 device_address, u32 option) {
36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
37 }
38
39 Result MapAligned(KPageTable* page_table, VAddr process_address, size_t size,
40 u64 device_address, u32 option) {
41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
42 }
43
44 Result Unmap(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address);
45
46 static void Initialize();
47
48private:
49 Result Map(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address,
50 u32 option, bool is_aligned);
51
52private:
53 KLightLock m_lock;
54 // KDevicePageTable m_table;
55 u64 m_space_address{};
56 u64 m_space_size{};
57 bool m_is_initialized{};
58};
59
60} // namespace Kernel
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index cb2512b0b..645c5b531 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -17,35 +17,41 @@ namespace Kernel {
17class KThread; 17class KThread;
18 18
19template <typename T> 19template <typename T>
20concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) { 20concept KPriorityQueueAffinityMask = !
21 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>; 21std::is_reference_v<T>&& requires(T& t) {
22 {t.SetAffinityMask(0)}; 22 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
23 { t.SetAffinityMask(0) };
23 24
24 { t.GetAffinity(0) } -> std::same_as<bool>; 25 { t.GetAffinity(0) } -> std::same_as<bool>;
25 {t.SetAffinity(0, false)}; 26 { t.SetAffinity(0, false) };
26 {t.SetAll()}; 27 { t.SetAll() };
27}; 28 };
28 29
29template <typename T> 30template <typename T>
30concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) { 31concept KPriorityQueueMember = !
31 {typename T::QueueEntry()}; 32std::is_reference_v<T>&& requires(T& t) {
32 {(typename T::QueueEntry()).Initialize()}; 33 { typename T::QueueEntry() };
33 {(typename T::QueueEntry()).SetPrev(std::addressof(t))}; 34 { (typename T::QueueEntry()).Initialize() };
34 {(typename T::QueueEntry()).SetNext(std::addressof(t))}; 35 { (typename T::QueueEntry()).SetPrev(std::addressof(t)) };
35 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; 36 { (typename T::QueueEntry()).SetNext(std::addressof(t)) };
36 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; 37 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
37 { t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>; 38 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
38 39 {
39 {t.GetAffinityMask()}; 40 t.GetPriorityQueueEntry(0)
40 { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask; 41 } -> std::same_as<typename T::QueueEntry&>;
41 42
42 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>; 43 { t.GetAffinityMask() };
43 { t.GetPriority() } -> Common::ConvertibleTo<s32>; 44 {
44 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>; 45 std::remove_cvref_t<decltype(t.GetAffinityMask())>()
45}; 46 } -> KPriorityQueueAffinityMask;
47
48 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
49 { t.GetPriority() } -> Common::ConvertibleTo<s32>;
50 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
51 };
46 52
47template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority> 53template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
48requires KPriorityQueueMember<Member> 54 requires KPriorityQueueMember<Member>
49class KPriorityQueue { 55class KPriorityQueue {
50public: 56public:
51 using AffinityMaskType = std::remove_cv_t< 57 using AffinityMaskType = std::remove_cv_t<
diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h
index 857e21156..59b3e32ae 100644
--- a/src/core/hle/kernel/k_scoped_lock.h
+++ b/src/core/hle/kernel/k_scoped_lock.h
@@ -9,13 +9,14 @@
9namespace Kernel { 9namespace Kernel {
10 10
11template <typename T> 11template <typename T>
12concept KLockable = !std::is_reference_v<T> && requires(T & t) { 12concept KLockable = !
13 { t.Lock() } -> std::same_as<void>; 13std::is_reference_v<T>&& requires(T& t) {
14 { t.Unlock() } -> std::same_as<void>; 14 { t.Lock() } -> std::same_as<void>;
15}; 15 { t.Unlock() } -> std::same_as<void>;
16 };
16 17
17template <typename T> 18template <typename T>
18requires KLockable<T> 19 requires KLockable<T>
19class [[nodiscard]] KScopedLock { 20class [[nodiscard]] KScopedLock {
20public: 21public:
21 explicit KScopedLock(T* l) : lock_ptr(l) { 22 explicit KScopedLock(T* l) : lock_ptr(l) {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 9d771de0e..8b8dc51be 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -677,7 +677,7 @@ private:
677 union SyncObjectBuffer { 677 union SyncObjectBuffer {
678 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; 678 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
679 std::array<Handle, 679 std::array<Handle,
680 Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> 680 Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))>
681 handles; 681 handles;
682 constexpr SyncObjectBuffer() {} 682 constexpr SyncObjectBuffer() {}
683 }; 683 };
@@ -698,10 +698,8 @@ private:
698 }; 698 };
699 699
700 template <typename T> 700 template <typename T>
701 requires( 701 requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
702 std::same_as<T, KThread> || 702 static constexpr int Compare(const T& lhs, const KThread& rhs) {
703 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
704 const KThread& rhs) {
705 const u64 l_key = lhs.GetConditionVariableKey(); 703 const u64 l_key = lhs.GetConditionVariableKey();
706 const u64 r_key = rhs.GetConditionVariableKey(); 704 const u64 r_key = rhs.GetConditionVariableKey();
707 705
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index fe0cff084..71254eb55 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -70,10 +70,8 @@ public:
70 } 70 }
71 71
72 template <typename T> 72 template <typename T>
73 requires(std::same_as<T, KThreadLocalPage> || 73 requires(std::same_as<T, KThreadLocalPage> || std::same_as<T, RedBlackKeyType>)
74 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, 74 static constexpr int Compare(const T& lhs, const KThreadLocalPage& rhs) {
75 const KThreadLocalPage&
76 rhs) {
77 const VAddr lval = GetRedBlackKey(lhs); 75 const VAddr lval = GetRedBlackKey(lhs);
78 const VAddr rval = GetRedBlackKey(rhs); 76 const VAddr rval = GetRedBlackKey(rhs);
79 77
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d22f8d2c..5f52e1e95 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -35,6 +35,7 @@ class GlobalSchedulerContext;
35class KAutoObjectWithListContainer; 35class KAutoObjectWithListContainer;
36class KClientSession; 36class KClientSession;
37class KDebug; 37class KDebug;
38class KDeviceAddressSpace;
38class KDynamicPageManager; 39class KDynamicPageManager;
39class KEvent; 40class KEvent;
40class KEventInfo; 41class KEventInfo;
@@ -359,6 +360,8 @@ public:
359 return slab_heap_container->transfer_memory; 360 return slab_heap_container->transfer_memory;
360 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 361 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
361 return slab_heap_container->code_memory; 362 return slab_heap_container->code_memory;
363 } else if constexpr (std::is_same_v<T, KDeviceAddressSpace>) {
364 return slab_heap_container->device_address_space;
362 } else if constexpr (std::is_same_v<T, KPageBuffer>) { 365 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
363 return slab_heap_container->page_buffer; 366 return slab_heap_container->page_buffer;
364 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { 367 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
@@ -431,6 +434,7 @@ private:
431 KSlabHeap<KThread> thread; 434 KSlabHeap<KThread> thread;
432 KSlabHeap<KTransferMemory> transfer_memory; 435 KSlabHeap<KTransferMemory> transfer_memory;
433 KSlabHeap<KCodeMemory> code_memory; 436 KSlabHeap<KCodeMemory> code_memory;
437 KSlabHeap<KDeviceAddressSpace> device_address_space;
434 KSlabHeap<KPageBuffer> page_buffer; 438 KSlabHeap<KPageBuffer> page_buffer;
435 KSlabHeap<KThreadLocalPage> thread_local_page; 439 KSlabHeap<KThreadLocalPage> thread_local_page;
436 KSlabHeap<KSessionRequest> session_request; 440 KSlabHeap<KSessionRequest> session_request;
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 33eebcef6..e90c35601 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -3,6 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <bitset>
7
8#include "common/bit_field.h"
6#include "common/common_funcs.h" 9#include "common/common_funcs.h"
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
@@ -496,6 +499,19 @@ enum class MemoryMapping : u32 {
496 Memory = 2, 499 Memory = 2,
497}; 500};
498 501
502enum class MapDeviceAddressSpaceFlag : u32 {
503 None = (0U << 0),
504 NotIoRegister = (1U << 0),
505};
506DECLARE_ENUM_FLAG_OPERATORS(MapDeviceAddressSpaceFlag);
507
508union MapDeviceAddressSpaceOption {
509 u32 raw;
510 BitField<0, 16, MemoryPermission> permission;
511 BitField<16, 1, MapDeviceAddressSpaceFlag> flags;
512 BitField<17, 15, u32> reserved;
513};
514
499enum class KernelDebugType : u32 { 515enum class KernelDebugType : u32 {
500 Thread = 0, 516 Thread = 0,
501 ThreadCallStack = 1, 517 ThreadCallStack = 1,
@@ -592,4 +608,7 @@ struct CreateProcessParameter {
592}; 608};
593static_assert(sizeof(CreateProcessParameter) == 0x30); 609static_assert(sizeof(CreateProcessParameter) == 0x30);
594 610
611constexpr size_t NumSupervisorCalls = 0xC0;
612using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;
613
595} // namespace Kernel::Svc 614} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h
new file mode 100644
index 000000000..e4f47b34b
--- /dev/null
+++ b/src/core/hle/kernel/svc_version.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "common/literals.h"
9
10namespace Kernel::Svc {
11
12constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
13 return sdk + 4;
14}
15constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
16 return svc - 4;
17}
18
19constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
20 return sdk;
21}
22constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
23 return svc;
24}
25
26union KernelVersion {
27 u32 value;
28 BitField<0, 4, u32> minor_version;
29 BitField<4, 13, u32> major_version;
30};
31
32constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
33 return decltype(KernelVersion::minor_version)::FormatValue(minor) |
34 decltype(KernelVersion::major_version)::FormatValue(major);
35}
36
37constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
38 return std::bit_cast<decltype(KernelVersion::major_version)>(encoded).Value();
39}
40
41constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
42 return std::bit_cast<decltype(KernelVersion::minor_version)>(encoded).Value();
43}
44
45// Nintendo doesn't support programs targeting SVC versions < 3.0.
46constexpr inline u32 RequiredKernelMajorVersion = 3;
47constexpr inline u32 RequiredKernelMinorVersion = 0;
48constexpr inline u32 RequiredKernelVersion =
49 EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
50
51// This is the highest SVC version supported, to be updated on new kernel releases.
52// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
53constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
54constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
55constexpr inline u32 SupportedKernelVersion =
56 EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
57
58} // namespace Kernel::Svc
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 22999c942..ebcf6e164 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1124,7 +1124,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
1124 IPC::RequestParser rp{ctx}; 1124 IPC::RequestParser rp{ctx};
1125 1125
1126 const u64 offset{rp.Pop<u64>()}; 1126 const u64 offset{rp.Pop<u64>()};
1127 const std::vector<u8> data{ctx.ReadBuffer()}; 1127 const auto data{ctx.ReadBuffer()};
1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)}; 1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
1129 1129
1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 3a1c231b6..0ee28752c 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -112,7 +112,7 @@ private:
112 void RequestUpdate(Kernel::HLERequestContext& ctx) { 112 void RequestUpdate(Kernel::HLERequestContext& ctx) {
113 LOG_TRACE(Service_Audio, "called"); 113 LOG_TRACE(Service_Audio, "called");
114 114
115 std::vector<u8> input{ctx.ReadBuffer(0)}; 115 const auto input{ctx.ReadBuffer(0)};
116 116
117 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for 117 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
118 // checking size 0. Performance size is 0 for most games. 118 // checking size 0. Performance size is 0 for most games.
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 825fb8bcc..e01f87356 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -93,7 +93,7 @@ private:
93 ctx.WriteBuffer(samples); 93 ctx.WriteBuffer(samples);
94 } 94 }
95 95
96 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, 96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
97 std::vector<opus_int16>& output, u64* out_performance_time) const { 97 std::vector<opus_int16>& output, u64* out_performance_time) const {
98 const auto start_time = std::chrono::steady_clock::now(); 98 const auto start_time = std::chrono::steady_clock::now();
99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d183e5829..fb8686859 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -122,7 +122,7 @@ private:
122 122
123 void ImportTicket(Kernel::HLERequestContext& ctx) { 123 void ImportTicket(Kernel::HLERequestContext& ctx) {
124 const auto ticket = ctx.ReadBuffer(); 124 const auto ticket = ctx.ReadBuffer();
125 const auto cert = ctx.ReadBuffer(1); 125 [[maybe_unused]] const auto cert = ctx.ReadBuffer(1);
126 126
127 if (ticket.size() < sizeof(Core::Crypto::Ticket)) { 127 if (ticket.size() < sizeof(Core::Crypto::Ticket)) {
128 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); 128 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fbb16a7da..447d624e1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -190,7 +190,7 @@ private:
190 return; 190 return;
191 } 191 }
192 192
193 const std::vector<u8> data = ctx.ReadBuffer(); 193 const auto data = ctx.ReadBuffer();
194 194
195 ASSERT_MSG( 195 ASSERT_MSG(
196 static_cast<s64>(data.size()) <= length, 196 static_cast<s64>(data.size()) <= length,
@@ -401,11 +401,8 @@ public:
401 } 401 }
402 402
403 void RenameFile(Kernel::HLERequestContext& ctx) { 403 void RenameFile(Kernel::HLERequestContext& ctx) {
404 std::vector<u8> buffer = ctx.ReadBuffer(0); 404 const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
405 const std::string src_name = Common::StringFromBuffer(buffer); 405 const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
406
407 buffer = ctx.ReadBuffer(1);
408 const std::string dst_name = Common::StringFromBuffer(buffer);
409 406
410 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); 407 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
411 408
@@ -1086,7 +1083,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1086} 1083}
1087 1084
1088void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { 1085void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
1089 const auto raw = ctx.ReadBuffer(); 1086 const auto raw = ctx.ReadBufferCopy();
1090 auto log = Common::StringFromFixedZeroTerminatedBuffer( 1087 auto log = Common::StringFromFixedZeroTerminatedBuffer(
1091 reinterpret_cast<const char*>(raw.data()), raw.size()); 1088 reinterpret_cast<const char*>(raw.data()), raw.size());
1092 1089
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 49b6d45fe..ce21b69e3 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -228,7 +228,8 @@ private:
228 return; 228 return;
229 } 229 }
230 230
231 control = ctx.ReadBuffer(); 231 // TODO: Can this be a span?
232 control = ctx.ReadBufferCopy();
232 233
233 IPC::ResponseBuilder rb{ctx, 2}; 234 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess); 235 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 5713f1288..513ea485a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -428,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
428 return; 428 return;
429 } 429 }
430 430
431 // This function is unique to yuzu for the turbo buttons to work properly
432 controller.device->TurboButtonUpdate();
433
431 auto& pad_entry = controller.npad_pad_state; 434 auto& pad_entry = controller.npad_pad_state;
432 auto& trigger_entry = controller.npad_trigger_state; 435 auto& trigger_entry = controller.npad_trigger_state;
433 const auto button_state = controller.device->GetNpadButtons(); 436 const auto button_state = controller.device->GetNpadButtons();
@@ -755,11 +758,12 @@ Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
755 return hid_core.GetSupportedStyleTag(); 758 return hid_core.GetSupportedStyleTag();
756} 759}
757 760
758void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { 761void Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
762 const auto length = data.size();
759 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 763 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
760 supported_npad_id_types.clear(); 764 supported_npad_id_types.clear();
761 supported_npad_id_types.resize(length / sizeof(u32)); 765 supported_npad_id_types.resize(length / sizeof(u32));
762 std::memcpy(supported_npad_id_types.data(), data, length); 766 std::memcpy(supported_npad_id_types.data(), data.data(), length);
763} 767}
764 768
765void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 769void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 1a589cca2..1f7d33459 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <mutex> 8#include <mutex>
9#include <span>
9 10
10#include "common/bit_field.h" 11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -95,7 +96,7 @@ public:
95 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
96 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 97 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
97 98
98 void SetSupportedNpadIdTypes(u8* data, std::size_t length); 99 void SetSupportedNpadIdTypes(std::span<const u8> data);
99 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 100 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
100 std::size_t GetSupportedNpadIdTypesSize() const; 101 std::size_t GetSupportedNpadIdTypesSize() const;
101 102
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index bf28440c6..f15f1a6bb 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1026,7 +1026,7 @@ void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
1026 const auto applet_resource_user_id{rp.Pop<u64>()}; 1026 const auto applet_resource_user_id{rp.Pop<u64>()};
1027 1027
1028 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1028 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1030 1030
1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1032 1032
@@ -2104,7 +2104,7 @@ void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) {
2104 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2104 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2105 const auto unknown{rp.Pop<u64>()}; 2105 const auto unknown{rp.Pop<u64>()};
2106 2106
2107 const auto buffer = ctx.ReadBuffer(); 2107 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2108 2108
2109 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", 2109 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2110 connection_handle.npad_id, unknown); 2110 connection_handle.npad_id, unknown);
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
index d3960f506..65e301137 100644
--- a/src/core/hle/service/hid/hidbus/hidbus_base.h
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/hle/result.h" 9#include "core/hle/result.h"
9 10
@@ -150,7 +151,7 @@ public:
150 } 151 }
151 152
152 // Assigns a command from data 153 // Assigns a command from data
153 virtual bool SetCommand(const std::vector<u8>& data) { 154 virtual bool SetCommand(std::span<const u8> data) {
154 return {}; 155 return {};
155 } 156 }
156 157
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
index 78ed47014..35847cbdd 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.cpp
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -116,7 +116,7 @@ std::vector<u8> RingController::GetReply() const {
116 } 116 }
117} 117}
118 118
119bool RingController::SetCommand(const std::vector<u8>& data) { 119bool RingController::SetCommand(std::span<const u8> data) {
120 if (data.size() < 4) { 120 if (data.size() < 4) {
121 LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); 121 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
122 command = RingConCommands::Error; 122 command = RingConCommands::Error;
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
index 845ce85a5..c2fb386b1 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.h
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/hle/service/hid/hidbus/hidbus_base.h" 10#include "core/hle/service/hid/hidbus/hidbus_base.h"
@@ -31,7 +32,7 @@ public:
31 u8 GetDeviceId() const override; 32 u8 GetDeviceId() const override;
32 33
33 // Assigns a command from data 34 // Assigns a command from data
34 bool SetCommand(const std::vector<u8>& data) override; 35 bool SetCommand(std::span<const u8> data) override;
35 36
36 // Returns a reply from a command 37 // Returns a reply from a command
37 std::vector<u8> GetReply() const override; 38 std::vector<u8> GetReply() const override;
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
index dd439f60a..d0e760314 100644
--- a/src/core/hle/service/hid/hidbus/starlink.cpp
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -42,7 +42,7 @@ std::vector<u8> Starlink::GetReply() const {
42 return {}; 42 return {};
43} 43}
44 44
45bool Starlink::SetCommand(const std::vector<u8>& data) { 45bool Starlink::SetCommand(std::span<const u8> data) {
46 LOG_ERROR(Service_HID, "Command not implemented"); 46 LOG_ERROR(Service_HID, "Command not implemented");
47 return false; 47 return false;
48} 48}
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
index 0b1b7ba49..07c800e6e 100644
--- a/src/core/hle/service/hid/hidbus/starlink.h
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -29,7 +29,7 @@ public:
29 u8 GetDeviceId() const override; 29 u8 GetDeviceId() const override;
30 30
31 // Assigns a command from data 31 // Assigns a command from data
32 bool SetCommand(const std::vector<u8>& data) override; 32 bool SetCommand(std::span<const u8> data) override;
33 33
34 // Returns a reply from a command 34 // Returns a reply from a command
35 std::vector<u8> GetReply() const override; 35 std::vector<u8> GetReply() const override;
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
index e477443e3..07632c872 100644
--- a/src/core/hle/service/hid/hidbus/stubbed.cpp
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -43,7 +43,7 @@ std::vector<u8> HidbusStubbed::GetReply() const {
43 return {}; 43 return {};
44} 44}
45 45
46bool HidbusStubbed::SetCommand(const std::vector<u8>& data) { 46bool HidbusStubbed::SetCommand(std::span<const u8> data) {
47 LOG_ERROR(Service_HID, "Command not implemented"); 47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false; 48 return false;
49} 49}
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
index 91165ceff..38eaa0ecc 100644
--- a/src/core/hle/service/hid/hidbus/stubbed.h
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -29,7 +29,7 @@ public:
29 u8 GetDeviceId() const override; 29 u8 GetDeviceId() const override;
30 30
31 // Assigns a command from data 31 // Assigns a command from data
32 bool SetCommand(const std::vector<u8>& data) override; 32 bool SetCommand(std::span<const u8> data) override;
33 33
34 // Returns a reply from a command 34 // Returns a reply from a command
35 std::vector<u8> GetReply() const override; 35 std::vector<u8> GetReply() const override;
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 8f2920c51..1295a44c7 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -62,7 +62,7 @@ public:
62 const auto parameters{rp.PopRaw<InputParameters>()}; 62 const auto parameters{rp.PopRaw<InputParameters>()};
63 63
64 // Optional input/output buffers 64 // Optional input/output buffers
65 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; 65 const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
66 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); 66 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
67 67
68 // Function call prototype: 68 // Function call prototype:
@@ -132,7 +132,7 @@ public:
132 const auto command{rp.PopRaw<u64>()}; 132 const auto command{rp.PopRaw<u64>()};
133 133
134 // Optional input/output buffers 134 // Optional input/output buffers
135 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; 135 const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
136 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); 136 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
137 137
138 // Function call prototype: 138 // Function call prototype:
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index c49c61cff..e5099d61f 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -412,7 +412,7 @@ public:
412 } 412 }
413 413
414 void SetAdvertiseData(Kernel::HLERequestContext& ctx) { 414 void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
415 std::vector<u8> read_buffer = ctx.ReadBuffer(); 415 const auto read_buffer = ctx.ReadBuffer();
416 416
417 IPC::ResponseBuilder rb{ctx, 2}; 417 IPC::ResponseBuilder rb{ctx, 2};
418 rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); 418 rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
@@ -464,7 +464,7 @@ public:
464 parameters.security_config.passphrase_size, 464 parameters.security_config.passphrase_size,
465 parameters.security_config.security_mode, parameters.local_communication_version); 465 parameters.security_config.security_mode, parameters.local_communication_version);
466 466
467 const std::vector<u8> read_buffer = ctx.ReadBuffer(); 467 const auto read_buffer = ctx.ReadBuffer();
468 if (read_buffer.size() != sizeof(NetworkInfo)) { 468 if (read_buffer.size() != sizeof(NetworkInfo)) {
469 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); 469 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
470 IPC::ResponseBuilder rb{ctx, 2}; 470 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 204b0e757..c562e04d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -3,7 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
6#include <vector> 7#include <vector>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
9 11
@@ -31,7 +33,7 @@ public:
31 * @param output A buffer where the output data will be written to. 33 * @param output A buffer where the output data will be written to.
32 * @returns The result code of the ioctl. 34 * @returns The result code of the ioctl.
33 */ 35 */
34 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 std::vector<u8>& output) = 0; 37 std::vector<u8>& output) = 0;
36 38
37 /** 39 /**
@@ -42,8 +44,8 @@ public:
42 * @param output A buffer where the output data will be written to. 44 * @param output A buffer where the output data will be written to.
43 * @returns The result code of the ioctl. 45 * @returns The result code of the ioctl.
44 */ 46 */
45 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; 48 std::span<const u8> inline_input, std::vector<u8>& output) = 0;
47 49
48 /** 50 /**
49 * Handles an ioctl3 request. 51 * Handles an ioctl3 request.
@@ -53,7 +55,7 @@ public:
53 * @param inline_output A buffer where the inlined output data will be written to. 55 * @param inline_output A buffer where the inlined output data will be written to.
54 * @returns The result code of the ioctl. 56 * @returns The result code of the ioctl.
55 */ 57 */
56 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 std::vector<u8>& output, std::vector<u8>& inline_output) = 0; 59 std::vector<u8>& output, std::vector<u8>& inline_output) = 0;
58 60
59 /** 61 /**
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 4122fc98d..5a5b2e305 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,19 +17,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} 17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
18nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
19 19
20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::vector<u8>& output) { 21 std::vector<u8>& output) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
23 return NvResult::NotImplemented; 23 return NvResult::NotImplemented;
24} 24}
25 25
26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 const std::vector<u8>& inline_input, std::vector<u8>& output) { 27 std::span<const u8> inline_input, std::vector<u8>& output) {
28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
29 return NvResult::NotImplemented; 29 return NvResult::NotImplemented;
30} 30}
31 31
32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
33 std::vector<u8>& output, std::vector<u8>& inline_output) { 33 std::vector<u8>& output, std::vector<u8>& inline_output) {
34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
35 return NvResult::NotImplemented; 35 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 04217ab12..81bd7960a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -25,12 +25,12 @@ public:
25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); 25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
26 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::vector<u8>& output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
33 std::vector<u8>& output, std::vector<u8>& inline_output) override; 33 std::vector<u8>& inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index b635e6ed1..681bd0867 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -27,7 +27,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con
27 27
28nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
29 29
30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::vector<u8>& output) { 31 std::vector<u8>& output) {
32 switch (command.group) { 32 switch (command.group) {
33 case 'A': 33 case 'A':
@@ -60,13 +60,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 const std::vector<u8>& inline_input, std::vector<u8>& output) { 64 std::span<const u8> inline_input, std::vector<u8>& output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
68 68
69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
70 std::vector<u8>& output, std::vector<u8>& inline_output) { 70 std::vector<u8>& output, std::vector<u8>& inline_output) {
71 switch (command.group) { 71 switch (command.group) {
72 case 'A': 72 case 'A':
@@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 87void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 88void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 89
90NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) {
91 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
93 93
@@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
141 return NvResult::Success; 141 return NvResult::Success;
142} 142}
143 143
144NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) {
145 IoctlAllocSpace params{}; 145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size()); 146 std::memcpy(&params, input.data(), input.size());
147 147
@@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
220 mapping_map.erase(offset); 220 mapping_map.erase(offset);
221} 221}
222 222
223NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) {
224 IoctlFreeSpace params{}; 224 IoctlFreeSpace params{};
225 std::memcpy(&params, input.data(), input.size()); 225 std::memcpy(&params, input.data(), input.size());
226 226
@@ -266,7 +266,7 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
266 return NvResult::Success; 266 return NvResult::Success;
267} 267}
268 268
269NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) {
270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
271 271
272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
@@ -320,7 +320,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
320 return NvResult::Success; 320 return NvResult::Success;
321} 321}
322 322
323NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 323NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) {
324 IoctlMapBufferEx params{}; 324 IoctlMapBufferEx params{};
325 std::memcpy(&params, input.data(), input.size()); 325 std::memcpy(&params, input.data(), input.size());
326 326
@@ -424,7 +424,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
424 return NvResult::Success; 424 return NvResult::Success;
425} 425}
426 426
427NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 427NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
428 IoctlUnmapBuffer params{}; 428 IoctlUnmapBuffer params{};
429 std::memcpy(&params, input.data(), input.size()); 429 std::memcpy(&params, input.data(), input.size());
430 430
@@ -463,7 +463,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
463 return NvResult::Success; 463 return NvResult::Success;
464} 464}
465 465
466NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 466NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) {
467 IoctlBindChannel params{}; 467 IoctlBindChannel params{};
468 std::memcpy(&params, input.data(), input.size()); 468 std::memcpy(&params, input.data(), input.size());
469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
@@ -492,7 +492,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
492 }; 492 };
493} 493}
494 494
495NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 495NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) {
496 IoctlGetVaRegions params{}; 496 IoctlGetVaRegions params{};
497 std::memcpy(&params, input.data(), input.size()); 497 std::memcpy(&params, input.data(), input.size());
498 498
@@ -511,7 +511,7 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
511 return NvResult::Success; 511 return NvResult::Success;
512} 512}
513 513
514NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 514NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output,
515 std::vector<u8>& inline_output) { 515 std::vector<u8>& inline_output) {
516 IoctlGetVaRegions params{}; 516 IoctlGetVaRegions params{};
517 std::memcpy(&params, input.data(), input.size()); 517 std::memcpy(&params, input.data(), input.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 86fe71c75..1aba8d579 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -47,12 +47,12 @@ public:
47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); 47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
48 ~nvhost_as_gpu() override; 48 ~nvhost_as_gpu() override;
49 49
50 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 50 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
51 std::vector<u8>& output) override; 51 std::vector<u8>& output) override;
52 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 52 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
53 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 53 std::span<const u8> inline_input, std::vector<u8>& output) override;
54 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 54 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
55 std::vector<u8>& output, std::vector<u8>& inline_output) override; 55 std::vector<u8>& inline_output) override;
56 56
57 void OnOpen(DeviceFD fd) override; 57 void OnOpen(DeviceFD fd) override;
58 void OnClose(DeviceFD fd) override; 58 void OnClose(DeviceFD fd) override;
@@ -138,17 +138,17 @@ private:
138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
139 "IoctlGetVaRegions is incorrect size"); 139 "IoctlGetVaRegions is incorrect size");
140 140
141 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); 141 NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output);
142 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 142 NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output);
143 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); 143 NvResult Remap(std::span<const u8> input, std::vector<u8>& output);
144 NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); 144 NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output);
145 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 145 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
146 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); 146 NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output);
147 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output);
148 148
149 void GetVARegionsImpl(IoctlGetVaRegions& params); 149 void GetVARegionsImpl(IoctlGetVaRegions& params);
150 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); 150 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output);
151 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 151 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output,
152 std::vector<u8>& inline_output); 152 std::vector<u8>& inline_output);
153 153
154 void FreeMappingLocked(u64 offset); 154 void FreeMappingLocked(u64 offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index eee11fab8..0cdde82a7 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -34,7 +34,7 @@ nvhost_ctrl::~nvhost_ctrl() {
34 } 34 }
35} 35}
36 36
37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
38 std::vector<u8>& output) { 38 std::vector<u8>& output) {
39 switch (command.group) { 39 switch (command.group) {
40 case 0x0: 40 case 0x0:
@@ -63,13 +63,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
63 return NvResult::NotImplemented; 63 return NvResult::NotImplemented;
64} 64}
65 65
66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
67 const std::vector<u8>& inline_input, std::vector<u8>& output) { 67 std::span<const u8> inline_input, std::vector<u8>& output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented; 69 return NvResult::NotImplemented;
70} 70}
71 71
72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
73 std::vector<u8>& output, std::vector<u8>& inline_outpu) { 73 std::vector<u8>& output, std::vector<u8>& inline_outpu) {
74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
75 return NvResult::NotImplemented; 75 return NvResult::NotImplemented;
@@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 79
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 80void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 81
82NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) {
83 IocGetConfigParams params{}; 83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params)); 84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
@@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 87 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 88}
89 89
90NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
91 bool is_allocation) { 91 bool is_allocation) {
92 IocCtrlEventWaitParams params{}; 92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params)); 93 std::memcpy(&params, input.data(), sizeof(params));
@@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 231 return NvResult::Success;
232} 232}
233 233
234NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) {
235 IocCtrlEventRegisterParams params{}; 235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params)); 236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 237 const u32 event_id = params.user_event_id;
@@ -252,8 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
252 return NvResult::Success; 252 return NvResult::Success;
253} 253}
254 254
255NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, 255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) {
256 std::vector<u8>& output) {
257 IocCtrlEventUnregisterParams params{}; 256 IocCtrlEventUnregisterParams params{};
258 std::memcpy(&params, input.data(), sizeof(params)); 257 std::memcpy(&params, input.data(), sizeof(params));
259 const u32 event_id = params.user_event_id & 0x00FF; 258 const u32 event_id = params.user_event_id & 0x00FF;
@@ -263,7 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
263 return FreeEvent(event_id); 262 return FreeEvent(event_id);
264} 263}
265 264
266NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, 265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,
267 std::vector<u8>& output) { 266 std::vector<u8>& output) {
268 IocCtrlEventUnregisterBatchParams params{}; 267 IocCtrlEventUnregisterBatchParams params{};
269 std::memcpy(&params, input.data(), sizeof(params)); 268 std::memcpy(&params, input.data(), sizeof(params));
@@ -282,7 +281,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
282 return NvResult::Success; 281 return NvResult::Success;
283} 282}
284 283
285NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 284NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) {
286 IocCtrlEventClearParams params{}; 285 IocCtrlEventClearParams params{};
287 std::memcpy(&params, input.data(), sizeof(params)); 286 std::memcpy(&params, input.data(), sizeof(params));
288 287
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 0b56d7070..dd2e7888a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -25,12 +25,12 @@ public:
25 NvCore::Container& core); 25 NvCore::Container& core);
26 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::vector<u8>& output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
33 std::vector<u8>& output, std::vector<u8>& inline_output) override; 33 std::vector<u8>& inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
@@ -186,13 +186,13 @@ private:
186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size"); 187 "IocCtrlEventKill is incorrect size");
188 188
189 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output);
190 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 190 NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
191 bool is_allocation); 191 bool is_allocation);
192 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output);
193 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output);
194 NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output); 194 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output);
195 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output);
196 196
197 NvResult FreeEvent(u32 slot); 197 NvResult FreeEvent(u32 slot);
198 198
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index b97813fbc..be3c083db 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -21,7 +21,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
21 events_interface.FreeEvent(unknown_event); 21 events_interface.FreeEvent(unknown_event);
22} 22}
23 23
24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) { 25 std::vector<u8>& output) {
26 switch (command.group) { 26 switch (command.group) {
27 case 'G': 27 case 'G':
@@ -53,13 +53,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u
53 return NvResult::NotImplemented; 53 return NvResult::NotImplemented;
54} 54}
55 55
56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 const std::vector<u8>& inline_input, std::vector<u8>& output) { 57 std::span<const u8> inline_input, std::vector<u8>& output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented; 59 return NvResult::NotImplemented;
60} 60}
61 61
62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
63 std::vector<u8>& output, std::vector<u8>& inline_output) { 63 std::vector<u8>& output, std::vector<u8>& inline_output) {
64 switch (command.group) { 64 switch (command.group) {
65 case 'G': 65 case 'G':
@@ -82,8 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 84
85NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, 85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) {
86 std::vector<u8>& output) {
87 LOG_DEBUG(Service_NVDRV, "called"); 86 LOG_DEBUG(Service_NVDRV, "called");
88 IoctlCharacteristics params{}; 87 IoctlCharacteristics params{};
89 std::memcpy(&params, input.data(), input.size()); 88 std::memcpy(&params, input.data(), input.size());
@@ -128,7 +127,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
128 return NvResult::Success; 127 return NvResult::Success;
129} 128}
130 129
131NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
132 std::vector<u8>& inline_output) { 131 std::vector<u8>& inline_output) {
133 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
134 IoctlCharacteristics params{}; 133 IoctlCharacteristics params{};
@@ -176,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::
176 return NvResult::Success; 175 return NvResult::Success;
177} 176}
178 177
179NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { 178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) {
180 IoctlGpuGetTpcMasksArgs params{}; 179 IoctlGpuGetTpcMasksArgs params{};
181 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
182 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -187,7 +186,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<
187 return NvResult::Success; 186 return NvResult::Success;
188} 187}
189 188
190NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
191 std::vector<u8>& inline_output) { 190 std::vector<u8>& inline_output) {
192 IoctlGpuGetTpcMasksArgs params{}; 191 IoctlGpuGetTpcMasksArgs params{};
193 std::memcpy(&params, input.data(), input.size()); 192 std::memcpy(&params, input.data(), input.size());
@@ -200,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<
200 return NvResult::Success; 199 return NvResult::Success;
201} 200}
202 201
203NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) {
204 LOG_DEBUG(Service_NVDRV, "called"); 203 LOG_DEBUG(Service_NVDRV, "called");
205 204
206 IoctlActiveSlotMask params{}; 205 IoctlActiveSlotMask params{};
@@ -213,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::v
213 return NvResult::Success; 212 return NvResult::Success;
214} 213}
215 214
216NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) {
217 LOG_DEBUG(Service_NVDRV, "called"); 216 LOG_DEBUG(Service_NVDRV, "called");
218 217
219 IoctlZcullGetCtxSize params{}; 218 IoctlZcullGetCtxSize params{};
@@ -225,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vec
225 return NvResult::Success; 224 return NvResult::Success;
226} 225}
227 226
228NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) {
229 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
230 229
231 IoctlNvgpuGpuZcullGetInfoArgs params{}; 230 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -248,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector
248 return NvResult::Success; 247 return NvResult::Success;
249} 248}
250 249
251NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { 250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) {
252 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 251 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
253 252
254 IoctlZbcSetTable params{}; 253 IoctlZbcSetTable params{};
@@ -264,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<
264 return NvResult::Success; 263 return NvResult::Success;
265} 264}
266 265
267NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { 266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) {
268 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 267 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
269 268
270 IoctlZbcQueryTable params{}; 269 IoctlZbcQueryTable params{};
@@ -274,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vecto
274 return NvResult::Success; 273 return NvResult::Success;
275} 274}
276 275
277NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { 276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) {
278 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 277 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
279 278
280 IoctlFlushL2 params{}; 279 IoctlFlushL2 params{};
@@ -284,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>&
284 return NvResult::Success; 283 return NvResult::Success;
285} 284}
286 285
287NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { 286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) {
288 LOG_DEBUG(Service_NVDRV, "called"); 287 LOG_DEBUG(Service_NVDRV, "called");
289 288
290 IoctlGetGpuTime params{}; 289 IoctlGetGpuTime params{};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 1e8f254e2..b9333d9d3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -21,12 +21,12 @@ public:
21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_); 21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
22 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
23 23
24 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) override; 25 std::vector<u8>& output) override;
26 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 27 std::span<const u8> inline_input, std::vector<u8>& output) override;
28 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
29 std::vector<u8>& output, std::vector<u8>& inline_output) override; 29 std::vector<u8>& inline_output) override;
30 30
31 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
32 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
@@ -151,21 +151,21 @@ private:
151 }; 151 };
152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
153 153
154 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 154 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output);
155 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 155 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
156 std::vector<u8>& inline_output); 156 std::vector<u8>& inline_output);
157 157
158 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 158 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output);
159 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 159 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
160 std::vector<u8>& inline_output); 160 std::vector<u8>& inline_output);
161 161
162 NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 162 NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output);
163 NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output);
164 NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); 164 NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output);
165 NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 165 NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output);
166 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output);
167 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output);
168 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output);
169 169
170 EventInterface& events_interface; 170 EventInterface& events_interface;
171 171
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index e123564c6..d2308fffc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -46,7 +46,7 @@ nvhost_gpu::~nvhost_gpu() {
46 syncpoint_manager.FreeSyncpoint(channel_syncpoint); 46 syncpoint_manager.FreeSyncpoint(channel_syncpoint);
47} 47}
48 48
49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
50 std::vector<u8>& output) { 50 std::vector<u8>& output) {
51 switch (command.group) { 51 switch (command.group) {
52 case 0x0: 52 case 0x0:
@@ -98,8 +98,8 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
98 return NvResult::NotImplemented; 98 return NvResult::NotImplemented;
99}; 99};
100 100
101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
102 const std::vector<u8>& inline_input, std::vector<u8>& output) { 102 std::span<const u8> inline_input, std::vector<u8>& output) {
103 switch (command.group) { 103 switch (command.group) {
104 case 'H': 104 case 'H':
105 switch (command.cmd) { 105 switch (command.cmd) {
@@ -112,7 +112,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& i
112 return NvResult::NotImplemented; 112 return NvResult::NotImplemented;
113} 113}
114 114
115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
116 std::vector<u8>& output, std::vector<u8>& inline_output) { 116 std::vector<u8>& output, std::vector<u8>& inline_output) {
117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
118 return NvResult::NotImplemented; 118 return NvResult::NotImplemented;
@@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 121void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 122void nvhost_gpu::OnClose(DeviceFD fd) {}
123 123
124NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
125 IoctlSetNvmapFD params{}; 125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size()); 126 std::memcpy(&params, input.data(), input.size());
127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& o
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135 135
136 IoctlClientData params{}; 136 IoctlClientData params{};
@@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>
139 return NvResult::Success; 139 return NvResult::Success;
140} 140}
141 141
142NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) {
143 LOG_DEBUG(Service_NVDRV, "called"); 143 LOG_DEBUG(Service_NVDRV, "called");
144 144
145 IoctlClientData params{}; 145 IoctlClientData params{};
@@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>
149 return NvResult::Success; 149 return NvResult::Success;
150} 150}
151 151
152NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { 152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 153 std::memcpy(&zcull_params, input.data(), input.size());
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 155 zcull_params.mode);
@@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& ou
158 return NvResult::Success; 158 return NvResult::Success;
159} 159}
160 160
161NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { 161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) {
162 IoctlSetErrorNotifier params{}; 162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size()); 163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
@@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<
168 return NvResult::Success; 168 return NvResult::Success;
169} 169}
170 170
171NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { 171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 172 std::memcpy(&channel_priority, input.data(), input.size());
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174 174
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { 178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) {
179 IoctlAllocGpfifoEx2 params{}; 179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 181 LOG_WARNING(Service_NVDRV,
@@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
197 return NvResult::Success; 197 return NvResult::Success;
198} 198}
199 199
200NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { 200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) {
201 IoctlAllocObjCtx params{}; 201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -293,7 +293,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
293 return NvResult::Success; 293 return NvResult::Success;
294} 294}
295 295
296NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, 296NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
297 bool kickoff) { 297 bool kickoff) {
298 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 298 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
299 UNIMPLEMENTED(); 299 UNIMPLEMENTED();
@@ -314,8 +314,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<
314 return SubmitGPFIFOImpl(params, output, std::move(entries)); 314 return SubmitGPFIFOImpl(params, output, std::move(entries));
315} 315}
316 316
317NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, 317NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
318 const std::vector<u8>& input_inline,
319 std::vector<u8>& output) { 318 std::vector<u8>& output) {
320 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 319 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
321 UNIMPLEMENTED(); 320 UNIMPLEMENTED();
@@ -328,7 +327,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
328 return SubmitGPFIFOImpl(params, output, std::move(entries)); 327 return SubmitGPFIFOImpl(params, output, std::move(entries));
329} 328}
330 329
331NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 330NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
332 IoctlGetWaitbase params{}; 331 IoctlGetWaitbase params{};
333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 332 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 333 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
@@ -338,7 +337,7 @@ NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>&
338 return NvResult::Success; 337 return NvResult::Success;
339} 338}
340 339
341NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 340NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) {
342 IoctlChannelSetTimeout params{}; 341 IoctlChannelSetTimeout params{};
343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 342 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 343 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
@@ -346,7 +345,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector
346 return NvResult::Success; 345 return NvResult::Success;
347} 346}
348 347
349NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { 348NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) {
350 IoctlSetTimeslice params{}; 349 IoctlSetTimeslice params{};
351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 350 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 351 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 1e4ecd55b..3ca58202d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -40,12 +40,12 @@ public:
40 NvCore::Container& core); 40 NvCore::Container& core);
41 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
42 42
43 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
44 std::vector<u8>& output) override; 44 std::vector<u8>& output) override;
45 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 46 std::span<const u8> inline_input, std::vector<u8>& output) override;
47 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
48 std::vector<u8>& output, std::vector<u8>& inline_output) override; 48 std::vector<u8>& inline_output) override;
49 49
50 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
51 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
@@ -186,23 +186,23 @@ private:
186 u32_le channel_priority{}; 186 u32_le channel_priority{};
187 u32_le channel_timeslice{}; 187 u32_le channel_timeslice{};
188 188
189 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
190 NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output); 190 NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output);
191 NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output); 191 NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output);
192 NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output);
193 NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output);
194 NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 194 NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output);
195 NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output);
196 NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 196 NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
198 Tegra::CommandList&& entries); 198 Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, 199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
200 bool kickoff = false); 200 bool kickoff = false);
201 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline, 201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
202 std::vector<u8>& output); 202 std::vector<u8>& output);
203 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 203 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
204 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output);
205 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output);
206 206
207 EventInterface& events_interface; 207 EventInterface& events_interface;
208 NvCore::Container& core; 208 NvCore::Container& core;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 1703f9cc3..0c7aee1b8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,7 +15,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} 15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
55 return NvResult::NotImplemented; 55 return NvResult::NotImplemented;
56} 56}
57 57
58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 const std::vector<u8>& inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::vector<u8>& output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::vector<u8>& output, std::vector<u8>& inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index c1b4e53e8..0d615bbcb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,12 +13,12 @@ public:
13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); 13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
14 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
15 15
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
17 std::vector<u8>& output) override; 17 std::vector<u8>& output) override;
18 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 19 std::span<const u8> inline_input, std::vector<u8>& output) override;
20 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
21 std::vector<u8>& output, std::vector<u8>& inline_output) override; 21 std::vector<u8>& inline_output) override;
22 22
23 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 99eede702..7bcef105b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -23,7 +23,7 @@ namespace {
23// Copies count amount of type T from the input vector into the dst vector. 23// Copies count amount of type T from the input vector into the dst vector.
24// Returns the number of bytes written into dst. 24// Returns the number of bytes written into dst.
25template <typename T> 25template <typename T>
26std::size_t SliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count, 26std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::size_t count,
27 std::size_t offset) { 27 std::size_t offset) {
28 if (dst.empty()) { 28 if (dst.empty()) {
29 return 0; 29 return 0;
@@ -63,7 +63,7 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); 63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64} 64}
65 65
66NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 66NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
67 IoctlSetNvmapFD params{}; 67 IoctlSetNvmapFD params{};
68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD)); 68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -72,7 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
72 return NvResult::Success; 72 return NvResult::Success;
73} 73}
74 74
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, 75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,
76 std::vector<u8>& output) { 76 std::vector<u8>& output) {
77 IoctlSubmit params{}; 77 IoctlSubmit params{};
78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
@@ -121,7 +121,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
121 return NvResult::Success; 121 return NvResult::Success;
122} 122}
123 123
124NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { 124NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) {
125 IoctlGetSyncpoint params{}; 125 IoctlGetSyncpoint params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -133,7 +133,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
133 return NvResult::Success; 133 return NvResult::Success;
134} 134}
135 135
136NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 136NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
137 IoctlGetWaitbase params{}; 137 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
@@ -142,7 +142,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vec
142 return NvResult::Success; 142 return NvResult::Success;
143} 143}
144 144
145NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 145NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) {
146 IoctlMapBuffer params{}; 146 IoctlMapBuffer params{};
147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -159,7 +159,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
159 return NvResult::Success; 159 return NvResult::Success;
160} 160}
161 161
162NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 162NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
163 IoctlMapBuffer params{}; 163 IoctlMapBuffer params{};
164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -173,8 +173,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
173 return NvResult::Success; 173 return NvResult::Success;
174} 174}
175 175
176NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, 176NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) {
177 std::vector<u8>& output) {
178 std::memcpy(&submit_timeout, input.data(), input.size()); 177 std::memcpy(&submit_timeout, input.data(), input.size());
179 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 178 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
180 return NvResult::Success; 179 return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index fe76100c8..5af26a26f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -107,13 +107,13 @@ protected:
107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(const std::vector<u8>& input); 110 NvResult SetNVMAPfd(std::span<const u8> input);
111 NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output);
112 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output);
113 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
114 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output);
115 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 115 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
116 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output);
117 117
118 Kernel::KEvent* QueryEvent(u32 event_id) override; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119 119
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index bdbc2f9e1..39f30e7c8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -12,7 +12,7 @@ namespace Service::Nvidia::Devices {
12nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {} 12nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}
13nvhost_nvjpg::~nvhost_nvjpg() = default; 13nvhost_nvjpg::~nvhost_nvjpg() = default;
14 14
15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) { 16 std::vector<u8>& output) {
17 switch (command.group) { 17 switch (command.group) {
18 case 'H': 18 case 'H':
@@ -31,13 +31,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
31 return NvResult::NotImplemented; 31 return NvResult::NotImplemented;
32} 32}
33 33
34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 const std::vector<u8>& inline_input, std::vector<u8>& output) { 35 std::span<const u8> inline_input, std::vector<u8>& output) {
36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
37 return NvResult::NotImplemented; 37 return NvResult::NotImplemented;
38} 38}
39 39
40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
41 std::vector<u8>& output, std::vector<u8>& inline_output) { 41 std::vector<u8>& output, std::vector<u8>& inline_output) {
42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
43 return NvResult::NotImplemented; 43 return NvResult::NotImplemented;
@@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 46void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 47void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 48
49NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
50 IoctlSetNvmapFD params{}; 50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size()); 51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 440e7d371..41b57e872 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -15,12 +15,12 @@ public:
15 explicit nvhost_nvjpg(Core::System& system_); 15 explicit nvhost_nvjpg(Core::System& system_);
16 ~nvhost_nvjpg() override; 16 ~nvhost_nvjpg() override;
17 17
18 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) override; 19 std::vector<u8>& output) override;
20 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 21 std::span<const u8> inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
23 std::vector<u8>& output, std::vector<u8>& inline_output) override; 23 std::vector<u8>& inline_output) override;
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
@@ -33,7 +33,7 @@ private:
33 33
34 s32_le nvmap_fd{}; 34 s32_le nvmap_fd{};
35 35
36 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 36 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
37}; 37};
38 38
39} // namespace Service::Nvidia::Devices 39} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 73f97136e..b0ea402a7 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,7 +15,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
15 15
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
55 return NvResult::NotImplemented; 55 return NvResult::NotImplemented;
56} 56}
57 57
58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 const std::vector<u8>& inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::vector<u8>& output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::vector<u8>& output, std::vector<u8>& inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index f164caafb..b5e350a83 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -12,12 +12,12 @@ public:
12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core); 12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
13 ~nvhost_vic(); 13 ~nvhost_vic();
14 14
15 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) override; 16 std::vector<u8>& output) override;
17 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
18 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 18 std::span<const u8> inline_input, std::vector<u8>& output) override;
19 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
20 std::vector<u8>& output, std::vector<u8>& inline_output) override; 20 std::vector<u8>& inline_output) override;
21 21
22 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
23 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index fa29db758..29c1e0f01 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -25,7 +25,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
25 25
26nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
27 27
28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) { 29 std::vector<u8>& output) {
30 switch (command.group) { 30 switch (command.group) {
31 case 0x1: 31 case 0x1:
@@ -54,13 +54,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
54 return NvResult::NotImplemented; 54 return NvResult::NotImplemented;
55} 55}
56 56
57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
58 const std::vector<u8>& inline_input, std::vector<u8>& output) { 58 std::span<const u8> inline_input, std::vector<u8>& output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 std::vector<u8>& output, std::vector<u8>& inline_output) { 64 std::vector<u8>& output, std::vector<u8>& inline_output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
@@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
69void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
71 71
72NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
73 IocCreateParams params; 73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
89 return NvResult::Success; 89 return NvResult::Success;
90} 90}
91 91
92NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
93 IocAllocParams params; 93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
@@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output)
137 return result; 137 return result;
138} 138}
139 139
140NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 140NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
141 IocGetIdParams params; 141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params)); 142 std::memcpy(&params, input.data(), sizeof(params));
143 143
@@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output)
161 return NvResult::Success; 161 return NvResult::Success;
162} 162}
163 163
164NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
165 IocFromIdParams params; 165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
167 167
@@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
192 return NvResult::Success; 192 return NvResult::Success;
193} 193}
194 194
195NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { 195NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 197
198 IocParamParams params; 198 IocParamParams params;
@@ -241,7 +241,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
241 return NvResult::Success; 241 return NvResult::Success;
242} 242}
243 243
244NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 244NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) {
245 IocFreeParams params; 245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params)); 246 std::memcpy(&params, input.data(), sizeof(params));
247 247
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index e9bfd0358..82bd3b118 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -26,12 +26,12 @@ public:
26 nvmap(const nvmap&) = delete; 26 nvmap(const nvmap&) = delete;
27 nvmap& operator=(const nvmap&) = delete; 27 nvmap& operator=(const nvmap&) = delete;
28 28
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
30 std::vector<u8>& output) override; 30 std::vector<u8>& output) override;
31 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
32 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 32 std::span<const u8> inline_input, std::vector<u8>& output) override;
33 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
34 std::vector<u8>& output, std::vector<u8>& inline_output) override; 34 std::vector<u8>& inline_output) override;
35 35
36 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
@@ -106,12 +106,12 @@ private:
106 }; 106 };
107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
108 108
109 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output);
110 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output);
111 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output);
112 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output);
113 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult IocParam(std::span<const u8> input, std::vector<u8>& output);
114 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult IocFree(std::span<const u8> input, std::vector<u8>& output);
115 115
116 NvCore::Container& container; 116 NvCore::Container& container;
117 NvCore::NvMap& file; 117 NvCore::NvMap& file;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 6fc8565c0..52d27e755 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -124,7 +124,7 @@ DeviceFD Module::Open(const std::string& device_name) {
124 return fd; 124 return fd;
125} 125}
126 126
127NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 127NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
128 std::vector<u8>& output) { 128 std::vector<u8>& output) {
129 if (fd < 0) { 129 if (fd < 0) {
130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
@@ -141,8 +141,8 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
141 return itr->second->Ioctl1(fd, command, input, output); 141 return itr->second->Ioctl1(fd, command, input, output);
142} 142}
143 143
144NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 144NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
145 const std::vector<u8>& inline_input, std::vector<u8>& output) { 145 std::span<const u8> inline_input, std::vector<u8>& output) {
146 if (fd < 0) { 146 if (fd < 0) {
147 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 147 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
148 return NvResult::InvalidState; 148 return NvResult::InvalidState;
@@ -158,7 +158,7 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
158 return itr->second->Ioctl2(fd, command, input, inline_input, output); 158 return itr->second->Ioctl2(fd, command, input, inline_input, output);
159} 159}
160 160
161NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 161NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
162 std::vector<u8>& output, std::vector<u8>& inline_output) { 162 std::vector<u8>& output, std::vector<u8>& inline_output) {
163 if (fd < 0) { 163 if (fd < 0) {
164 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 164 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index f3c81bd88..b09b6e585 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -7,6 +7,7 @@
7#include <functional> 7#include <functional>
8#include <list> 8#include <list>
9#include <memory> 9#include <memory>
10#include <span>
10#include <string> 11#include <string>
11#include <unordered_map> 12#include <unordered_map>
12#include <vector> 13#include <vector>
@@ -79,14 +80,13 @@ public:
79 DeviceFD Open(const std::string& device_name); 80 DeviceFD Open(const std::string& device_name);
80 81
81 /// Sends an ioctl command to the specified file descriptor. 82 /// Sends an ioctl command to the specified file descriptor.
82 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output);
83 std::vector<u8>& output);
84 84
85 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
86 const std::vector<u8>& inline_input, std::vector<u8>& output); 86 std::span<const u8> inline_input, std::vector<u8>& output);
87 87
88 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
89 std::vector<u8>& output, std::vector<u8>& inline_output); 89 std::vector<u8>& inline_output);
90 90
91 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
92 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index e601b5da1..bcbe05b0d 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -815,8 +815,8 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
815 815
816void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { 816void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
817 Status status{Status::NoError}; 817 Status status{Status::NoError};
818 Parcel parcel_in{ctx.ReadBuffer()}; 818 InputParcel parcel_in{ctx.ReadBuffer()};
819 Parcel parcel_out{}; 819 OutputParcel parcel_out{};
820 820
821 switch (code) { 821 switch (code) {
822 case TransactionId::Connect: { 822 case TransactionId::Connect: {
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
index 4043c91f1..769e8c0a3 100644
--- a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -9,7 +9,7 @@
9 9
10namespace Service::android { 10namespace Service::android {
11 11
12QueueBufferInput::QueueBufferInput(Parcel& parcel) { 12QueueBufferInput::QueueBufferInput(InputParcel& parcel) {
13 parcel.ReadFlattened(*this); 13 parcel.ReadFlattened(*this);
14} 14}
15 15
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
index 6ea327bbe..2969f0fd5 100644
--- a/src/core/hle/service/nvflinger/graphic_buffer_producer.h
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -14,11 +14,11 @@
14 14
15namespace Service::android { 15namespace Service::android {
16 16
17class Parcel; 17class InputParcel;
18 18
19#pragma pack(push, 1) 19#pragma pack(push, 1)
20struct QueueBufferInput final { 20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel); 21 explicit QueueBufferInput(InputParcel& parcel);
22 22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, 23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, 24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
index f3fa2587d..d1b6201e0 100644
--- a/src/core/hle/service/nvflinger/parcel.h
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/alignment.h" 10#include "common/alignment.h"
@@ -12,18 +13,17 @@
12 13
13namespace Service::android { 14namespace Service::android {
14 15
15class Parcel final { 16struct ParcelHeader {
16public: 17 u32 data_size;
17 static constexpr std::size_t DefaultBufferSize = 0x40; 18 u32 data_offset;
18 19 u32 objects_size;
19 Parcel() : buffer(DefaultBufferSize) {} 20 u32 objects_offset;
20 21};
21 template <typename T> 22static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size");
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25 23
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) { 24class InputParcel final {
25public:
26 explicit InputParcel(std::span<const u8> in_data) : read_buffer(std::move(in_data)) {
27 DeserializeHeader(); 27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); 28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 } 29 }
@@ -31,9 +31,9 @@ public:
31 template <typename T> 31 template <typename T>
32 void Read(T& val) { 32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size()); 34 ASSERT(read_index + sizeof(T) <= read_buffer.size());
35 35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T)); 36 std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T); 37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4); 38 read_index = Common::AlignUp(read_index, 4);
39 } 39 }
@@ -62,10 +62,10 @@ public:
62 template <typename T> 62 template <typename T>
63 T ReadUnaligned() { 63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size()); 65 ASSERT(read_index + sizeof(T) <= read_buffer.size());
66 66
67 T val; 67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T)); 68 std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T); 69 read_index += sizeof(T);
70 return val; 70 return val;
71 } 71 }
@@ -101,6 +101,31 @@ public:
101 return token; 101 return token;
102 } 102 }
103 103
104 void DeserializeHeader() {
105 ASSERT(read_buffer.size() > sizeof(ParcelHeader));
106
107 ParcelHeader header{};
108 std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader));
109
110 read_index = header.data_offset;
111 }
112
113private:
114 std::span<const u8> read_buffer;
115 std::size_t read_index = 0;
116};
117
118class OutputParcel final {
119public:
120 static constexpr std::size_t DefaultBufferSize = 0x40;
121
122 OutputParcel() : buffer(DefaultBufferSize) {}
123
124 template <typename T>
125 explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) {
126 Write(out_data);
127 }
128
104 template <typename T> 129 template <typename T>
105 void Write(const T& val) { 130 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
@@ -133,40 +158,20 @@ public:
133 WriteObject(ptr.get()); 158 WriteObject(ptr.get());
134 } 159 }
135 160
136 void DeserializeHeader() {
137 ASSERT(buffer.size() > sizeof(Header));
138
139 Header header{};
140 std::memcpy(&header, buffer.data(), sizeof(Header));
141
142 read_index = header.data_offset;
143 }
144
145 std::vector<u8> Serialize() const { 161 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0); 162 ParcelHeader header{};
147 163 header.data_size = static_cast<u32>(write_index - sizeof(ParcelHeader));
148 Header header{}; 164 header.data_offset = sizeof(ParcelHeader);
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4; 165 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); 166 header.objects_offset = static_cast<u32>(sizeof(ParcelHeader) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header)); 167 std::memcpy(buffer.data(), &header, sizeof(ParcelHeader));
154 168
155 return buffer; 169 return buffer;
156 } 170 }
157 171
158private: 172private:
159 struct Header {
160 u32 data_size;
161 u32 data_offset;
162 u32 objects_size;
163 u32 objects_offset;
164 };
165 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
166
167 mutable std::vector<u8> buffer; 173 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0; 174 std::size_t write_index = sizeof(ParcelHeader);
169 std::size_t write_index = sizeof(Header);
170}; 175};
171 176
172} // namespace Service::android 177} // namespace Service::android
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 78f897d3e..01040b32a 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -63,7 +63,7 @@ private:
63 return ctx.ReadBuffer(1); 63 return ctx.ReadBuffer(1);
64 } 64 }
65 65
66 return std::vector<u8>{}; 66 return std::span<const u8>{};
67 }(); 67 }();
68 68
69 LOG_DEBUG(Service_PREPO, 69 LOG_DEBUG(Service_PREPO,
@@ -90,7 +90,7 @@ private:
90 return ctx.ReadBuffer(1); 90 return ctx.ReadBuffer(1);
91 } 91 }
92 92
93 return std::vector<u8>{}; 93 return std::span<const u8>{};
94 }(); 94 }();
95 95
96 LOG_DEBUG(Service_PREPO, 96 LOG_DEBUG(Service_PREPO,
@@ -142,7 +142,7 @@ private:
142 return ctx.ReadBuffer(1); 142 return ctx.ReadBuffer(1);
143 } 143 }
144 144
145 return std::vector<u8>{}; 145 return std::span<const u8>{};
146 }(); 146 }();
147 147
148 LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", 148 LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
@@ -166,7 +166,7 @@ private:
166 return ctx.ReadBuffer(1); 166 return ctx.ReadBuffer(1);
167 } 167 }
168 168
169 return std::vector<u8>{}; 169 return std::span<const u8>{};
170 }(); 170 }();
171 171
172 LOG_DEBUG(Service_PREPO, 172 LOG_DEBUG(Service_PREPO,
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 9e94a462f..bdb499268 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -208,7 +208,6 @@ void BSD::Bind(Kernel::HLERequestContext& ctx) {
208 const s32 fd = rp.Pop<s32>(); 208 const s32 fd = rp.Pop<s32>();
209 209
210 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); 210 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
211
212 BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer())); 211 BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
213} 212}
214 213
@@ -312,7 +311,7 @@ void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
312 const u32 level = rp.Pop<u32>(); 311 const u32 level = rp.Pop<u32>();
313 const OptName optname = static_cast<OptName>(rp.Pop<u32>()); 312 const OptName optname = static_cast<OptName>(rp.Pop<u32>());
314 313
315 const std::vector<u8> buffer = ctx.ReadBuffer(); 314 const auto buffer = ctx.ReadBuffer();
316 const u8* optval = buffer.empty() ? nullptr : buffer.data(); 315 const u8* optval = buffer.empty() ? nullptr : buffer.data();
317 size_t optlen = buffer.size(); 316 size_t optlen = buffer.size();
318 317
@@ -489,7 +488,7 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
489 return {fd, Errno::SUCCESS}; 488 return {fd, Errno::SUCCESS};
490} 489}
491 490
492std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, 491std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
493 s32 nfds, s32 timeout) { 492 s32 nfds, s32 timeout) {
494 if (write_buffer.size() < nfds * sizeof(PollFD)) { 493 if (write_buffer.size() < nfds * sizeof(PollFD)) {
495 return {-1, Errno::INVAL}; 494 return {-1, Errno::INVAL};
@@ -584,7 +583,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
584 return {new_fd, Errno::SUCCESS}; 583 return {new_fd, Errno::SUCCESS};
585} 584}
586 585
587Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) { 586Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
588 if (!IsFileDescriptorValid(fd)) { 587 if (!IsFileDescriptorValid(fd)) {
589 return Errno::BADF; 588 return Errno::BADF;
590 } 589 }
@@ -595,7 +594,7 @@ Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
595 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); 594 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
596} 595}
597 596
598Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) { 597Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
599 if (!IsFileDescriptorValid(fd)) { 598 if (!IsFileDescriptorValid(fd)) {
600 return Errno::BADF; 599 return Errno::BADF;
601 } 600 }
@@ -800,15 +799,15 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
800 return {ret, bsd_errno}; 799 return {ret, bsd_errno};
801} 800}
802 801
803std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) { 802std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
804 if (!IsFileDescriptorValid(fd)) { 803 if (!IsFileDescriptorValid(fd)) {
805 return {-1, Errno::BADF}; 804 return {-1, Errno::BADF};
806 } 805 }
807 return Translate(file_descriptors[fd]->socket->Send(message, flags)); 806 return Translate(file_descriptors[fd]->socket->Send(message, flags));
808} 807}
809 808
810std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, 809std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
811 const std::vector<u8>& addr) { 810 std::span<const u8> addr) {
812 if (!IsFileDescriptorValid(fd)) { 811 if (!IsFileDescriptorValid(fd)) {
813 return {-1, Errno::BADF}; 812 return {-1, Errno::BADF};
814 } 813 }
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 81e855e0f..56bb3f8b1 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -44,7 +45,7 @@ private:
44 45
45 s32 nfds; 46 s32 nfds;
46 s32 timeout; 47 s32 timeout;
47 std::vector<u8> read_buffer; 48 std::span<const u8> read_buffer;
48 std::vector<u8> write_buffer; 49 std::vector<u8> write_buffer;
49 s32 ret{}; 50 s32 ret{};
50 Errno bsd_errno{}; 51 Errno bsd_errno{};
@@ -65,7 +66,7 @@ private:
65 void Response(Kernel::HLERequestContext& ctx); 66 void Response(Kernel::HLERequestContext& ctx);
66 67
67 s32 fd; 68 s32 fd;
68 std::vector<u8> addr; 69 std::span<const u8> addr;
69 Errno bsd_errno{}; 70 Errno bsd_errno{};
70 }; 71 };
71 72
@@ -98,7 +99,7 @@ private:
98 99
99 s32 fd; 100 s32 fd;
100 u32 flags; 101 u32 flags;
101 std::vector<u8> message; 102 std::span<const u8> message;
102 s32 ret{}; 103 s32 ret{};
103 Errno bsd_errno{}; 104 Errno bsd_errno{};
104 }; 105 };
@@ -109,8 +110,8 @@ private:
109 110
110 s32 fd; 111 s32 fd;
111 u32 flags; 112 u32 flags;
112 std::vector<u8> message; 113 std::span<const u8> message;
113 std::vector<u8> addr; 114 std::span<const u8> addr;
114 s32 ret{}; 115 s32 ret{};
115 Errno bsd_errno{}; 116 Errno bsd_errno{};
116 }; 117 };
@@ -143,11 +144,11 @@ private:
143 void ExecuteWork(Kernel::HLERequestContext& ctx, Work work); 144 void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
144 145
145 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); 146 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
146 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, 147 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
147 s32 nfds, s32 timeout); 148 s32 nfds, s32 timeout);
148 std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer); 149 std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
149 Errno BindImpl(s32 fd, const std::vector<u8>& addr); 150 Errno BindImpl(s32 fd, std::span<const u8> addr);
150 Errno ConnectImpl(s32 fd, const std::vector<u8>& addr); 151 Errno ConnectImpl(s32 fd, std::span<const u8> addr);
151 Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer); 152 Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
152 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); 153 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
153 Errno ListenImpl(s32 fd, s32 backlog); 154 Errno ListenImpl(s32 fd, s32 backlog);
@@ -157,9 +158,9 @@ private:
157 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 158 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
158 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, 159 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
159 std::vector<u8>& addr); 160 std::vector<u8>& addr);
160 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message); 161 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
161 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, 162 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
162 const std::vector<u8>& addr); 163 std::span<const u8> addr);
163 Errno CloseImpl(s32 fd); 164 Errno CloseImpl(s32 fd);
164 165
165 s32 FindFreeFileDescriptorHandle() noexcept; 166 s32 FindFreeFileDescriptorHandle() noexcept;
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 097c37d7a..e96eda7f3 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -243,4 +243,4 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
243 rb.Push(0); 243 rb.Push(0);
244} 244}
245 245
246} // namespace Service::Sockets \ No newline at end of file 246} // namespace Service::Sockets
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 3735e0452..dcf47083f 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -101,7 +101,7 @@ private:
101 void ImportServerPki(Kernel::HLERequestContext& ctx) { 101 void ImportServerPki(Kernel::HLERequestContext& ctx) {
102 IPC::RequestParser rp{ctx}; 102 IPC::RequestParser rp{ctx};
103 const auto certificate_format = rp.PopEnum<CertificateFormat>(); 103 const auto certificate_format = rp.PopEnum<CertificateFormat>();
104 const auto pkcs_12_certificates = ctx.ReadBuffer(0); 104 [[maybe_unused]] const auto pkcs_12_certificates = ctx.ReadBuffer(0);
105 105
106 constexpr u64 server_id = 0; 106 constexpr u64 server_id = 0;
107 107
@@ -113,13 +113,13 @@ private:
113 } 113 }
114 114
115 void ImportClientPki(Kernel::HLERequestContext& ctx) { 115 void ImportClientPki(Kernel::HLERequestContext& ctx) {
116 const auto pkcs_12_certificate = ctx.ReadBuffer(0); 116 [[maybe_unused]] const auto pkcs_12_certificate = ctx.ReadBuffer(0);
117 const auto ascii_password = [&ctx] { 117 [[maybe_unused]] const auto ascii_password = [&ctx] {
118 if (ctx.CanReadBuffer(1)) { 118 if (ctx.CanReadBuffer(1)) {
119 return ctx.ReadBuffer(1); 119 return ctx.ReadBuffer(1);
120 } 120 }
121 121
122 return std::vector<u8>{}; 122 return std::span<const u8>{};
123 }(); 123 }();
124 124
125 constexpr u64 client_id = 0; 125 constexpr u64 client_id = 0;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bb283e74e..2fb631183 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -603,7 +603,7 @@ private:
603 return; 603 return;
604 } 604 }
605 605
606 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; 606 const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
607 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); 607 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
608 608
609 IPC::ResponseBuilder rb{ctx, 4}; 609 IPC::ResponseBuilder rb{ctx, 4};
@@ -649,7 +649,7 @@ private:
649 return; 649 return;
650 } 650 }
651 651
652 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; 652 const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
653 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); 653 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
654 654
655 IPC::ResponseBuilder rb{ctx, 6}; 655 IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 282ea1ff9..7494fb62d 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -550,7 +550,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
550 return {-1, GetAndLogLastError()}; 550 return {-1, GetAndLogLastError()};
551} 551}
552 552
553std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { 553std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
554 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 554 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
555 ASSERT(flags == 0); 555 ASSERT(flags == 0);
556 556
@@ -563,7 +563,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
563 return {-1, GetAndLogLastError()}; 563 return {-1, GetAndLogLastError()};
564} 564}
565 565
566std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, 566std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
567 const SockAddrIn* addr) { 567 const SockAddrIn* addr) {
568 ASSERT(flags == 0); 568 ASSERT(flags == 0);
569 569
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 1e1c42cea..7a77171c2 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -182,7 +182,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes
182 return {static_cast<u32>(read_bytes), Errno::SUCCESS}; 182 return {static_cast<u32>(read_bytes), Errno::SUCCESS};
183} 183}
184 184
185std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { 185std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
186 LOG_WARNING(Network, "(STUBBED) called"); 186 LOG_WARNING(Network, "(STUBBED) called");
187 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 187 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
188 ASSERT(flags == 0); 188 ASSERT(flags == 0);
@@ -200,7 +200,7 @@ void ProxySocket::SendPacket(ProxyPacket& packet) {
200 } 200 }
201} 201}
202 202
203std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, 203std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
204 const SockAddrIn* addr) { 204 const SockAddrIn* addr) {
205 ASSERT(flags == 0); 205 ASSERT(flags == 0);
206 206
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
index f12b5f567..9421492bc 100644
--- a/src/core/internal_network/socket_proxy.h
+++ b/src/core/internal_network/socket_proxy.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <mutex> 6#include <mutex>
7#include <span>
7#include <vector> 8#include <vector>
8#include <queue> 9#include <queue>
9 10
@@ -48,11 +49,11 @@ public:
48 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, 49 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
49 std::size_t max_length); 50 std::size_t max_length);
50 51
51 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; 52 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
52 53
53 void SendPacket(ProxyPacket& packet); 54 void SendPacket(ProxyPacket& packet);
54 55
55 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 56 std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
56 const SockAddrIn* addr) override; 57 const SockAddrIn* addr) override;
57 58
58 Errno SetLinger(bool enable, u32 linger) override; 59 Errno SetLinger(bool enable, u32 linger) override;
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 2e328c645..4c7489258 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -5,6 +5,7 @@
5 5
6#include <map> 6#include <map>
7#include <memory> 7#include <memory>
8#include <span>
8#include <utility> 9#include <utility>
9 10
10#if defined(_WIN32) 11#if defined(_WIN32)
@@ -66,9 +67,9 @@ public:
66 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, 67 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
67 SockAddrIn* addr) = 0; 68 SockAddrIn* addr) = 0;
68 69
69 virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; 70 virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
70 71
71 virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 72 virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
72 const SockAddrIn* addr) = 0; 73 const SockAddrIn* addr) = 0;
73 74
74 virtual Errno SetLinger(bool enable, u32 linger) = 0; 75 virtual Errno SetLinger(bool enable, u32 linger) = 0;
@@ -138,9 +139,9 @@ public:
138 139
139 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; 140 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
140 141
141 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; 142 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
142 143
143 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 144 std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
144 const SockAddrIn* addr) override; 145 const SockAddrIn* addr) override;
145 146
146 Errno SetLinger(bool enable, u32 linger) override; 147 Errno SetLinger(bool enable, u32 linger) override;
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 77821e047..59dfb8767 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -312,7 +312,7 @@ void Reporter::SaveUnimplementedAppletReport(
312} 312}
313 313
314void Reporter::SavePlayReport(PlayReportType type, u64 title_id, 314void Reporter::SavePlayReport(PlayReportType type, u64 title_id,
315 const std::vector<std::vector<u8>>& data, 315 const std::vector<std::span<const u8>>& data,
316 std::optional<u64> process_id, std::optional<u128> user_id) const { 316 std::optional<u64> process_id, std::optional<u128> user_id) const {
317 if (!IsReportingEnabled()) { 317 if (!IsReportingEnabled()) {
318 return; 318 return;
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 9fdb9d6c1..bb11f8e7c 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <optional> 7#include <optional>
8#include <span>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -56,7 +57,8 @@ public:
56 System, 57 System,
57 }; 58 };
58 59
59 void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data, 60 void SavePlayReport(PlayReportType type, u64 title_id,
61 const std::vector<std::span<const u8>>& data,
60 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; 62 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
61 63
62 // Used by error applet 64 // Used by error applet
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index cedc94e63..4fcfb4510 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -668,12 +668,10 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
668 return "Right Joycon"; 668 return "Right Joycon";
669 case Joycon::ControllerType::Pro: 669 case Joycon::ControllerType::Pro:
670 return "Pro Controller"; 670 return "Pro Controller";
671 case Joycon::ControllerType::Grip:
672 return "Grip Controller";
673 case Joycon::ControllerType::Dual: 671 case Joycon::ControllerType::Dual:
674 return "Dual Joycon"; 672 return "Dual Joycon";
675 default: 673 default:
676 return "Unknown Joycon"; 674 return "Unknown Switch Controller";
677 } 675 }
678} 676}
679} // namespace InputCommon 677} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index 316d383d8..2149ab7fd 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,7 +15,7 @@ using SerialNumber = std::array<u8, 15>;
15struct Battery; 15struct Battery;
16struct Color; 16struct Color;
17struct MotionData; 17struct MotionData;
18enum class ControllerType; 18enum class ControllerType : u8;
19enum class DriverResult; 19enum class DriverResult;
20enum class IrsResolution; 20enum class IrsResolution;
21class JoyconDriver; 21class JoyconDriver;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 3775e2d35..8f94c9f45 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -162,14 +162,14 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
162} 162}
163 163
164void JoyconDriver::OnNewData(std::span<u8> buffer) { 164void JoyconDriver::OnNewData(std::span<u8> buffer) {
165 const auto report_mode = static_cast<InputReport>(buffer[0]); 165 const auto report_mode = static_cast<ReportMode>(buffer[0]);
166 166
167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion 167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
168 // experience 168 // experience
169 switch (report_mode) { 169 switch (report_mode) {
170 case InputReport::STANDARD_FULL_60HZ: 170 case ReportMode::STANDARD_FULL_60HZ:
171 case InputReport::NFC_IR_MODE_60HZ: 171 case ReportMode::NFC_IR_MODE_60HZ:
172 case InputReport::SIMPLE_HID_MODE: { 172 case ReportMode::SIMPLE_HID_MODE: {
173 const auto now = std::chrono::steady_clock::now(); 173 const auto now = std::chrono::steady_clock::now();
174 const auto new_delta_time = static_cast<u64>( 174 const auto new_delta_time = static_cast<u64>(
175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
@@ -190,7 +190,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
190 }; 190 };
191 191
192 // TODO: Remove this when calibration is properly loaded and not calculated 192 // TODO: Remove this when calibration is properly loaded and not calculated
193 if (ring_connected && report_mode == InputReport::STANDARD_FULL_60HZ) { 193 if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
194 InputReportActive data{}; 194 InputReportActive data{};
195 memcpy(&data, buffer.data(), sizeof(InputReportActive)); 195 memcpy(&data, buffer.data(), sizeof(InputReportActive));
196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input); 196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
@@ -228,16 +228,16 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
228 } 228 }
229 229
230 switch (report_mode) { 230 switch (report_mode) {
231 case InputReport::STANDARD_FULL_60HZ: 231 case ReportMode::STANDARD_FULL_60HZ:
232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); 232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
233 break; 233 break;
234 case InputReport::NFC_IR_MODE_60HZ: 234 case ReportMode::NFC_IR_MODE_60HZ:
235 joycon_poller->ReadNfcIRMode(buffer, motion_status); 235 joycon_poller->ReadNfcIRMode(buffer, motion_status);
236 break; 236 break;
237 case InputReport::SIMPLE_HID_MODE: 237 case ReportMode::SIMPLE_HID_MODE:
238 joycon_poller->ReadPassiveMode(buffer); 238 joycon_poller->ReadPassiveMode(buffer);
239 break; 239 break;
240 case InputReport::SUBCMD_REPLY: 240 case ReportMode::SUBCMD_REPLY:
241 LOG_DEBUG(Input, "Unhandled command reply"); 241 LOG_DEBUG(Input, "Unhandled command reply");
242 break; 242 break;
243 default: 243 default:
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
index f6e7e97d5..d8f040f75 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.cpp
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -13,33 +13,33 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
13 13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { 14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 std::vector<u8> buffer;
17 DriverResult result{DriverResult::Success}; 16 DriverResult result{DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false;
18 calibration = {}; 19 calibration = {};
19 20
20 result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer);
21
22 if (result == DriverResult::Success) { 21 if (result == DriverResult::Success) {
23 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
24 if (has_user_calibration) {
25 result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer);
26 } else {
27 result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer);
28 }
29 } 23 }
30 24
31 if (result == DriverResult::Success) { 25 // Read User defined calibration
32 calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 26 if (result == DriverResult::Success && has_user_calibration) {
33 calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
34 calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
35 calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
36 calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
37 calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
38 } 28 }
39 29
40 // Nintendo fix for drifting stick 30 // Read Factory calibration
41 // result = ReadSPI(0x60, 0x86 ,buffer, 16); 31 if (result == DriverResult::Success && !has_user_calibration) {
42 // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 }
34
35 if (result == DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
39 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
40 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
41 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
42 }
43 43
44 // Set a valid default calibration if data is missing 44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration); 45 ValidateCalibration(calibration);
@@ -49,33 +49,33 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration
49 49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { 50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this); 51 ScopedSetBlocking sb(this);
52 std::vector<u8> buffer;
53 DriverResult result{DriverResult::Success}; 52 DriverResult result{DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false;
54 calibration = {}; 55 calibration = {};
55 56
56 result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer);
57
58 if (result == DriverResult::Success) { 57 if (result == DriverResult::Success) {
59 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
60 if (has_user_calibration) {
61 result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer);
62 } else {
63 result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer);
64 }
65 } 59 }
66 60
67 if (result == DriverResult::Success) { 61 // Read User defined calibration
68 calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 62 if (result == DriverResult::Success && has_user_calibration) {
69 calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
70 calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); 64 }
71 calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); 65
72 calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); 66 // Read Factory calibration
73 calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); 67 if (result == DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
74 } 69 }
75 70
76 // Nintendo fix for drifting stick 71 if (result == DriverResult::Success) {
77 // buffer = ReadSPI(0x60, 0x98 , 16); 72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
78 // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
75 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
76 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
77 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
78 }
79 79
80 // Set a valid default calibration if data is missing 80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration); 81 ValidateCalibration(calibration);
@@ -85,39 +85,41 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio
85 85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { 86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this); 87 ScopedSetBlocking sb(this);
88 std::vector<u8> buffer;
89 DriverResult result{DriverResult::Success}; 88 DriverResult result{DriverResult::Success};
89 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false;
90 calibration = {}; 91 calibration = {};
91 92
92 result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer);
93
94 if (result == DriverResult::Success) { 93 if (result == DriverResult::Success) {
95 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
96 if (has_user_calibration) { 95 }
97 result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); 96
98 } else { 97 // Read User defined calibration
99 result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); 98 if (result == DriverResult::Success && has_user_calibration) {
100 } 99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 }
101
102 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
101 } 105 }
102 106
103 if (result == DriverResult::Success) { 107 if (result == DriverResult::Success) {
104 IMUCalibration device_calibration{}; 108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
105 memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); 109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
106 calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; 110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
107 calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1];
108 calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2];
109 111
110 calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; 112 calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
111 calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; 113 calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
112 calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; 114 calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
113 115
114 calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; 116 calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
115 calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; 117 calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
116 calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; 118 calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
117 119
118 calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; 120 calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
119 calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; 121 calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
120 calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; 122 calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
121 } 123 }
122 124
123 ValidateCalibration(calibration); 125 ValidateCalibration(calibration);
@@ -127,10 +129,12 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati
127 129
128DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, 130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
129 s16 current_value) { 131 s16 current_value) {
132 constexpr s16 DefaultRingRange{800};
133
130 // TODO: Get default calibration form ring itself 134 // TODO: Get default calibration form ring itself
131 if (ring_data_max == 0 && ring_data_min == 0) { 135 if (ring_data_max == 0 && ring_data_min == 0) {
132 ring_data_max = current_value + 800; 136 ring_data_max = current_value + DefaultRingRange;
133 ring_data_min = current_value - 800; 137 ring_data_min = current_value - DefaultRingRange;
134 ring_data_default = current_value; 138 ring_data_default = current_value;
135 } 139 }
136 ring_data_max = std::max(ring_data_max, current_value); 140 ring_data_max = std::max(ring_data_max, current_value);
@@ -143,42 +147,72 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio
143 return DriverResult::Success; 147 return DriverResult::Success;
144} 148}
145 149
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false;
155 if (result == DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 }
159 return result;
160}
161
162u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
163 return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
164}
165
166u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
167 return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
168}
169
146void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { 170void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
147 constexpr u16 DefaultStickCenter{2048}; 171 constexpr u16 DefaultStickCenter{0x800};
148 constexpr u16 DefaultStickRange{1740}; 172 constexpr u16 DefaultStickRange{0x6cc};
149 173
150 if (calibration.x.center == 0xFFF || calibration.x.center == 0) { 174 calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
151 calibration.x.center = DefaultStickCenter; 175 calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
152 } 176 calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
153 if (calibration.x.max == 0xFFF || calibration.x.max == 0) { 177
154 calibration.x.max = DefaultStickRange; 178 calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
179 calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
180 calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
181}
182
183void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
184 constexpr s16 DefaultAccelerometerScale{0x4000};
185 constexpr s16 DefaultGyroScale{0x3be7};
186 constexpr s16 DefaultOffset{0};
187
188 for (auto& sensor : calibration.accelerometer) {
189 sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
190 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
155 } 191 }
156 if (calibration.x.min == 0xFFF || calibration.x.min == 0) { 192 for (auto& sensor : calibration.gyro) {
157 calibration.x.min = DefaultStickRange; 193 sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
194 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
158 } 195 }
196}
159 197
160 if (calibration.y.center == 0xFFF || calibration.y.center == 0) { 198u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
161 calibration.y.center = DefaultStickCenter; 199 if (value == 0) {
162 } 200 return default_value;
163 if (calibration.y.max == 0xFFF || calibration.y.max == 0) {
164 calibration.y.max = DefaultStickRange;
165 } 201 }
166 if (calibration.y.min == 0xFFF || calibration.y.min == 0) { 202 if (value == 0xFFF) {
167 calibration.y.min = DefaultStickRange; 203 return default_value;
168 } 204 }
205 return value;
169} 206}
170 207
171void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { 208s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
172 for (auto& sensor : calibration.accelerometer) { 209 if (value == 0) {
173 if (sensor.scale == 0) { 210 return default_value;
174 sensor.scale = 0x4000;
175 }
176 } 211 }
177 for (auto& sensor : calibration.gyro) { 212 if (value == 0xFFF) {
178 if (sensor.scale == 0) { 213 return default_value;
179 sensor.scale = 0x3be7;
180 }
181 } 214 }
215 return value;
182} 216}
183 217
184} // namespace InputCommon::Joycon 218} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
index afb52a36a..c6fd0f729 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.h
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -53,9 +53,27 @@ public:
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); 53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54 54
55private: 55private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58
59 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
61
62 /// Converts a raw calibration block to an u16 value containing the y axis value
63 u16 GetYAxisCalibrationValue(std::span<u8> block) const;
64
65 /// Ensures that all joystick calibration values are set
56 void ValidateCalibration(JoyStickCalibration& calibration); 66 void ValidateCalibration(JoyStickCalibration& calibration);
67
68 /// Ensures that all motion calibration values are set
57 void ValidateCalibration(MotionCalibration& calibration); 69 void ValidateCalibration(MotionCalibration& calibration);
58 70
71 /// Returns the default value if the value is either zero or 0xFFF
72 u16 ValidateValue(u16 value, u16 default_value) const;
73
74 /// Returns the default value if the value is either zero or 0xFFF
75 s16 ValidateValue(s16 value, s16 default_value) const;
76
59 s16 ring_data_max = 0; 77 s16 ring_data_max = 0;
60 s16 ring_data_default = 0; 78 s16 ring_data_default = 0;
61 s16 ring_data_min = 0; 79 s16 ring_data_min = 0;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 417d0dcc5..2b42a4555 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -22,12 +22,9 @@ void JoyconCommonProtocol::SetNonBlocking() {
22} 22}
23 23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { 24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 std::vector<u8> buffer; 25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26 const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer);
27 controller_type = ControllerType::None;
28 26
29 if (result == DriverResult::Success) { 27 if (result == DriverResult::Success) {
30 controller_type = static_cast<ControllerType>(buffer[0]);
31 // Fallback to 3rd party pro controllers 28 // Fallback to 3rd party pro controllers
32 if (controller_type == ControllerType::None) { 29 if (controller_type == ControllerType::None) {
33 controller_type = ControllerType::Pro; 30 controller_type = ControllerType::Pro;
@@ -40,6 +37,7 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type
40DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { 37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
41 ControllerType controller_type{ControllerType::None}; 38 ControllerType controller_type{ControllerType::None};
42 const auto result = GetDeviceType(controller_type); 39 const auto result = GetDeviceType(controller_type);
40
43 if (result != DriverResult::Success || controller_type == ControllerType::None) { 41 if (result != DriverResult::Success || controller_type == ControllerType::None) {
44 return DriverResult::UnsupportedControllerType; 42 return DriverResult::UnsupportedControllerType;
45 } 43 }
@@ -62,7 +60,7 @@ DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
62 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); 60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
63} 61}
64 62
65DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) { 63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
66 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); 64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
67 65
68 if (result == -1) { 66 if (result == -1) {
@@ -72,15 +70,15 @@ DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) {
72 return DriverResult::Success; 70 return DriverResult::Success;
73} 71}
74 72
75DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector<u8>& output) { 73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) {
76 constexpr int timeout_mili = 66; 75 constexpr int timeout_mili = 66;
77 constexpr int MaxTries = 15; 76 constexpr int MaxTries = 15;
78 int tries = 0; 77 int tries = 0;
79 output.resize(MaxSubCommandResponseSize);
80 78
81 do { 79 do {
82 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 80 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
83 MaxSubCommandResponseSize, timeout_mili); 81 sizeof(SubCommandResponse), timeout_mili);
84 82
85 if (result < 1) { 83 if (result < 1) {
86 LOG_ERROR(Input, "No response from joycon"); 84 LOG_ERROR(Input, "No response from joycon");
@@ -88,27 +86,28 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vec
88 if (tries++ > MaxTries) { 86 if (tries++ > MaxTries) {
89 return DriverResult::Timeout; 87 return DriverResult::Timeout;
90 } 88 }
91 } while (output[0] != 0x21 && output[14] != static_cast<u8>(sc)); 89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
92 90 output.sub_command != sc);
93 if (output[0] != 0x21 && output[14] != static_cast<u8>(sc)) {
94 return DriverResult::WrongReply;
95 }
96 91
97 return DriverResult::Success; 92 return DriverResult::Success;
98} 93}
99 94
100DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, 95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
101 std::vector<u8>& output) { 96 SubCommandResponse& output) {
102 std::vector<u8> local_buffer(MaxResponseSize); 97 SubCommandPacket packet{
103 98 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
104 local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD); 99 .packet_counter = GetCounter(),
105 local_buffer[1] = GetCounter(); 100 .sub_command = sc,
106 local_buffer[10] = static_cast<u8>(sc); 101 .command_data = {},
107 for (std::size_t i = 0; i < buffer.size(); ++i) { 102 };
108 local_buffer[11 + i] = buffer[i]; 103
104 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters;
109 } 106 }
110 107
111 auto result = SendData(local_buffer); 108 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109
110 auto result = SendData(packet);
112 111
113 if (result != DriverResult::Success) { 112 if (result != DriverResult::Success) {
114 return result; 113 return result;
@@ -120,44 +119,57 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
120} 119}
121 120
122DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
123 std::vector<u8> output; 122 SubCommandResponse output{};
124 return SendSubCommand(sc, buffer, output); 123 return SendSubCommand(sc, buffer, output);
125} 124}
126 125
127DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { 126DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
128 std::vector<u8> local_buffer(MaxResponseSize); 127 SubCommandPacket packet{
129 128 .output_report = OutputReport::MCU_DATA,
130 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); 129 .packet_counter = GetCounter(),
131 local_buffer[1] = GetCounter(); 130 .sub_command = sc,
132 local_buffer[10] = static_cast<u8>(sc); 131 .command_data = {},
133 for (std::size_t i = 0; i < buffer.size(); ++i) { 132 };
134 local_buffer[11 + i] = buffer[i]; 133
134 if (buffer.size() > packet.command_data.size()) {
135 return DriverResult::InvalidParameters;
135 } 136 }
136 137
137 return SendData(local_buffer); 138 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
139
140 return SendData(packet);
138} 141}
139 142
140DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { 143DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
141 std::vector<u8> local_buffer(MaxResponseSize); 144 VibrationPacket packet{
142 145 .output_report = OutputReport::RUMBLE_ONLY,
143 local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY); 146 .packet_counter = GetCounter(),
144 local_buffer[1] = GetCounter(); 147 .vibration_data = {},
148 };
149
150 if (buffer.size() > packet.vibration_data.size()) {
151 return DriverResult::InvalidParameters;
152 }
145 153
146 memcpy(local_buffer.data() + 2, buffer.data(), buffer.size()); 154 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
147 155
148 return SendData(local_buffer); 156 return SendData(packet);
149} 157}
150 158
151DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { 159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5;
152 constexpr std::size_t MaxTries = 10; 161 constexpr std::size_t MaxTries = 10;
153 std::size_t tries = 0; 162 std::size_t tries = 0;
154 std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; 163 SubCommandResponse response{};
155 std::vector<u8> local_buffer(size + 20); 164 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
156 165 const ReadSpiPacket packet_data{
157 buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); 166 .spi_address = addr,
158 buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); 167 .size = static_cast<u8>(output.size()),
168 };
169
170 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
159 do { 171 do {
160 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, local_buffer); 172 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
161 if (result != DriverResult::Success) { 173 if (result != DriverResult::Success) {
162 return result; 174 return result;
163 } 175 }
@@ -165,10 +177,14 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8
165 if (tries++ > MaxTries) { 177 if (tries++ > MaxTries) {
166 return DriverResult::Timeout; 178 return DriverResult::Timeout;
167 } 179 }
168 } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); 180 } while (response.spi_address != addr);
181
182 if (response.command_data.size() < packet_data.size + HeaderSize) {
183 return DriverResult::WrongReply;
184 }
169 185
170 // Remove header from output 186 // Remove header from output
171 output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); 187 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
172 return DriverResult::Success; 188 return DriverResult::Success;
173} 189}
174 190
@@ -177,7 +193,7 @@ DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
177 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); 193 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
178 194
179 if (result != DriverResult::Success) { 195 if (result != DriverResult::Success) {
180 LOG_ERROR(Input, "SendMCUData failed with error {}", result); 196 LOG_ERROR(Input, "Failed with error {}", result);
181 } 197 }
182 198
183 return result; 199 return result;
@@ -192,22 +208,21 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
192 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); 208 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
193 209
194 if (result != DriverResult::Success) { 210 if (result != DriverResult::Success) {
195 LOG_ERROR(Input, "Set MCU config failed with error {}", result); 211 LOG_ERROR(Input, "Failed with error {}", result);
196 } 212 }
197 213
198 return result; 214 return result;
199} 215}
200 216
201DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, 217DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
202 std::vector<u8>& output) { 218 MCUCommandResponse& output) {
203 const int report_mode = static_cast<u8>(report_mode_);
204 constexpr int TimeoutMili = 200; 219 constexpr int TimeoutMili = 200;
205 constexpr int MaxTries = 9; 220 constexpr int MaxTries = 9;
206 int tries = 0; 221 int tries = 0;
207 output.resize(0x170);
208 222
209 do { 223 do {
210 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 0x170, TimeoutMili); 224 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
225 sizeof(MCUCommandResponse), TimeoutMili);
211 226
212 if (result < 1) { 227 if (result < 1) {
213 LOG_ERROR(Input, "No response from joycon attempt {}", tries); 228 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
@@ -215,28 +230,29 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_,
215 if (tries++ > MaxTries) { 230 if (tries++ > MaxTries) {
216 return DriverResult::Timeout; 231 return DriverResult::Timeout;
217 } 232 }
218 } while (output[0] != report_mode || output[49] == 0xFF); 233 } while (output.input_report.report_mode != report_mode ||
219 234 output.mcu_report == MCUReport::EmptyAwaitingCmd);
220 if (output[0] != report_mode || output[49] == 0xFF) {
221 return DriverResult::WrongReply;
222 }
223 235
224 return DriverResult::Success; 236 return DriverResult::Success;
225} 237}
226 238
227DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, 239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
228 std::span<const u8> buffer, 240 std::span<const u8> buffer,
229 std::vector<u8>& output) { 241 MCUCommandResponse& output) {
230 std::vector<u8> local_buffer(MaxResponseSize); 242 SubCommandPacket packet{
231 243 .output_report = OutputReport::MCU_DATA,
232 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); 244 .packet_counter = GetCounter(),
233 local_buffer[1] = GetCounter(); 245 .sub_command = sc,
234 local_buffer[9] = static_cast<u8>(sc); 246 .command_data = {},
235 for (std::size_t i = 0; i < buffer.size(); ++i) { 247 };
236 local_buffer[10 + i] = buffer[i]; 248
249 if (buffer.size() > packet.command_data.size()) {
250 return DriverResult::InvalidParameters;
237 } 251 }
238 252
239 auto result = SendData(local_buffer); 253 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
254
255 auto result = SendData(packet);
240 256
241 if (result != DriverResult::Success) { 257 if (result != DriverResult::Success) {
242 return result; 258 return result;
@@ -248,7 +264,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubComman
248} 264}
249 265
250DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { 266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
251 std::vector<u8> output; 267 MCUCommandResponse output{};
252 constexpr std::size_t MaxTries{8}; 268 constexpr std::size_t MaxTries{8};
253 std::size_t tries{}; 269 std::size_t tries{};
254 270
@@ -263,7 +279,8 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
263 if (tries++ > MaxTries) { 279 if (tries++ > MaxTries) {
264 return DriverResult::WrongReply; 280 return DriverResult::WrongReply;
265 } 281 }
266 } while (output[49] != 1 || output[56] != static_cast<u8>(mode)); 282 } while (output.mcu_report != MCUReport::StateReport ||
283 output.mcu_data[6] != static_cast<u8>(mode));
267 284
268 return DriverResult::Success; 285 return DriverResult::Success;
269} 286}
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 903bcf402..f44f73ba4 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -57,22 +57,31 @@ public:
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 SendData(std::span<const u8> buffer); 60 DriverResult SendRawData(std::span<const u8> buffer);
61
62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer);
68 }
61 69
62 /** 70 /**
63 * Waits for incoming data of the joycon device that matchs the subcommand 71 * Waits for incoming data of the joycon device that matchs the subcommand
64 * @param sub_command type of data to be returned 72 * @param sub_command type of data to be returned
65 * @returns a buffer containing the responce 73 * @returns a buffer containing the response
66 */ 74 */
67 DriverResult GetSubCommandResponse(SubCommand sub_command, std::vector<u8>& output); 75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
68 76
69 /** 77 /**
70 * Sends a sub command to the device and waits for it's reply 78 * Sends a sub command to the device and waits for it's reply
71 * @param sc sub command to be send 79 * @param sc sub command to be send
72 * @param buffer data to be send 80 * @param buffer data to be send
73 * @returns output buffer containing the responce 81 * @returns output buffer containing the response
74 */ 82 */
75 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output); 83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output);
76 85
77 /** 86 /**
78 * Sends a sub command to the device and waits for it's reply and ignores the output 87 * Sends a sub command to the device and waits for it's reply and ignores the output
@@ -97,10 +106,29 @@ public:
97 /** 106 /**
98 * Reads the SPI memory stored on the joycon 107 * Reads the SPI memory stored on the joycon
99 * @param Initial address location 108 * @param Initial address location
100 * @param size in bytes to be read 109 * @returns output buffer containing the response
101 * @returns output buffer containing the responce
102 */ 110 */
103 DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); 111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112
113 /**
114 * Reads the SPI memory stored on the joycon
115 * @param Initial address location
116 * @returns output object containing the response
117 */
118 template <typename Output>
119 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer;
122 output = {};
123
124 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) {
126 return result;
127 }
128
129 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success;
131 }
104 132
105 /** 133 /**
106 * Enables MCU chip on the joycon 134 * Enables MCU chip on the joycon
@@ -117,19 +145,19 @@ public:
117 /** 145 /**
118 * Waits until there's MCU data available. On timeout returns error 146 * Waits until there's MCU data available. On timeout returns error
119 * @param report mode of the expected reply 147 * @param report mode of the expected reply
120 * @returns a buffer containing the responce 148 * @returns a buffer containing the response
121 */ 149 */
122 DriverResult GetMCUDataResponse(ReportMode report_mode_, std::vector<u8>& output); 150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
123 151
124 /** 152 /**
125 * Sends data to the MCU chip and waits for it's reply 153 * Sends data to the MCU chip and waits for it's reply
126 * @param report mode of the expected reply 154 * @param report mode of the expected reply
127 * @param sub command to be send 155 * @param sub command to be send
128 * @param buffer data to be send 156 * @param buffer data to be send
129 * @returns output buffer containing the responce 157 * @returns output buffer containing the response
130 */ 158 */
131 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer, 159 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
132 std::vector<u8>& output); 160 MCUCommandResponse& output);
133 161
134 /** 162 /**
135 * Wait's until the MCU chip is on the specified mode 163 * Wait's until the MCU chip is on the specified mode
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 63cfb1369..548a4b9e3 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -32,13 +32,13 @@ DriverResult GenericProtocol::TriggersElapsed() {
32 32
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { 33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this); 34 ScopedSetBlocking sb(this);
35 std::vector<u8> output; 35 SubCommandResponse output{};
36 36
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); 37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38 38
39 device_info = {}; 39 device_info = {};
40 if (result == DriverResult::Success) { 40 if (result == DriverResult::Success) {
41 memcpy(&device_info, output.data(), sizeof(DeviceInfo)); 41 device_info = output.device_info;
42 } 42 }
43 43
44 return result; 44 return result;
@@ -71,8 +71,8 @@ DriverResult GenericProtocol::GetBattery(u32& battery_level) {
71 71
72DriverResult GenericProtocol::GetColor(Color& color) { 72DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this); 73 ScopedSetBlocking sb(this);
74 std::vector<u8> buffer; 74 std::array<u8, 12> buffer{};
75 const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); 75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76 76
77 color = {}; 77 color = {};
78 if (result == DriverResult::Success) { 78 if (result == DriverResult::Success) {
@@ -87,8 +87,8 @@ DriverResult GenericProtocol::GetColor(Color& color) {
87 87
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { 88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this); 89 ScopedSetBlocking sb(this);
90 std::vector<u8> buffer; 90 std::array<u8, 16> buffer{};
91 const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); 91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92 92
93 serial_number = {}; 93 serial_number = {};
94 if (result == DriverResult::Success) { 94 if (result == DriverResult::Success) {
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
index 09e17bc5b..731fd5981 100644
--- a/src/input_common/helpers/joycon_protocol/irs.cpp
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -132,7 +132,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
132DriverResult IrsProtocol::ConfigureIrs() { 132DriverResult 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 std::vector<u8> output; 135 SubCommandResponse output{};
136 std::size_t tries = 0; 136 std::size_t tries = 0;
137 137
138 const IrsConfigure irs_configuration{ 138 const IrsConfigure irs_configuration{
@@ -158,7 +158,7 @@ DriverResult IrsProtocol::ConfigureIrs() {
158 if (tries++ >= max_tries) { 158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply; 159 return DriverResult::WrongReply;
160 } 160 }
161 } while (output[15] != 0x0b); 161 } while (output.command_data[0] != 0x0b);
162 162
163 return DriverResult::Success; 163 return DriverResult::Success;
164} 164}
@@ -167,7 +167,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1"); 167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success}; 168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28; 169 constexpr std::size_t max_tries = 28;
170 std::vector<u8> output; 170 SubCommandResponse output{};
171 std::size_t tries = 0; 171 std::size_t tries = 0;
172 172
173 const IrsWriteRegisters irs_registers{ 173 const IrsWriteRegisters irs_registers{
@@ -218,7 +218,8 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
218 if (tries++ >= max_tries) { 218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply; 219 return DriverResult::WrongReply;
220 } 220 }
221 } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); 221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23);
222 223
223 return DriverResult::Success; 224 return DriverResult::Success;
224} 225}
@@ -226,7 +227,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
226DriverResult IrsProtocol::WriteRegistersStep2() { 227DriverResult IrsProtocol::WriteRegistersStep2() {
227 LOG_DEBUG(Input, "WriteRegistersStep2"); 228 LOG_DEBUG(Input, "WriteRegistersStep2");
228 constexpr std::size_t max_tries = 28; 229 constexpr std::size_t max_tries = 28;
229 std::vector<u8> output; 230 SubCommandResponse output{};
230 std::size_t tries = 0; 231 std::size_t tries = 0;
231 232
232 const IrsWriteRegisters irs_registers{ 233 const IrsWriteRegisters irs_registers{
@@ -260,7 +261,7 @@ DriverResult IrsProtocol::WriteRegistersStep2() {
260 if (tries++ >= max_tries) { 261 if (tries++ >= max_tries) {
261 return DriverResult::WrongReply; 262 return DriverResult::WrongReply;
262 } 263 }
263 } while (output[15] != 0x13 && output[15] != 0x23); 264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
264 265
265 return DriverResult::Success; 266 return DriverResult::Success;
266} 267}
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 182d2c15b..b91934990 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -19,20 +19,24 @@
19namespace InputCommon::Joycon { 19namespace InputCommon::Joycon {
20constexpr u32 MaxErrorCount = 50; 20constexpr u32 MaxErrorCount = 50;
21constexpr u32 MaxBufferSize = 368; 21constexpr u32 MaxBufferSize = 368;
22constexpr u32 MaxResponseSize = 49;
23constexpr u32 MaxSubCommandResponseSize = 64;
24constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; 22constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
25 23
26using MacAddress = std::array<u8, 6>; 24using MacAddress = std::array<u8, 6>;
27using SerialNumber = std::array<u8, 15>; 25using SerialNumber = std::array<u8, 15>;
28 26
29enum class ControllerType { 27enum class ControllerType : u8 {
30 None, 28 None = 0x00,
31 Left, 29 Left = 0x01,
32 Right, 30 Right = 0x02,
33 Pro, 31 Pro = 0x03,
34 Grip, 32 Dual = 0x05, // TODO: Verify this id
35 Dual, 33 LarkHvc1 = 0x07,
34 LarkHvc2 = 0x08,
35 LarkNesLeft = 0x09,
36 LarkNesRight = 0x0A,
37 Lucia = 0x0B,
38 Lagon = 0x0C,
39 Lager = 0x0D,
36}; 40};
37 41
38enum class PadAxes { 42enum class PadAxes {
@@ -99,14 +103,6 @@ enum class OutputReport : u8 {
99 USB_CMD = 0x80, 103 USB_CMD = 0x80,
100}; 104};
101 105
102enum class InputReport : u8 {
103 SUBCMD_REPLY = 0x21,
104 STANDARD_FULL_60HZ = 0x30,
105 NFC_IR_MODE_60HZ = 0x31,
106 SIMPLE_HID_MODE = 0x3F,
107 INPUT_USB_RESPONSE = 0x81,
108};
109
110enum class FeatureReport : u8 { 106enum class FeatureReport : u8 {
111 Last_SUBCMD = 0x02, 107 Last_SUBCMD = 0x02,
112 OTA_GW_UPGRADE = 0x70, 108 OTA_GW_UPGRADE = 0x70,
@@ -143,9 +139,10 @@ enum class SubCommand : u8 {
143 ENABLE_VIBRATION = 0x48, 139 ENABLE_VIBRATION = 0x48,
144 GET_REGULATED_VOLTAGE = 0x50, 140 GET_REGULATED_VOLTAGE = 0x50,
145 SET_EXTERNAL_CONFIG = 0x58, 141 SET_EXTERNAL_CONFIG = 0x58,
146 UNKNOWN_RINGCON = 0x59, 142 GET_EXTERNAL_DEVICE_INFO = 0x59,
147 UNKNOWN_RINGCON2 = 0x5A, 143 ENABLE_EXTERNAL_POLLING = 0x5A,
148 UNKNOWN_RINGCON3 = 0x5C, 144 DISABLE_EXTERNAL_POLLING = 0x5B,
145 SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
149}; 146};
150 147
151enum class UsbSubCommand : u8 { 148enum class UsbSubCommand : u8 {
@@ -159,26 +156,31 @@ enum class UsbSubCommand : u8 {
159 SEND_UART = 0x92, 156 SEND_UART = 0x92,
160}; 157};
161 158
162enum class CalMagic : u8 { 159enum class CalibrationMagic : u8 {
163 USR_MAGIC_0 = 0xB2, 160 USR_MAGIC_0 = 0xB2,
164 USR_MAGIC_1 = 0xA1, 161 USR_MAGIC_1 = 0xA1,
165 USRR_MAGI_SIZE = 2,
166}; 162};
167 163
168enum class CalAddr { 164enum class SpiAddress : u16 {
169 SERIAL_NUMBER = 0X6000, 165 MAGIC = 0x0000,
170 DEVICE_TYPE = 0X6012, 166 MAC_ADDRESS = 0x0015,
171 COLOR_EXIST = 0X601B, 167 PAIRING_INFO = 0x2000,
172 FACT_LEFT_DATA = 0X603d, 168 SHIPMENT = 0x5000,
173 FACT_RIGHT_DATA = 0X6046, 169 SERIAL_NUMBER = 0x6000,
174 COLOR_DATA = 0X6050, 170 DEVICE_TYPE = 0x6012,
175 FACT_IMU_DATA = 0X6020, 171 FORMAT_VERSION = 0x601B,
176 USER_LEFT_MAGIC = 0X8010, 172 FACT_IMU_DATA = 0x6020,
177 USER_LEFT_DATA = 0X8012, 173 FACT_LEFT_DATA = 0x603d,
178 USER_RIGHT_MAGIC = 0X801B, 174 FACT_RIGHT_DATA = 0x6046,
179 USER_RIGHT_DATA = 0X801D, 175 COLOR_DATA = 0x6050,
180 USER_IMU_MAGIC = 0X8026, 176 DESIGN_VARIATION = 0x605C,
181 USER_IMU_DATA = 0X8028, 177 SENSOR_DATA = 0x6080,
178 USER_LEFT_MAGIC = 0x8010,
179 USER_LEFT_DATA = 0x8012,
180 USER_RIGHT_MAGIC = 0x801B,
181 USER_RIGHT_DATA = 0x801D,
182 USER_IMU_MAGIC = 0x8026,
183 USER_IMU_DATA = 0x8028,
182}; 184};
183 185
184enum class ReportMode : u8 { 186enum class ReportMode : u8 {
@@ -186,10 +188,12 @@ enum class ReportMode : u8 {
186 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01, 188 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
187 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02, 189 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
188 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03, 190 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
191 SUBCMD_REPLY = 0x21,
189 MCU_UPDATE_STATE = 0x23, 192 MCU_UPDATE_STATE = 0x23,
190 STANDARD_FULL_60HZ = 0x30, 193 STANDARD_FULL_60HZ = 0x30,
191 NFC_IR_MODE_60HZ = 0x31, 194 NFC_IR_MODE_60HZ = 0x31,
192 SIMPLE_HID_MODE = 0x3F, 195 SIMPLE_HID_MODE = 0x3F,
196 INPUT_USB_RESPONSE = 0x81,
193}; 197};
194 198
195enum class GyroSensitivity : u8 { 199enum class GyroSensitivity : u8 {
@@ -360,10 +364,16 @@ enum class IrRegistersAddress : u16 {
360 DenoiseColor = 0x6901, 364 DenoiseColor = 0x6901,
361}; 365};
362 366
367enum class ExternalDeviceId : u16 {
368 RingController = 0x2000,
369 Starlink = 0x2800,
370};
371
363enum class DriverResult { 372enum class DriverResult {
364 Success, 373 Success,
365 WrongReply, 374 WrongReply,
366 Timeout, 375 Timeout,
376 InvalidParameters,
367 UnsupportedControllerType, 377 UnsupportedControllerType,
368 HandleInUse, 378 HandleInUse,
369 ErrorReadingData, 379 ErrorReadingData,
@@ -396,10 +406,35 @@ struct MotionData {
396 u64 delta_timestamp{}; 406 u64 delta_timestamp{};
397}; 407};
398 408
409// Output from SPI read command containing user calibration magic
410struct MagicSpiCalibration {
411 CalibrationMagic first;
412 CalibrationMagic second;
413};
414static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
415
416// Output from SPI read command containing left joystick calibration
417struct JoystickLeftSpiCalibration {
418 std::array<u8, 3> max;
419 std::array<u8, 3> center;
420 std::array<u8, 3> min;
421};
422static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
423 "JoystickLeftSpiCalibration is an invalid size");
424
425// Output from SPI read command containing right joystick calibration
426struct JoystickRightSpiCalibration {
427 std::array<u8, 3> center;
428 std::array<u8, 3> min;
429 std::array<u8, 3> max;
430};
431static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
432 "JoystickRightSpiCalibration is an invalid size");
433
399struct JoyStickAxisCalibration { 434struct JoyStickAxisCalibration {
400 u16 max{1}; 435 u16 max;
401 u16 min{1}; 436 u16 min;
402 u16 center{0}; 437 u16 center;
403}; 438};
404 439
405struct JoyStickCalibration { 440struct JoyStickCalibration {
@@ -407,6 +442,14 @@ struct JoyStickCalibration {
407 JoyStickAxisCalibration y; 442 JoyStickAxisCalibration y;
408}; 443};
409 444
445struct ImuSpiCalibration {
446 std::array<s16, 3> accelerometer_offset;
447 std::array<s16, 3> accelerometer_scale;
448 std::array<s16, 3> gyroscope_offset;
449 std::array<s16, 3> gyroscope_scale;
450};
451static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
452
410struct RingCalibration { 453struct RingCalibration {
411 s16 default_value; 454 s16 default_value;
412 s16 max_value; 455 s16 max_value;
@@ -453,7 +496,7 @@ static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
453 496
454#pragma pack(push, 1) 497#pragma pack(push, 1)
455struct InputReportPassive { 498struct InputReportPassive {
456 InputReport report_mode; 499 ReportMode report_mode;
457 u16 button_input; 500 u16 button_input;
458 u8 stick_state; 501 u8 stick_state;
459 std::array<u8, 10> unknown_data; 502 std::array<u8, 10> unknown_data;
@@ -461,7 +504,7 @@ struct InputReportPassive {
461static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size"); 504static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
462 505
463struct InputReportActive { 506struct InputReportActive {
464 InputReport report_mode; 507 ReportMode report_mode;
465 u8 packet_id; 508 u8 packet_id;
466 Battery battery_status; 509 Battery battery_status;
467 std::array<u8, 3> button_input; 510 std::array<u8, 3> button_input;
@@ -475,7 +518,7 @@ struct InputReportActive {
475static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size"); 518static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
476 519
477struct InputReportNfcIr { 520struct InputReportNfcIr {
478 InputReport report_mode; 521 ReportMode report_mode;
479 u8 packet_id; 522 u8 packet_id;
480 Battery battery_status; 523 Battery battery_status;
481 std::array<u8, 3> button_input; 524 std::array<u8, 3> button_input;
@@ -488,14 +531,6 @@ struct InputReportNfcIr {
488static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); 531static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
489#pragma pack(pop) 532#pragma pack(pop)
490 533
491struct IMUCalibration {
492 std::array<s16, 3> accelerometer_offset;
493 std::array<s16, 3> accelerometer_scale;
494 std::array<s16, 3> gyroscope_offset;
495 std::array<s16, 3> gyroscope_scale;
496};
497static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size");
498
499struct NFCReadBlock { 534struct NFCReadBlock {
500 u8 start; 535 u8 start;
501 u8 end; 536 u8 end;
@@ -581,9 +616,11 @@ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid siz
581 616
582struct DeviceInfo { 617struct DeviceInfo {
583 FirmwareVersion firmware; 618 FirmwareVersion firmware;
619 std::array<u8, 2> unknown_1;
584 MacAddress mac_address; 620 MacAddress mac_address;
621 std::array<u8, 2> unknown_2;
585}; 622};
586static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size"); 623static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size");
587 624
588struct MotionStatus { 625struct MotionStatus {
589 bool is_enabled; 626 bool is_enabled;
@@ -599,6 +636,53 @@ struct RingStatus {
599 s16 min_value; 636 s16 min_value;
600}; 637};
601 638
639struct VibrationPacket {
640 OutputReport output_report;
641 u8 packet_counter;
642 std::array<u8, 0x8> vibration_data;
643};
644static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size");
645
646struct SubCommandPacket {
647 OutputReport output_report;
648 u8 packet_counter;
649 INSERT_PADDING_BYTES(0x8); // This contains vibration data
650 SubCommand sub_command;
651 std::array<u8, 0x26> command_data;
652};
653static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
654
655#pragma pack(push, 1)
656struct ReadSpiPacket {
657 SpiAddress spi_address;
658 INSERT_PADDING_BYTES(0x2);
659 u8 size;
660};
661static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size");
662
663struct SubCommandResponse {
664 InputReportPassive input_report;
665 SubCommand sub_command;
666 union {
667 std::array<u8, 0x30> command_data;
668 SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand
669 ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand
670 DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand
671 };
672 u8 crc; // This is never used
673};
674static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size");
675#pragma pack(pop)
676
677struct MCUCommandResponse {
678 InputReportNfcIr input_report;
679 INSERT_PADDING_BYTES(0x8);
680 MCUReport mcu_report;
681 std::array<u8, 0x13D> mcu_data;
682 u8 crc;
683};
684static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
685
602struct JoyconCallbacks { 686struct JoyconCallbacks {
603 std::function<void(Battery)> on_battery_data; 687 std::function<void(Battery)> on_battery_data;
604 std::function<void(Color)> on_color_data; 688 std::function<void(Color)> on_color_data;
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 5c0f71722..eeba82986 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -110,7 +110,7 @@ bool NfcProtocol::HasAmiibo() {
110 110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() { 111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10; 112 constexpr std::size_t timeout_limit = 10;
113 std::vector<u8> output; 113 MCUCommandResponse output{};
114 std::size_t tries = 0; 114 std::size_t tries = 0;
115 115
116 do { 116 do {
@@ -122,8 +122,9 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
122 if (tries++ > timeout_limit) { 122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout; 123 return DriverResult::Timeout;
124 } 124 }
125 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || 125 } while (output.mcu_report != MCUReport::NFCState ||
126 output[56] != 0x00); 126 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
127 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
127 128
128 return DriverResult::Success; 129 return DriverResult::Success;
129} 130}
@@ -131,7 +132,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
131DriverResult NfcProtocol::StartPolling(TagFoundData& data) { 132DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
132 LOG_DEBUG(Input, "Start Polling for tag"); 133 LOG_DEBUG(Input, "Start Polling for tag");
133 constexpr std::size_t timeout_limit = 7; 134 constexpr std::size_t timeout_limit = 7;
134 std::vector<u8> output; 135 MCUCommandResponse output{};
135 std::size_t tries = 0; 136 std::size_t tries = 0;
136 137
137 do { 138 do {
@@ -142,18 +143,20 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
142 if (tries++ > timeout_limit) { 143 if (tries++ > timeout_limit) {
143 return DriverResult::Timeout; 144 return DriverResult::Timeout;
144 } 145 }
145 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09); 146 } while (output.mcu_report != MCUReport::NFCState ||
147 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
148 output.mcu_data[6] != 0x09);
146 149
147 data.type = output[62]; 150 data.type = output.mcu_data[12];
148 data.uuid.resize(output[64]); 151 data.uuid.resize(output.mcu_data[14]);
149 memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); 152 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
150 153
151 return DriverResult::Success; 154 return DriverResult::Success;
152} 155}
153 156
154DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { 157DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
155 constexpr std::size_t timeout_limit = 10; 158 constexpr std::size_t timeout_limit = 10;
156 std::vector<u8> output; 159 MCUCommandResponse output{};
157 std::size_t tries = 0; 160 std::size_t tries = 0;
158 161
159 std::string uuid_string; 162 std::string uuid_string;
@@ -168,23 +171,24 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
168 // Read Tag data 171 // Read Tag data
169 while (true) { 172 while (true) {
170 auto result = SendReadAmiiboRequest(output, ntag_pages); 173 auto result = SendReadAmiiboRequest(output, ntag_pages);
171 const auto mcu_report = static_cast<MCUReport>(output[49]); 174 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
172 const auto nfc_status = static_cast<NFCStatus>(output[56]);
173 175
174 if (result != DriverResult::Success) { 176 if (result != DriverResult::Success) {
175 return result; 177 return result;
176 } 178 }
177 179
178 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && 180 if ((output.mcu_report == MCUReport::NFCReadData ||
181 output.mcu_report == MCUReport::NFCState) &&
179 nfc_status == NFCStatus::TagLost) { 182 nfc_status == NFCStatus::TagLost) {
180 return DriverResult::ErrorReadingData; 183 return DriverResult::ErrorReadingData;
181 } 184 }
182 185
183 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) { 186 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
187 output.mcu_data[2] == 0x01) {
184 if (data.type != 2) { 188 if (data.type != 2) {
185 continue; 189 continue;
186 } 190 }
187 switch (output[74]) { 191 switch (output.mcu_data[24]) {
188 case 0: 192 case 0:
189 ntag_pages = NFCPages::Block135; 193 ntag_pages = NFCPages::Block135;
190 break; 194 break;
@@ -200,14 +204,14 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
200 continue; 204 continue;
201 } 205 }
202 206
203 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 207 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
204 // finished 208 // finished
205 SendStopPollingRequest(output); 209 SendStopPollingRequest(output);
206 return DriverResult::Success; 210 return DriverResult::Success;
207 } 211 }
208 212
209 // Ignore other state reports 213 // Ignore other state reports
210 if (mcu_report == MCUReport::NFCState) { 214 if (output.mcu_report == MCUReport::NFCState) {
211 continue; 215 continue;
212 } 216 }
213 217
@@ -221,7 +225,7 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
221 225
222DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { 226DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
223 constexpr std::size_t timeout_limit = 10; 227 constexpr std::size_t timeout_limit = 10;
224 std::vector<u8> output; 228 MCUCommandResponse output{};
225 std::size_t tries = 0; 229 std::size_t tries = 0;
226 230
227 NFCPages ntag_pages = NFCPages::Block135; 231 NFCPages ntag_pages = NFCPages::Block135;
@@ -229,36 +233,38 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
229 // Read Tag data 233 // Read Tag data
230 while (true) { 234 while (true) {
231 auto result = SendReadAmiiboRequest(output, ntag_pages); 235 auto result = SendReadAmiiboRequest(output, ntag_pages);
232 const auto mcu_report = static_cast<MCUReport>(output[49]); 236 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
233 const auto nfc_status = static_cast<NFCStatus>(output[56]);
234 237
235 if (result != DriverResult::Success) { 238 if (result != DriverResult::Success) {
236 return result; 239 return result;
237 } 240 }
238 241
239 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && 242 if ((output.mcu_report == MCUReport::NFCReadData ||
243 output.mcu_report == MCUReport::NFCState) &&
240 nfc_status == NFCStatus::TagLost) { 244 nfc_status == NFCStatus::TagLost) {
241 return DriverResult::ErrorReadingData; 245 return DriverResult::ErrorReadingData;
242 } 246 }
243 247
244 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { 248 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
245 std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; 249 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
246 if (output[52] == 0x01) { 250 if (output.mcu_data[2] == 0x01) {
247 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); 251 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
252 payload_size - 60);
248 ntag_buffer_pos += payload_size - 60; 253 ntag_buffer_pos += payload_size - 60;
249 } else { 254 } else {
250 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); 255 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
256 payload_size);
251 } 257 }
252 continue; 258 continue;
253 } 259 }
254 260
255 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 261 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
256 LOG_INFO(Input, "Finished reading amiibo"); 262 LOG_INFO(Input, "Finished reading amiibo");
257 return DriverResult::Success; 263 return DriverResult::Success;
258 } 264 }
259 265
260 // Ignore other state reports 266 // Ignore other state reports
261 if (mcu_report == MCUReport::NFCState) { 267 if (output.mcu_report == MCUReport::NFCState) {
262 continue; 268 continue;
263 } 269 }
264 270
@@ -270,7 +276,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
270 return DriverResult::Success; 276 return DriverResult::Success;
271} 277}
272 278
273DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { 279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
274 NFCRequestState request{ 280 NFCRequestState request{
275 .sub_command = MCUSubCommand::ReadDeviceMode, 281 .sub_command = MCUSubCommand::ReadDeviceMode,
276 .command_argument = NFCReadCommand::StartPolling, 282 .command_argument = NFCReadCommand::StartPolling,
@@ -294,7 +300,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) {
294 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 300 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
295} 301}
296 302
297DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) { 303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
298 NFCRequestState request{ 304 NFCRequestState request{
299 .sub_command = MCUSubCommand::ReadDeviceMode, 305 .sub_command = MCUSubCommand::ReadDeviceMode,
300 .command_argument = NFCReadCommand::StopPolling, 306 .command_argument = NFCReadCommand::StopPolling,
@@ -311,7 +317,7 @@ DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) {
311 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 317 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
312} 318}
313 319
314DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) { 320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
315 NFCRequestState request{ 321 NFCRequestState request{
316 .sub_command = MCUSubCommand::ReadDeviceMode, 322 .sub_command = MCUSubCommand::ReadDeviceMode,
317 .command_argument = NFCReadCommand::StartWaitingRecieve, 323 .command_argument = NFCReadCommand::StartWaitingRecieve,
@@ -328,7 +334,7 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output
328 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 334 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
329} 335}
330 336
331DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) { 337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
332 NFCRequestState request{ 338 NFCRequestState request{
333 .sub_command = MCUSubCommand::ReadDeviceMode, 339 .sub_command = MCUSubCommand::ReadDeviceMode,
334 .command_argument = NFCReadCommand::Ntag, 340 .command_argument = NFCReadCommand::Ntag,
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index e63665aa9..11e263e07 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -45,13 +45,13 @@ private:
45 45
46 DriverResult GetAmiiboData(std::vector<u8>& data); 46 DriverResult GetAmiiboData(std::vector<u8>& data);
47 47
48 DriverResult SendStartPollingRequest(std::vector<u8>& output); 48 DriverResult SendStartPollingRequest(MCUCommandResponse& output);
49 49
50 DriverResult SendStopPollingRequest(std::vector<u8>& output); 50 DriverResult SendStopPollingRequest(MCUCommandResponse& output);
51 51
52 DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output); 52 DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
53 53
54 DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages); 54 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
55 55
56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
57 57
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
index 7f8e093fa..9bb15e935 100644
--- a/src/input_common/helpers/joycon_protocol/poller.cpp
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -31,9 +31,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
31 case Joycon::ControllerType::Pro: 31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status); 32 UpdateActiveProPadInput(data, motion_status);
33 break; 33 break;
34 case Joycon::ControllerType::Grip: 34 default:
35 case Joycon::ControllerType::Dual:
36 case Joycon::ControllerType::None:
37 break; 35 break;
38 } 36 }
39 37
@@ -58,9 +56,7 @@ void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
58 case Joycon::ControllerType::Pro: 56 case Joycon::ControllerType::Pro:
59 UpdatePasiveProPadInput(data); 57 UpdatePasiveProPadInput(data);
60 break; 58 break;
61 case Joycon::ControllerType::Grip: 59 default:
62 case Joycon::ControllerType::Dual:
63 case Joycon::ControllerType::None:
64 break; 60 break;
65 } 61 }
66} 62}
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
index 12f81309e..190cef812 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.cpp
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -70,14 +70,12 @@ DriverResult RingConProtocol::StartRingconPolling() {
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { 70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected"); 71 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28; 72 constexpr std::size_t max_tries = 28;
73 constexpr u8 ring_controller_id = 0x20; 73 SubCommandResponse output{};
74 std::vector<u8> output;
75 std::size_t tries = 0; 74 std::size_t tries = 0;
76 is_connected = false; 75 is_connected = false;
77 76
78 do { 77 do {
79 std::array<u8, 1> empty_data{}; 78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
80 const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output);
81 79
82 if (result != DriverResult::Success) { 80 if (result != DriverResult::Success) {
83 return result; 81 return result;
@@ -86,7 +84,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
86 if (tries++ >= max_tries) { 84 if (tries++ >= max_tries) {
87 return DriverResult::NoDeviceDetected; 85 return DriverResult::NoDeviceDetected;
88 } 86 }
89 } while (output[16] != ring_controller_id); 87 } while (output.external_device_id != ExternalDeviceId::RingController);
90 88
91 is_connected = true; 89 is_connected = true;
92 return DriverResult::Success; 90 return DriverResult::Success;
@@ -100,14 +98,14 @@ DriverResult RingConProtocol::ConfigureRing() {
100 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; 99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
102 100
103 const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config); 101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
104 102
105 if (result != DriverResult::Success) { 103 if (result != DriverResult::Success) {
106 return result; 104 return result;
107 } 105 }
108 106
109 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; 107 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
110 return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data); 108 return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data);
111} 109}
112 110
113bool RingConProtocol::IsEnabled() const { 111bool RingConProtocol::IsEnabled() const {
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 096c23b07..a6be6dac1 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -15,6 +15,9 @@ public:
15 // do not play nicely with the theoretical maximum range. 15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior. 16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f; 17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18 static constexpr float TAU = Common::PI * 2.0f;
19 // Use wider angle to ease the transition.
20 static constexpr float APERTURE = TAU * 0.15f;
18 21
19 using Button = std::unique_ptr<Common::Input::InputDevice>; 22 using Button = std::unique_ptr<Common::Input::InputDevice>;
20 23
@@ -61,30 +64,23 @@ public:
61 } 64 }
62 65
63 bool IsAngleGreater(float old_angle, float new_angle) const { 66 bool IsAngleGreater(float old_angle, float new_angle) const {
64 constexpr float TAU = Common::PI * 2.0f; 67 const float top_limit = new_angle + APERTURE;
65 // Use wider angle to ease the transition.
66 constexpr float aperture = TAU * 0.15f;
67 const float top_limit = new_angle + aperture;
68 return (old_angle > new_angle && old_angle <= top_limit) || 68 return (old_angle > new_angle && old_angle <= top_limit) ||
69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); 69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
70 } 70 }
71 71
72 bool IsAngleSmaller(float old_angle, float new_angle) const { 72 bool IsAngleSmaller(float old_angle, float new_angle) const {
73 constexpr float TAU = Common::PI * 2.0f; 73 const float bottom_limit = new_angle - APERTURE;
74 // Use wider angle to ease the transition.
75 constexpr float aperture = TAU * 0.15f;
76 const float bottom_limit = new_angle - aperture;
77 return (old_angle >= bottom_limit && old_angle < new_angle) || 74 return (old_angle >= bottom_limit && old_angle < new_angle) ||
78 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); 75 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
79 } 76 }
80 77
81 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { 78 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
82 constexpr float TAU = Common::PI * 2.0f;
83 float new_angle = angle; 79 float new_angle = angle;
84 80
85 auto time_difference = static_cast<float>( 81 auto time_difference = static_cast<float>(
86 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 82 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
87 time_difference /= 1000.0f * 1000.0f; 83 time_difference /= 1000.0f;
88 if (time_difference > 0.5f) { 84 if (time_difference > 0.5f) {
89 time_difference = 0.5f; 85 time_difference = 0.5f;
90 } 86 }
@@ -201,8 +197,6 @@ public:
201 } 197 }
202 198
203 void UpdateStatus() { 199 void UpdateStatus() {
204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
205
206 bool r = right_status; 200 bool r = right_status;
207 bool l = left_status; 201 bool l = left_status;
208 bool u = up_status; 202 bool u = up_status;
@@ -220,7 +214,7 @@ public:
220 214
221 // Move if a key is pressed 215 // Move if a key is pressed
222 if (r || l || u || d) { 216 if (r || l || u || d) {
223 amplitude = coef; 217 amplitude = modifier_status.value ? modifier_scale : MAX_RANGE;
224 } else { 218 } else {
225 amplitude = 0; 219 amplitude = 0;
226 } 220 }
@@ -274,30 +268,17 @@ public:
274 Common::Input::StickStatus status{}; 268 Common::Input::StickStatus status{};
275 status.x.properties = properties; 269 status.x.properties = properties;
276 status.y.properties = properties; 270 status.y.properties = properties;
271
277 if (Settings::values.emulate_analog_keyboard) { 272 if (Settings::values.emulate_analog_keyboard) {
278 const auto now = std::chrono::steady_clock::now(); 273 const auto now = std::chrono::steady_clock::now();
279 float angle_ = GetAngle(now); 274 const float angle_ = GetAngle(now);
280 status.x.raw_value = std::cos(angle_) * amplitude; 275 status.x.raw_value = std::cos(angle_) * amplitude;
281 status.y.raw_value = std::sin(angle_) * amplitude; 276 status.y.raw_value = std::sin(angle_) * amplitude;
282 return status; 277 return status;
283 } 278 }
284 constexpr float SQRT_HALF = 0.707106781f; 279
285 int x = 0, y = 0; 280 status.x.raw_value = std::cos(goal_angle) * amplitude;
286 if (right_status) { 281 status.y.raw_value = std::sin(goal_angle) * amplitude;
287 ++x;
288 }
289 if (left_status) {
290 --x;
291 }
292 if (up_status) {
293 ++y;
294 }
295 if (down_status) {
296 --y;
297 }
298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
301 return status; 282 return status;
302 } 283 }
303 284
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 15cbf7e5f..8c6a6521a 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -16,10 +16,10 @@ public:
16 16
17class InputFromButton final : public Common::Input::InputDevice { 17class InputFromButton final : public Common::Input::InputDevice {
18public: 18public:
19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, 19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
20 InputEngine* input_engine_) 20 bool inverted_, InputEngine* input_engine_)
21 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), 21 : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
22 input_engine(input_engine_) { 22 inverted(inverted_), input_engine(input_engine_) {
23 UpdateCallback engine_callback{[this]() { OnChange(); }}; 23 UpdateCallback engine_callback{[this]() { OnChange(); }};
24 const InputIdentifier input_identifier{ 24 const InputIdentifier input_identifier{
25 .identifier = identifier, 25 .identifier = identifier,
@@ -40,6 +40,7 @@ public:
40 .value = input_engine->GetButton(identifier, button), 40 .value = input_engine->GetButton(identifier, button),
41 .inverted = inverted, 41 .inverted = inverted,
42 .toggle = toggle, 42 .toggle = toggle,
43 .turbo = turbo,
43 }; 44 };
44 } 45 }
45 46
@@ -68,6 +69,7 @@ public:
68private: 69private:
69 const PadIdentifier identifier; 70 const PadIdentifier identifier;
70 const int button; 71 const int button;
72 const bool turbo;
71 const bool toggle; 73 const bool toggle;
72 const bool inverted; 74 const bool inverted;
73 int callback_key; 75 int callback_key;
@@ -77,10 +79,10 @@ private:
77 79
78class InputFromHatButton final : public Common::Input::InputDevice { 80class InputFromHatButton final : public Common::Input::InputDevice {
79public: 81public:
80 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, 82 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
81 bool inverted_, InputEngine* input_engine_) 83 bool toggle_, bool inverted_, InputEngine* input_engine_)
82 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), 84 : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
83 inverted(inverted_), input_engine(input_engine_) { 85 toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
84 UpdateCallback engine_callback{[this]() { OnChange(); }}; 86 UpdateCallback engine_callback{[this]() { OnChange(); }};
85 const InputIdentifier input_identifier{ 87 const InputIdentifier input_identifier{
86 .identifier = identifier, 88 .identifier = identifier,
@@ -101,6 +103,7 @@ public:
101 .value = input_engine->GetHatButton(identifier, button, direction), 103 .value = input_engine->GetHatButton(identifier, button, direction),
102 .inverted = inverted, 104 .inverted = inverted,
103 .toggle = toggle, 105 .toggle = toggle,
106 .turbo = turbo,
104 }; 107 };
105 } 108 }
106 109
@@ -130,6 +133,7 @@ private:
130 const PadIdentifier identifier; 133 const PadIdentifier identifier;
131 const int button; 134 const int button;
132 const u8 direction; 135 const u8 direction;
136 const bool turbo;
133 const bool toggle; 137 const bool toggle;
134 const bool inverted; 138 const bool inverted;
135 int callback_key; 139 int callback_key;
@@ -853,14 +857,15 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
853 const auto keyboard_key = params.Get("code", 0); 857 const auto keyboard_key = params.Get("code", 0);
854 const auto toggle = params.Get("toggle", false) != 0; 858 const auto toggle = params.Get("toggle", false) != 0;
855 const auto inverted = params.Get("inverted", false) != 0; 859 const auto inverted = params.Get("inverted", false) != 0;
860 const auto turbo = params.Get("turbo", false) != 0;
856 input_engine->PreSetController(identifier); 861 input_engine->PreSetController(identifier);
857 input_engine->PreSetButton(identifier, button_id); 862 input_engine->PreSetButton(identifier, button_id);
858 input_engine->PreSetButton(identifier, keyboard_key); 863 input_engine->PreSetButton(identifier, keyboard_key);
859 if (keyboard_key != 0) { 864 if (keyboard_key != 0) {
860 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted, 865 return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
861 input_engine.get()); 866 input_engine.get());
862 } 867 }
863 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, 868 return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
864 input_engine.get()); 869 input_engine.get());
865} 870}
866 871
@@ -876,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
876 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 881 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
877 const auto toggle = params.Get("toggle", false) != 0; 882 const auto toggle = params.Get("toggle", false) != 0;
878 const auto inverted = params.Get("inverted", false) != 0; 883 const auto inverted = params.Get("inverted", false) != 0;
884 const auto turbo = params.Get("turbo", false) != 0;
879 885
880 input_engine->PreSetController(identifier); 886 input_engine->PreSetController(identifier);
881 input_engine->PreSetHatButton(identifier, button_id); 887 input_engine->PreSetHatButton(identifier, button_id);
882 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, 888 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
883 input_engine.get()); 889 inverted, input_engine.get());
884} 890}
885 891
886std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( 892std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 0cb1e193e..fd4a61a4d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -279,6 +279,8 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
279 header += "OPTION NV_internal;" 279 header += "OPTION NV_internal;"
280 "OPTION NV_shader_storage_buffer;" 280 "OPTION NV_shader_storage_buffer;"
281 "OPTION NV_gpu_program_fp64;"; 281 "OPTION NV_gpu_program_fp64;";
282 // TODO: Enable only when MS is used
283 header += "OPTION NV_texture_multisample;";
282 if (info.uses_int64_bit_atomics) { 284 if (info.uses_int64_bit_atomics) {
283 header += "OPTION NV_shader_atomic_int64;"; 285 header += "OPTION NV_shader_atomic_int64;";
284 } 286 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index e67e80fac..85ee27333 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -59,7 +59,14 @@ std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
59 } 59 }
60} 60}
61 61
62std::string_view TextureType(IR::TextureInstInfo info) { 62bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
63 if (info.type == TextureType::Buffer) {
64 return false;
65 }
66 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
67}
68
69std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
63 if (info.is_depth) { 70 if (info.is_depth) {
64 switch (info.type) { 71 switch (info.type) {
65 case TextureType::Color1D: 72 case TextureType::Color1D:
@@ -88,9 +95,9 @@ std::string_view TextureType(IR::TextureInstInfo info) {
88 return "ARRAY1D"; 95 return "ARRAY1D";
89 case TextureType::Color2D: 96 case TextureType::Color2D:
90 case TextureType::Color2DRect: 97 case TextureType::Color2DRect:
91 return "2D"; 98 return is_ms ? "2DMS" : "2D";
92 case TextureType::ColorArray2D: 99 case TextureType::ColorArray2D:
93 return "ARRAY2D"; 100 return is_ms ? "ARRAY2DMS" : "ARRAY2D";
94 case TextureType::Color3D: 101 case TextureType::Color3D:
95 return "3D"; 102 return "3D";
96 case TextureType::ColorCube: 103 case TextureType::ColorCube:
@@ -510,15 +517,16 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) { 517 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
511 const auto info{inst.Flags<IR::TextureInstInfo>()}; 518 const auto info{inst.Flags<IR::TextureInstInfo>()};
512 const auto sparse_inst{PrepareSparse(inst)}; 519 const auto sparse_inst{PrepareSparse(inst)};
520 const bool is_multisample{ms.type != Type::Void};
513 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""}; 521 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
514 const std::string_view type{TextureType(info)}; 522 const std::string_view type{TextureType(info, is_multisample)};
515 const std::string texture{Texture(ctx, info, index)}; 523 const std::string texture{Texture(ctx, info, index)};
516 const std::string offset_vec{Offset(ctx, offset)}; 524 const std::string offset_vec{Offset(ctx, offset)};
517 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)}; 525 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
518 const Register ret{ctx.reg_alloc.Define(inst)}; 526 const Register ret{ctx.reg_alloc.Define(inst)};
519 if (info.type == TextureType::Buffer) { 527 if (info.type == TextureType::Buffer) {
520 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec); 528 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
521 } else if (ms.type != Type::Void) { 529 } else if (is_multisample) {
522 ctx.Add("MOV.S {}.w,{};" 530 ctx.Add("MOV.S {}.w,{};"
523 "TXFMS.F{} {},{},{},{}{};", 531 "TXFMS.F{} {},{},{},{}{};",
524 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec); 532 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
@@ -531,10 +539,11 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531} 539}
532 540
533void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 541void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
534 ScalarS32 lod) { 542 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
535 const auto info{inst.Flags<IR::TextureInstInfo>()}; 543 const auto info{inst.Flags<IR::TextureInstInfo>()};
536 const std::string texture{Texture(ctx, info, index)}; 544 const std::string texture{Texture(ctx, info, index)};
537 const std::string_view type{TextureType(info)}; 545 const bool is_msaa{IsTextureMsaa(ctx, info)};
546 const std::string_view type{TextureType(info, is_msaa)};
538 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type); 547 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
539} 548}
540 549
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index 415a249e4..1a1ea61d5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -581,7 +581,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
581void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 581void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
582 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); 582 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
583void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 583void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
584 ScalarS32 lod); 584 ScalarS32 lod, const IR::Value& skip_mips);
585void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); 585void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
586void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 586void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
587 const IR::Value& coord, const IR::Value& derivatives, 587 const IR::Value& coord, const IR::Value& derivatives,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index cecdbb9d6..f335c8af0 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -25,6 +25,13 @@ std::string Image(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::V
25 return fmt::format("img{}{}", def.binding, index_offset); 25 return fmt::format("img{}{}", def.binding, index_offset);
26} 26}
27 27
28bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
29 if (info.type == TextureType::Buffer) {
30 return false;
31 }
32 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
33}
34
28std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) { 35std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
29 switch (info.type) { 36 switch (info.type) {
30 case TextureType::Color1D: 37 case TextureType::Color1D:
@@ -414,7 +421,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
414 421
415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 422void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
416 std::string_view coords, std::string_view offset, std::string_view lod, 423 std::string_view coords, std::string_view offset, std::string_view lod,
417 [[maybe_unused]] std::string_view ms) { 424 std::string_view ms) {
418 const auto info{inst.Flags<IR::TextureInstInfo>()}; 425 const auto info{inst.Flags<IR::TextureInstInfo>()};
419 if (info.has_bias) { 426 if (info.has_bias) {
420 throw NotImplementedException("EmitImageFetch Bias texture samples"); 427 throw NotImplementedException("EmitImageFetch Bias texture samples");
@@ -431,19 +438,24 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
431 ctx.AddU1("{}=true;", *sparse_inst); 438 ctx.AddU1("{}=true;", *sparse_inst);
432 } 439 }
433 if (!sparse_inst || !supports_sparse) { 440 if (!sparse_inst || !supports_sparse) {
434 if (!offset.empty()) { 441 const auto int_coords{CoordsCastToInt(coords, info)};
435 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, 442 if (!ms.empty()) {
436 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info)); 443 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
444 } else if (!offset.empty()) {
445 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
446 CoordsCastToInt(offset, info));
437 } else { 447 } else {
438 if (info.type == TextureType::Buffer) { 448 if (info.type == TextureType::Buffer) {
439 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords); 449 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
440 } else { 450 } else {
441 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, 451 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, lod);
442 CoordsCastToInt(coords, info), lod);
443 } 452 }
444 } 453 }
445 return; 454 return;
446 } 455 }
456 if (!ms.empty()) {
457 throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
458 }
447 if (!offset.empty()) { 459 if (!offset.empty()) {
448 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", 460 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
449 *sparse_inst, texture, CastToIntVec(coords, info), lod, 461 *sparse_inst, texture, CastToIntVec(coords, info), lod,
@@ -455,29 +467,36 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
455} 467}
456 468
457void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 469void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
458 std::string_view lod) { 470 std::string_view lod, const IR::Value& skip_mips_val) {
459 const auto info{inst.Flags<IR::TextureInstInfo>()}; 471 const auto info{inst.Flags<IR::TextureInstInfo>()};
460 const auto texture{Texture(ctx, info, index)}; 472 const auto texture{Texture(ctx, info, index)};
473 const bool is_msaa{IsTextureMsaa(ctx, info)};
474 const bool skip_mips{skip_mips_val.U1()};
475 const auto mips{skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture)};
476 if (is_msaa && !skip_mips) {
477 throw NotImplementedException("EmitImageQueryDimensions MSAA QueryLevels");
478 }
479 if (info.type == TextureType::Buffer && !skip_mips) {
480 throw NotImplementedException("EmitImageQueryDimensions TextureType::Buffer QueryLevels");
481 }
482 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
483 const auto lod_str{uses_lod ? fmt::format(",int({})", lod) : ""};
461 switch (info.type) { 484 switch (info.type) {
462 case TextureType::Color1D: 485 case TextureType::Color1D:
463 return ctx.AddU32x4( 486 return ctx.AddU32x4("{}=uvec4(uint(textureSize({}{})),0u,0u,{});", inst, texture, lod_str,
464 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst, 487 mips);
465 texture, lod, texture);
466 case TextureType::ColorArray1D: 488 case TextureType::ColorArray1D:
467 case TextureType::Color2D: 489 case TextureType::Color2D:
468 case TextureType::ColorCube: 490 case TextureType::ColorCube:
469 case TextureType::Color2DRect: 491 case TextureType::Color2DRect:
470 return ctx.AddU32x4( 492 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({}{})),0u,{});", inst, texture, lod_str,
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, 493 mips);
472 texture, lod, texture);
473 case TextureType::ColorArray2D: 494 case TextureType::ColorArray2D:
474 case TextureType::Color3D: 495 case TextureType::Color3D:
475 case TextureType::ColorArrayCube: 496 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4( 497 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({}{})),{});", inst, texture, lod_str, mips);
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture,
478 lod, texture);
479 case TextureType::Buffer: 498 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); 499 return ctx.AddU32x4("{}=uvec4(uint(textureSize({})),0u,0u,{});", inst, texture, mips);
481 } 500 }
482 throw LogicError("Unspecified image type {}", info.type.Value()); 501 throw LogicError("Unspecified image type {}", info.type.Value());
483} 502}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index c6df1dba7..8d0a65047 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -654,7 +654,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
654 std::string_view coords, std::string_view offset, std::string_view lod, 654 std::string_view coords, std::string_view offset, std::string_view lod,
655 std::string_view ms); 655 std::string_view ms);
656void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 656void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
657 std::string_view lod); 657 std::string_view lod, const IR::Value& skip_mips);
658void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 658void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
659 std::string_view coords); 659 std::string_view coords);
660void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 660void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 5d01ec0cd..1b006e811 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -61,24 +61,28 @@ std::string OutputDecorator(Stage stage, u32 size) {
61 } 61 }
62} 62}
63 63
64std::string_view SamplerType(TextureType type, bool is_depth) { 64std::string_view DepthSamplerType(TextureType type) {
65 if (is_depth) { 65 switch (type) {
66 switch (type) { 66 case TextureType::Color1D:
67 case TextureType::Color1D: 67 return "sampler1DShadow";
68 return "sampler1DShadow"; 68 case TextureType::ColorArray1D:
69 case TextureType::ColorArray1D: 69 return "sampler1DArrayShadow";
70 return "sampler1DArrayShadow"; 70 case TextureType::Color2D:
71 case TextureType::Color2D: 71 return "sampler2DShadow";
72 return "sampler2DShadow"; 72 case TextureType::ColorArray2D:
73 case TextureType::ColorArray2D: 73 return "sampler2DArrayShadow";
74 return "sampler2DArrayShadow"; 74 case TextureType::ColorCube:
75 case TextureType::ColorCube: 75 return "samplerCubeShadow";
76 return "samplerCubeShadow"; 76 case TextureType::ColorArrayCube:
77 case TextureType::ColorArrayCube: 77 return "samplerCubeArrayShadow";
78 return "samplerCubeArrayShadow"; 78 default:
79 default: 79 throw NotImplementedException("Texture type: {}", type);
80 throw NotImplementedException("Texture type: {}", type); 80 }
81 } 81}
82
83std::string_view ColorSamplerType(TextureType type, bool is_multisample = false) {
84 if (is_multisample) {
85 ASSERT(type == TextureType::Color2D || type == TextureType::ColorArray2D);
82 } 86 }
83 switch (type) { 87 switch (type) {
84 case TextureType::Color1D: 88 case TextureType::Color1D:
@@ -87,9 +91,9 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
87 return "sampler1DArray"; 91 return "sampler1DArray";
88 case TextureType::Color2D: 92 case TextureType::Color2D:
89 case TextureType::Color2DRect: 93 case TextureType::Color2DRect:
90 return "sampler2D"; 94 return is_multisample ? "sampler2DMS" : "sampler2D";
91 case TextureType::ColorArray2D: 95 case TextureType::ColorArray2D:
92 return "sampler2DArray"; 96 return is_multisample ? "sampler2DMSArray" : "sampler2DArray";
93 case TextureType::Color3D: 97 case TextureType::Color3D:
94 return "sampler3D"; 98 return "sampler3D";
95 case TextureType::ColorCube: 99 case TextureType::ColorCube:
@@ -677,7 +681,7 @@ void EmitContext::SetupTextures(Bindings& bindings) {
677 texture_buffers.reserve(info.texture_buffer_descriptors.size()); 681 texture_buffers.reserve(info.texture_buffer_descriptors.size());
678 for (const auto& desc : info.texture_buffer_descriptors) { 682 for (const auto& desc : info.texture_buffer_descriptors) {
679 texture_buffers.push_back({bindings.texture, desc.count}); 683 texture_buffers.push_back({bindings.texture, desc.count});
680 const auto sampler_type{SamplerType(TextureType::Buffer, false)}; 684 const auto sampler_type{ColorSamplerType(TextureType::Buffer)};
681 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 685 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
682 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 686 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
683 sampler_type, bindings.texture, array_decorator); 687 sampler_type, bindings.texture, array_decorator);
@@ -686,7 +690,8 @@ void EmitContext::SetupTextures(Bindings& bindings) {
686 textures.reserve(info.texture_descriptors.size()); 690 textures.reserve(info.texture_descriptors.size());
687 for (const auto& desc : info.texture_descriptors) { 691 for (const auto& desc : info.texture_descriptors) {
688 textures.push_back({bindings.texture, desc.count}); 692 textures.push_back({bindings.texture, desc.count});
689 const auto sampler_type{SamplerType(desc.type, desc.is_depth)}; 693 const auto sampler_type{desc.is_depth ? DepthSamplerType(desc.type)
694 : ColorSamplerType(desc.type, desc.is_multisample)};
690 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 695 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
691 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 696 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
692 sampler_type, bindings.texture, array_decorator); 697 sampler_type, bindings.texture, array_decorator);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index c898ce12f..02073c420 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -201,6 +201,13 @@ Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
201 } 201 }
202} 202}
203 203
204bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
205 if (info.type == TextureType::Buffer) {
206 return false;
207 }
208 return ctx.textures.at(info.descriptor_index).is_multisample;
209}
210
204Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { 211Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
205 const auto info{inst->Flags<IR::TextureInstInfo>()}; 212 const auto info{inst->Flags<IR::TextureInstInfo>()};
206 if (info.relaxed_precision != 0) { 213 if (info.relaxed_precision != 0) {
@@ -445,29 +452,33 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 452 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
446} 453}
447 454
448Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { 455Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
456 const IR::Value& skip_mips_val) {
449 const auto info{inst->Flags<IR::TextureInstInfo>()}; 457 const auto info{inst->Flags<IR::TextureInstInfo>()};
450 const Id image{TextureImage(ctx, info, index)}; 458 const Id image{TextureImage(ctx, info, index)};
451 const Id zero{ctx.u32_zero_value}; 459 const Id zero{ctx.u32_zero_value};
452 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 460 const bool skip_mips{skip_mips_val.U1()};
461 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
462 const bool is_msaa{IsTextureMsaa(ctx, info)};
463 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
464 const auto query{[&](Id type) {
465 return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
466 : ctx.OpImageQuerySize(type, image);
467 }};
453 switch (info.type) { 468 switch (info.type) {
454 case TextureType::Color1D: 469 case TextureType::Color1D:
455 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), 470 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
456 zero, zero, mips());
457 case TextureType::ColorArray1D: 471 case TextureType::ColorArray1D:
458 case TextureType::Color2D: 472 case TextureType::Color2D:
459 case TextureType::ColorCube: 473 case TextureType::ColorCube:
460 case TextureType::Color2DRect: 474 case TextureType::Color2DRect:
461 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), 475 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
462 zero, mips());
463 case TextureType::ColorArray2D: 476 case TextureType::ColorArray2D:
464 case TextureType::Color3D: 477 case TextureType::Color3D:
465 case TextureType::ColorArrayCube: 478 case TextureType::ColorArrayCube:
466 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), 479 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
467 mips());
468 case TextureType::Buffer: 480 case TextureType::Buffer:
469 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero, 481 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
470 zero, mips());
471 } 482 }
472 throw LogicError("Unspecified image type {}", info.type.Value()); 483 throw LogicError("Unspecified image type {}", info.type.Value());
473} 484}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index db12e8176..a440b557d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -539,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
539 const IR::Value& offset, const IR::Value& offset2, Id dref); 539 const IR::Value& offset, const IR::Value& offset2, Id dref);
540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
541 Id lod, Id ms); 541 Id lod, Id ms);
542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); 542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
543 const IR::Value& skip_mips);
543Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
544Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
545 Id derivates, Id offset, Id lod_clamp); 546 Id derivates, Id offset, Id lod_clamp);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 3b97721e1..d48d4860e 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1288,6 +1288,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
1288 .pointer_type = pointer_type, 1288 .pointer_type = pointer_type,
1289 .image_type = image_type, 1289 .image_type = image_type,
1290 .count = desc.count, 1290 .count = desc.count,
1291 .is_multisample = desc.is_multisample,
1291 }); 1292 });
1292 if (profile.supported_spirv >= 0x00010400) { 1293 if (profile.supported_spirv >= 0x00010400) {
1293 interfaces.push_back(id); 1294 interfaces.push_back(id);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index dbc5c55b9..768a4fbb5 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -35,6 +35,7 @@ struct TextureDefinition {
35 Id pointer_type; 35 Id pointer_type;
36 Id image_type; 36 Id image_type;
37 u32 count; 37 u32 count;
38 bool is_multisample;
38}; 39};
39 40
40struct TextureBufferDefinition { 41struct TextureBufferDefinition {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 430797d23..b7caa4246 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1846,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu
1846 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); 1846 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
1847} 1847}
1848 1848
1849Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { 1849Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1850 const IR::U1& skip_mips) {
1850 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions 1851 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions
1851 : Opcode::BindlessImageQueryDimensions}; 1852 : Opcode::BindlessImageQueryDimensions};
1852 return Inst(op, handle, lod); 1853 return Inst(op, handle, lod, skip_mips);
1853} 1854}
1854 1855
1855Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, 1856Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1856 TextureInstInfo info) { 1857 const IR::U1& skip_mips, TextureInstInfo info) {
1857 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); 1858 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
1858} 1859}
1859 1860
1860Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { 1861Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 7aaaa4ab0..f3c81dbe1 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -320,9 +320,10 @@ public:
320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, 320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
321 const F32& dref, const F32& lod, 321 const F32& dref, const F32& lod,
322 const Value& offset, TextureInstInfo info); 322 const Value& offset, TextureInstInfo info);
323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
324 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, 323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
325 TextureInstInfo info); 324 const IR::U1& skip_mips);
325 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
326 const IR::U1& skip_mips, TextureInstInfo info);
326 327
327 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, 328 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
328 TextureInstInfo info); 329 TextureInstInfo info);
@@ -408,7 +409,8 @@ private:
408 } 409 }
409 410
410 template <typename T> 411 template <typename T>
411 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags { 412 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>)
413 struct Flags {
412 Flags() = default; 414 Flags() = default;
413 Flags(T proxy_) : proxy{proxy_} {} 415 Flags(T proxy_) : proxy{proxy_} {}
414 416
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 24e82f802..4447d67b0 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -482,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32,
482OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 482OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
483OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 483OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
484OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 484OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
485OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) 485OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, )
486OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) 486OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
487OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 487OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
488OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) 488OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
@@ -495,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32,
495OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 495OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
496OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 496OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
497OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 497OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
498OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) 498OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, )
499OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) 499OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
500OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 500OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
501OPCODE(BoundImageRead, U32x4, U32, Opaque, ) 501OPCODE(BoundImageRead, U32x4, U32, Opaque, )
@@ -508,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq
508OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) 508OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
509OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) 509OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
510OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) 510OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
511OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) 511OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
512OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) 512OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
513OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) 513OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
514OPCODE(ImageRead, U32x4, Opaque, Opaque, ) 514OPCODE(ImageRead, U32x4, Opaque, Opaque, )
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 883dfa24e..c27546b0e 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -43,7 +43,6 @@ public:
43 explicit Value(u8 value) noexcept; 43 explicit Value(u8 value) noexcept;
44 explicit Value(u16 value) noexcept; 44 explicit Value(u16 value) noexcept;
45 explicit Value(u32 value) noexcept; 45 explicit Value(u32 value) noexcept;
46 explicit Value(s32 value) noexcept;
47 explicit Value(f32 value) noexcept; 46 explicit Value(f32 value) noexcept;
48 explicit Value(u64 value) noexcept; 47 explicit Value(u64 value) noexcept;
49 explicit Value(f64 value) noexcept; 48 explicit Value(f64 value) noexcept;
@@ -66,7 +65,6 @@ public:
66 [[nodiscard]] u8 U8() const; 65 [[nodiscard]] u8 U8() const;
67 [[nodiscard]] u16 U16() const; 66 [[nodiscard]] u16 U16() const;
68 [[nodiscard]] u32 U32() const; 67 [[nodiscard]] u32 U32() const;
69 [[nodiscard]] s32 S32() const;
70 [[nodiscard]] f32 F32() const; 68 [[nodiscard]] f32 F32() const;
71 [[nodiscard]] u64 U64() const; 69 [[nodiscard]] u64 U64() const;
72 [[nodiscard]] f64 F64() const; 70 [[nodiscard]] f64 F64() const;
@@ -86,7 +84,6 @@ private:
86 u8 imm_u8; 84 u8 imm_u8;
87 u16 imm_u16; 85 u16 imm_u16;
88 u32 imm_u32; 86 u32 imm_u32;
89 s32 imm_s32;
90 f32 imm_f32; 87 f32 imm_f32;
91 u64 imm_u64; 88 u64 imm_u64;
92 f64 imm_f64; 89 f64 imm_f64;
@@ -101,9 +98,8 @@ public:
101 TypedValue() = default; 98 TypedValue() = default;
102 99
103 template <IR::Type other_type> 100 template <IR::Type other_type>
104 requires((other_type & type_) != IR::Type::Void) explicit(false) 101 requires((other_type & type_) != IR::Type::Void)
105 TypedValue(const TypedValue<other_type>& value) 102 explicit(false) TypedValue(const TypedValue<other_type>& value) : Value(value) {}
106 : Value(value) {}
107 103
108 explicit TypedValue(const Value& value) : Value(value) { 104 explicit TypedValue(const Value& value) : Value(value) {
109 if ((value.Type() & type_) == IR::Type::Void) { 105 if ((value.Type() & type_) == IR::Type::Void) {
@@ -194,16 +190,16 @@ public:
194 void ReplaceOpcode(IR::Opcode opcode); 190 void ReplaceOpcode(IR::Opcode opcode);
195 191
196 template <typename FlagsType> 192 template <typename FlagsType>
197 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) 193 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
198 [[nodiscard]] FlagsType Flags() const noexcept { 194 [[nodiscard]] FlagsType Flags() const noexcept {
199 FlagsType ret; 195 FlagsType ret;
200 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret)); 196 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret));
201 return ret; 197 return ret;
202 } 198 }
203 199
204 template <typename FlagsType> 200 template <typename FlagsType>
205 requires(sizeof(FlagsType) <= sizeof(u32) && 201 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
206 std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept { 202 void SetFlags(FlagsType value) noexcept {
207 std::memcpy(&flags, &value, sizeof(value)); 203 std::memcpy(&flags, &value, sizeof(value));
208 } 204 }
209 205
@@ -379,14 +375,6 @@ inline u32 Value::U32() const {
379 return imm_u32; 375 return imm_u32;
380} 376}
381 377
382inline s32 Value::S32() const {
383 if (IsIdentity()) {
384 return inst->Arg(0).S32();
385 }
386 DEBUG_ASSERT(type == Type::S32);
387 return imm_s32;
388}
389
390inline f32 Value::F32() const { 378inline f32 Value::F32() const {
391 if (IsIdentity()) { 379 if (IsIdentity()) {
392 return inst->Arg(0).F32(); 380 return inst->Arg(0).F32();
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
index f8cfd4ab6..39af62559 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
@@ -15,11 +15,13 @@ enum class Mode : u64 {
15 SamplePos = 5, 15 SamplePos = 5,
16}; 16};
17 17
18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) { 18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) {
19 switch (mode) { 19 switch (mode) {
20 case Mode::Dimension: { 20 case Mode::Dimension: {
21 const bool needs_num_mips{((mask >> 3) & 1) != 0};
22 const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
21 const IR::U32 lod{v.X(src_reg)}; 23 const IR::U32 lod{v.X(src_reg)};
22 return v.ir.ImageQueryDimension(handle, lod); 24 return v.ir.ImageQueryDimension(handle, lod, skip_mips);
23 } 25 }
24 case Mode::TextureType: 26 case Mode::TextureType:
25 case Mode::SamplePos: 27 case Mode::SamplePos:
@@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
46 handle = v.X(src_reg); 48 handle = v.X(src_reg);
47 ++src_reg; 49 ++src_reg;
48 } 50 }
49 const IR::Value query{Query(v, handle, txq.mode, src_reg)}; 51 const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)};
50 IR::Reg dest_reg{txq.dest_reg}; 52 IR::Reg dest_reg{txq.dest_reg};
51 for (int element = 0; element < 4; ++element) { 53 for (int element = 0; element < 4; ++element) {
52 if (((txq.mask >> element) & 1) == 0) { 54 if (((txq.mask >> element) & 1) == 0) {
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 70b620dcb..d374c976a 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -355,21 +355,21 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
355 }; 355 };
356} 356}
357 357
358TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 358u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; 361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) 362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
363 << cbuf.secondary_shift_left}; 363 << cbuf.secondary_shift_left};
364 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return lhs_raw | rhs_raw;
365}
366
367TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
368 return env.ReadTextureType(GetTextureHandle(env, cbuf));
365} 369}
366 370
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { 371TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373} 373}
374 374
375class Descriptors { 375class Descriptors {
@@ -386,8 +386,10 @@ public:
386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { 386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
387 return desc.cbuf_index == existing.cbuf_index && 387 return desc.cbuf_index == existing.cbuf_index &&
388 desc.cbuf_offset == existing.cbuf_offset && 388 desc.cbuf_offset == existing.cbuf_offset &&
389 desc.shift_left == existing.shift_left &&
389 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 390 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
390 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 391 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
392 desc.secondary_shift_left == existing.secondary_shift_left &&
391 desc.count == existing.count && desc.size_shift == existing.size_shift && 393 desc.count == existing.count && desc.size_shift == existing.size_shift &&
392 desc.has_secondary == existing.has_secondary; 394 desc.has_secondary == existing.has_secondary;
393 }); 395 });
@@ -405,15 +407,20 @@ public:
405 } 407 }
406 408
407 u32 Add(const TextureDescriptor& desc) { 409 u32 Add(const TextureDescriptor& desc) {
408 return Add(texture_descriptors, desc, [&desc](const auto& existing) { 410 const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
409 return desc.type == existing.type && desc.is_depth == existing.is_depth && 411 return desc.type == existing.type && desc.is_depth == existing.is_depth &&
410 desc.has_secondary == existing.has_secondary && 412 desc.has_secondary == existing.has_secondary &&
411 desc.cbuf_index == existing.cbuf_index && 413 desc.cbuf_index == existing.cbuf_index &&
412 desc.cbuf_offset == existing.cbuf_offset && 414 desc.cbuf_offset == existing.cbuf_offset &&
415 desc.shift_left == existing.shift_left &&
413 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 416 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
414 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 417 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
418 desc.secondary_shift_left == existing.secondary_shift_left &&
415 desc.count == existing.count && desc.size_shift == existing.size_shift; 419 desc.count == existing.count && desc.size_shift == existing.size_shift;
416 }); 420 })};
421 // TODO: Read this from TIC
422 texture_descriptors[index].is_multisample |= desc.is_multisample;
423 return index;
417 } 424 }
418 425
419 u32 Add(const ImageDescriptor& desc) { 426 u32 Add(const ImageDescriptor& desc) {
@@ -452,7 +459,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
452 const IR::Value coord(inst.Arg(1)); 459 const IR::Value coord(inst.Arg(1));
453 const IR::Value handle(ir.Imm32(0)); 460 const IR::Value handle(ir.Imm32(0));
454 const IR::U32 lod{ir.Imm32(0)}; 461 const IR::U32 lod{ir.Imm32(0)};
455 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); 462 const IR::U1 skip_mips{ir.Imm1(true)};
463 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info);
456 inst.SetArg( 464 inst.SetArg(
457 1, ir.CompositeConstruct( 465 1, ir.CompositeConstruct(
458 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), 466 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)),
diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h
index 2b42c4ba2..5d648b159 100644
--- a/src/shader_recompiler/object_pool.h
+++ b/src/shader_recompiler/object_pool.h
@@ -10,7 +10,7 @@
10namespace Shader { 10namespace Shader {
11 11
12template <typename T> 12template <typename T>
13requires std::is_destructible_v<T> 13 requires std::is_destructible_v<T>
14class ObjectPool { 14class ObjectPool {
15public: 15public:
16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} { 16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
@@ -18,7 +18,7 @@ public:
18 } 18 }
19 19
20 template <typename... Args> 20 template <typename... Args>
21 requires std::is_constructible_v<T, Args...> 21 requires std::is_constructible_v<T, Args...>
22 [[nodiscard]] T* Create(Args&&... args) { 22 [[nodiscard]] T* Create(Args&&... args) {
23 return std::construct_at(Memory(), std::forward<Args>(args)...); 23 return std::construct_at(Memory(), std::forward<Args>(args)...);
24 } 24 }
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index dad7b07d4..52cd5bb81 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -45,6 +45,8 @@ set(SHADER_FILES
45 smaa_neighborhood_blending.vert 45 smaa_neighborhood_blending.vert
46 smaa_neighborhood_blending.frag 46 smaa_neighborhood_blending.frag
47 vulkan_blit_depth_stencil.frag 47 vulkan_blit_depth_stencil.frag
48 vulkan_color_clear.frag
49 vulkan_color_clear.vert
48 vulkan_fidelityfx_fsr_easu_fp16.comp 50 vulkan_fidelityfx_fsr_easu_fp16.comp
49 vulkan_fidelityfx_fsr_easu_fp32.comp 51 vulkan_fidelityfx_fsr_easu_fp32.comp
50 vulkan_fidelityfx_fsr_rcas_fp16.comp 52 vulkan_fidelityfx_fsr_rcas_fp16.comp
diff --git a/src/video_core/host_shaders/vulkan_color_clear.frag b/src/video_core/host_shaders/vulkan_color_clear.frag
new file mode 100644
index 000000000..617bf01e1
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.frag
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6layout (push_constant) uniform PushConstants {
7 vec4 clear_color;
8};
9
10layout(location = 0) out vec4 color;
11
12void main() {
13 color = clear_color;
14}
diff --git a/src/video_core/host_shaders/vulkan_color_clear.vert b/src/video_core/host_shaders/vulkan_color_clear.vert
new file mode 100644
index 000000000..d85883141
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.vert
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6void main() {
7 float x = float((gl_VertexIndex & 1) << 2);
8 float y = float((gl_VertexIndex & 2) << 1);
9 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
10}
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 26d066004..1a0cea9b7 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -30,7 +30,7 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep
30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, 30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_,
31 BufferCache& buffer_cache_, ProgramManager& program_manager_, 31 BufferCache& buffer_cache_, ProgramManager& program_manager_,
32 const Shader::Info& info_, std::string code, 32 const Shader::Info& info_, std::string code,
33 std::vector<u32> code_v) 33 std::vector<u32> code_v, bool force_context_flush)
34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
35 program_manager{program_manager_}, info{info_} { 35 program_manager{program_manager_}, info{info_} {
36 switch (device.GetShaderBackend()) { 36 switch (device.GetShaderBackend()) {
@@ -63,6 +63,15 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
63 writes_global_memory = !use_storage_buffers && 63 writes_global_memory = !use_storage_buffers &&
64 std::ranges::any_of(info.storage_buffers_descriptors, 64 std::ranges::any_of(info.storage_buffers_descriptors,
65 [](const auto& desc) { return desc.is_written; }); 65 [](const auto& desc) { return desc.is_written; });
66 if (force_context_flush) {
67 std::scoped_lock lock{built_mutex};
68 built_fence.Create();
69 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
70 glFlush();
71 built_condvar.notify_one();
72 } else {
73 is_built = true;
74 }
66} 75}
67 76
68void ComputePipeline::Configure() { 77void ComputePipeline::Configure() {
@@ -142,6 +151,9 @@ void ComputePipeline::Configure() {
142 } 151 }
143 texture_cache.FillComputeImageViews(std::span(views.data(), views.size())); 152 texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
144 153
154 if (!is_built) {
155 WaitForBuild();
156 }
145 if (assembly_program.handle != 0) { 157 if (assembly_program.handle != 0) {
146 program_manager.BindComputeAssemblyProgram(assembly_program.handle); 158 program_manager.BindComputeAssemblyProgram(assembly_program.handle);
147 } else { 159 } else {
@@ -223,4 +235,13 @@ void ComputePipeline::Configure() {
223 } 235 }
224} 236}
225 237
238void ComputePipeline::WaitForBuild() {
239 if (built_fence.handle == 0) {
240 std::unique_lock lock{built_mutex};
241 built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
242 }
243 ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
244 is_built = true;
245}
246
226} // namespace OpenGL 247} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 6534dec32..9bcc72b59 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -50,7 +50,8 @@ class ComputePipeline {
50public: 50public:
51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, 51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_,
52 BufferCache& buffer_cache_, ProgramManager& program_manager_, 52 BufferCache& buffer_cache_, ProgramManager& program_manager_,
53 const Shader::Info& info_, std::string code, std::vector<u32> code_v); 53 const Shader::Info& info_, std::string code, std::vector<u32> code_v,
54 bool force_context_flush = false);
54 55
55 void Configure(); 56 void Configure();
56 57
@@ -65,6 +66,8 @@ public:
65 } 66 }
66 67
67private: 68private:
69 void WaitForBuild();
70
68 TextureCache& texture_cache; 71 TextureCache& texture_cache;
69 BufferCache& buffer_cache; 72 BufferCache& buffer_cache;
70 Tegra::MemoryManager* gpu_memory; 73 Tegra::MemoryManager* gpu_memory;
@@ -81,6 +84,11 @@ private:
81 84
82 bool use_storage_buffers{}; 85 bool use_storage_buffers{};
83 bool writes_global_memory{}; 86 bool writes_global_memory{};
87
88 std::mutex built_mutex;
89 std::condition_variable built_condvar;
90 OGLSync built_fence{};
91 bool is_built{false};
84}; 92};
85 93
86} // namespace OpenGL 94} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c115dabe1..29491e762 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -176,7 +176,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
176 std::array<std::string, 5> sources, 176 std::array<std::string, 5> sources,
177 std::array<std::vector<u32>, 5> sources_spirv, 177 std::array<std::vector<u32>, 5> sources_spirv,
178 const std::array<const Shader::Info*, 5>& infos, 178 const std::array<const Shader::Info*, 5>& infos,
179 const GraphicsPipelineKey& key_) 179 const GraphicsPipelineKey& key_, bool force_context_flush)
180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, 180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
181 state_tracker{state_tracker_}, key{key_} { 181 state_tracker{state_tracker_}, key{key_} {
182 if (shader_notify) { 182 if (shader_notify) {
@@ -231,7 +231,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
231 const bool in_parallel = thread_worker != nullptr; 231 const bool in_parallel = thread_worker != nullptr;
232 const auto backend = device.GetShaderBackend(); 232 const auto backend = device.GetShaderBackend();
233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv), 233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv),
234 shader_notify, backend, in_parallel](ShaderContext::Context*) mutable { 234 shader_notify, backend, in_parallel,
235 force_context_flush](ShaderContext::Context*) mutable {
235 for (size_t stage = 0; stage < 5; ++stage) { 236 for (size_t stage = 0; stage < 5; ++stage) {
236 switch (backend) { 237 switch (backend) {
237 case Settings::ShaderBackend::GLSL: 238 case Settings::ShaderBackend::GLSL:
@@ -251,7 +252,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
251 break; 252 break;
252 } 253 }
253 } 254 }
254 if (in_parallel) { 255 if (force_context_flush || in_parallel) {
255 std::scoped_lock lock{built_mutex}; 256 std::scoped_lock lock{built_mutex};
256 built_fence.Create(); 257 built_fence.Create();
257 // Flush this context to ensure compilation commands and fence are in the GPU pipe. 258 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 1c06b3655..7bab3be0a 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -78,7 +78,7 @@ public:
78 std::array<std::string, 5> sources, 78 std::array<std::string, 5> sources,
79 std::array<std::vector<u32>, 5> sources_spirv, 79 std::array<std::vector<u32>, 5> sources_spirv,
80 const std::array<const Shader::Info*, 5>& infos, 80 const std::array<const Shader::Info*, 5>& infos,
81 const GraphicsPipelineKey& key_); 81 const GraphicsPipelineKey& key_, bool force_context_flush = false);
82 82
83 void Configure(bool is_indexed) { 83 void Configure(bool is_indexed) {
84 configure_func(this, is_indexed); 84 configure_func(this, is_indexed);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7dd854e0f..626ea7dcb 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -286,7 +286,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
286 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 286 file.read(reinterpret_cast<char*>(&key), sizeof(key));
287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
288 ctx->pools.ReleaseContents(); 288 ctx->pools.ReleaseContents();
289 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 289 auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)};
290 std::scoped_lock lock{state.mutex}; 290 std::scoped_lock lock{state.mutex};
291 if (pipeline) { 291 if (pipeline) {
292 compute_cache.emplace(key, std::move(pipeline)); 292 compute_cache.emplace(key, std::move(pipeline));
@@ -307,7 +307,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
307 env_ptrs.push_back(&env); 307 env_ptrs.push_back(&env);
308 } 308 }
309 ctx->pools.ReleaseContents(); 309 ctx->pools.ReleaseContents();
310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
311 std::scoped_lock lock{state.mutex}; 311 std::scoped_lock lock{state.mutex};
312 if (pipeline) { 312 if (pipeline) {
313 graphics_cache.emplace(key, std::move(pipeline)); 313 graphics_cache.emplace(key, std::move(pipeline));
@@ -439,7 +439,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
439 439
440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( 440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
442 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 442 std::span<Shader::Environment* const> envs, bool use_shader_workers,
443 bool force_context_flush) try {
443 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 444 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
444 size_t env_index{}; 445 size_t env_index{};
445 u32 total_storage_buffers{}; 446 u32 total_storage_buffers{};
@@ -531,10 +532,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
531 } 532 }
532 previous_program = &program; 533 previous_program = &program;
533 } 534 }
534 auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; 535 auto* const thread_worker{use_shader_workers ? workers.get() : nullptr};
535 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager, 536 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager,
536 state_tracker, thread_worker, &shader_notify, sources, 537 state_tracker, thread_worker, &shader_notify, sources,
537 sources_spirv, infos, key); 538 sources_spirv, infos, key, force_context_flush);
538 539
539} catch (Shader::Exception& exception) { 540} catch (Shader::Exception& exception) {
540 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 541 LOG_ERROR(Render_OpenGL, "{}", exception.what());
@@ -559,8 +560,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
559} 560}
560 561
561std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline( 562std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
562 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, 563 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
563 Shader::Environment& env) try { 564 bool force_context_flush) try {
564 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 565 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
565 566
566 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 567 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -589,7 +590,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
589 } 590 }
590 591
591 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager, 592 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager,
592 program.info, code, code_spirv); 593 program.info, code, code_spirv, force_context_flush);
593} catch (Shader::Exception& exception) { 594} catch (Shader::Exception& exception) {
594 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 595 LOG_ERROR(Render_OpenGL, "{}", exception.what());
595 return nullptr; 596 return nullptr;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index f82420592..6b9732fca 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -50,14 +50,16 @@ private:
50 50
51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
53 std::span<Shader::Environment* const> envs, bool build_in_parallel); 53 std::span<Shader::Environment* const> envs, bool use_shader_workers,
54 bool force_context_flush = false);
54 55
55 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key, 56 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
56 const VideoCommon::ShaderInfo* shader); 57 const VideoCommon::ShaderInfo* shader);
57 58
58 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools, 59 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
59 const ComputePipelineKey& key, 60 const ComputePipelineKey& key,
60 Shader::Environment& env); 61 Shader::Environment& env,
62 bool force_context_flush = false);
61 63
62 std::unique_ptr<ShaderWorker> CreateWorkers() const; 64 std::unique_ptr<ShaderWorker> CreateWorkers() const;
63 65
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index dd00d3edf..cf2964a3f 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -12,6 +12,8 @@
12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" 12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
15#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h"
16#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h"
15#include "video_core/renderer_vulkan/blit_image.h" 17#include "video_core/renderer_vulkan/blit_image.h"
16#include "video_core/renderer_vulkan/maxwell_to_vk.h" 18#include "video_core/renderer_vulkan/maxwell_to_vk.h"
17#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -69,10 +71,11 @@ constexpr VkDescriptorSetLayoutCreateInfo TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CRE
69 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()), 71 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()),
70 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(), 72 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(),
71}; 73};
72constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{ 74template <VkShaderStageFlags stageFlags, size_t size>
73 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, 75inline constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{
76 .stageFlags = stageFlags,
74 .offset = 0, 77 .offset = 0,
75 .size = sizeof(PushConstants), 78 .size = static_cast<u32>(size),
76}; 79};
77constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{ 80constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{
78 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 81 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -125,10 +128,8 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE
125 .alphaToCoverageEnable = VK_FALSE, 128 .alphaToCoverageEnable = VK_FALSE,
126 .alphaToOneEnable = VK_FALSE, 129 .alphaToOneEnable = VK_FALSE,
127}; 130};
128constexpr std::array DYNAMIC_STATES{ 131constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
129 VK_DYNAMIC_STATE_VIEWPORT, 132 VK_DYNAMIC_STATE_BLEND_CONSTANTS};
130 VK_DYNAMIC_STATE_SCISSOR,
131};
132constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ 133constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
133 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 134 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
134 .pNext = nullptr, 135 .pNext = nullptr,
@@ -205,15 +206,15 @@ inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
205}; 206};
206 207
207constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo( 208constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo(
208 const VkDescriptorSetLayout* set_layout) { 209 const VkDescriptorSetLayout* set_layout, vk::Span<VkPushConstantRange> push_constants) {
209 return VkPipelineLayoutCreateInfo{ 210 return VkPipelineLayoutCreateInfo{
210 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 211 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
211 .pNext = nullptr, 212 .pNext = nullptr,
212 .flags = 0, 213 .flags = 0,
213 .setLayoutCount = 1, 214 .setLayoutCount = (set_layout != nullptr ? 1u : 0u),
214 .pSetLayouts = set_layout, 215 .pSetLayouts = set_layout,
215 .pushConstantRangeCount = 1, 216 .pushConstantRangeCount = push_constants.size(),
216 .pPushConstantRanges = &PUSH_CONSTANT_RANGE, 217 .pPushConstantRanges = push_constants.data(),
217 }; 218 };
218} 219}
219 220
@@ -302,8 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
302 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr); 303 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
303} 304}
304 305
305void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, 306void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
306 const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
307 const VkOffset2D offset{ 307 const VkOffset2D offset{
308 .x = std::min(dst_region.start.x, dst_region.end.x), 308 .x = std::min(dst_region.start.x, dst_region.end.x),
309 .y = std::min(dst_region.start.y, dst_region.end.y), 309 .y = std::min(dst_region.start.y, dst_region.end.y),
@@ -325,6 +325,13 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
325 .offset = offset, 325 .offset = offset,
326 .extent = extent, 326 .extent = extent,
327 }; 327 };
328 cmdbuf.SetViewport(0, viewport);
329 cmdbuf.SetScissor(0, scissor);
330}
331
332void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
333 const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
334 BindBlitState(cmdbuf, dst_region);
328 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) / 335 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) /
329 static_cast<float>(src_size.width); 336 static_cast<float>(src_size.width);
330 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) / 337 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) /
@@ -335,8 +342,6 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
335 static_cast<float>(src_region.start.y) / 342 static_cast<float>(src_region.start.y) /
336 static_cast<float>(src_size.height)}, 343 static_cast<float>(src_size.height)},
337 }; 344 };
338 cmdbuf.SetViewport(0, viewport);
339 cmdbuf.SetScissor(0, scissor);
340 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); 345 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
341} 346}
342 347
@@ -408,13 +413,20 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
408 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, 413 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)},
409 two_textures_descriptor_allocator{ 414 two_textures_descriptor_allocator{
410 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, 415 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)},
411 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout( 416 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
412 PipelineLayoutCreateInfo(one_texture_set_layout.address()))), 417 one_texture_set_layout.address(),
413 two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( 418 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
414 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 419 two_textures_pipeline_layout(
420 device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
421 two_textures_set_layout.address(),
422 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
423 clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
424 nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
415 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 425 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
416 blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)), 426 blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
417 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), 427 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
428 clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
429 clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
418 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 430 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
419 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 431 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
420 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 432 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
@@ -553,6 +565,30 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
553 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); 565 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
554} 566}
555 567
568void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
569 const std::array<f32, 4>& clear_color,
570 const Region2D& dst_region) {
571 const BlitImagePipelineKey key{
572 .renderpass = dst_framebuffer->RenderPass(),
573 .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
574 };
575 const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
576 const VkPipelineLayout layout = *clear_color_pipeline_layout;
577 scheduler.RequestRenderpass(dst_framebuffer);
578 scheduler.Record(
579 [pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) {
580 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
581 const std::array blend_color = {
582 (color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f,
583 (color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f};
584 cmdbuf.SetBlendConstants(blend_color.data());
585 BindBlitState(cmdbuf, dst_region);
586 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color);
587 cmdbuf.Draw(3, 1, 0, 0);
588 });
589 scheduler.InvalidateState();
590}
591
556void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 592void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
557 const ImageView& src_image_view) { 593 const ImageView& src_image_view) {
558 const VkPipelineLayout layout = *one_texture_pipeline_layout; 594 const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -728,6 +764,58 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
728 return *blit_depth_stencil_pipelines.back(); 764 return *blit_depth_stencil_pipelines.back();
729} 765}
730 766
767VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
768 const auto it = std::ranges::find(clear_color_keys, key);
769 if (it != clear_color_keys.end()) {
770 return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
771 }
772 clear_color_keys.push_back(key);
773 const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
774 const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
775 .blendEnable = VK_TRUE,
776 .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
777 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
778 .colorBlendOp = VK_BLEND_OP_ADD,
779 .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
780 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
781 .alphaBlendOp = VK_BLEND_OP_ADD,
782 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
783 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
784 };
785 const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
786 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
787 .pNext = nullptr,
788 .flags = 0,
789 .logicOpEnable = VK_FALSE,
790 .logicOp = VK_LOGIC_OP_CLEAR,
791 .attachmentCount = 1,
792 .pAttachments = &color_blend_attachment_state,
793 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
794 };
795 clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
796 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
797 .pNext = nullptr,
798 .flags = 0,
799 .stageCount = static_cast<u32>(stages.size()),
800 .pStages = stages.data(),
801 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
802 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
803 .pTessellationState = nullptr,
804 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
805 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
806 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
807 .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
808 .pColorBlendState = &color_blend_state_generic_create_info,
809 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
810 .layout = *clear_color_pipeline_layout,
811 .renderPass = key.renderpass,
812 .subpass = 0,
813 .basePipelineHandle = VK_NULL_HANDLE,
814 .basePipelineIndex = 0,
815 }));
816 return *clear_color_pipelines.back();
817}
818
731void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, 819void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
732 bool is_target_depth) { 820 bool is_target_depth) {
733 if (pipeline) { 821 if (pipeline) {
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index be8a9a2f6..2976a7d91 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -61,6 +61,9 @@ public:
61 61
62 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); 62 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
63 63
64 void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
65 const std::array<f32, 4>& clear_color, const Region2D& dst_region);
66
64private: 67private:
65 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 68 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
66 const ImageView& src_image_view); 69 const ImageView& src_image_view);
@@ -72,6 +75,8 @@ private:
72 75
73 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); 76 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
74 77
78 [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
79
75 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); 80 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
76 81
77 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 82 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
@@ -97,9 +102,12 @@ private:
97 DescriptorAllocator two_textures_descriptor_allocator; 102 DescriptorAllocator two_textures_descriptor_allocator;
98 vk::PipelineLayout one_texture_pipeline_layout; 103 vk::PipelineLayout one_texture_pipeline_layout;
99 vk::PipelineLayout two_textures_pipeline_layout; 104 vk::PipelineLayout two_textures_pipeline_layout;
105 vk::PipelineLayout clear_color_pipeline_layout;
100 vk::ShaderModule full_screen_vert; 106 vk::ShaderModule full_screen_vert;
101 vk::ShaderModule blit_color_to_color_frag; 107 vk::ShaderModule blit_color_to_color_frag;
102 vk::ShaderModule blit_depth_stencil_frag; 108 vk::ShaderModule blit_depth_stencil_frag;
109 vk::ShaderModule clear_color_vert;
110 vk::ShaderModule clear_color_frag;
103 vk::ShaderModule convert_depth_to_float_frag; 111 vk::ShaderModule convert_depth_to_float_frag;
104 vk::ShaderModule convert_float_to_depth_frag; 112 vk::ShaderModule convert_float_to_depth_frag;
105 vk::ShaderModule convert_abgr8_to_d24s8_frag; 113 vk::ShaderModule convert_abgr8_to_d24s8_frag;
@@ -112,6 +120,8 @@ private:
112 std::vector<vk::Pipeline> blit_color_pipelines; 120 std::vector<vk::Pipeline> blit_color_pipelines;
113 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys; 121 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys;
114 std::vector<vk::Pipeline> blit_depth_stencil_pipelines; 122 std::vector<vk::Pipeline> blit_depth_stencil_pipelines;
123 std::vector<BlitImagePipelineKey> clear_color_keys;
124 std::vector<vk::Pipeline> clear_color_pipelines;
115 vk::Pipeline convert_d32_to_r32_pipeline; 125 vk::Pipeline convert_d32_to_r32_pipeline;
116 vk::Pipeline convert_r32_to_d32_pipeline; 126 vk::Pipeline convert_r32_to_d32_pipeline;
117 vk::Pipeline convert_d16_to_r16_pipeline; 127 vk::Pipeline convert_d16_to_r16_pipeline;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 86ef0daeb..719edbcfb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -394,7 +394,15 @@ void RasterizerVulkan::Clear(u32 layer_count) {
394 cmdbuf.ClearAttachments(attachment, clear_rect); 394 cmdbuf.ClearAttachments(attachment, clear_rect);
395 }); 395 });
396 } else { 396 } else {
397 UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); 397 u8 color_mask = static_cast<u8>(regs.clear_surface.R | regs.clear_surface.G << 1 |
398 regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
399 Region2D dst_region = {
400 Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
401 Offset2D{.x = clear_rect.rect.offset.x +
402 static_cast<s32>(clear_rect.rect.extent.width),
403 .y = clear_rect.rect.offset.y +
404 static_cast<s32>(clear_rect.rect.extent.height)}};
405 blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
398 } 406 }
399 } 407 }
400 408
diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h
index ee4240288..1bad83fb4 100644
--- a/src/video_core/texture_cache/descriptor_table.h
+++ b/src/video_core/texture_cache/descriptor_table.h
@@ -19,9 +19,7 @@ public:
19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} 19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}
20 20
21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { 21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { 22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; }
23 return false;
24 }
25 Refresh(gpu_addr, limit); 23 Refresh(gpu_addr, limit);
26 return true; 24 return true;
27 } 25 }
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 852ec2519..e9100091e 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
101 break; 101 break;
102 } 102 }
103 if (num_samples > 1) {
104 size.width *= NumSamplesX(config.msaa_mode);
105 size.height *= NumSamplesY(config.msaa_mode);
106 }
103 if (type != ImageType::Linear) { 107 if (type != ImageType::Linear) {
104 // FIXME: Call this without passing *this 108 // FIXME: Call this without passing *this
105 layer_stride = CalculateLayerStride(*this); 109 layer_stride = CalculateLayerStride(*this);
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index d552bccf0..203ac1b11 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -51,4 +51,48 @@ namespace VideoCommon {
51 return 1; 51 return 1;
52} 52}
53 53
54[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
55 using Tegra::Texture::MsaaMode;
56 switch (msaa_mode) {
57 case MsaaMode::Msaa1x1:
58 return 1;
59 case MsaaMode::Msaa2x1:
60 case MsaaMode::Msaa2x1_D3D:
61 case MsaaMode::Msaa2x2:
62 case MsaaMode::Msaa2x2_VC4:
63 case MsaaMode::Msaa2x2_VC12:
64 return 2;
65 case MsaaMode::Msaa4x2:
66 case MsaaMode::Msaa4x2_D3D:
67 case MsaaMode::Msaa4x2_VC8:
68 case MsaaMode::Msaa4x2_VC24:
69 case MsaaMode::Msaa4x4:
70 return 4;
71 }
72 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
73 return 1;
74}
75
76[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
77 using Tegra::Texture::MsaaMode;
78 switch (msaa_mode) {
79 case MsaaMode::Msaa1x1:
80 case MsaaMode::Msaa2x1:
81 case MsaaMode::Msaa2x1_D3D:
82 return 1;
83 case MsaaMode::Msaa2x2:
84 case MsaaMode::Msaa2x2_VC4:
85 case MsaaMode::Msaa2x2_VC12:
86 case MsaaMode::Msaa4x2:
87 case MsaaMode::Msaa4x2_D3D:
88 case MsaaMode::Msaa4x2_VC8:
89 case MsaaMode::Msaa4x2_VC24:
90 return 2;
91 case MsaaMode::Msaa4x4:
92 return 4;
93 }
94 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
95 return 1;
96}
97
54} // namespace VideoCommon 98} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 1e2aad76a..9df6a2903 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -29,7 +29,7 @@ struct SlotId {
29}; 29};
30 30
31template <class T> 31template <class T>
32requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> 32 requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
33class SlotVector { 33class SlotVector {
34public: 34public:
35 class Iterator { 35 class Iterator {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 4b7e3b01b..723690e71 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -182,12 +182,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
182 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 182 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
183 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); 183 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
184 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); 184 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
185 const QString turbo = QString::fromStdString(param.Get("turbo", false) ? "$" : "");
185 const auto common_button_name = input_subsystem->GetButtonName(param); 186 const auto common_button_name = input_subsystem->GetButtonName(param);
186 187
187 // Retrieve the names from Qt 188 // Retrieve the names from Qt
188 if (param.Get("engine", "") == "keyboard") { 189 if (param.Get("engine", "") == "keyboard") {
189 const QString button_str = GetKeyName(param.Get("code", 0)); 190 const QString button_str = GetKeyName(param.Get("code", 0));
190 return QObject::tr("%1%2%3").arg(toggle, inverted, button_str); 191 return QObject::tr("%1%2%3%4").arg(turbo, toggle, inverted, button_str);
191 } 192 }
192 193
193 if (common_button_name == Common::Input::ButtonNames::Invalid) { 194 if (common_button_name == Common::Input::ButtonNames::Invalid) {
@@ -201,7 +202,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
201 if (common_button_name == Common::Input::ButtonNames::Value) { 202 if (common_button_name == Common::Input::ButtonNames::Value) {
202 if (param.Has("hat")) { 203 if (param.Has("hat")) {
203 const QString hat = GetDirectionName(param.Get("direction", "")); 204 const QString hat = GetDirectionName(param.Get("direction", ""));
204 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); 205 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, hat);
205 } 206 }
206 if (param.Has("axis")) { 207 if (param.Has("axis")) {
207 const QString axis = QString::fromStdString(param.Get("axis", "")); 208 const QString axis = QString::fromStdString(param.Get("axis", ""));
@@ -219,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
219 } 220 }
220 if (param.Has("button")) { 221 if (param.Has("button")) {
221 const QString button = QString::fromStdString(param.Get("button", "")); 222 const QString button = QString::fromStdString(param.Get("button", ""));
222 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); 223 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button);
223 } 224 }
224 } 225 }
225 226
226 QString button_name = GetButtonName(common_button_name); 227 QString button_name = GetButtonName(common_button_name);
227 if (param.Has("hat")) { 228 if (param.Has("hat")) {
228 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); 229 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name);
229 } 230 }
230 if (param.Has("axis")) { 231 if (param.Has("axis")) {
231 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 232 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
@@ -234,7 +235,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
234 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 235 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
235 } 236 }
236 if (param.Has("button")) { 237 if (param.Has("button")) {
237 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); 238 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button_name);
238 } 239 }
239 240
240 return QObject::tr("[unknown]"); 241 return QObject::tr("[unknown]");
@@ -395,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
395 button_map[button_id]->setText(ButtonToText(param)); 396 button_map[button_id]->setText(ButtonToText(param));
396 emulated_controller->SetButtonParam(button_id, param); 397 emulated_controller->SetButtonParam(button_id, param);
397 }); 398 });
399 context_menu.addAction(tr("Turbo button"), [&] {
400 const bool turbo_value = !param.Get("turbo", false);
401 param.Set("turbo", turbo_value);
402 button_map[button_id]->setText(ButtonToText(param));
403 emulated_controller->SetButtonParam(button_id, param);
404 });
398 } 405 }
399 if (param.Has("axis")) { 406 if (param.Has("axis")) {
400 context_menu.addAction(tr("Invert axis"), [&] { 407 context_menu.addAction(tr("Invert axis"), [&] {
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 68af6c20c..c287220fc 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -81,7 +81,6 @@ void PlayerControlPreview::UpdateColors() {
81 colors.outline = QColor(0, 0, 0); 81 colors.outline = QColor(0, 0, 0);
82 colors.primary = QColor(225, 225, 225); 82 colors.primary = QColor(225, 225, 225);
83 colors.button = QColor(109, 111, 114); 83 colors.button = QColor(109, 111, 114);
84 colors.button2 = QColor(109, 111, 114);
85 colors.button2 = QColor(77, 80, 84); 84 colors.button2 = QColor(77, 80, 84);
86 colors.slider_arrow = QColor(65, 68, 73); 85 colors.slider_arrow = QColor(65, 68, 73);
87 colors.font2 = QColor(0, 0, 0); 86 colors.font2 = QColor(0, 0, 0);
@@ -100,6 +99,7 @@ void PlayerControlPreview::UpdateColors() {
100 colors.led_off = QColor(170, 238, 255); 99 colors.led_off = QColor(170, 238, 255);
101 colors.indicator2 = QColor(59, 165, 93); 100 colors.indicator2 = QColor(59, 165, 93);
102 colors.charging = QColor(250, 168, 26); 101 colors.charging = QColor(250, 168, 26);
102 colors.button_turbo = QColor(217, 158, 4);
103 103
104 colors.left = colors.primary; 104 colors.left = colors.primary;
105 colors.right = colors.primary; 105 colors.right = colors.primary;
@@ -2469,7 +2469,6 @@ void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
2469void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, 2469void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2470 const Common::Input::ButtonStatus& pressed, float width, 2470 const Common::Input::ButtonStatus& pressed, float width,
2471 float height, Direction direction, float radius) { 2471 float height, Direction direction, float radius) {
2472 p.setBrush(button_color);
2473 if (pressed.value) { 2472 if (pressed.value) {
2474 switch (direction) { 2473 switch (direction) {
2475 case Direction::Left: 2474 case Direction::Left:
@@ -2487,16 +2486,16 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2487 case Direction::None: 2486 case Direction::None:
2488 break; 2487 break;
2489 } 2488 }
2490 p.setBrush(colors.highlight);
2491 } 2489 }
2492 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; 2490 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
2491 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2493 p.drawRoundedRect(rect, radius, radius); 2492 p.drawRoundedRect(rect, radius, radius);
2494} 2493}
2495void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, 2494void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
2496 const Common::Input::ButtonStatus& pressed, 2495 const Common::Input::ButtonStatus& pressed,
2497 int button_size) { 2496 int button_size) {
2498 p.setPen(colors.outline); 2497 p.setPen(colors.outline);
2499 p.setBrush(pressed.value ? colors.highlight : colors.button); 2498 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2500 DrawRectangle(p, center, button_size, button_size / 3.0f); 2499 DrawRectangle(p, center, button_size, button_size / 3.0f);
2501} 2500}
2502void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, 2501void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
@@ -2504,7 +2503,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
2504 int button_size) { 2503 int button_size) {
2505 // Draw outer line 2504 // Draw outer line
2506 p.setPen(colors.outline); 2505 p.setPen(colors.outline);
2507 p.setBrush(pressed.value ? colors.highlight : colors.button); 2506 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2508 DrawRectangle(p, center, button_size, button_size / 3.0f); 2507 DrawRectangle(p, center, button_size, button_size / 3.0f);
2509 DrawRectangle(p, center, button_size / 3.0f, button_size); 2508 DrawRectangle(p, center, button_size / 3.0f, button_size);
2510 2509
@@ -2526,7 +2525,7 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
2526 } 2525 }
2527 2526
2528 p.setPen(colors.outline); 2527 p.setPen(colors.outline);
2529 p.setBrush(pressed.value ? colors.highlight : colors.button); 2528 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2530 DrawPolygon(p, button_x); 2529 DrawPolygon(p, button_x);
2531} 2530}
2532 2531
@@ -2539,7 +2538,7 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
2539 } 2538 }
2540 2539
2541 p.setPen(colors.outline); 2540 p.setPen(colors.outline);
2542 p.setBrush(pressed.value ? colors.highlight : colors.button); 2541 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2543 DrawPolygon(p, button_x); 2542 DrawPolygon(p, button_x);
2544} 2543}
2545 2544
@@ -2553,17 +2552,15 @@ void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
2553 } 2552 }
2554 2553
2555 p.setPen(colors.outline); 2554 p.setPen(colors.outline);
2556 p.setBrush(pressed.value ? colors.highlight : colors.button2); 2555 p.setBrush(GetButtonColor(colors.button2, pressed.value, pressed.turbo));
2557 DrawPolygon(p, button_x); 2556 DrawPolygon(p, button_x);
2558} 2557}
2559 2558
2560void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, 2559void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
2561 const Common::Input::ButtonStatus& pressed, 2560 const Common::Input::ButtonStatus& pressed,
2562 float button_size) { 2561 float button_size) {
2563 p.setBrush(button_color); 2562
2564 if (pressed.value) { 2563 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2565 p.setBrush(colors.highlight);
2566 }
2567 p.drawEllipse(center, button_size, button_size); 2564 p.drawEllipse(center, button_size, button_size);
2568} 2565}
2569 2566
@@ -2620,7 +2617,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2620 2617
2621 // Draw arrow button 2618 // Draw arrow button
2622 p.setPen(pressed.value ? colors.highlight : colors.button); 2619 p.setPen(pressed.value ? colors.highlight : colors.button);
2623 p.setBrush(pressed.value ? colors.highlight : colors.button); 2620 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2624 DrawPolygon(p, arrow_button); 2621 DrawPolygon(p, arrow_button);
2625 2622
2626 switch (direction) { 2623 switch (direction) {
@@ -2672,10 +2669,20 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2672 2669
2673 // Draw arrow button 2670 // Draw arrow button
2674 p.setPen(colors.outline); 2671 p.setPen(colors.outline);
2675 p.setBrush(pressed.value ? colors.highlight : colors.button); 2672 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2676 DrawPolygon(p, qtrigger_button); 2673 DrawPolygon(p, qtrigger_button);
2677} 2674}
2678 2675
2676QColor PlayerControlPreview::GetButtonColor(QColor default_color, bool is_pressed, bool turbo) {
2677 if (is_pressed && turbo) {
2678 return colors.button_turbo;
2679 }
2680 if (is_pressed) {
2681 return colors.highlight;
2682 }
2683 return default_color;
2684}
2685
2679void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, 2686void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2680 Common::Input::BatteryLevel battery) { 2687 Common::Input::BatteryLevel battery) {
2681 if (battery == Common::Input::BatteryLevel::None) { 2688 if (battery == Common::Input::BatteryLevel::None) {
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index b258c6d77..0e9e95e85 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -81,6 +81,7 @@ private:
81 QColor right{}; 81 QColor right{};
82 QColor button{}; 82 QColor button{};
83 QColor button2{}; 83 QColor button2{};
84 QColor button_turbo{};
84 QColor font{}; 85 QColor font{};
85 QColor font2{}; 86 QColor font2{};
86 QColor highlight{}; 87 QColor highlight{};
@@ -183,6 +184,7 @@ private:
183 const Common::Input::ButtonStatus& pressed, float size = 1.0f); 184 const Common::Input::ButtonStatus& pressed, float size = 1.0f);
184 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, 185 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
185 const Common::Input::ButtonStatus& pressed); 186 const Common::Input::ButtonStatus& pressed);
187 QColor GetButtonColor(QColor default_color, bool is_pressed, bool turbo);
186 188
187 // Draw battery functions 189 // Draw battery functions
188 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery); 190 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 9bb69cab1..41ef4250a 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -58,13 +58,16 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
58 std::vector<std::string> profile_names; 58 std::vector<std::string> profile_names;
59 profile_names.reserve(map_profiles.size()); 59 profile_names.reserve(map_profiles.size());
60 60
61 for (const auto& [profile_name, config] : map_profiles) { 61 auto it = map_profiles.cbegin();
62 while (it != map_profiles.cend()) {
63 const auto& [profile_name, config] = *it;
62 if (!ProfileExistsInFilesystem(profile_name)) { 64 if (!ProfileExistsInFilesystem(profile_name)) {
63 DeleteProfile(profile_name); 65 it = map_profiles.erase(it);
64 continue; 66 continue;
65 } 67 }
66 68
67 profile_names.push_back(profile_name); 69 profile_names.push_back(profile_name);
70 ++it;
68 } 71 }
69 72
70 std::stable_sort(profile_names.begin(), profile_names.end()); 73 std::stable_sort(profile_names.begin(), profile_names.end());
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 08c275696..6c93e3511 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -77,6 +77,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
77 // UI Buttons 77 // UI Buttons
78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); 78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); 79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
80 connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
80 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); 81 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
81 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); 82 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
82 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); 83 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
@@ -329,6 +330,16 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
329 return true; 330 return true;
330 } 331 }
331 332
333 // filter by empty rooms
334 if (filter_empty) {
335 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
336 int player_count =
337 sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
338 if (player_count == 0) {
339 return false;
340 }
341 }
342
332 // filter by filled rooms 343 // filter by filled rooms
333 if (filter_full) { 344 if (filter_full) {
334 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); 345 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
@@ -399,6 +410,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
399 invalidate(); 410 invalidate();
400} 411}
401 412
413void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
414 filter_empty = filter;
415 invalidate();
416}
417
402void LobbyFilterProxyModel::SetFilterFull(bool filter) { 418void LobbyFilterProxyModel::SetFilterFull(bool filter) {
403 filter_full = filter; 419 filter_full = filter;
404 invalidate(); 420 invalidate();
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index 300dad13e..2674ae7c3 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -130,12 +130,14 @@ public:
130 130
131public slots: 131public slots:
132 void SetFilterOwned(bool); 132 void SetFilterOwned(bool);
133 void SetFilterEmpty(bool);
133 void SetFilterFull(bool); 134 void SetFilterFull(bool);
134 void SetFilterSearch(const QString&); 135 void SetFilterSearch(const QString&);
135 136
136private: 137private:
137 QStandardItemModel* game_list; 138 QStandardItemModel* game_list;
138 bool filter_owned = false; 139 bool filter_owned = false;
140 bool filter_empty = false;
139 bool filter_full = false; 141 bool filter_full = false;
140 QString filter_search; 142 QString filter_search;
141}; 143};
diff --git a/src/yuzu/multiplayer/lobby.ui b/src/yuzu/multiplayer/lobby.ui
index 4c9901c9a..0ef0ef762 100644
--- a/src/yuzu/multiplayer/lobby.ui
+++ b/src/yuzu/multiplayer/lobby.ui
@@ -78,6 +78,13 @@
78 </widget> 78 </widget>
79 </item> 79 </item>
80 <item> 80 <item>
81 <widget class="QCheckBox" name="hide_empty">
82 <property name="text">
83 <string>Hide Empty Rooms</string>
84 </property>
85 </widget>
86 </item>
87 <item>
81 <widget class="QCheckBox" name="hide_full"> 88 <widget class="QCheckBox" name="hide_full">
82 <property name="text"> 89 <property name="text">
83 <string>Hide Full Rooms</string> 90 <string>Hide Full Rooms</string>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 527017282..9c34cdc6e 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -176,6 +176,9 @@ void Config::ReadValues() {
176 Settings::values.debug_pad_analogs[i] = default_param; 176 Settings::values.debug_pad_analogs[i] = default_param;
177 } 177 }
178 178
179 ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
180 ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
181 ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
179 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); 182 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
180 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); 183 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
181 ReadSetting("ControlsGeneral", Settings::values.motion_enabled); 184 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 67d230462..3f3651dbe 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -14,6 +14,7 @@ const char* sdl2_config_file =
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values 14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15 15
16# Indicates if this player should be connected at boot 16# Indicates if this player should be connected at boot
17# 0 (default): Disabled, 1: Enabled
17connected= 18connected=
18 19
19# for button input, the following devices are available: 20# for button input, the following devices are available:
@@ -94,6 +95,18 @@ motionright=
94# 0 (default): Disabled, 1: Enabled 95# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled = 96debug_pad_enabled =
96 97
98# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
99# 0 (default): Disabled, 1: Enabled
100enable_raw_input =
101
102# Enable yuzu joycon driver instead of SDL drive.
103# 0: Disabled, 1 (default): Enabled
104enable_joycon_driver =
105
106# Emulates an analog input from buttons. Allowing to dial any angle.
107# 0 (default): Disabled, 1: Enabled
108emulate_analog_keyboard =
109
97# Whether to enable or disable vibration 110# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled 111# 0: Disabled, 1 (default): Enabled
99vibration_enabled= 112vibration_enabled=
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 31f28a507..5450b8c38 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -18,11 +18,11 @@
18 18
19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) 19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
20 : input_subsystem{input_subsystem_}, system{system_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 21 input_subsystem->Initialize();
22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
23 exit(1); 24 exit(1);
24 } 25 }
25 input_subsystem->Initialize();
26 SDL_SetMainReady(); 26 SDL_SetMainReady();
27} 27}
28 28
@@ -32,10 +32,6 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
32 SDL_Quit(); 32 SDL_Quit();
33} 33}
34 34
35void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
36 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
37}
38
39InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { 35InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
40 switch (button) { 36 switch (button) {
41 case SDL_BUTTON_LEFT: 37 case SDL_BUTTON_LEFT:
@@ -53,44 +49,36 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
53 } 49 }
54} 50}
55 51
52std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
53 int w, h;
54 SDL_GetWindowSize(render_window, &w, &h);
55 const float fx = static_cast<float>(touch_x) / w;
56 const float fy = static_cast<float>(touch_y) / h;
57
58 return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
59}
60
56void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 61void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
57 const auto mouse_button = SDLButtonToMouseButton(button); 62 const auto mouse_button = SDLButtonToMouseButton(button);
58 if (state == SDL_PRESSED) { 63 if (state == SDL_PRESSED) {
59 input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button); 64 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
65 input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, mouse_button);
60 } else { 66 } else {
61 input_subsystem->GetMouse()->ReleaseButton(mouse_button); 67 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
62 } 68 }
63} 69}
64 70
65std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { 71void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
66 int w, h; 72 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
67 SDL_GetWindowSize(render_window, &w, &h); 73 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, 0, 0);
68
69 touch_x *= w;
70 touch_y *= h;
71
72 return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
73 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
74} 74}
75 75
76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { 76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
77 int width, height; 77 input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
78 SDL_GetWindowSize(render_window, &width, &height);
79 const auto [px, py] = TouchToPixelPos(x, y);
80 const float fx = px * 1.0f / width;
81 const float fy = py * 1.0f / height;
82
83 input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
84} 78}
85 79
86void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { 80void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
87 int width, height; 81 input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
88 SDL_GetWindowSize(render_window, &width, &height);
89 const auto [px, py] = TouchToPixelPos(x, y);
90 const float fx = px * 1.0f / width;
91 const float fy = py * 1.0f / height;
92
93 input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
94} 82}
95 83
96void EmuWindow_SDL2::OnFingerUp() { 84void EmuWindow_SDL2::OnFingerUp() {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 25c23e2a5..d9b453dee 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -38,17 +38,17 @@ protected:
38 /// Called by WaitEvent when a key is pressed or released. 38 /// Called by WaitEvent when a key is pressed or released.
39 void OnKeyEvent(int key, u8 state); 39 void OnKeyEvent(int key, u8 state);
40 40
41 /// Called by WaitEvent when the mouse moves.
42 void OnMouseMotion(s32 x, s32 y);
43
44 /// Converts a SDL mouse button into MouseInput mouse button 41 /// Converts a SDL mouse button into MouseInput mouse button
45 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const; 42 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
46 43
44 /// Translates pixel position to float position
45 std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
46
47 /// Called by WaitEvent when a mouse button is pressed or released 47 /// Called by WaitEvent when a mouse button is pressed or released
48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
49 49
50 /// Translates pixel position (0..1) to pixel positions 50 /// Called by WaitEvent when the mouse moves.
51 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 51 void OnMouseMotion(s32 x, s32 y);
52 52
53 /// Called by WaitEvent when a finger starts touching the touchscreen 53 /// Called by WaitEvent when a finger starts touching the touchscreen
54 void OnFingerDown(float x, float y, std::size_t id); 54 void OnFingerDown(float x, float y, std::size_t id);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 91133569d..d1f7b1d49 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -62,13 +62,15 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
62static void PrintHelp(const char* argv0) { 62static void PrintHelp(const char* argv0) {
63 std::cout << "Usage: " << argv0 63 std::cout << "Usage: " << argv0
64 << " [options] <filename>\n" 64 << " [options] <filename>\n"
65 "-m, --multiplayer=nick:password@address:port" 65 "-c, --config Load the specified configuration file\n"
66 " Nickname, password, address and port for multiplayer\n"
67 "-f, --fullscreen Start in fullscreen mode\n" 66 "-f, --fullscreen Start in fullscreen mode\n"
67 "-g, --game File path of the game to load\n"
68 "-h, --help Display this help and exit\n" 68 "-h, --help Display this help and exit\n"
69 "-v, --version Output version information and exit\n" 69 "-m, --multiplayer=nick:password@address:port"
70 " Nickname, password, address and port for multiplayer\n"
70 "-p, --program Pass following string as arguments to executable\n" 71 "-p, --program Pass following string as arguments to executable\n"
71 "-c, --config Load the specified configuration file\n"; 72 "-u, --user Select a specific user profile from 0 to 7\n"
73 "-v, --version Output version information and exit\n";
72} 74}
73 75
74static void PrintVersion() { 76static void PrintVersion() {
@@ -199,6 +201,7 @@ int main(int argc, char** argv) {
199 std::string filepath; 201 std::string filepath;
200 std::optional<std::string> config_path; 202 std::optional<std::string> config_path;
201 std::string program_args; 203 std::string program_args;
204 std::optional<int> selected_user;
202 205
203 bool use_multiplayer = false; 206 bool use_multiplayer = false;
204 bool fullscreen = false; 207 bool fullscreen = false;
@@ -209,12 +212,14 @@ int main(int argc, char** argv) {
209 212
210 static struct option long_options[] = { 213 static struct option long_options[] = {
211 // clang-format off 214 // clang-format off
212 {"multiplayer", required_argument, 0, 'm'}, 215 {"config", required_argument, 0, 'c'},
213 {"fullscreen", no_argument, 0, 'f'}, 216 {"fullscreen", no_argument, 0, 'f'},
214 {"help", no_argument, 0, 'h'}, 217 {"help", no_argument, 0, 'h'},
215 {"version", no_argument, 0, 'v'}, 218 {"game", required_argument, 0, 'g'},
219 {"multiplayer", required_argument, 0, 'm'},
216 {"program", optional_argument, 0, 'p'}, 220 {"program", optional_argument, 0, 'p'},
217 {"config", required_argument, 0, 'c'}, 221 {"user", required_argument, 0, 'u'},
222 {"version", no_argument, 0, 'v'},
218 {0, 0, 0, 0}, 223 {0, 0, 0, 0},
219 // clang-format on 224 // clang-format on
220 }; 225 };
@@ -223,6 +228,21 @@ int main(int argc, char** argv) {
223 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); 228 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
224 if (arg != -1) { 229 if (arg != -1) {
225 switch (static_cast<char>(arg)) { 230 switch (static_cast<char>(arg)) {
231 case 'c':
232 config_path = optarg;
233 break;
234 case 'f':
235 fullscreen = true;
236 LOG_INFO(Frontend, "Starting in fullscreen mode...");
237 break;
238 case 'h':
239 PrintHelp(argv[0]);
240 return 0;
241 case 'g': {
242 const std::string str_arg(optarg);
243 filepath = str_arg;
244 break;
245 }
226 case 'm': { 246 case 'm': {
227 use_multiplayer = true; 247 use_multiplayer = true;
228 const std::string str_arg(optarg); 248 const std::string str_arg(optarg);
@@ -255,23 +275,16 @@ int main(int argc, char** argv) {
255 } 275 }
256 break; 276 break;
257 } 277 }
258 case 'f': 278 case 'p':
259 fullscreen = true; 279 program_args = argv[optind];
260 LOG_INFO(Frontend, "Starting in fullscreen mode..."); 280 ++optind;
261 break; 281 break;
262 case 'h': 282 case 'u':
263 PrintHelp(argv[0]); 283 selected_user = atoi(optarg);
264 return 0; 284 return 0;
265 case 'v': 285 case 'v':
266 PrintVersion(); 286 PrintVersion();
267 return 0; 287 return 0;
268 case 'p':
269 program_args = argv[optind];
270 ++optind;
271 break;
272 case 'c':
273 config_path = optarg;
274 break;
275 } 288 }
276 } else { 289 } else {
277#ifdef _WIN32 290#ifdef _WIN32
@@ -295,6 +308,10 @@ int main(int argc, char** argv) {
295 Settings::values.program_args = program_args; 308 Settings::values.program_args = program_args;
296 } 309 }
297 310
311 if (selected_user.has_value()) {
312 Settings::values.current_user = std::clamp(*selected_user, 0, 7);
313 }
314
298#ifdef _WIN32 315#ifdef _WIN32
299 LocalFree(argv_w); 316 LocalFree(argv_w);
300#endif 317#endif