feat(objects): async tracked abstraction
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::{
|
||||
input::{INPUT_HANDLER_REGISTRY, InputDataType, InputHandler, InputMethod, Tip},
|
||||
spatial::Spatial,
|
||||
},
|
||||
objects::{ObjectHandle, SpatialRef, Tracked},
|
||||
objects::{AsyncTracked, ObjectHandle, SpatialRef, Tracked},
|
||||
};
|
||||
use bevy::{asset::Handle, ecs::resource::Resource};
|
||||
use bevy::{math::Affine3, prelude::*};
|
||||
@@ -268,7 +268,7 @@ pub struct OxrControllerInput {
|
||||
model_part: Arc<ModelPart>,
|
||||
capture_manager: CaptureManager,
|
||||
datamap: ControllerDatamap,
|
||||
tracked: ObjectHandle<Tracked>,
|
||||
tracked: AsyncTracked,
|
||||
space: Option<XrSpace>,
|
||||
}
|
||||
impl OxrControllerInput {
|
||||
@@ -279,7 +279,7 @@ impl OxrControllerInput {
|
||||
HandSide::Right => "right",
|
||||
};
|
||||
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 node = spatial.node().unwrap();
|
||||
node.set_enabled(false);
|
||||
@@ -314,13 +314,7 @@ impl OxrControllerInput {
|
||||
if let Some(node) = self.input.spatial.node() {
|
||||
node.set_enabled(enabled);
|
||||
}
|
||||
tokio::spawn({
|
||||
// this is suboptimal since it probably allocates a fresh string every frame
|
||||
let handle = self.tracked.clone();
|
||||
async move {
|
||||
handle.set_tracked(enabled).await;
|
||||
}
|
||||
});
|
||||
self.tracked.set_tracked(enabled);
|
||||
}
|
||||
fn update(
|
||||
&mut self,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::nodes::{
|
||||
input::{Hand, InputMethod, Joint},
|
||||
spatial::Spatial,
|
||||
};
|
||||
use crate::objects::{ObjectHandle, SpatialRef, Tracked};
|
||||
use crate::objects::{AsyncTracked, ObjectHandle, SpatialRef, Tracked};
|
||||
use crate::{BevyMaterial, DbusConnection, ObjectRegistryRes, PreFrameWait};
|
||||
use bevy::prelude::Transform as BevyTransform;
|
||||
use bevy::prelude::*;
|
||||
@@ -52,23 +52,14 @@ fn update_hands(
|
||||
joints_query: Query<&XrHandBoneEntities>,
|
||||
) {
|
||||
let (Some(session), Some(state), Some(ref_space)) = (session, state, ref_space) else {
|
||||
tokio::task::spawn({
|
||||
let left = hands.left.tracked.clone();
|
||||
let right = hands.right.tracked.clone();
|
||||
async move {
|
||||
left.set_tracked(false);
|
||||
right.set_tracked(false);
|
||||
}
|
||||
});
|
||||
hands.left.tracked.set_tracked(false);
|
||||
hands.right.tracked.set_tracked(false);
|
||||
return;
|
||||
};
|
||||
let get_joints = |hand: &mut OxrHandInput| -> Option<openxr::HandJointLocations> {
|
||||
let Some(tracker) = hand.tracker.as_ref() else {
|
||||
hand.input.spatial.node().unwrap().set_enabled(false);
|
||||
let handle = hand.tracked.clone();
|
||||
tokio::task::spawn(async move {
|
||||
handle.set_tracked(false);
|
||||
});
|
||||
hand.tracked.set_tracked(false);
|
||||
return None;
|
||||
};
|
||||
// this won't be correct with pipelined rendering
|
||||
@@ -182,7 +173,7 @@ pub struct OxrHandInput {
|
||||
input: Arc<InputMethod>,
|
||||
capture_manager: CaptureManager,
|
||||
datamap: HandDatamap,
|
||||
tracked: ObjectHandle<Tracked>,
|
||||
tracked: AsyncTracked,
|
||||
tracker: Option<openxr::HandTracker>,
|
||||
captured: bool,
|
||||
material: Handle<BevyMaterial>,
|
||||
@@ -201,7 +192,7 @@ impl OxrHandInput {
|
||||
HandSide::Right => "right",
|
||||
} + "/palm"),
|
||||
);
|
||||
let tracked = Tracked::new(
|
||||
let tracked = AsyncTracked::new(
|
||||
connection,
|
||||
&("/org/stardustxr/Hand/".to_string()
|
||||
+ match side {
|
||||
@@ -243,13 +234,7 @@ impl OxrHandInput {
|
||||
if let Some(node) = self.input.spatial.node() {
|
||||
node.set_enabled(enabled);
|
||||
}
|
||||
tokio::spawn({
|
||||
// this is suboptimal since it probably allocates a fresh string every frame
|
||||
let handle = self.tracked.clone();
|
||||
async move {
|
||||
handle.set_tracked(enabled).await;
|
||||
}
|
||||
});
|
||||
self.tracked.set_tracked(enabled);
|
||||
}
|
||||
fn update(
|
||||
&mut self,
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
marker::PhantomData,
|
||||
sync::{Arc, atomic::Ordering},
|
||||
};
|
||||
use tokio::{sync::mpsc, task::AbortHandle};
|
||||
use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjectPath};
|
||||
|
||||
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);
|
||||
impl 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 super::{ObjectHandle, SpatialRef, Tracked};
|
||||
use super::{AsyncTracked, ObjectHandle, SpatialRef, Tracked};
|
||||
|
||||
pub struct PlaySpacePlugin;
|
||||
impl Plugin for PlaySpacePlugin {
|
||||
@@ -31,7 +31,7 @@ impl Plugin for PlaySpacePlugin {
|
||||
fn setup(connection: Res<DbusConnection>, mut cmds: Commands) {
|
||||
let (spatial, spatial_handle) = SpatialRef::create(&connection, "/org/stardustxr/PlaySpace");
|
||||
// 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 play_space_data = Arc::new(RwLock::default());
|
||||
tokio::task::spawn({
|
||||
@@ -80,12 +80,7 @@ fn update(
|
||||
(session, stage, ref_space, state)
|
||||
else {
|
||||
play_space.bounds.write().drain(..);
|
||||
tokio::task::spawn({
|
||||
let handle = play_space.tracked_handle.clone();
|
||||
async move {
|
||||
handle.set_tracked(false).await;
|
||||
}
|
||||
});
|
||||
play_space.tracked_handle.set_tracked(false);
|
||||
|
||||
play_space
|
||||
.spatial
|
||||
@@ -103,12 +98,7 @@ fn update(
|
||||
| SpaceLocationFlags::ORIENTATION_VALID
|
||||
| SpaceLocationFlags::ORIENTATION_TRACKED,
|
||||
);
|
||||
tokio::task::spawn({
|
||||
let handle = play_space.tracked_handle.clone();
|
||||
async move {
|
||||
handle.set_tracked(is_tracked).await;
|
||||
}
|
||||
});
|
||||
play_space.tracked_handle.set_tracked(is_tracked);
|
||||
if is_tracked {
|
||||
play_space
|
||||
.spatial
|
||||
@@ -140,7 +130,7 @@ fn update(
|
||||
pub struct PlaySpace {
|
||||
spatial: Arc<Spatial>,
|
||||
_spatial_handle: ObjectHandle<SpatialRef>,
|
||||
tracked_handle: ObjectHandle<Tracked>,
|
||||
tracked_handle: AsyncTracked,
|
||||
bounds: Arc<RwLock<Vec<(f64, f64)>>>,
|
||||
}
|
||||
pub struct PlaySpaceBounds(Arc<RwLock<Vec<(f64, f64)>>>);
|
||||
|
||||
Reference in New Issue
Block a user