refactor: improve performance a lot

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2024-12-27 05:42:21 +01:00
parent 19367927a8
commit 2efdbec2ca
15 changed files with 328 additions and 72 deletions

View File

@@ -9,6 +9,11 @@ license = "GPLv2"
repository = "https://github.com/StardustXR/stardust-xr-server/"
homepage = "https://stardustxr.org"
[patch.crates-io]
tracing-tracy = { git = "https://github.com/nagisa/rust_tracy_client", tag = "tracy-client-v0.17.6" }
tracy-client = { git = "https://github.com/nagisa/rust_tracy_client", tag = "tracy-client-v0.17.6" }
tracy-client-sys = { git = "https://github.com/nagisa/rust_tracy_client", tag = "tracy-client-v0.17.6" }
[workspace]
members = ["codegen"]
@@ -46,7 +51,12 @@ auto_link_exclude_list = [
]
[dependencies]
bevy = { version = "0.15", features = ["wayland", "mp3", "wav"] }
bevy = { version = "0.15", features = [
"wayland",
"mp3",
"wav",
"trace_tracy",
] }
bevy_mod_xr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }
bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }
bevy_xr_utils = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }

View File

@@ -49,6 +49,93 @@ pub fn codegen_item_panel_protocol(_input: proc_macro::TokenStream) -> proc_macr
codegen_protocol(ITEM_PANEL_PROTOCOL)
}
#[proc_macro]
pub fn codegen_id_to_name_functions(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut aspect_map: Vec<(u64, String)> = Vec::new();
let mut method_map: Vec<(u64, String)> = Vec::new();
let mut signal_map: Vec<(u64, String)> = Vec::new();
let protocols = [
ROOT_PROTOCOL,
NODE_PROTOCOL,
SPATIAL_PROTOCOL,
FIELD_PROTOCOL,
AUDIO_PROTOCOL,
DRAWABLE_PROTOCOL,
INPUT_PROTOCOL,
ITEM_PROTOCOL,
ITEM_CAMERA_PROTOCOL,
ITEM_PANEL_PROTOCOL,
];
for aspect in protocols
.into_iter()
.filter_map(|p| Protocol::parse(p).ok())
.flat_map(|v| v.aspects)
{
aspect_map.push((aspect.id, aspect.name));
for m in aspect.members.into_iter() {
match m._type {
MemberType::Signal => &mut signal_map,
MemberType::Method => &mut method_map,
}
.push((m.opcode, m.name));
}
}
let aspect_id_to_name = aspect_map
.iter()
.map(|(id, name)| {
quote! {
#id => #name,
}
})
.reduce(fold_tokens);
let aspect_id_to_name_fn = quote! {
pub const fn aspect_id_to_name(id: u64) -> &'static str {
match id {
#aspect_id_to_name
_ => "Unknown"
}
}
};
let method_id_to_name = method_map
.iter()
.map(|(id, name)| {
quote! {
#id => #name,
}
})
.reduce(fold_tokens);
let method_id_to_name_fn = quote! {
pub const fn method_id_to_name(id: u64) -> &'static str {
match id {
#method_id_to_name
_ => "Unknown"
}
}
};
let signal_id_to_name = signal_map
.iter()
.map(|(id, name)| {
quote! {
#id => #name,
}
})
.reduce(fold_tokens);
let signal_id_to_name_fn = quote! {
pub const fn signal_id_to_name(id: u64) -> &'static str {
match id {
#signal_id_to_name
_ => "Unknown"
}
}
};
quote! {
#aspect_id_to_name_fn
#method_id_to_name_fn
#signal_id_to_name_fn
}.into()
}
fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream {
let protocol = Protocol::parse(protocol).unwrap();
let interface = protocol

View File

@@ -71,9 +71,10 @@ fn yeet_entities(
query: Query<Entity, With<TemporaryEntity>>,
reader: Res<DestroyEntityReader>,
) {
query
.iter()
.for_each(|e| cmds.entity(e).despawn_recursive());
query.iter().for_each(|e| {
info!("yeeting component entities");
cmds.entity(e).despawn_recursive();
});
reader
.0
.try_iter()

View File

@@ -7,3 +7,4 @@ pub mod registry;
pub mod resource;
pub mod scenegraph;
pub mod task;
pub mod queued_mutex;

119
src/core/queued_mutex.rs Normal file
View File

@@ -0,0 +1,119 @@
use std::{
ops::{Deref, DerefMut},
sync::atomic::Ordering,
};
use parking_lot::{
lock_api::{MutexGuard, RwLockReadGuard},
Mutex, RawMutex, RawRwLock, RwLock,
};
use portable_atomic::AtomicU8;
pub struct QueuedMutex<T: Clone> {
mutable: Mutex<Option<T>>,
readers: AtomicU8,
inner: RwLock<T>,
}
impl<T: Clone + Default> Default for QueuedMutex<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: Clone> QueuedMutex<T> {
pub const fn new(value: T) -> QueuedMutex<T> {
Self {
mutable: Mutex::new(None),
readers: AtomicU8::new(0),
inner: RwLock::new(value),
}
}
pub fn lock(&self) -> QueuedMutexLockGuard<'_, T> {
let mut guard = self.mutable.lock();
if guard.is_none() {
guard.replace(self.inner.read().clone());
}
QueuedMutexLockGuard { mutex: self, guard }
}
pub fn read_now(&self) -> QueuedMutexReadGuard<'_, T> {
let guard = self.inner.read();
self.readers.add(1, Ordering::Relaxed);
QueuedMutexReadGuard {
mutex: self,
guard: Some(guard),
}
}
}
pub struct QueuedMutexLockGuard<'a, T: Clone> {
mutex: &'a QueuedMutex<T>,
guard: MutexGuard<'a, RawMutex, Option<T>>,
}
impl<T: Clone> Deref for QueuedMutexLockGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self.guard.as_ref() {
Some(v) => v,
None => unreachable!(),
}
}
}
impl<T: Clone> DerefMut for QueuedMutexLockGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self.guard.as_mut() {
Some(v) => v,
None => unreachable!(),
}
}
}
impl<T: Clone> Drop for QueuedMutexLockGuard<'_, T> {
fn drop(&mut self) {
if self.mutex.readers.load(Ordering::Relaxed) != 0 {
return;
}
let mut write_lock = self.mutex.inner.write();
*write_lock = match self.guard.take() {
Some(v) => v,
None => unreachable!(),
}
}
}
pub struct QueuedMutexReadGuard<'a, T: Clone> {
mutex: &'a QueuedMutex<T>,
guard: Option<RwLockReadGuard<'a, RawRwLock, T>>,
}
impl<T: Clone> Deref for QueuedMutexReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self.guard.as_ref() {
Some(v) => v,
None => unreachable!(),
}
}
}
impl<T: Clone> Drop for QueuedMutexReadGuard<'_, T> {
fn drop(&mut self) {
drop(self.guard.take());
if self
.mutex
.readers
.fetch_sub(1, Ordering::Relaxed)
.wrapping_sub(1)
!= 0
{
return;
}
let mut write_lock = self.mutex.inner.write();
if let Some(v) = self.mutex.mutable.lock().take() {
*write_lock = v
}
}
}

View File

@@ -16,6 +16,8 @@ use std::sync::{Arc, Weak};
use tokio::sync::oneshot;
use tracing::{debug, debug_span};
stardust_xr_server_codegen::codegen_id_to_name_functions!();
#[derive(Default)]
pub struct Scenegraph {
pub(super) client: OnceCell<Weak<Client>>,
@@ -96,7 +98,15 @@ impl scenegraph::Scenegraph for Scenegraph {
let Some(client) = self.get_client() else {
return Err(ScenegraphError::NodeNotFound);
};
debug_span!("Handle signal", aspect_id, node_id, method).in_scope(|| {
debug_span!(
"Handle signal",
aspect_id,
aspect_name = aspect_id_to_name(aspect_id),
node_id,
method,
signal_name = signal_id_to_name(method)
)
.in_scope(|| {
self.get_node(node_id)
.ok_or(ScenegraphError::NodeNotFound)?
.send_local_signal(
@@ -123,7 +133,14 @@ impl scenegraph::Scenegraph for Scenegraph {
let _ = response.send(Err(ScenegraphError::NodeNotFound));
return;
};
debug!(aspect_id, node_id, method, "Handle method");
debug!(
aspect_id,
aspect_name = aspect_id_to_name(aspect_id),
node_id,
method,
method_name = method_id_to_name(method),
"Handle method"
);
let Some(node) = self.get_node(node_id) else {
let _ = response.send(Err(ScenegraphError::NodeNotFound));
return;

View File

@@ -42,6 +42,7 @@ use bevy_mod_xr::session::{XrFirst, XrPreDestroySession, XrSessionCreated, XrSes
use bevy_mod_xr::spaces::XrPrimaryReferenceSpace;
use bevy_plugin::{DbusConnection, InputUpdate, StardustBevyPlugin, StardustFirst};
use clap::Parser;
use tracing::level_filters::LevelFilter;
use core::client::Client;
use core::task;
use directories::ProjectDirs;
@@ -198,7 +199,7 @@ async fn setup() {
let cli_args = cli_args.clone();
let dbus_connection = dbus_connection.clone();
move || {
stereokit_loop(
bevy_loop(
sk_ready_notifier,
project_dirs,
cli_args,
@@ -228,7 +229,7 @@ async fn setup() {
info!("Cleanly shut down Stardust");
}
fn stereokit_loop(
fn bevy_loop(
sk_ready_notifier: Arc<Notify>,
project_dirs: Option<ProjectDirs>,
args: CliArgs,
@@ -331,6 +332,7 @@ fn stereokit_loop(
cams.iter().for_each(|e| {
cmds.entity(e).remove::<Skybox>();
});
let _span = debug_span!("spawn");
cmds.insert_resource(ClearColor(Color::NONE));
}
*last_hidden = env_hidden;
@@ -392,7 +394,7 @@ fn stereokit_loop(
bevy_app.insert_resource(objects);
let bevy_step = |world: &mut World| {
fn bevy_step(world: &mut World) {
let _span = debug_span!("Bevy step");
let _span = _span.enter();
// camera::update(token);
@@ -428,6 +430,7 @@ fn stereokit_loop(
.run_system_cached(should_run_frame_loop)
.unwrap_or(true)
{
let _span = debug_span!("eeping").entered();
let mut waiter = world.remove_resource::<OxrFrameWaiter>().unwrap();
let state = waiter.wait().unwrap();
world.insert_resource(OxrFrameState(state));

View File

@@ -163,18 +163,8 @@ impl Drop for Sound {
}
}
<<<<<<< HEAD
=======
<<<<<<< HEAD
create_interface!(AudioInterface);
>>>>>>> 0ec4c0f (refactor: get models fully working)
struct AudioInterface;
impl InterfaceAspect for AudioInterface {
=======
pub fn update() {}
impl InterfaceAspect for Interface {
>>>>>>> 2f5c92d (refactor: get models fully working)
#[doc = "Create a sound node. WAV and MP3 are supported."]
fn create_sound(
_node: Arc<Node>,

View File

@@ -19,6 +19,7 @@ use glam::{Vec3, Vec3A};
use parking_lot::Mutex;
use prisma::Lerp;
use std::{collections::VecDeque, sync::Arc};
use tracing::{debug_span, info};
static LINES_REGISTRY: Registry<Lines> = Registry::new();
@@ -27,6 +28,7 @@ pub struct Lines {
data: Mutex<Vec<Line>>,
}
impl Lines {
#[tracing::instrument]
pub fn add_to(node: &Arc<Node>, lines: Vec<Line>) -> Result<Arc<Lines>> {
*node
.get_aspect::<Spatial>()
@@ -34,15 +36,15 @@ impl Lines {
.bounding_box_calc
.lock() = {
if let Ok(lines) = node.get_aspect::<Lines>() {
Aabb3d::from_point_cloud(
Isometry3d::IDENTITY,
Aabb3d::from_point_cloud(Isometry3d::IDENTITY, {
let _span = debug_span!("add_to data lock").entered();
lines
.data
.lock()
.iter()
.flat_map(|line| line.points.iter())
.map(|point| Vec3A::from(point.point)),
)
.map(|point| Vec3A::from(point.point))
})
} else {
Aabb3d::new(Vec3A::ZERO, Vec3A::ZERO)
}
@@ -59,7 +61,9 @@ impl Lines {
fn draw(&self, mesh: &mut Mesh, view: &GlobalTransform) -> Transform {
let transform_mat = self.space.global_transform();
let _span = debug_span!("draw data lock").entered();
let data = self.data.lock().clone();
drop(_span);
let global_to_view = view.compute_matrix().inverse();
let local_to_view = transform_mat.inverse() * global_to_view;
let view_to_local = local_to_view.inverse();
@@ -139,12 +143,11 @@ struct BevyLinePoint {
color: Srgba,
thickness: f32,
}
impl Aspect for Lines {
const NAME: &'static str = "Lines";
}
impl LinesAspect for Lines {
#[tracing::instrument]
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
let lines_aspect = node.get_aspect::<Lines>()?;
let _span = debug_span!("set_lines data lock").entered();
*lines_aspect.data.lock() = lines;
Ok(())
}
@@ -167,9 +170,23 @@ pub fn draw_all(
..Default::default()
};
let mat_handle = materials.add(material);
for lines in LINES_REGISTRY.get_valid_contents() {
let _span = debug_span!("line registry get valid contents").entered();
let vec = LINES_REGISTRY.get_valid_contents();
drop(_span);
info!("len {}", vec.len());
for lines in vec {
let _span = debug_span!("outer if span").entered();
if let Some(node) = lines.space.node() {
if node.enabled() && !lines.data.lock().is_empty() {
let _span = debug_span!("lines_data lock").entered();
let w = lines.data.lock().is_empty();
drop(_span);
let _span = debug_span!("node enabled check").entered();
let e = node.enabled();
drop(_span);
let _span = debug_span!("if span").entered();
if e && !w {
let _span = debug_span!("create line mesh").entered();
info!("spawning line");
// Does this rebuild the mesh every frame? yes, is this problematic? probably,
// would a shader work better? yes, do i care? not right now
let mut mesh = Mesh::new(

View File

@@ -336,18 +336,6 @@ fn create_model_parts_for_loaded_models(
*space.bounding_box_calc.lock() = Aabb3d::new(aabb.center, aabb.half_extents);
<<<<<<< HEAD
let model_part = Arc::new(ModelPart {
entity: MainWorldEntity(entity),
path: part_path,
space,
model: Arc::downgrade(model),
pending_material_parameters: Mutex::new(FxHashMap::default()),
pending_material_replacement: Mutex::new(None),
aliases: AliasList::default(),
});
node.add_aspect_raw(model_part.clone());
=======
let model_part = Arc::new(ModelPart {
entity: OnceCell::from(entity),
path: part_path,
@@ -361,7 +349,6 @@ fn create_model_parts_for_loaded_models(
info!("fresh {}", &model_part.path);
model_part
});
>>>>>>> 0ec4c0f (refactor: get models fully working)
parts.push(model_part.clone());
}
cmds.entity(entity).remove::<UnprocessedModel>();
@@ -440,13 +427,6 @@ impl ModelAspect for Model {
part_path: String,
) -> Result<()> {
let model = node.get_aspect::<Model>()?;
<<<<<<< HEAD
let parts = model.parts.lock();
let Some(part) = parts.iter().find(|p| p.path == part_path) else {
let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>();
bail!("Couldn't find model part at path {part_path}, all available paths: {paths:?}",);
};
=======
let mut parts = model.parts.lock();
let part =
parts
@@ -476,7 +456,6 @@ impl ModelAspect for Model {
parts.push(model_part.clone());
model_part
});
>>>>>>> 0ec4c0f (refactor: get models fully working)
Alias::create_with_id(
&part.space.node().unwrap(),
&calling_client,

View File

@@ -29,6 +29,7 @@ use glam::{vec3, Mat4, Vec2, Vec3};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::{ffi::OsStr, path::PathBuf, sync::Arc};
use tracing::{info, info_span};
use super::{TextAspect, TextStyle};
@@ -68,15 +69,14 @@ fn update_text(mut surface_query: Query<(&mut Transform)>) {
else {
continue;
};
let data = text.data.lock();
// let data = text.data.lock();
*transform = Transform::from_matrix(
text.space.global_transform()
* Mat4::from_scale(vec3(
data.character_height,
data.character_height,
data.character_height,
)),
text.space.global_transform(), // * Mat4::from_scale(vec3(
// data.character_height,
// data.character_height,
// data.character_height,
// )),
);
}
}
@@ -90,7 +90,10 @@ fn spawn_text(
asset_server: Res<AssetServer>,
) {
for text in reader.try_iter() {
let _span = info_span!("spawning text").entered();
let _span2 = info_span!("text data lock").entered();
let data = text.data.lock();
drop(_span2);
let size = Extent3d {
width: (512.0 * data.bounds.as_ref().map(|v| v.bounds.x).unwrap_or(1.0)).floor() as u32,
height: (512.0 * data.bounds.as_ref().map(|v| v.bounds.y).unwrap_or(1.0)).floor()
@@ -168,6 +171,8 @@ fn spawn_text(
MeshMaterial3d(mats.add(DefaultMaterial {
base_color_texture: Some(image_handle),
unlit: true,
// would Cutout be enough here?
alpha_mode: bevy::prelude::AlphaMode::Blend,
..default()
})),
))

View File

@@ -8,6 +8,7 @@ mod tip;
pub use handler::*;
pub use method::*;
use tracing::debug_span;
use super::fields::Field;
use super::spatial::Spatial;
@@ -140,6 +141,7 @@ pub fn process_input() {
.clone()
// filter out methods without the handler in their handler order
.filter(|a| {
let _span = debug_span!("handlder_order lock").entered();
a.handler_order
.lock()
.iter()

View File

@@ -10,6 +10,7 @@ pub mod spatial;
use self::alias::Alias;
use crate::core::client::Client;
use crate::core::error::{Result, ServerError};
use crate::core::queued_mutex::QueuedMutex;
use crate::core::registry::Registry;
use crate::core::scenegraph::MethodResponseSender;
use parking_lot::Mutex;
@@ -25,6 +26,7 @@ use std::fmt::Debug;
use std::os::fd::OwnedFd;
use std::sync::{Arc, Weak};
use std::vec::Vec;
use tracing::{debug_span, info};
#[derive(Default)]
pub struct Message {
@@ -125,17 +127,24 @@ impl Node {
))
}
pub fn enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
&& if let Ok(spatial) = self.get_aspect::<Spatial>() {
spatial
.global_transform()
.to_scale_rotation_translation()
.0
.length_squared()
> 0.0
} else {
true
}
let bool = {
let _span = debug_span!("load atomic bool").entered();
self.enabled.load(Ordering::Relaxed)
};
bool && if let Ok(spatial) = {
let _span = debug_span!("get spatial aspect").entered();
self.get_aspect::<Spatial>()
} {
let _span = debug_span!("check if scale is zero").entered();
spatial
.global_transform()
.to_scale_rotation_translation()
.0
.length_squared()
> 0.0
} else {
true
}
}
pub fn set_enabled(&self, enabled: bool) {
self.enabled.store(enabled, Ordering::Relaxed)
@@ -324,7 +333,7 @@ pub trait Aspect: Any + Send + Sync + 'static {
}
#[derive(Default)]
struct Aspects(Mutex<FxHashMap<u64, Arc<dyn Aspect>>>);
struct Aspects(QueuedMutex<FxHashMap<u64, Arc<dyn Aspect>>>);
impl Aspects {
fn add<A: AspectIdentifier>(&self, t: A) -> Arc<A> {
let aspect = Arc::new(t);

View File

@@ -38,7 +38,7 @@ use openxr::{ActionSet, Posef};
use serde::{Deserialize, Serialize};
use stardust_xr::values::Datamap;
use std::{ops::Deref, sync::Arc};
use tracing::error;
use tracing::{debug_span, error};
use zbus::Connection;
#[derive(Default, Debug, Deserialize, Serialize)]
@@ -236,11 +236,15 @@ fn spawn_controllers(
HandSide::Left => "left",
HandSide::Right => "right",
};
let _span = debug_span!("create SpatialRef").entered();
let (spatial, object_handle) = SpatialRef::create(
&connection,
&("/org/stardustxr/Controller/".to_string() + side),
);
drop(_span);
let tip = InputDataType::Tip(Tip::default());
let _span = debug_span!("create input method").entered();
let Ok(input) = (|| -> color_eyre::Result<Arc<InputMethod>> {
Ok(InputMethod::add_to(
&spatial.node().unwrap(),
@@ -250,6 +254,8 @@ fn spawn_controllers(
})() else {
continue;
};
drop(_span);
let _span = debug_span!("create actions").entered();
let actions = {
let set = session
.instance()
@@ -275,6 +281,8 @@ fn spawn_controllers(
.unwrap(),
}
};
drop(_span);
let _span = debug_span!("spawn").entered();
cmds.spawn((
SceneRoot(handle.clone()),
SkController {

View File

@@ -12,7 +12,10 @@ use crate::objects::{ObjectHandle, SpatialRef};
use crate::DefaultMaterial;
use bevy::app::{Plugin, PostUpdate};
use bevy::asset::{AssetServer, Assets, Handle};
use bevy::prelude::{Commands, Component, Entity, Query, Res, ResMut};
use bevy::prelude::{
Commands, Component, Entity, Gizmos, IntoSystemConfigs as _, Query, Res, ResMut,
};
use bevy::utils::default;
use bevy_mod_openxr::helper_traits::{ToQuat, ToVec3};
use bevy_mod_openxr::resources::OxrFrameState;
use bevy_mod_openxr::session::OxrSession;
@@ -41,6 +44,13 @@ impl Plugin for StardustHandPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_systems(XrSessionCreated, create_hands);
app.add_systems(PostUpdate, update_hands);
app.add_systems(PostUpdate, draw_hand_gizmos.after(update_hands));
}
}
fn draw_hand_gizmos(mut gizmos: Gizmos, query: Query<&SkHand>) {
for hand in query.iter() {
gizmos.axes(hand.palm_spatial.global_transform(), 0.05);
}
}
@@ -87,8 +97,6 @@ fn update_hands(
update_joint(&mut finger.intermediate, joints[finger_index + 2]);
update_joint(&mut finger.proximal, joints[finger_index + 1]);
update_joint(&mut finger.metacarpal, joints[finger_index]);
// Why?
finger.tip.radius = 0.0;
}
update_joint(&mut hand_input.palm, joints[HandBone::Palm as usize]);
hand.palm_spatial