feat(objects): async tracked abstraction
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::{
|
|||||||
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler, InputMethod, Tip},
|
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler, InputMethod, Tip},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
},
|
},
|
||||||
objects::{ObjectHandle, SpatialRef, Tracked},
|
objects::{AsyncTracked, ObjectHandle, SpatialRef, Tracked},
|
||||||
};
|
};
|
||||||
use bevy::{asset::Handle, ecs::resource::Resource};
|
use bevy::{asset::Handle, ecs::resource::Resource};
|
||||||
use bevy::{math::Affine3, prelude::*};
|
use bevy::{math::Affine3, prelude::*};
|
||||||
@@ -268,7 +268,7 @@ pub struct OxrControllerInput {
|
|||||||
model_part: Arc<ModelPart>,
|
model_part: Arc<ModelPart>,
|
||||||
capture_manager: CaptureManager,
|
capture_manager: CaptureManager,
|
||||||
datamap: ControllerDatamap,
|
datamap: ControllerDatamap,
|
||||||
tracked: ObjectHandle<Tracked>,
|
tracked: AsyncTracked,
|
||||||
space: Option<XrSpace>,
|
space: Option<XrSpace>,
|
||||||
}
|
}
|
||||||
impl OxrControllerInput {
|
impl OxrControllerInput {
|
||||||
@@ -279,7 +279,7 @@ impl OxrControllerInput {
|
|||||||
HandSide::Right => "right",
|
HandSide::Right => "right",
|
||||||
};
|
};
|
||||||
let (spatial, object_handle) = SpatialRef::create(connection, &path);
|
let (spatial, object_handle) = SpatialRef::create(connection, &path);
|
||||||
let tracked = Tracked::new(connection, &path);
|
let tracked = AsyncTracked::new(connection, &path);
|
||||||
let tip = InputDataType::Tip(Tip::default());
|
let tip = InputDataType::Tip(Tip::default());
|
||||||
let node = spatial.node().unwrap();
|
let node = spatial.node().unwrap();
|
||||||
node.set_enabled(false);
|
node.set_enabled(false);
|
||||||
@@ -314,13 +314,7 @@ impl OxrControllerInput {
|
|||||||
if let Some(node) = self.input.spatial.node() {
|
if let Some(node) = self.input.spatial.node() {
|
||||||
node.set_enabled(enabled);
|
node.set_enabled(enabled);
|
||||||
}
|
}
|
||||||
tokio::spawn({
|
self.tracked.set_tracked(enabled);
|
||||||
// this is suboptimal since it probably allocates a fresh string every frame
|
|
||||||
let handle = self.tracked.clone();
|
|
||||||
async move {
|
|
||||||
handle.set_tracked(enabled).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::nodes::{
|
|||||||
input::{Hand, InputMethod, Joint},
|
input::{Hand, InputMethod, Joint},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
};
|
};
|
||||||
use crate::objects::{ObjectHandle, SpatialRef, Tracked};
|
use crate::objects::{AsyncTracked, ObjectHandle, SpatialRef, Tracked};
|
||||||
use crate::{BevyMaterial, DbusConnection, ObjectRegistryRes, PreFrameWait};
|
use crate::{BevyMaterial, DbusConnection, ObjectRegistryRes, PreFrameWait};
|
||||||
use bevy::prelude::Transform as BevyTransform;
|
use bevy::prelude::Transform as BevyTransform;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -52,23 +52,14 @@ fn update_hands(
|
|||||||
joints_query: Query<&XrHandBoneEntities>,
|
joints_query: Query<&XrHandBoneEntities>,
|
||||||
) {
|
) {
|
||||||
let (Some(session), Some(state), Some(ref_space)) = (session, state, ref_space) else {
|
let (Some(session), Some(state), Some(ref_space)) = (session, state, ref_space) else {
|
||||||
tokio::task::spawn({
|
hands.left.tracked.set_tracked(false);
|
||||||
let left = hands.left.tracked.clone();
|
hands.right.tracked.set_tracked(false);
|
||||||
let right = hands.right.tracked.clone();
|
|
||||||
async move {
|
|
||||||
left.set_tracked(false);
|
|
||||||
right.set_tracked(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let get_joints = |hand: &mut OxrHandInput| -> Option<openxr::HandJointLocations> {
|
let get_joints = |hand: &mut OxrHandInput| -> Option<openxr::HandJointLocations> {
|
||||||
let Some(tracker) = hand.tracker.as_ref() else {
|
let Some(tracker) = hand.tracker.as_ref() else {
|
||||||
hand.input.spatial.node().unwrap().set_enabled(false);
|
hand.input.spatial.node().unwrap().set_enabled(false);
|
||||||
let handle = hand.tracked.clone();
|
hand.tracked.set_tracked(false);
|
||||||
tokio::task::spawn(async move {
|
|
||||||
handle.set_tracked(false);
|
|
||||||
});
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
// this won't be correct with pipelined rendering
|
// this won't be correct with pipelined rendering
|
||||||
@@ -182,7 +173,7 @@ pub struct OxrHandInput {
|
|||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
capture_manager: CaptureManager,
|
capture_manager: CaptureManager,
|
||||||
datamap: HandDatamap,
|
datamap: HandDatamap,
|
||||||
tracked: ObjectHandle<Tracked>,
|
tracked: AsyncTracked,
|
||||||
tracker: Option<openxr::HandTracker>,
|
tracker: Option<openxr::HandTracker>,
|
||||||
captured: bool,
|
captured: bool,
|
||||||
material: Handle<BevyMaterial>,
|
material: Handle<BevyMaterial>,
|
||||||
@@ -201,7 +192,7 @@ impl OxrHandInput {
|
|||||||
HandSide::Right => "right",
|
HandSide::Right => "right",
|
||||||
} + "/palm"),
|
} + "/palm"),
|
||||||
);
|
);
|
||||||
let tracked = Tracked::new(
|
let tracked = AsyncTracked::new(
|
||||||
connection,
|
connection,
|
||||||
&("/org/stardustxr/Hand/".to_string()
|
&("/org/stardustxr/Hand/".to_string()
|
||||||
+ match side {
|
+ match side {
|
||||||
@@ -243,13 +234,7 @@ impl OxrHandInput {
|
|||||||
if let Some(node) = self.input.spatial.node() {
|
if let Some(node) = self.input.spatial.node() {
|
||||||
node.set_enabled(enabled);
|
node.set_enabled(enabled);
|
||||||
}
|
}
|
||||||
tokio::spawn({
|
self.tracked.set_tracked(enabled);
|
||||||
// this is suboptimal since it probably allocates a fresh string every frame
|
|
||||||
let handle = self.tracked.clone();
|
|
||||||
async move {
|
|
||||||
handle.set_tracked(enabled).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use std::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
sync::{Arc, atomic::Ordering},
|
sync::{Arc, atomic::Ordering},
|
||||||
};
|
};
|
||||||
|
use tokio::{sync::mpsc, task::AbortHandle};
|
||||||
use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjectPath};
|
use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjectPath};
|
||||||
|
|
||||||
pub mod hmd;
|
pub mod hmd;
|
||||||
@@ -43,6 +44,48 @@ impl<I: Interface> Drop for ObjectHandle<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper around ObjectHandle<Tracked> that batches async updates
|
||||||
|
/// instead of spawning a tokio task for each state change
|
||||||
|
pub struct AsyncTracked {
|
||||||
|
pub sender: mpsc::UnboundedSender<bool>,
|
||||||
|
pub _handle: ObjectHandle<Tracked>,
|
||||||
|
pub _abort_handle: AbortHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncTracked {
|
||||||
|
pub fn new(connection: &Connection, path: &str) -> Self {
|
||||||
|
let handle = Tracked::new(connection, path);
|
||||||
|
let (sender, mut receiver) = mpsc::unbounded_channel::<bool>();
|
||||||
|
|
||||||
|
// Spawn a single long-running task that processes state updates
|
||||||
|
let task = tokio::task::spawn({
|
||||||
|
let handle = handle.clone();
|
||||||
|
async move {
|
||||||
|
while let Some(is_tracked) = receiver.recv().await {
|
||||||
|
let _ = handle.set_tracked(is_tracked).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sender,
|
||||||
|
_handle: handle,
|
||||||
|
_abort_handle: task.abort_handle(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tracked(&self, is_tracked: bool) {
|
||||||
|
// Just send over channel instead of spawning a task
|
||||||
|
let _ = self.sender.send(is_tracked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AsyncTracked {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self._abort_handle.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SpatialRef(u64, OwnedNode);
|
pub struct SpatialRef(u64, OwnedNode);
|
||||||
impl SpatialRef {
|
impl SpatialRef {
|
||||||
pub fn create(connection: &Connection, path: &str) -> (Arc<Spatial>, ObjectHandle<SpatialRef>) {
|
pub fn create(connection: &Connection, path: &str) -> (Arc<Spatial>, ObjectHandle<SpatialRef>) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use zbus::{Connection, ObjectServer, interface};
|
|||||||
|
|
||||||
use crate::{DbusConnection, PreFrameWait, nodes::spatial::Spatial};
|
use crate::{DbusConnection, PreFrameWait, nodes::spatial::Spatial};
|
||||||
|
|
||||||
use super::{ObjectHandle, SpatialRef, Tracked};
|
use super::{AsyncTracked, ObjectHandle, SpatialRef, Tracked};
|
||||||
|
|
||||||
pub struct PlaySpacePlugin;
|
pub struct PlaySpacePlugin;
|
||||||
impl Plugin for PlaySpacePlugin {
|
impl Plugin for PlaySpacePlugin {
|
||||||
@@ -31,7 +31,7 @@ impl Plugin for PlaySpacePlugin {
|
|||||||
fn setup(connection: Res<DbusConnection>, mut cmds: Commands) {
|
fn setup(connection: Res<DbusConnection>, mut cmds: Commands) {
|
||||||
let (spatial, spatial_handle) = SpatialRef::create(&connection, "/org/stardustxr/PlaySpace");
|
let (spatial, spatial_handle) = SpatialRef::create(&connection, "/org/stardustxr/PlaySpace");
|
||||||
// the OpenXR session might not exist quite yet
|
// the OpenXR session might not exist quite yet
|
||||||
let tracked = Tracked::new(&connection, "/org/stardustxr/PlaySpace");
|
let tracked = AsyncTracked::new(&connection, "/org/stardustxr/PlaySpace");
|
||||||
let dbus_connection = connection.clone();
|
let dbus_connection = connection.clone();
|
||||||
let play_space_data = Arc::new(RwLock::default());
|
let play_space_data = Arc::new(RwLock::default());
|
||||||
tokio::task::spawn({
|
tokio::task::spawn({
|
||||||
@@ -80,12 +80,7 @@ fn update(
|
|||||||
(session, stage, ref_space, state)
|
(session, stage, ref_space, state)
|
||||||
else {
|
else {
|
||||||
play_space.bounds.write().drain(..);
|
play_space.bounds.write().drain(..);
|
||||||
tokio::task::spawn({
|
play_space.tracked_handle.set_tracked(false);
|
||||||
let handle = play_space.tracked_handle.clone();
|
|
||||||
async move {
|
|
||||||
handle.set_tracked(false).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
play_space
|
play_space
|
||||||
.spatial
|
.spatial
|
||||||
@@ -103,12 +98,7 @@ fn update(
|
|||||||
| SpaceLocationFlags::ORIENTATION_VALID
|
| SpaceLocationFlags::ORIENTATION_VALID
|
||||||
| SpaceLocationFlags::ORIENTATION_TRACKED,
|
| SpaceLocationFlags::ORIENTATION_TRACKED,
|
||||||
);
|
);
|
||||||
tokio::task::spawn({
|
play_space.tracked_handle.set_tracked(is_tracked);
|
||||||
let handle = play_space.tracked_handle.clone();
|
|
||||||
async move {
|
|
||||||
handle.set_tracked(is_tracked).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if is_tracked {
|
if is_tracked {
|
||||||
play_space
|
play_space
|
||||||
.spatial
|
.spatial
|
||||||
@@ -140,7 +130,7 @@ fn update(
|
|||||||
pub struct PlaySpace {
|
pub struct PlaySpace {
|
||||||
spatial: Arc<Spatial>,
|
spatial: Arc<Spatial>,
|
||||||
_spatial_handle: ObjectHandle<SpatialRef>,
|
_spatial_handle: ObjectHandle<SpatialRef>,
|
||||||
tracked_handle: ObjectHandle<Tracked>,
|
tracked_handle: AsyncTracked,
|
||||||
bounds: Arc<RwLock<Vec<(f64, f64)>>>,
|
bounds: Arc<RwLock<Vec<(f64, f64)>>>,
|
||||||
}
|
}
|
||||||
pub struct PlaySpaceBounds(Arc<RwLock<Vec<(f64, f64)>>>);
|
pub struct PlaySpaceBounds(Arc<RwLock<Vec<(f64, f64)>>>);
|
||||||
|
|||||||
Reference in New Issue
Block a user