summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------externals/ffmpeg0
m---------externals/mbedtls0
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp347
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h69
-rw-r--r--src/core/hle/service/ssl/ssl.cpp42
-rw-r--r--src/input_common/main.cpp5
-rw-r--r--src/input_common/sdl/sdl.h3
-rw-r--r--src/input_common/sdl/sdl_impl.cpp153
-rw-r--r--src/input_common/sdl/sdl_impl.h1
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp26
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp38
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h57
-rw-r--r--src/video_core/texture_cache/types.h7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp8
-rw-r--r--src/yuzu/configuration/configure_ui.cpp1
19 files changed, 647 insertions, 142 deletions
diff --git a/externals/ffmpeg b/externals/ffmpeg
-Subproject 6b6b9e593dd4d3aaf75f48d40a13ef03bdef9fd
+Subproject 79e8d17024e6c6328a40fcee191ffd70798a9c6
diff --git a/externals/mbedtls b/externals/mbedtls
-Subproject eac2416b8fdb2cb9c867a538100bf95326bad75
+Subproject 8c88150ca139e06aa2aae8349df8292a88148ea
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index bb77d8959..9e5df3bb7 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -1,10 +1,9 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/math_util.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
@@ -12,10 +11,19 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
-constexpr f32 angle_threshold = 0.08f;
-constexpr f32 pinch_threshold = 100.0f;
-Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase{system_} {}
+// HW is around 700, value is set to 400 to make it easier to trigger with mouse
+constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
+constexpr f32 angle_threshold = 0.015f; // Threshold in radians
+constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
+constexpr f32 press_delay = 0.5f; // Time in seconds
+constexpr f32 double_tap_delay = 0.35f; // Time in seconds
+
+constexpr f32 Square(s32 num) {
+ return static_cast<f32>(num * num);
+}
+
+Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
@@ -24,6 +32,8 @@ void Controller_Gesture::OnInit() {
keyboard_finger_id[id] = MAX_POINTS;
udp_finger_id[id] = MAX_POINTS;
}
+ shared_memory.header.entry_count = 0;
+ force_update = true;
}
void Controller_Gesture::OnRelease() {}
@@ -38,17 +48,23 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
shared_memory.header.last_entry_index = 0;
return;
}
- shared_memory.header.entry_count = 16;
- const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ ReadTouchInput();
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ GestureProperties gesture = GetGestureProperties();
+ f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) /
+ (1000 * 1000 * 1000);
- // TODO(german77): Implement all gesture types
+ // Only update if necesary
+ if (!ShouldUpdateGesture(gesture, time_difference)) {
+ return;
+ }
+ last_update_timestamp = shared_memory.header.timestamp;
+ UpdateGestureSharedMemory(data, size, gesture, time_difference);
+}
+
+void Controller_Gesture::ReadTouchInput() {
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
@@ -63,50 +79,71 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
}
}
+}
- TouchType type = TouchType::Idle;
- Attribute attributes{};
- GestureProperties gesture = GetGestureProperties();
- if (last_gesture.active_points != gesture.active_points) {
- ++last_gesture.detection_count;
+bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
+ f32 time_difference) {
+ const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ if (force_update) {
+ force_update = false;
+ return true;
}
- if (gesture.active_points > 0) {
- if (last_gesture.active_points == 0) {
- attributes.is_new_touch.Assign(true);
- last_gesture.average_distance = gesture.average_distance;
- last_gesture.angle = gesture.angle;
- }
- type = TouchType::Touch;
- if (gesture.mid_point.x != last_entry.x || gesture.mid_point.y != last_entry.y) {
- type = TouchType::Pan;
- }
- if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
- type = TouchType::Pinch;
- }
- if (std::abs(gesture.angle - last_gesture.angle) > angle_threshold) {
- type = TouchType::Rotate;
+ // Update if coordinates change
+ for (size_t id = 0; id < MAX_POINTS; id++) {
+ if (gesture.points[id].x != last_gesture.points[id].x ||
+ gesture.points[id].y != last_gesture.points[id].y) {
+ return true;
}
+ }
+
+ // Update on press and hold event after 0.5 seconds
+ if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 &&
+ time_difference > press_delay) {
+ return enable_press_and_tap;
+ }
+
+ return false;
+}
+
+void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
+ GestureProperties& gesture,
+ f32 time_difference) {
+ TouchType type = TouchType::Idle;
+ Attribute attributes{};
+
+ const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
- cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
- cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
- // TODO: Find how velocities are calculated
- cur_entry.vel_x = static_cast<float>(cur_entry.delta_x) * 150.1f;
- cur_entry.vel_y = static_cast<float>(cur_entry.delta_y) * 150.1f;
+ if (shared_memory.header.entry_count < 16) {
+ shared_memory.header.entry_count++;
+ }
- // Slowdown the rate of change for less flapping
- last_gesture.average_distance =
- (last_gesture.average_distance * 0.9f) + (gesture.average_distance * 0.1f);
- last_gesture.angle = (last_gesture.angle * 0.9f) + (gesture.angle * 0.1f);
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ // Reset values to default
+ cur_entry.delta_x = 0;
+ cur_entry.delta_y = 0;
+ cur_entry.vel_x = 0;
+ cur_entry.vel_y = 0;
+ cur_entry.direction = Direction::None;
+ cur_entry.rotation_angle = 0;
+ cur_entry.scale = 0;
+
+ if (gesture.active_points > 0) {
+ if (last_gesture.active_points == 0) {
+ NewGesture(gesture, type, attributes);
+ } else {
+ UpdateExistingGesture(gesture, type, time_difference);
+ }
} else {
- cur_entry.delta_x = 0;
- cur_entry.delta_y = 0;
- cur_entry.vel_x = 0;
- cur_entry.vel_y = 0;
+ EndGesture(gesture, last_gesture, type, attributes, time_difference);
}
- last_gesture.active_points = gesture.active_points;
- cur_entry.detection_count = last_gesture.detection_count;
+
+ // Apply attributes
+ cur_entry.detection_count = gesture.detection_count;
cur_entry.type = type;
cur_entry.attributes = attributes;
cur_entry.x = gesture.mid_point.x;
@@ -116,12 +153,190 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
cur_entry.points[id].x = gesture.points[id].x;
cur_entry.points[id].y = gesture.points[id].y;
}
- cur_entry.rotation_angle = 0;
- cur_entry.scale = 0;
+ last_gesture = gesture;
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
+void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
+ Attribute& attributes) {
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ gesture.detection_count++;
+ type = TouchType::Touch;
+
+ // New touch after cancel is not considered new
+ if (last_entry.type != TouchType::Cancel) {
+ attributes.is_new_touch.Assign(1);
+ enable_press_and_tap = true;
+ }
+}
+
+void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
+ f32 time_difference) {
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+
+ // Promote to pan type if touch moved
+ for (size_t id = 0; id < MAX_POINTS; id++) {
+ if (gesture.points[id].x != last_gesture.points[id].x ||
+ gesture.points[id].y != last_gesture.points[id].y) {
+ type = TouchType::Pan;
+ break;
+ }
+ }
+
+ // Number of fingers changed cancel the last event and clear data
+ if (gesture.active_points != last_gesture.active_points) {
+ type = TouchType::Cancel;
+ enable_press_and_tap = false;
+ gesture.active_points = 0;
+ gesture.mid_point = {};
+ for (size_t id = 0; id < MAX_POINTS; id++) {
+ gesture.points[id].x = 0;
+ gesture.points[id].y = 0;
+ }
+ return;
+ }
+
+ // Calculate extra parameters of panning
+ if (type == TouchType::Pan) {
+ UpdatePanEvent(gesture, last_gesture, type, time_difference);
+ return;
+ }
+
+ // Promote to press type
+ if (last_entry.type == TouchType::Touch) {
+ type = TouchType::Press;
+ }
+}
+
+void Controller_Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type, Attribute& attributes, f32 time_difference) {
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ if (last_gesture.active_points != 0) {
+ switch (last_entry.type) {
+ case TouchType::Touch:
+ if (enable_press_and_tap) {
+ SetTapEvent(gesture, last_gesture, type, attributes);
+ return;
+ }
+ type = TouchType::Cancel;
+ force_update = true;
+ break;
+ case TouchType::Press:
+ case TouchType::Tap:
+ case TouchType::Swipe:
+ case TouchType::Pinch:
+ case TouchType::Rotate:
+ type = TouchType::Complete;
+ force_update = true;
+ break;
+ case TouchType::Pan:
+ EndPanEvent(gesture, last_gesture, type, time_difference);
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+ if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) {
+ gesture.detection_count++;
+ }
+}
+
+void Controller_Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type, Attribute& attributes) {
+ type = TouchType::Tap;
+ gesture = last_gesture;
+ force_update = true;
+ f32 tap_time_difference =
+ static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
+ last_tap_timestamp = last_update_timestamp;
+ if (tap_time_difference < double_tap_delay) {
+ attributes.is_double_tap.Assign(1);
+ }
+}
+
+void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type, f32 time_difference) {
+ auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
+ cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
+
+ cur_entry.vel_x = static_cast<f32>(cur_entry.delta_x) / time_difference;
+ cur_entry.vel_y = static_cast<f32>(cur_entry.delta_y) / time_difference;
+ last_pan_time_difference = time_difference;
+
+ // Promote to pinch type
+ if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
+ type = TouchType::Pinch;
+ cur_entry.scale = gesture.average_distance / last_gesture.average_distance;
+ }
+
+ const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) /
+ (1 + (gesture.angle * last_gesture.angle)));
+ // Promote to rotate type
+ if (std::abs(angle_between_two_lines) > angle_threshold) {
+ type = TouchType::Rotate;
+ cur_entry.scale = 0;
+ cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
+ }
+}
+
+void Controller_Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type, f32 time_difference) {
+ auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ cur_entry.vel_x =
+ static_cast<f32>(last_entry.delta_x) / (last_pan_time_difference + time_difference);
+ cur_entry.vel_y =
+ static_cast<f32>(last_entry.delta_y) / (last_pan_time_difference + time_difference);
+ const f32 curr_vel =
+ std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
+
+ // Set swipe event with parameters
+ if (curr_vel > swipe_threshold) {
+ SetSwipeEvent(gesture, last_gesture, type);
+ return;
+ }
+
+ // End panning without swipe
+ type = TouchType::Complete;
+ cur_entry.vel_x = 0;
+ cur_entry.vel_y = 0;
+ force_update = true;
+}
+
+void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type) {
+ auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ const auto& last_entry =
+ shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ type = TouchType::Swipe;
+ gesture = last_gesture;
+ force_update = true;
+ cur_entry.delta_x = last_entry.delta_x;
+ cur_entry.delta_y = last_entry.delta_y;
+ if (std::abs(cur_entry.delta_x) > std::abs(cur_entry.delta_y)) {
+ if (cur_entry.delta_x > 0) {
+ cur_entry.direction = Direction::Right;
+ return;
+ }
+ cur_entry.direction = Direction::Left;
+ return;
+ }
+ if (cur_entry.delta_y > 0) {
+ cur_entry.direction = Direction::Down;
+ return;
+ }
+ cur_entry.direction = Direction::Up;
+}
+
void Controller_Gesture::OnLoadInputDevices() {
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
@@ -183,23 +398,33 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
for (size_t id = 0; id < gesture.active_points; ++id) {
gesture.points[id].x =
- static_cast<int>(active_fingers[id].x * Layout::ScreenUndocked::Width);
+ static_cast<s32>(active_fingers[id].x * Layout::ScreenUndocked::Width);
gesture.points[id].y =
- static_cast<int>(active_fingers[id].y * Layout::ScreenUndocked::Height);
- gesture.mid_point.x += static_cast<int>(gesture.points[id].x / gesture.active_points);
- gesture.mid_point.y += static_cast<int>(gesture.points[id].y / gesture.active_points);
+ static_cast<s32>(active_fingers[id].y * Layout::ScreenUndocked::Height);
+
+ // Hack: There is no touch in docked but games still allow it
+ if (Settings::values.use_docked_mode.GetValue()) {
+ gesture.points[id].x =
+ static_cast<s32>(active_fingers[id].x * Layout::ScreenDocked::Width);
+ gesture.points[id].y =
+ static_cast<s32>(active_fingers[id].y * Layout::ScreenDocked::Height);
+ }
+
+ gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
+ gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
- const double distance =
- std::pow(static_cast<float>(gesture.mid_point.x - gesture.points[id].x), 2) +
- std::pow(static_cast<float>(gesture.mid_point.y - gesture.points[id].y), 2);
- gesture.average_distance +=
- static_cast<float>(distance) / static_cast<float>(gesture.active_points);
+ const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
+ Square(gesture.mid_point.y - gesture.points[id].y));
+ gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
}
- gesture.angle = std::atan2(static_cast<float>(gesture.mid_point.y - gesture.points[0].y),
- static_cast<float>(gesture.mid_point.x - gesture.points[0].x));
+ gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
+ static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
+
+ gesture.detection_count = last_gesture.detection_count;
+
return gesture;
}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 7c357b977..18110a6ad 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -7,7 +7,6 @@
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
@@ -35,10 +34,10 @@ private:
enum class TouchType : u32 {
Idle, // Nothing touching the screen
- Complete, // Unknown. End of touch?
- Cancel, // Never triggered
- Touch, // Pressing without movement
- Press, // Never triggered
+ Complete, // Set at the end of a touch event
+ Cancel, // Set when the number of fingers change
+ Touch, // A finger just touched the screen
+ Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
@@ -58,8 +57,8 @@ private:
union {
u32_le raw{};
- BitField<0, 1, u32> is_new_touch;
- BitField<1, 1, u32> is_double_tap;
+ BitField<4, 1, u32> is_new_touch;
+ BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
@@ -73,10 +72,9 @@ private:
struct GestureState {
s64_le sampling_number;
s64_le sampling_number2;
-
s64_le detection_count;
TouchType type;
- Direction dir;
+ Direction direction;
s32_le x;
s32_le y;
s32_le delta_x;
@@ -84,8 +82,8 @@ private:
f32 vel_x;
f32 vel_y;
Attribute attributes;
- u32 scale;
- u32 rotation_angle;
+ f32 scale;
+ f32 rotation_angle;
s32_le point_count;
std::array<Points, 4> points;
};
@@ -109,10 +107,46 @@ private:
Points mid_point{};
s64_le detection_count{};
u64_le delta_time{};
- float average_distance{};
- float angle{};
+ f32 average_distance{};
+ f32 angle{};
};
+ // Reads input from all available input engines
+ void ReadTouchInput();
+
+ // Returns true if gesture state needs to be updated
+ bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
+
+ // Updates the shared memory to the next state
+ void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
+ f32 time_difference);
+
+ // Initializes new gesture
+ void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes);
+
+ // Updates existing gesture state
+ void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference);
+
+ // Terminates exiting gesture
+ void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture, TouchType& type,
+ Attribute& attributes, f32 time_difference);
+
+ // Set current event to a tap event
+ void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture, TouchType& type,
+ Attribute& attributes);
+
+ // Calculates and set the extra parameters related to a pan event
+ void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type, f32 time_difference);
+
+ // Terminates the pan event
+ void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture, TouchType& type,
+ f32 time_difference);
+
+ // Set current event to a swipe event
+ void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture,
+ TouchType& type);
+
// Returns an unused finger id, if there is no fingers avaliable MAX_FINGERS will be returned
std::optional<size_t> GetUnusedFingerID() const;
@@ -134,6 +168,11 @@ private:
std::array<size_t, MAX_FINGERS> keyboard_finger_id;
std::array<size_t, MAX_FINGERS> udp_finger_id;
std::array<Finger, MAX_POINTS> fingers;
- GestureProperties last_gesture;
+ GestureProperties last_gesture{};
+ s64_le last_update_timestamp{};
+ s64_le last_tap_timestamp{};
+ f32 last_pan_time_difference{};
+ bool force_update{false};
+ bool enable_press_and_tap{false};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index dc2baca4a..2c8899ae0 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -10,6 +10,11 @@
namespace Service::SSL {
+enum class CertificateFormat : u32 {
+ Pem = 1,
+ Der = 2,
+};
+
class ISslConnection final : public ServiceFramework<ISslConnection> {
public:
explicit ISslConnection(Core::System& system_) : ServiceFramework{system_, "ISslConnection"} {
@@ -58,8 +63,8 @@ public:
{1, nullptr, "GetOption"},
{2, &ISslContext::CreateConnection, "CreateConnection"},
{3, nullptr, "GetConnectionCount"},
- {4, nullptr, "ImportServerPki"},
- {5, nullptr, "ImportClientPki"},
+ {4, &ISslContext::ImportServerPki, "ImportServerPki"},
+ {5, &ISslContext::ImportClientPki, "ImportClientPki"},
{6, nullptr, "RemoveServerPki"},
{7, nullptr, "RemoveClientPki"},
{8, nullptr, "RegisterInternalPki"},
@@ -94,6 +99,39 @@ private:
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISslConnection>(system);
}
+
+ void ImportServerPki(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto certificate_format = rp.PopEnum<CertificateFormat>();
+ const auto pkcs_12_certificates = ctx.ReadBuffer(0);
+
+ constexpr u64 server_id = 0;
+
+ LOG_WARNING(Service_SSL, "(STUBBED) called, certificate_format={}", certificate_format);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(server_id);
+ }
+
+ void ImportClientPki(Kernel::HLERequestContext& ctx) {
+ const auto pkcs_12_certificate = ctx.ReadBuffer(0);
+ const auto ascii_password = [&ctx] {
+ if (ctx.CanReadBuffer(1)) {
+ return ctx.ReadBuffer(1);
+ }
+
+ return std::vector<u8>{};
+ }();
+
+ constexpr u64 client_id = 0;
+
+ LOG_WARNING(Service_SSL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(client_id);
+ }
};
class SSL final : public ServiceFramework<SSL> {
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 7c4e7dd3b..7399c3648 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -153,6 +153,11 @@ struct InputSubsystem::Impl {
// TODO return the correct motion device
return {};
}
+#ifdef HAVE_SDL2
+ if (params.Get("class", "") == "sdl") {
+ return sdl->GetMotionMappingForDevice(params);
+ }
+#endif
return {};
}
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 42bbf14d4..b5d41bba4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -37,6 +37,9 @@ public:
virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
return {};
}
+ virtual MotionMapping GetMotionMappingForDevice(const Common::ParamPackage&) {
+ return {};
+ }
};
class NullState : public State {
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index f682a6db4..822d0b555 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -29,6 +29,7 @@
#endif
#include "common/logging/log.h"
+#include "common/math_util.h"
#include "common/param_package.h"
#include "common/settings_input.h"
#include "common/threadsafe_queue.h"
@@ -68,13 +69,57 @@ public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
SDL_GameController* game_controller)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
- sdl_controller{game_controller, &SDL_GameControllerClose} {}
+ sdl_controller{game_controller, &SDL_GameControllerClose} {
+ EnableMotion();
+ }
+
+ void EnableMotion() {
+ if (sdl_controller) {
+ SDL_GameController* controller = sdl_controller.get();
+ if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) {
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
+ has_accel = true;
+ }
+ if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) {
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
+ has_gyro = true;
+ }
+ }
+ }
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
state.buttons.insert_or_assign(button, value);
}
+ void SetMotion(SDL_ControllerSensorEvent event) {
+ constexpr float gravity_constant = 9.80665f;
+ std::lock_guard lock{mutex};
+ u64 time_difference = event.timestamp - last_motion_update;
+ last_motion_update = event.timestamp;
+ switch (event.sensor) {
+ case SDL_SENSOR_ACCEL: {
+ const Common::Vec3f acceleration = {-event.data[0], event.data[2], -event.data[1]};
+ motion.SetAcceleration(acceleration / gravity_constant);
+ break;
+ }
+ case SDL_SENSOR_GYRO: {
+ const Common::Vec3f gyroscope = {event.data[0], -event.data[2], event.data[1]};
+ motion.SetGyroscope(gyroscope / (Common::PI * 2));
+ break;
+ }
+ }
+
+ // Ignore duplicated timestamps
+ if (time_difference == 0) {
+ return;
+ }
+
+ motion.SetGyroThreshold(0.0001f);
+ motion.UpdateRotation(time_difference * 1000);
+ motion.UpdateOrientation(time_difference * 1000);
+ }
+
bool GetButton(int button) const {
std::lock_guard lock{mutex};
return state.buttons.at(button);
@@ -121,6 +166,14 @@ public:
return std::make_tuple(x, y);
}
+ bool HasGyro() const {
+ return has_gyro;
+ }
+
+ bool HasAccel() const {
+ return has_accel;
+ }
+
const MotionInput& GetMotion() const {
return motion;
}
@@ -173,8 +226,11 @@ private:
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
- // Motion is initialized without PID values as motion input is not aviable for SDL2
- MotionInput motion{0.0f, 0.0f, 0.0f};
+ // Motion is initialized with the PID values
+ MotionInput motion{0.3f, 0.005f, 0.0f};
+ u64 last_motion_update{};
+ bool has_gyro{false};
+ bool has_accel{false};
};
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
@@ -296,6 +352,12 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
}
break;
}
+ case SDL_CONTROLLERSENSORUPDATE: {
+ if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
+ joystick->SetMotion(event.csensor);
+ }
+ break;
+ }
case SDL_JOYDEVICEREMOVED:
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
@@ -449,6 +511,18 @@ private:
std::shared_ptr<SDLJoystick> joystick;
};
+class SDLMotion final : public Input::MotionDevice {
+public:
+ explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {}
+
+ Input::MotionStatus GetStatus() const override {
+ return joystick->GetMotion().GetMotion();
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+};
+
class SDLDirectionMotion final : public Input::MotionDevice {
public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -658,6 +732,10 @@ public:
auto joystick = state.GetSDLJoystickByGUID(guid, port);
+ if (params.Has("motion")) {
+ return std::make_unique<SDLMotion>(joystick);
+ }
+
if (params.Has("hat")) {
const int hat = params.Get("hat", 0);
const std::string direction_name = params.Get("direction", "");
@@ -717,6 +795,17 @@ SDLState::SDLState() {
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
+ // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
+
+ // Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
+ // GameController and not a generic one
+ SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
+
+ // Turn off Pro controller home led
+ SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
+
// If the frontend is going to manage the event loop, then we don't start one here
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
@@ -853,6 +942,13 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s
return params;
}
+Common::ParamPackage BuildMotionParam(int port, std::string guid) {
+ Common::ParamPackage params({{"engine", "sdl"}, {"motion", "0"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ return params;
+}
+
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
@@ -907,6 +1003,35 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve
}
break;
}
+ case SDL_CONTROLLERSENSORUPDATE: {
+ bool is_motion_shaking = false;
+ constexpr float gyro_threshold = 5.0f;
+ constexpr float accel_threshold = 11.0f;
+ if (event.csensor.sensor == SDL_SENSOR_ACCEL) {
+ const Common::Vec3f acceleration = {-event.csensor.data[0], event.csensor.data[2],
+ -event.csensor.data[1]};
+ if (acceleration.Length() > accel_threshold) {
+ is_motion_shaking = true;
+ }
+ }
+
+ if (event.csensor.sensor == SDL_SENSOR_GYRO) {
+ const Common::Vec3f gyroscope = {event.csensor.data[0], -event.csensor.data[2],
+ event.csensor.data[1]};
+ if (gyroscope.Length() > gyro_threshold) {
+ is_motion_shaking = true;
+ }
+ }
+
+ if (!is_motion_shaking) {
+ break;
+ }
+
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.csensor.which)) {
+ return BuildMotionParam(joystick->GetPort(), joystick->GetGUID());
+ }
+ break;
+ }
}
return {};
}
@@ -1036,6 +1161,27 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
return mapping;
}
+MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return {};
+ }
+
+ joystick->EnableMotion();
+
+ if (!joystick->HasGyro() && !joystick->HasAccel()) {
+ return {};
+ }
+
+ MotionMapping mapping = {};
+ mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
+ BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
+ return mapping;
+}
namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
@@ -1149,6 +1295,7 @@ public:
[[fallthrough]];
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
+ case SDL_CONTROLLERSENSORUPDATE:
return {SDLEventToMotionParamPackage(state, event)};
}
return std::nullopt;
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 8b7363f56..121e01913 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -57,6 +57,7 @@ public:
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
+ MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
private:
void InitJoystick(int joystick_index);
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 623b43d8a..ffe9edc1b 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -543,8 +543,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
}
void TextureCacheRuntime::BlitFramebuffer(Framebuffer* dst, Framebuffer* src,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
state_tracker.NotifyScissor0();
@@ -560,9 +559,9 @@ void TextureCacheRuntime::BlitFramebuffer(Framebuffer* dst, Framebuffer* src,
const GLbitfield buffer_bits = dst->BufferBits();
const bool has_depth = (buffer_bits & ~GL_COLOR_BUFFER_BIT) != 0;
const bool is_linear = !has_depth && filter == Tegra::Engines::Fermi2D::Filter::Bilinear;
- glBlitNamedFramebuffer(src->Handle(), dst->Handle(), src_region[0].x, src_region[0].y,
- src_region[1].x, src_region[1].y, dst_region[0].x, dst_region[0].y,
- dst_region[1].x, dst_region[1].y, buffer_bits,
+ glBlitNamedFramebuffer(src->Handle(), dst->Handle(), src_region.start.x, src_region.start.y,
+ src_region.end.x, src_region.end.y, dst_region.start.x,
+ dst_region.start.y, dst_region.end.x, dst_region.end.y, buffer_bits,
is_linear ? GL_LINEAR : GL_NEAREST);
}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 3c871541b..df8be12ff 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -28,7 +28,7 @@ using VideoCommon::ImageId;
using VideoCommon::ImageViewId;
using VideoCommon::ImageViewType;
using VideoCommon::NUM_RT;
-using VideoCommon::Offset2D;
+using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
struct ImageBufferMap {
@@ -73,10 +73,8 @@ public:
void EmulateCopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
- void BlitFramebuffer(Framebuffer* dst, Framebuffer* src,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
- Tegra::Engines::Fermi2D::Filter filter,
+ void BlitFramebuffer(Framebuffer* dst, Framebuffer* src, const Region2D& dst_region,
+ const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
void AccelerateImageUpload(Image& image, const ImageBufferMap& map,
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 1f6a169ae..b7f5b8bc2 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -289,16 +289,15 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
}
-void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region) {
+void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
+ const Region2D& src_region) {
const VkOffset2D offset{
- .x = std::min(dst_region[0].x, dst_region[1].x),
- .y = std::min(dst_region[0].y, dst_region[1].y),
+ .x = std::min(dst_region.start.x, dst_region.end.x),
+ .y = std::min(dst_region.start.y, dst_region.end.y),
};
const VkExtent2D extent{
- .width = static_cast<u32>(std::abs(dst_region[1].x - dst_region[0].x)),
- .height = static_cast<u32>(std::abs(dst_region[1].y - dst_region[0].y)),
+ .width = static_cast<u32>(std::abs(dst_region.end.x - dst_region.start.x)),
+ .height = static_cast<u32>(std::abs(dst_region.end.y - dst_region.start.y)),
};
const VkViewport viewport{
.x = static_cast<float>(offset.x),
@@ -313,11 +312,12 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout,
.offset = offset,
.extent = extent,
};
- const float scale_x = static_cast<float>(src_region[1].x - src_region[0].x);
- const float scale_y = static_cast<float>(src_region[1].y - src_region[0].y);
+ const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x);
+ const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y);
const PushConstants push_constants{
.tex_scale = {scale_x, scale_y},
- .tex_offset = {static_cast<float>(src_region[0].x), static_cast<float>(src_region[0].y)},
+ .tex_offset = {static_cast<float>(src_region.start.x),
+ static_cast<float>(src_region.start.y)},
};
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
@@ -353,8 +353,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
BlitImageHelper::~BlitImageHelper() = default;
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
const bool is_linear = filter == Tegra::Engines::Fermi2D::Filter::Bilinear;
@@ -383,8 +382,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
VkImageView src_depth_view, VkImageView src_stencil_view,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 43fd3d737..0d81a06ed 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -13,7 +13,7 @@
namespace Vulkan {
-using VideoCommon::Offset2D;
+using VideoCommon::Region2D;
class Device;
class Framebuffer;
@@ -35,15 +35,13 @@ public:
~BlitImageHelper();
void BlitColor(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view,
- VkImageView src_stencil_view, const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
- Tegra::Engines::Fermi2D::Filter filter,
+ VkImageView src_stencil_view, const Region2D& dst_region,
+ const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 017348e05..bdd0ce8bc 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -490,8 +490,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
write_barrier);
}
-[[nodiscard]] VkImageBlit MakeImageBlit(const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
const VkImageSubresourceLayers& dst_layers,
const VkImageSubresourceLayers& src_layers) {
return VkImageBlit{
@@ -499,13 +498,13 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
.srcOffsets =
{
{
- .x = src_region[0].x,
- .y = src_region[0].y,
+ .x = src_region.start.x,
+ .y = src_region.start.y,
.z = 0,
},
{
- .x = src_region[1].x,
- .y = src_region[1].y,
+ .x = src_region.end.x,
+ .y = src_region.end.y,
.z = 1,
},
},
@@ -513,42 +512,42 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
.dstOffsets =
{
{
- .x = dst_region[0].x,
- .y = dst_region[0].y,
+ .x = dst_region.start.x,
+ .y = dst_region.start.y,
.z = 0,
},
{
- .x = dst_region[1].x,
- .y = dst_region[1].y,
+ .x = dst_region.end.x,
+ .y = dst_region.end.y,
.z = 1,
},
},
};
}
-[[nodiscard]] VkImageResolve MakeImageResolve(const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+[[nodiscard]] VkImageResolve MakeImageResolve(const Region2D& dst_region,
+ const Region2D& src_region,
const VkImageSubresourceLayers& dst_layers,
const VkImageSubresourceLayers& src_layers) {
return VkImageResolve{
.srcSubresource = src_layers,
.srcOffset =
{
- .x = src_region[0].x,
- .y = src_region[0].y,
+ .x = src_region.start.x,
+ .y = src_region.start.y,
.z = 0,
},
.dstSubresource = dst_layers,
.dstOffset =
{
- .x = dst_region[0].x,
- .y = dst_region[0].y,
+ .x = dst_region.start.x,
+ .y = dst_region.start.y,
.z = 0,
},
.extent =
{
- .width = static_cast<u32>(dst_region[1].x - dst_region[0].x),
- .height = static_cast<u32>(dst_region[1].y - dst_region[0].y),
+ .width = static_cast<u32>(dst_region.end.x - dst_region.start.x),
+ .height = static_cast<u32>(dst_region.end.y - dst_region.start.y),
.depth = 1,
},
};
@@ -602,8 +601,7 @@ StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
}
void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
const VkImageAspectFlags aspect_mask = ImageAspectMask(src.format);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 628785d5e..4a57d378b 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -16,7 +16,7 @@ namespace Vulkan {
using VideoCommon::ImageId;
using VideoCommon::NUM_RT;
-using VideoCommon::Offset2D;
+using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
using VideoCore::Surface::PixelFormat;
@@ -71,8 +71,7 @@ struct TextureCacheRuntime {
[[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
- const std::array<Offset2D, 2>& dst_region,
- const std::array<Offset2D, 2>& src_region,
+ const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 98e33c3a0..59b7c678b 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -148,7 +148,9 @@ public:
/// Blit an image with the given parameters
void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
- const Tegra::Engines::Fermi2D::Config& copy);
+ const Tegra::Engines::Fermi2D::Config& copy,
+ std::optional<Region2D> src_region_override = {},
+ std::optional<Region2D> dst_region_override = {});
/// Invalidate the contents of the color buffer index
/// These contents become unspecified, the cache can assume aggressive optimizations.
@@ -615,7 +617,9 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
template <class P>
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
- const Tegra::Engines::Fermi2D::Config& copy) {
+ const Tegra::Engines::Fermi2D::Config& copy,
+ std::optional<Region2D> src_override,
+ std::optional<Region2D> dst_override) {
const BlitImages images = GetBlitImages(dst, src);
const ImageId dst_id = images.dst_id;
const ImageId src_id = images.src_id;
@@ -631,20 +635,42 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
- const std::array src_region{
- Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
- Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
+
+ // out of bounds texture blit checking
+ const bool use_override = src_override.has_value();
+ const s32 src_x0 = copy.src_x0 >> src_samples_x;
+ s32 src_x1 = use_override ? src_override->end.x : copy.src_x1 >> src_samples_x;
+ const s32 src_y0 = copy.src_y0 >> src_samples_y;
+ const s32 src_y1 = copy.src_y1 >> src_samples_y;
+
+ const auto src_width = static_cast<s32>(src_image.info.size.width);
+ const bool width_oob = src_x1 > src_width;
+ const auto width_diff = width_oob ? src_x1 - src_width : 0;
+ if (width_oob) {
+ src_x1 = src_width;
+ }
+
+ const Region2D src_dimensions{
+ Offset2D{.x = src_x0, .y = src_y0},
+ Offset2D{.x = src_x1, .y = src_y1},
};
+ const auto src_region = use_override ? *src_override : src_dimensions;
const std::optional src_base = src_image.TryFindBase(src.Address());
const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
- const std::array dst_region{
- Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
- Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y},
+
+ const s32 dst_x0 = copy.dst_x0 >> dst_samples_x;
+ const s32 dst_x1 = copy.dst_x1 >> dst_samples_x;
+ const s32 dst_y0 = copy.dst_y0 >> dst_samples_y;
+ const s32 dst_y1 = copy.dst_y1 >> dst_samples_y;
+ const Region2D dst_dimensions{
+ Offset2D{.x = dst_x0, .y = dst_y0},
+ Offset2D{.x = dst_x1 - width_diff, .y = dst_y1},
};
+ const auto dst_region = use_override ? *dst_override : dst_dimensions;
// Always call this after src_framebuffer_id was queried, as the address might be invalidated.
Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
@@ -661,6 +687,21 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
copy.operation);
}
+
+ if (width_oob) {
+ // Continue copy of the oob region of the texture on the next row
+ auto oob_src = src;
+ oob_src.height++;
+ const Region2D src_region_override{
+ Offset2D{.x = 0, .y = src_y0 + 1},
+ Offset2D{.x = width_diff, .y = src_y1 + 1},
+ };
+ const Region2D dst_region_override{
+ Offset2D{.x = dst_x1 - width_diff, .y = dst_y0},
+ Offset2D{.x = dst_x1, .y = dst_y1},
+ };
+ BlitImage(dst, oob_src, copy, src_region_override, dst_region_override);
+ }
}
template <class P>
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 2ad2d72a6..c9571f7e4 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -64,6 +64,13 @@ struct Offset3D {
s32 z;
};
+struct Region2D {
+ constexpr auto operator<=>(const Region2D&) const noexcept = default;
+
+ Offset2D start;
+ Offset2D end;
+};
+
struct Extent2D {
constexpr auto operator<=>(const Extent2D&) const noexcept = default;
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index c9318c562..ab3512810 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -153,6 +153,10 @@ QString ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("Button %1").arg(button_str);
}
+ if (param.Has("motion")) {
+ return QObject::tr("SDL Motion");
+ }
+
return {};
}
@@ -1245,12 +1249,16 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
const auto& device = input_devices[ui->comboDevices->currentIndex()];
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
+ auto motion_mapping = input_subsystem->GetMotionMappingForDevice(device);
for (std::size_t i = 0; i < buttons_param.size(); ++i) {
buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
}
for (std::size_t i = 0; i < analogs_param.size(); ++i) {
analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
}
+ for (std::size_t i = 0; i < motions_param.size(); ++i) {
+ motions_param[i] = motion_mapping[static_cast<Settings::NativeMotion::Values>(i)];
+ }
UpdateUI();
}
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index f35c89e04..0cdaea8a4 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -46,6 +46,7 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
SetConfiguration();
// Force game list reload if any of the relevant settings are changed.
+ connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,