Skip to content

Commit 9907312

Browse files
committed
input: implement action set priorities
closes #22
1 parent c5453d6 commit 9907312

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

src/input.rs

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,19 @@ use custom_bindings::{BoolActionData, FloatActionData};
2020
use legacy::{setup_legacy_bindings, LegacyActionData};
2121
use log::{debug, info, trace, warn};
2222
use openvr::{self as vr, space_relation_to_openvr_pose};
23-
use openxr as xr;
23+
use openxr::{
24+
self as xr,
25+
sys::{self, ActiveActionSetPrioritiesEXT, ActiveActionSetPriorityEXT},
26+
};
2427
use slotmap::{new_key_type, Key, KeyData, SecondaryMap, SlotMap};
25-
use std::collections::HashMap;
2628
use std::ffi::{c_char, CStr, CString};
2729
use std::mem::ManuallyDrop;
2830
use std::path::PathBuf;
2931
use std::sync::{
3032
atomic::{AtomicU32, Ordering},
3133
Arc, Mutex, OnceLock, RwLock,
3234
};
35+
use std::{collections::HashMap, ptr};
3336

3437
new_key_type! {
3538
struct InputSourceKey;
@@ -819,41 +822,111 @@ impl<C: openxr_data::Compositor> vr::IVRInput010_Interface for Input<C> {
819822
let active_sets =
820823
unsafe { std::slice::from_raw_parts(active_sets, active_set_count as usize) };
821824

822-
if active_sets
823-
.iter()
824-
.any(|set| set.ulRestrictedToDevice != vr::k_ulInvalidInputValueHandle)
825-
{
826-
crate::warn_once!("Per device action set restriction is not implemented yet.");
827-
}
828-
829825
let data = self.openxr.session_data.get();
830826
let Some(actions) = data.input_data.get_loaded_actions() else {
831827
return vr::EVRInputError::InvalidParam;
832828
};
833829

834830
let set_map = self.set_map.read().unwrap();
831+
832+
// SteamVR uses an int32, OpenXR a uint32
833+
// Not sure what the correct behavior is here, does SteamVR allow negative values there? Does -1 mean it doesn't override legacy input?
834+
let min_priority = active_sets.iter().map(|set| set.nPriority).min().unwrap();
835+
836+
// +1 to make everything override legacy input
837+
let priority_offset = if min_priority < 0 {
838+
-min_priority + 1
839+
} else {
840+
1
841+
};
842+
835843
let mut sync_sets = Vec::with_capacity(active_sets.len() + 1);
844+
let mut priorities = Vec::with_capacity(active_sets.len() + 1);
836845
{
837846
tracy_span!("UpdateActionState generate active sets");
838847
for set in active_sets {
848+
let priority = (set.nPriority + priority_offset) as u32;
849+
let mut path = xr::Path::NULL;
850+
851+
// Restrict to device
852+
match self.subaction_path_from_handle(set.ulRestrictedToDevice) {
853+
Some(new_path) => {
854+
path = new_path;
855+
// Handle secondary action set
856+
if set.ulSecondaryActionSet != vr::k_ulInvalidInputValueHandle {
857+
let path = if path == self.openxr.left_hand.subaction_path {
858+
self.openxr.right_hand.subaction_path
859+
} else {
860+
self.openxr.left_hand.subaction_path
861+
};
862+
let key =
863+
ActionSetKey::from(KeyData::from_ffi(set.ulSecondaryActionSet));
864+
let name = set_map.get(key);
865+
let Some(set) = actions.sets.get(key) else {
866+
debug!(
867+
"Application passed invalid secondary action set key: {key:?} ({name:?})"
868+
);
869+
return vr::EVRInputError::InvalidHandle;
870+
};
871+
debug!("Activating secondary set {}", name.unwrap());
872+
sync_sets.push(xr::ActiveActionSet::with_subaction(set, path));
873+
// I assume the secondary action set must have the same priority as the set that activates it
874+
priorities.push(ActiveActionSetPriorityEXT {
875+
action_set: set.as_raw(),
876+
priority_override: priority,
877+
});
878+
}
879+
}
880+
None => (),
881+
};
882+
839883
let key = ActionSetKey::from(KeyData::from_ffi(set.ulActionSet));
840884
let name = set_map.get(key);
841885
let Some(set) = actions.sets.get(key) else {
842886
debug!("Application passed invalid action set key: {key:?} ({name:?})");
843887
return vr::EVRInputError::InvalidHandle;
844888
};
845889
debug!("Activating set {}", name.unwrap());
846-
sync_sets.push(set.into());
890+
sync_sets.push(xr::ActiveActionSet::with_subaction(set, path));
891+
priorities.push(ActiveActionSetPriorityEXT {
892+
action_set: set.as_raw(),
893+
priority_override: priority,
894+
});
847895
}
848896

849897
let legacy = data.input_data.legacy_actions.get().unwrap();
850898
sync_sets.push(xr::ActiveActionSet::new(&legacy.set));
899+
priorities.push(ActiveActionSetPriorityEXT {
900+
action_set: legacy.set.as_raw(),
901+
priority_override: 0,
902+
});
851903
self.legacy_packet_num.fetch_add(1, Ordering::Relaxed);
852904
}
853905

854906
{
855907
tracy_span!("xrSyncActions");
856-
data.session.sync_actions(&sync_sets).unwrap();
908+
let info = sys::ActionsSyncInfo {
909+
ty: sys::ActionsSyncInfo::TYPE,
910+
next: &ActiveActionSetPrioritiesEXT {
911+
ty: sys::ActiveActionSetPrioritiesEXT::TYPE,
912+
next: ptr::null(),
913+
action_set_priority_count: priorities.len() as u32,
914+
action_set_priorities: priorities.as_ptr(),
915+
} as *const _ as *const _,
916+
count_active_action_sets: sync_sets.len() as u32,
917+
active_action_sets: sync_sets.as_ptr() as _,
918+
};
919+
unsafe {
920+
let result =
921+
(&self.openxr.instance.fp().sync_actions)(data.session.as_raw(), &info);
922+
if result.into_raw() >= 0 {
923+
Ok(result)
924+
} else {
925+
Err(result)
926+
}
927+
}
928+
.unwrap();
929+
857930
// The IPC client won't be running during tests
858931
#[cfg(not(test))]
859932
self.skeletal_input_ipc

src/openxr_data.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl<C: Compositor> OpenXrData<C> {
8787
exts.khr_opengl_enable = supported_exts.khr_opengl_enable;
8888
exts.ext_hand_tracking = supported_exts.ext_hand_tracking;
8989
exts.khr_visibility_mask = supported_exts.khr_visibility_mask;
90+
exts.ext_active_action_set_priority = supported_exts.ext_active_action_set_priority;
9091

9192
let instance = entry
9293
.create_instance(

0 commit comments

Comments
 (0)