early-access version 2243

This commit is contained in:
pineappleEA 2021-11-27 00:35:19 +01:00
parent 576efadbd3
commit 78c60bdc98
16 changed files with 488 additions and 71 deletions

View File

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 2242. This is the source code for early-access 2243.
## Legal Notice ## Legal Notice

View File

@ -195,6 +195,20 @@ enum class ButtonNames {
ButtonX, ButtonX,
ButtonY, ButtonY,
ButtonStart, ButtonStart,
// DS4 button names
L1,
L2,
L3,
R1,
R2,
R3,
Circle,
Cross,
Square,
Triangle,
Share,
Options,
}; };
// Callback data consisting of an input type and the equivalent data status // Callback data consisting of an input type and the equivalent data status

View File

@ -560,6 +560,7 @@ struct Values {
Setting<bool> motion_enabled{true, "motion_enabled"}; Setting<bool> motion_enabled{true, "motion_enabled"};
BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"};
BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
BasicSetting<bool> tas_enable{false, "tas_enable"}; BasicSetting<bool> tas_enable{false, "tas_enable"};

View File

@ -30,8 +30,10 @@ void EmulatedConsole::SetTouchParams() {
} }
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"}; touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"}; touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
touch_params[index++] = Common::ParamPackage{"engine:cemuhookudp,axis_x:0,axis_y:1,button:0"}; touch_params[index++] =
touch_params[index++] = Common::ParamPackage{"engine:cemuhookudp,axis_x:2,axis_y:3,button:1"}; Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
const auto button_index = const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());

View File

@ -103,7 +103,7 @@ private:
// Send a request for getting pad data for the pad // Send a request for getting pad data for the pad
const Request::PadData pad_data{ const Request::PadData pad_data{
Request::PadData::Flags::AllPorts, Request::RegisterFlags::AllPads,
0, 0,
EMPTY_MAC_ADDRESS, EMPTY_MAC_ADDRESS,
}; };
@ -247,7 +247,12 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
for (std::size_t id = 0; id < data.touch.size(); ++id) { for (std::size_t id = 0; id < data.touch.size(); ++id) {
const auto touch_pad = data.touch[id]; const auto touch_pad = data.touch[id];
const int touch_id = static_cast<int>(client * 2 + id); const auto touch_axis_x_id =
static_cast<int>(id == 0 ? PadAxes::Touch1X : PadAxes::Touch2X);
const auto touch_axis_y_id =
static_cast<int>(id == 0 ? PadAxes::Touch1Y : PadAxes::Touch2Y);
const auto touch_button_id =
static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::touch2);
// TODO: Use custom calibration per device // TODO: Use custom calibration per device
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
@ -264,14 +269,35 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
static_cast<f32>(max_y - min_y); static_cast<f32>(max_y - min_y);
if (touch_pad.is_active) { if (touch_pad.is_active) {
SetAxis(identifier, touch_id * 2, x); SetAxis(identifier, touch_axis_x_id, x);
SetAxis(identifier, touch_id * 2 + 1, y); SetAxis(identifier, touch_axis_y_id, y);
SetButton(identifier, touch_id, true); SetButton(identifier, touch_button_id, true);
continue; continue;
} }
SetAxis(identifier, touch_id * 2, 0); SetAxis(identifier, touch_axis_x_id, 0);
SetAxis(identifier, touch_id * 2 + 1, 0); SetAxis(identifier, touch_axis_y_id, 0);
SetButton(identifier, touch_id, false); SetButton(identifier, touch_button_id, false);
}
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX),
(data.left_stick_x - 127.0f) / 127.0f);
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY),
(data.left_stick_y - 127.0f) / 127.0f);
SetAxis(identifier, static_cast<int>(PadAxes::RightStickX),
(data.right_stick_x - 127.0f) / 127.0f);
SetAxis(identifier, static_cast<int>(PadAxes::RightStickY),
(data.right_stick_y - 127.0f) / 127.0f);
static constexpr std::array<PadButton, 16> buttons{
PadButton::Share, PadButton::L3, PadButton::R3, PadButton::Options,
PadButton::Up, PadButton::Right, PadButton::Down, PadButton::Left,
PadButton::L2, PadButton::R2, PadButton::L1, PadButton::R1,
PadButton::Triangle, PadButton::Circle, PadButton::Cross, PadButton::Square};
for (std::size_t i = 0; i < buttons.size(); ++i) {
const bool button_status = (data.digital_button & (1U << i)) != 0;
const int button = static_cast<int>(buttons[i]);
SetButton(identifier, button, button_status);
} }
} }
@ -317,6 +343,170 @@ void UDPClient::Reset() {
} }
} }
std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
std::vector<Common::ParamPackage> devices;
if (!Settings::values.enable_udp_controller) {
return devices;
}
for (std::size_t client = 0; client < clients.size(); client++) {
if (clients[client].active != 1) {
continue;
}
for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
const std::size_t pad_index = client * PADS_PER_CLIENT + index;
if (!pads[pad_index].connected) {
continue;
}
const auto pad_identifier = GetPadIdentifier(pad_index);
Common::ParamPackage identifier{};
identifier.Set("engine", GetEngineName());
identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
identifier.Set("guid", pad_identifier.guid.Format());
identifier.Set("port", static_cast<int>(pad_identifier.port));
identifier.Set("pad", static_cast<int>(pad_identifier.pad));
devices.emplace_back(identifier);
}
}
return devices;
}
ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
// This list excludes any button that can't be really mapped
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 18>
switch_to_dsu_button = {
std::pair{Settings::NativeButton::A, PadButton::Circle},
{Settings::NativeButton::B, PadButton::Cross},
{Settings::NativeButton::X, PadButton::Triangle},
{Settings::NativeButton::Y, PadButton::Square},
{Settings::NativeButton::Plus, PadButton::Options},
{Settings::NativeButton::Minus, PadButton::Share},
{Settings::NativeButton::DLeft, PadButton::Left},
{Settings::NativeButton::DUp, PadButton::Up},
{Settings::NativeButton::DRight, PadButton::Right},
{Settings::NativeButton::DDown, PadButton::Down},
{Settings::NativeButton::L, PadButton::L1},
{Settings::NativeButton::R, PadButton::R1},
{Settings::NativeButton::ZL, PadButton::L2},
{Settings::NativeButton::ZR, PadButton::R2},
{Settings::NativeButton::SL, PadButton::L2},
{Settings::NativeButton::SR, PadButton::R2},
{Settings::NativeButton::LStick, PadButton::L3},
{Settings::NativeButton::RStick, PadButton::R3},
};
if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
return {};
}
ButtonMapping mapping{};
for (const auto& [switch_button, dsu_button] : switch_to_dsu_button) {
Common::ParamPackage button_params{};
button_params.Set("engine", GetEngineName());
button_params.Set("guid", params.Get("guid", ""));
button_params.Set("port", params.Get("port", 0));
button_params.Set("pad", params.Get("pad", 0));
button_params.Set("button", static_cast<int>(dsu_button));
mapping.insert_or_assign(switch_button, std::move(button_params));
}
return mapping;
}
AnalogMapping UDPClient::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
return {};
}
AnalogMapping mapping = {};
Common::ParamPackage left_analog_params;
left_analog_params.Set("engine", GetEngineName());
left_analog_params.Set("guid", params.Get("guid", ""));
left_analog_params.Set("port", params.Get("port", 0));
left_analog_params.Set("pad", params.Get("pad", 0));
left_analog_params.Set("axis_x", static_cast<int>(PadAxes::LeftStickX));
left_analog_params.Set("axis_y", static_cast<int>(PadAxes::LeftStickY));
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
Common::ParamPackage right_analog_params;
right_analog_params.Set("engine", GetEngineName());
right_analog_params.Set("guid", params.Get("guid", ""));
right_analog_params.Set("port", params.Get("port", 0));
right_analog_params.Set("pad", params.Get("pad", 0));
right_analog_params.Set("axis_x", static_cast<int>(PadAxes::RightStickX));
right_analog_params.Set("axis_y", static_cast<int>(PadAxes::RightStickY));
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
return mapping;
}
MotionMapping UDPClient::GetMotionMappingForDevice(const Common::ParamPackage& params) {
if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
return {};
}
MotionMapping mapping = {};
Common::ParamPackage motion_params;
motion_params.Set("engine", GetEngineName());
motion_params.Set("guid", params.Get("guid", ""));
motion_params.Set("port", params.Get("port", 0));
motion_params.Set("pad", params.Get("pad", 0));
motion_params.Set("motion", 0);
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(motion_params));
mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(motion_params));
return mapping;
}
Common::Input::ButtonNames UDPClient::GetUIButtonName(const Common::ParamPackage& params) const {
PadButton button = static_cast<PadButton>(params.Get("button", 0));
switch (button) {
case PadButton::Left:
return Common::Input::ButtonNames::ButtonLeft;
case PadButton::Right:
return Common::Input::ButtonNames::ButtonRight;
case PadButton::Down:
return Common::Input::ButtonNames::ButtonDown;
case PadButton::Up:
return Common::Input::ButtonNames::ButtonUp;
case PadButton::L1:
return Common::Input::ButtonNames::L1;
case PadButton::L2:
return Common::Input::ButtonNames::L2;
case PadButton::L3:
return Common::Input::ButtonNames::L3;
case PadButton::R1:
return Common::Input::ButtonNames::R1;
case PadButton::R2:
return Common::Input::ButtonNames::R2;
case PadButton::R3:
return Common::Input::ButtonNames::R3;
case PadButton::Circle:
return Common::Input::ButtonNames::Circle;
case PadButton::Cross:
return Common::Input::ButtonNames::Cross;
case PadButton::Square:
return Common::Input::ButtonNames::Square;
case PadButton::Triangle:
return Common::Input::ButtonNames::Triangle;
case PadButton::Share:
return Common::Input::ButtonNames::Share;
case PadButton::Options:
return Common::Input::ButtonNames::Options;
default:
return Common::Input::ButtonNames::Undefined;
}
}
Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& params) const {
if (params.Has("button")) {
return GetUIButtonName(params);
}
if (params.Has("axis")) {
return Common::Input::ButtonNames::Value;
}
if (params.Has("motion")) {
return Common::Input::ButtonNames::Engine;
}
return Common::Input::ButtonNames::Invalid;
}
void TestCommunication(const std::string& host, u16 port, void TestCommunication(const std::string& host, u16 port,
const std::function<void()>& success_callback, const std::function<void()>& success_callback,
const std::function<void()>& failure_callback) { const std::function<void()>& failure_callback) {

View File

@ -56,7 +56,61 @@ public:
void ReloadSockets(); void ReloadSockets();
/// Used for automapping features
std::vector<Common::ParamPackage> GetInputDevices() const override;
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
private: private:
enum class PadButton {
Undefined = 0x0000,
Share = 0x0001,
L3 = 0x0002,
R3 = 0x0004,
Options = 0x0008,
Up = 0x0010,
Right = 0x0020,
Down = 0x0040,
Left = 0x0080,
L2 = 0x0100,
R2 = 0x0200,
L1 = 0x0400,
R1 = 0x0800,
Triangle = 0x1000,
Circle = 0x2000,
Cross = 0x4000,
Square = 0x8000,
Touch1 = 0x10000,
touch2 = 0x20000,
};
enum class PadAxes : u8 {
LeftStickX,
LeftStickY,
RightStickX,
RightStickY,
AnalogLeft,
AnalogDown,
AnalogRight,
AnalogUp,
AnalogSquare,
AnalogCross,
AnalogCircle,
AnalogTriangle,
AnalogR1,
AnalogL1,
AnalogR2,
AnalogL3,
AnalogR3,
Touch1X,
Touch1Y,
Touch2X,
Touch2Y,
Undefined,
};
struct PadData { struct PadData {
std::size_t pad_index{}; std::size_t pad_index{};
bool connected{}; bool connected{};
@ -90,6 +144,8 @@ private:
const PadIdentifier GetPadIdentifier(std::size_t pad_index) const; const PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
const Common::UUID GetHostUUID(const std::string host) const; const Common::UUID GetHostUUID(const std::string host) const;
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
// Allocate clients for 8 udp servers // Allocate clients for 8 udp servers
static constexpr std::size_t MAX_UDP_CLIENTS = 8; static constexpr std::size_t MAX_UDP_CLIENTS = 8;
static constexpr std::size_t PADS_PER_CLIENT = 4; static constexpr std::size_t PADS_PER_CLIENT = 4;

View File

@ -56,6 +56,12 @@ constexpr Type GetMessageType();
namespace Request { namespace Request {
enum RegisterFlags : u8 {
AllPads,
PadID,
PadMACAdddress,
};
struct Version {}; struct Version {};
/** /**
* Requests the server to send information about what controllers are plugged into the ports * Requests the server to send information about what controllers are plugged into the ports
@ -77,13 +83,8 @@ static_assert(std::is_trivially_copyable_v<PortInfo>,
* timeout seems to be 5 seconds. * timeout seems to be 5 seconds.
*/ */
struct PadData { struct PadData {
enum class Flags : u8 {
AllPorts,
Id,
Mac,
};
/// Determines which method will be used as a look up for the controller /// Determines which method will be used as a look up for the controller
Flags flags{}; RegisterFlags flags{};
/// Index of the port of the controller to retrieve data about /// Index of the port of the controller to retrieve data about
u8 port_id{}; u8 port_id{};
/// Mac address of the controller to retrieve data about /// Mac address of the controller to retrieve data about
@ -113,6 +114,36 @@ Message<T> Create(const T data, const u32 client_id = 0) {
namespace Response { namespace Response {
enum class ConnectionType : u8 {
None,
Usb,
Bluetooth,
};
enum class State : u8 {
Disconnected,
Reserved,
Connected,
};
enum class Model : u8 {
None,
PartialGyro,
FullGyro,
Generic,
};
enum class Battery : u8 {
None = 0x00,
Dying = 0x01,
Low = 0x02,
Medium = 0x03,
High = 0x04,
Full = 0x05,
Charging = 0xEE,
Charged = 0xEF,
};
struct Version { struct Version {
u16_le version{}; u16_le version{};
}; };
@ -122,11 +153,11 @@ static_assert(std::is_trivially_copyable_v<Version>,
struct PortInfo { struct PortInfo {
u8 id{}; u8 id{};
u8 state{}; State state{};
u8 model{}; Model model{};
u8 connection_type{}; ConnectionType connection_type{};
MacAddress mac; MacAddress mac;
u8 battery{}; Battery battery{};
u8 is_pad_active{}; u8 is_pad_active{};
}; };
static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
@ -177,18 +208,18 @@ struct PadData {
u8 right_stick_y{}; u8 right_stick_y{};
struct AnalogButton { struct AnalogButton {
u8 button_8{}; u8 button_dpad_left_analog{};
u8 button_7{}; u8 button_dpad_down_analog{};
u8 button_6{}; u8 button_dpad_right_analog{};
u8 button_5{}; u8 button_dpad_up_analog{};
u8 button_12{}; u8 button_square_analog{};
u8 button_11{}; u8 button_cross_analog{};
u8 button_10{}; u8 button_circle_analog{};
u8 button_9{}; u8 button_triangle_analog{};
u8 button_16{}; u8 button_r1_analog{};
u8 button_15{}; u8 button_l1_analog{};
u8 button_14{}; u8 trigger_r2{};
u8 button_13{}; u8 trigger_l2{};
} analog_button; } analog_button;
std::array<TouchPad, 2> touch; std::array<TouchPad, 2> touch;

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included // Refer to the license.txt file included
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h"
#include "input_common/input_engine.h" #include "input_common/input_engine.h"
#include "input_common/input_mapping.h" #include "input_common/input_mapping.h"
@ -182,6 +183,11 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
if (data.engine == "keyboard" && data.pad.port != 0) { if (data.engine == "keyboard" && data.pad.port != 0) {
return false; return false;
} }
// To prevent mapping with two devices we disable any UDP except motion
if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
data.type != EngineInputType::Motion) {
return false;
}
// The following drivers don't need to be mapped // The following drivers don't need to be mapped
if (data.engine == "tas") { if (data.engine == "tas") {
return false; return false;

View File

@ -63,9 +63,12 @@ struct InputSubsystem::Impl {
udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp"); udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
udp_client->SetMappingCallback(mapping_callback); udp_client->SetMappingCallback(mapping_callback);
udp_client_factory = std::make_shared<InputFactory>(udp_client); udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(), Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
udp_client_factory); udp_client_input_factory);
Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(),
udp_client_output_factory);
tas_input = std::make_shared<TasInput::Tas>("tas"); tas_input = std::make_shared<TasInput::Tas>("tas");
tas_input->SetMappingCallback(mapping_callback); tas_input->SetMappingCallback(mapping_callback);
@ -110,6 +113,7 @@ struct InputSubsystem::Impl {
gcadapter.reset(); gcadapter.reset();
Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName()); Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName());
udp_client.reset(); udp_client.reset();
Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName()); Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
@ -137,6 +141,8 @@ struct InputSubsystem::Impl {
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
auto gcadapter_devices = gcadapter->GetInputDevices(); auto gcadapter_devices = gcadapter->GetInputDevices();
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
auto udp_devices = udp_client->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices(); auto sdl_devices = sdl->GetInputDevices();
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
@ -157,6 +163,9 @@ struct InputSubsystem::Impl {
if (engine == gcadapter->GetEngineName()) { if (engine == gcadapter->GetEngineName()) {
return gcadapter->GetAnalogMappingForDevice(params); return gcadapter->GetAnalogMappingForDevice(params);
} }
if (engine == udp_client->GetEngineName()) {
return udp_client->GetAnalogMappingForDevice(params);
}
if (engine == tas_input->GetEngineName()) { if (engine == tas_input->GetEngineName()) {
return tas_input->GetAnalogMappingForDevice(params); return tas_input->GetAnalogMappingForDevice(params);
} }
@ -177,6 +186,9 @@ struct InputSubsystem::Impl {
if (engine == gcadapter->GetEngineName()) { if (engine == gcadapter->GetEngineName()) {
return gcadapter->GetButtonMappingForDevice(params); return gcadapter->GetButtonMappingForDevice(params);
} }
if (engine == udp_client->GetEngineName()) {
return udp_client->GetButtonMappingForDevice(params);
}
if (engine == tas_input->GetEngineName()) { if (engine == tas_input->GetEngineName()) {
return tas_input->GetButtonMappingForDevice(params); return tas_input->GetButtonMappingForDevice(params);
} }
@ -194,8 +206,8 @@ struct InputSubsystem::Impl {
return {}; return {};
} }
const std::string engine = params.Get("engine", ""); const std::string engine = params.Get("engine", "");
if (engine == gcadapter->GetEngineName()) { if (engine == udp_client->GetEngineName()) {
return gcadapter->GetMotionMappingForDevice(params); return udp_client->GetMotionMappingForDevice(params);
} }
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
if (engine == sdl->GetEngineName()) { if (engine == sdl->GetEngineName()) {
@ -238,6 +250,9 @@ struct InputSubsystem::Impl {
if (engine == gcadapter->GetEngineName()) { if (engine == gcadapter->GetEngineName()) {
return true; return true;
} }
if (engine == udp_client->GetEngineName()) {
return true;
}
if (engine == tas_input->GetEngineName()) { if (engine == tas_input->GetEngineName()) {
return true; return true;
} }
@ -286,12 +301,13 @@ struct InputSubsystem::Impl {
std::shared_ptr<InputFactory> mouse_factory; std::shared_ptr<InputFactory> mouse_factory;
std::shared_ptr<InputFactory> gcadapter_input_factory; std::shared_ptr<InputFactory> gcadapter_input_factory;
std::shared_ptr<InputFactory> touch_screen_factory; std::shared_ptr<InputFactory> touch_screen_factory;
std::shared_ptr<InputFactory> udp_client_factory; std::shared_ptr<InputFactory> udp_client_input_factory;
std::shared_ptr<InputFactory> tas_input_factory; std::shared_ptr<InputFactory> tas_input_factory;
std::shared_ptr<OutputFactory> keyboard_output_factory; std::shared_ptr<OutputFactory> keyboard_output_factory;
std::shared_ptr<OutputFactory> mouse_output_factory; std::shared_ptr<OutputFactory> mouse_output_factory;
std::shared_ptr<OutputFactory> gcadapter_output_factory; std::shared_ptr<OutputFactory> gcadapter_output_factory;
std::shared_ptr<OutputFactory> udp_client_output_factory;
std::shared_ptr<OutputFactory> tas_output_factory; std::shared_ptr<OutputFactory> tas_output_factory;
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2

View File

@ -472,7 +472,7 @@ template <class P>
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) { const Tegra::Engines::Fermi2D::Config& copy) {
const BlitImages images = GetBlitImages(dst, src); const BlitImages images = GetBlitImages(dst, src, copy);
const ImageId dst_id = images.dst_id; const ImageId dst_id = images.dst_id;
const ImageId src_id = images.src_id; const ImageId src_id = images.src_id;
@ -762,12 +762,15 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
const bool broken_views = const bool broken_views =
runtime.HasBrokenTextureViewFormats() || True(options & RelaxedOptions::ForceBrokenViews); runtime.HasBrokenTextureViewFormats() || True(options & RelaxedOptions::ForceBrokenViews);
const bool native_bgr = runtime.HasNativeBgr(); const bool native_bgr = runtime.HasNativeBgr();
ImageId image_id; const bool flexible_formats = True(options & RelaxedOptions::Format);
ImageId image_id{};
boost::container::small_vector<ImageId, 1> image_ids;
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
if (True(existing_image.flags & ImageFlagBits::Remapped)) { if (True(existing_image.flags & ImageFlagBits::Remapped)) {
return false; return false;
} }
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear)
[[unlikely]] {
const bool strict_size = False(options & RelaxedOptions::Size) && const bool strict_size = False(options & RelaxedOptions::Size) &&
True(existing_image.flags & ImageFlagBits::Strong); True(existing_image.flags & ImageFlagBits::Strong);
const ImageInfo& existing = existing_image.info; const ImageInfo& existing = existing_image.info;
@ -776,17 +779,27 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
IsPitchLinearSameSize(existing, info, strict_size) && IsPitchLinearSameSize(existing, info, strict_size) &&
IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) { IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
image_id = existing_image_id; image_id = existing_image_id;
return true; image_ids.push_back(existing_image_id);
return !flexible_formats && existing.format == info.format;
} }
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
native_bgr)) { native_bgr)) {
image_id = existing_image_id; image_id = existing_image_id;
return true; image_ids.push_back(existing_image_id);
return !flexible_formats && existing_image.info.format == info.format;
} }
return false; return false;
}; };
ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda); ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda);
return image_id; if (image_ids.size() <= 1) [[likely]] {
return image_id;
}
auto image_ids_compare = [this](ImageId a, ImageId b) {
auto& image_a = slot_images[a];
auto& image_b = slot_images[b];
return image_a.modification_tick < image_b.modification_tick;
};
return *std::ranges::max_element(image_ids, image_ids_compare);
} }
template <class P> template <class P>
@ -1078,30 +1091,69 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
template <class P> template <class P>
typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages( typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src) { const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) {
static constexpr auto FIND_OPTIONS = RelaxedOptions::Samples; static constexpr auto FIND_OPTIONS = RelaxedOptions::Samples;
const GPUVAddr dst_addr = dst.Address(); const GPUVAddr dst_addr = dst.Address();
const GPUVAddr src_addr = src.Address(); const GPUVAddr src_addr = src.Address();
ImageInfo dst_info(dst); ImageInfo dst_info(dst);
ImageInfo src_info(src); ImageInfo src_info(src);
const bool can_be_depth_blit =
dst_info.format == src_info.format && copy.filter == Tegra::Engines::Fermi2D::Filter::Point;
ImageId dst_id; ImageId dst_id;
ImageId src_id; ImageId src_id;
const auto convert_depth_format = [](PixelFormat format) {
switch (format) {
case PixelFormat::R16_UNORM:
return PixelFormat::D16_UNORM;
case PixelFormat::A8B8G8R8_UNORM:
return PixelFormat::S8_UINT_D24_UNORM;
case PixelFormat::R32_FLOAT:
return PixelFormat::D32_FLOAT;
default:
return format;
}
};
auto insert_images = [&]() {
if (!src_id) {
src_id = InsertImage(src_info, src_addr, RelaxedOptions{});
}
if (!dst_id) {
dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{});
}
};
RelaxedOptions try_options = FIND_OPTIONS;
if (can_be_depth_blit) {
try_options |= RelaxedOptions::Format;
}
do { do {
has_deleted_images = false; has_deleted_images = false;
dst_id = FindImage(dst_info, dst_addr, FIND_OPTIONS); src_id = FindImage(src_info, src_addr, try_options);
src_id = FindImage(src_info, src_addr, FIND_OPTIONS); dst_id = FindImage(dst_info, dst_addr, try_options);
const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
DeduceBlitImages(dst_info, src_info, dst_image, src_image); if (src_image && src_image->info.num_samples > 1) {
ASSERT(GetFormatType(dst_info.format) == GetFormatType(src_info.format)); RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews};
RelaxedOptions find_options{}; src_id = FindOrInsertImage(src_info, src_addr, find_options);
if (src_info.num_samples > 1) { dst_id = FindOrInsertImage(dst_info, dst_addr, find_options);
// it's a resolve, we must enforce the same format. if (has_deleted_images) {
find_options = RelaxedOptions::ForceBrokenViews; continue;
}
break;
} }
src_id = FindOrInsertImage(src_info, src_addr, find_options); if (can_be_depth_blit) {
dst_id = FindOrInsertImage(dst_info, dst_addr, find_options); const ImageBase* const dst_image = src_id ? &slot_images[src_id] : nullptr;
DeduceBlitImages(dst_info, src_info, dst_image, src_image);
if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
continue;
}
}
insert_images();
} while (has_deleted_images); } while (has_deleted_images);
if (GetFormatType(dst_info.format) != SurfaceType::ColorTexture) {
src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{});
dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{});
}
return BlitImages{ return BlitImages{
.dst_id = dst_id, .dst_id = dst_id,
.src_id = src_id, .src_id = src_id,

View File

@ -252,7 +252,8 @@ private:
/// Return a blit image pair from the given guest blit parameters /// Return a blit image pair from the given guest blit parameters
[[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src); const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy);
/// Find or create a sampler from a guest descriptor sampler /// Find or create a sampler from a guest descriptor sampler
[[nodiscard]] SamplerId FindSampler(const TSCEntry& config); [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);

View File

@ -1151,19 +1151,26 @@ bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
const ImageBase* src) { const ImageBase* src) {
bool is_resolve = false; const auto original_src_format = src_info.format;
if (src) { const auto original_dst_format = dst_info.format;
is_resolve = src->info.num_samples > 1; if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
src_info.num_samples = src->info.num_samples; src_info.format = src->info.format;
src_info.size.width = src->info.size.width;
src_info.size.height = src->info.size.height;
} }
if (dst) { if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
dst_info.num_samples = dst->info.num_samples; dst_info.format = dst->info.format;
dst_info.size.width = dst->info.size.width; }
dst_info.size.height = dst->info.size.height; if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
dst_info.format = src->info.format;
}
if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
if (src) {
if (GetFormatType(src->info.format) == SurfaceType::ColorTexture) {
dst_info.format = original_dst_format;
}
} else {
src_info.format = dst->info.format;
}
} }
ASSERT(!is_resolve || dst_info.format == src_info.format);
} }
u32 MapSizeBytes(const ImageBase& image) { u32 MapSizeBytes(const ImageBase& image) {

View File

@ -447,6 +447,7 @@ void Config::ReadMotionTouchValues() {
Settings::values.touch_from_button_map_index = std::clamp( Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
ReadBasicSetting(Settings::values.udp_input_servers); ReadBasicSetting(Settings::values.udp_input_servers);
ReadBasicSetting(Settings::values.enable_udp_controller);
} }
void Config::ReadCoreValues() { void Config::ReadCoreValues() {
@ -942,6 +943,7 @@ void Config::SaveMotionTouchValues() {
WriteBasicSetting(Settings::values.touch_device); WriteBasicSetting(Settings::values.touch_device);
WriteBasicSetting(Settings::values.touch_from_button_map_index); WriteBasicSetting(Settings::values.touch_from_button_map_index);
WriteBasicSetting(Settings::values.udp_input_servers); WriteBasicSetting(Settings::values.udp_input_servers);
WriteBasicSetting(Settings::values.enable_udp_controller);
qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {

View File

@ -130,6 +130,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
static_cast<float>(ui->mouse_panning_sensitivity->value()); static_cast<float>(ui->mouse_panning_sensitivity->value());
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
} }
void ConfigureInputAdvanced::LoadConfiguration() { void ConfigureInputAdvanced::LoadConfiguration() {
@ -160,6 +161,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
UpdateUIEnabled(); UpdateUIEnabled();
} }

View File

@ -2642,6 +2642,19 @@
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="enable_udp_controller">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Enable UDP controllers (not needed for motion)</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="mouse_panning"> <widget class="QCheckBox" name="mouse_panning">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
@ -2654,7 +2667,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="4" column="2">
<widget class="QSpinBox" name="mouse_panning_sensitivity"> <widget class="QSpinBox" name="mouse_panning_sensitivity">
<property name="toolTip"> <property name="toolTip">
<string>Mouse sensitivity</string> <string>Mouse sensitivity</string>
@ -2676,14 +2689,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="motion_touch"> <widget class="QLabel" name="motion_touch">
<property name="text"> <property name="text">
<string>Motion / Touch</string> <string>Motion / Touch</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="5" column="2">
<widget class="QPushButton" name="buttonMotionTouch"> <widget class="QPushButton" name="buttonMotionTouch">
<property name="text"> <property name="text">
<string>Configure</string> <string>Configure</string>

View File

@ -78,6 +78,30 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
return QObject::tr("Y"); return QObject::tr("Y");
case Common::Input::ButtonNames::ButtonStart: case Common::Input::ButtonNames::ButtonStart:
return QObject::tr("Start"); return QObject::tr("Start");
case Common::Input::ButtonNames::L1:
return QObject::tr("L1");
case Common::Input::ButtonNames::L2:
return QObject::tr("L2");
case Common::Input::ButtonNames::L3:
return QObject::tr("L3");
case Common::Input::ButtonNames::R1:
return QObject::tr("R1");
case Common::Input::ButtonNames::R2:
return QObject::tr("R2");
case Common::Input::ButtonNames::R3:
return QObject::tr("R3");
case Common::Input::ButtonNames::Circle:
return QObject::tr("Circle");
case Common::Input::ButtonNames::Cross:
return QObject::tr("Cross");
case Common::Input::ButtonNames::Square:
return QObject::tr("Square");
case Common::Input::ButtonNames::Triangle:
return QObject::tr("Triangle");
case Common::Input::ButtonNames::Share:
return QObject::tr("Share");
case Common::Input::ButtonNames::Options:
return QObject::tr("Options");
default: default:
return QObject::tr("[undefined]"); return QObject::tr("[undefined]");
} }