Skip to content

Commit

Permalink
fix(Performance): Reduce poll rate for most source devices.
Browse files Browse the repository at this point in the history
- Reduces the poll rate for evdev source devices and gyro's to 125Hz. Disables gyro on low power devices (AYANEO AIR, AYN Loki Zero).
- Adds fix for bugs caused by stopping/starting the same target device type. We no longer stop a running device if it is requested to start.
  • Loading branch information
pastaq committed Jul 3, 2024
1 parent 9750170 commit 113927d
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 37 deletions.
14 changes: 7 additions & 7 deletions rootfs/usr/share/inputplumber/devices/50-ayaneo_air.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ source_devices:
evdev:
name: AT Translated Set 2 keyboard
phys_path: isa0060/serio0/input0
- group: imu
iio:
name: i2c-BMI0160:00
mount_matrix:
x: [0, -1, 0]
y: [0, 0, -1]
z: [1, 0, 0]
#- group: imu #TODO:reenable after we switch from polling
# iio:
# name: i2c-BMI0160:00
# mount_matrix:
# x: [0, -1, 0]
# y: [0, 0, -1]
# z: [1, 0, 0]

# The target input device(s) that the virtual device profile can use
target_devices:
Expand Down
45 changes: 45 additions & 0 deletions rootfs/usr/share/inputplumber/devices/50-ayn_loki_max.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/composite_device_v1.json
# Schema version number
version: 1

# The type of configuration schema
kind: CompositeDevice

# Name of the composite device mapping
name: AYN Loki Max

# Only use this profile if *any* of the given matches matches. If this list is
# empty, then the source devices will *always* be checked.
# /sys/class/dmi/id/product_name
matches:
- dmi_data:
product_name: Loki Max
sys_vendor: ayn

# One or more source devices to combine into a single virtual device. The events
# from these devices will be watched and translated according to the key map.
source_devices:
- group: gamepad
evdev:
name: Microsoft X-Box 360 pad
phys_path: usb-0000:74:00.0-1/input0
- group: keyboard
evdev:
name: AT Translated Set 2 keyboard
phys_path: isa0060/serio0/input0
- group: imu
iio:
name: i2c-BMI0160:00
mount_matrix:
x: [1, 0, 0]
y: [0, 0, -1]
z: [0, 1, 0]

# The target input device(s) that the virtual device profile can use
target_devices:
- xb360
- mouse
- keyboard

# The ID of a device event mapping in the 'event_maps' folder
capability_map_id: ayn1
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,19 @@ version: 1
kind: CompositeDevice

# Name of the composite device mapping
name: AYN Loki
name: AYN Loki MiniPro

# Only use this profile if *any* of the given matches matches. If this list is
# empty, then the source devices will *always* be checked.
# /sys/class/dmi/id/product_name
matches:
- dmi_data:
product_name: Loki Max
sys_vendor: ayn
- dmi_data:
product_name: Loki MiniPro
sys_vendor: ayn
- dmi_data:
product_name: Loki Zero
sys_vendor: ayn

# One or more source devices to combine into a single virtual device. The events
# from these devices will be watched and translated according to the key map.
source_devices:
- group: gamepad
evdev:
name: Microsoft X-Box 360 pad
phys_path: usb-0000:74:00.0-1/input0
- group: gamepad
evdev:
name: Microsoft X-Box 360 pad
phys_path: usb-0000:04:00.3-4/input0
- group: gamepad
evdev:
name: Microsoft X-Box 360 pad
Expand Down
45 changes: 45 additions & 0 deletions rootfs/usr/share/inputplumber/devices/50-ayn_loki_zero.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/composite_device_v1.json
# Schema version number
version: 1

# The type of configuration schema
kind: CompositeDevice

# Name of the composite device mapping
name: AYN Loki Zero

# Only use this profile if *any* of the given matches matches. If this list is
# empty, then the source devices will *always* be checked.
# /sys/class/dmi/id/product_name
matches:
- dmi_data:
product_name: Loki MiniPro
sys_vendor: ayn

# One or more source devices to combine into a single virtual device. The events
# from these devices will be watched and translated according to the key map.
source_devices:
- group: gamepad
evdev:
name: Microsoft X-Box 360 pad
phys_path: usb-0000:04:00.3-4/input0
- group: keyboard
evdev:
name: AT Translated Set 2 keyboard
phys_path: isa0060/serio0/input0
#- group: imu #TODO:reenable after we switch from polling
# iio:
# name: i2c-BMI0160:00
# mount_matrix:
# x: [1, 0, 0]
# y: [0, 0, -1]
# z: [0, 1, 0]

# The target input device(s) that the virtual device profile can use
target_devices:
- xb360
- mouse
- keyboard

# The ID of a device event mapping in the 'event_maps' folder
capability_map_id: ayn1
15 changes: 8 additions & 7 deletions src/drivers/iio_imu/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl Driver {
accel_info,
gyro,
gyro_info,
sample_delay: Duration::from_micros(2500), //400Hz
sample_delay: Duration::from_millis(8), //125Hz
})
}

Expand Down Expand Up @@ -192,12 +192,13 @@ impl Driver {
/// events again. Uses the fastest frequency set between the currently set accelerometer and
/// gyroscope sample rates. Called automatically when the sample_rate is changed.
pub fn calculate_sample_delay(&self) -> Result<Duration, Box<dyn Error>> {
let accel_rate = self.get_sample_rate("accel").unwrap_or(1.0);
let gyro_rate = self.get_sample_rate("gyro").unwrap_or(1.0);
let mut sample_delay = 1.0 / accel_rate.max(gyro_rate);
if sample_delay <= 0.0 {
sample_delay = 0.0025;
}
//let accel_rate = self.get_sample_rate("accel").unwrap_or(1.0);
//let gyro_rate = self.get_sample_rate("gyro").unwrap_or(1.0);
//let mut sample_delay = 1.0 / accel_rate.max(gyro_rate);
let sample_delay = 0.008;
//if sample_delay <= 0.0 {
// sample_delay = 0.0025;
//}
log::debug!("Updated sample delay is: {sample_delay} seconds.");

Ok(Duration::from_secs_f64(sample_delay))
Expand Down
60 changes: 55 additions & 5 deletions src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1736,9 +1736,36 @@ impl CompositeDevice {
return Ok(());
}

// Stop all old target devices
let targets_to_stop = self.target_devices.clone();
for (path, target) in targets_to_stop.into_iter() {
// Identify which target devices are new
let mut device_types_to_start: Vec<String> = vec![];
for kind in device_types.iter() {
if self.target_kind_running(kind).await? {
log::debug!("Target device {kind} already running, nothing to do.");
continue;
}

device_types_to_start.push(kind.clone());
}

// Identify the targets that need to close
let mut targets_to_stop: HashMap<String, mpsc::Sender<TargetCommand>> = HashMap::new();
for (path, target) in self.target_devices.clone().into_iter() {
let (tx, mut rx) = mpsc::channel(1);
let cmd = TargetCommand::GetType(tx);
if let Err(e) = target.send(cmd).await {
return Err(format!("Failed to request target type: {e:?}").into());
}
let Some(target_type) = rx.recv().await else {
return Err("Failed to receive target type".into());
};
if !device_types.contains(&target_type) {
log::debug!("Target device {path} not in new devices list. Adding to stop list.");
targets_to_stop.insert(path, target);
}
}

// Stop all old target devices that aren't going to persist
for (path, target) in targets_to_stop.clone().into_iter() {
log::debug!("Stopping old target device: {path}");
self.target_devices.remove(&path);
for (_, target_devices) in self.target_devices_by_capability.iter_mut() {
Expand All @@ -1753,8 +1780,8 @@ impl CompositeDevice {
return Err("No composite device DBus path found".into());
};

// Create target devices using the input manager
for kind in device_types {
// Create new target devices using the input manager
for kind in device_types_to_start {
log::debug!("Requesting to create device: {kind}");
let (sender, mut receiver) = mpsc::channel(1);
self.manager
Expand Down Expand Up @@ -1803,6 +1830,29 @@ impl CompositeDevice {
Ok(())
}

// Deterimines if a given target device kind is already running
async fn target_kind_running(&self, kind: &str) -> Result<bool, Box<dyn Error>> {
// TODO: Save this on the DS5 target device so we can properly look it up.
let kind = match kind {
"ds5" => "ds5_edge",
_ => kind,
};
for target in self.target_devices.values() {
let (tx, mut rx) = mpsc::channel(1);
let cmd = TargetCommand::GetType(tx);
if let Err(e) = target.send(cmd).await {
return Err(format!("Failed to request target type: {e:?}").into());
}
let Some(target_type) = rx.recv().await else {
return Err("Failed to receive target type".into());
};
if kind == target_type {
return Ok(true);
}
}
Ok(false)
}

// Get the capabilities of all target devices
async fn get_target_capabilities(&self) -> Result<HashSet<Capability>, Box<dyn Error>> {
let mut target_caps = HashSet::new();
Expand Down
2 changes: 1 addition & 1 deletion src/input/source/evdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use super::SourceCommand;
/// Size of the [SourceCommand] buffer for receiving output events
const BUFFER_SIZE: usize = 2048;
/// How long to sleep before polling for events.
const POLL_RATE: Duration = Duration::from_micros(1666);
const POLL_RATE: Duration = Duration::from_millis(8);

/// [EventDevice] represents an input device using the input subsystem.
#[derive(Debug)]
Expand Down
5 changes: 5 additions & 0 deletions src/input/target/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ impl DBusDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("dbus".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
};
}
Expand Down
6 changes: 6 additions & 0 deletions src/input/target/dualsense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ impl DualSenseDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("ds5-edge".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}

TargetCommand::Stop => return Err("Device stopped".into()),
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/input/target/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ impl KeyboardDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("keyboard".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
};
}
Expand Down
1 change: 1 addition & 0 deletions src/input/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ pub enum TargetCommand {
WriteEvent(NativeEvent),
SetCompositeDevice(CompositeDeviceClient),
GetCapabilities(Sender<Vec<Capability>>),
GetType(Sender<String>),
Stop,
}
5 changes: 5 additions & 0 deletions src/input/target/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ impl MouseDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("mouse".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/input/target/steam_deck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ impl SteamDeckDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("steam-deck".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/input/target/touchscreen_fts3528.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ impl Fts3528TouchscreenDevice {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("touchscreen-fts3528".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
};
}
Expand Down
7 changes: 6 additions & 1 deletion src/input/target/xb360.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use super::TargetCommand;
/// Size of the [TargetCommand] buffer for receiving input events
const BUFFER_SIZE: usize = 2048;
/// How long to sleep before polling for events.
const POLL_RATE: Duration = Duration::from_micros(1666);
const POLL_RATE: Duration = Duration::from_millis(8); //125Hz is standard for xbox controllers

#[derive(Debug)]
pub struct XBox360Controller {
Expand Down Expand Up @@ -127,6 +127,11 @@ impl XBox360Controller {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("xb360".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/input/target/xbox_elite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use super::TargetCommand;
/// Size of the [TargetCommand] buffer for receiving input events
const BUFFER_SIZE: usize = 2048;
/// How long to sleep before polling for events.
const POLL_RATE: Duration = Duration::from_micros(1666);
const POLL_RATE: Duration = Duration::from_millis(8); //125Hz is standard for xbox controllers

#[derive(Debug)]
pub struct XboxEliteController {
Expand Down Expand Up @@ -127,6 +127,11 @@ impl XboxEliteController {
log::error!("Failed to send target capabilities: {e:?}");
}
}
TargetCommand::GetType(tx) => {
if let Err(e) = tx.send("xbox-elite".to_string()).await {
log::error!("Failed to send target type: {e:?}");
}
}
TargetCommand::Stop => break,
}
}
Expand Down

0 comments on commit 113927d

Please sign in to comment.