Bevy Rewrite, attempt 2 #46

Merged
Schmarni-Dev merged 18 commits from bevy_for_real_this_time into dev 2025-06-30 05:04:00 -04:00
8 changed files with 128 additions and 91 deletions
Showing only changes of commit 4795fa8e0a - Show all commits

View File

@@ -24,8 +24,8 @@ path = "src/main.rs"
default = []
wayland = ["dep:smithay", "dep:wayland-scanner", "dep:wayland-backend"]
coderabbitai[bot] commented 2025-06-28 14:53:14 -04:00 (Migrated from github.com)
Review

⚠️ Potential issue

Breaking change: Wayland no longer in default features.

Removing wayland from default features might break existing users. Consider documenting this change prominently or keeping it as default if it's commonly used.

🤖 Prompt for AI Agents
In Cargo.toml at line 24, the default features list has been changed to exclude
"wayland," which is a breaking change. To fix this, either add "wayland" back to
the default features array if it is commonly used or update the documentation
clearly highlighting this removal so users are aware of the change and can
adjust their dependencies accordingly.
_⚠️ Potential issue_ **Breaking change: Wayland no longer in default features.** Removing `wayland` from default features might break existing users. Consider documenting this change prominently or keeping it as default if it's commonly used. <details> <summary>🤖 Prompt for AI Agents</summary> ``` In Cargo.toml at line 24, the default features list has been changed to exclude "wayland," which is a breaking change. To fix this, either add "wayland" back to the default features array if it is commonly used or update the documentation clearly highlighting this removal so users are aware of the change and can adjust their dependencies accordingly. ``` </details> <!-- This is an auto-generated comment by CodeRabbit --> <!-- fingerprinting:phantom:medusa:lion -->
profile_tokio = ["dep:console-subscriber", "tokio/tracing"]
profile_app = ["dep:tracing-tracy", "bevy/trace_tracy"]
change_tracking = ["bevy/track_location"]
profile_app = ["dep:tracing-tracy", "bevy/trace_tracy", "bevy/trace"]
bevy_debugging = ["bevy/bevy_remote", "bevy/track_location"]
[package.metadata.appimage]
auto_link = true
@@ -64,7 +64,7 @@ color-eyre = { version = "0.6.3", default-features = false }
clap = { version = "4.5.13", features = ["derive"] }
console-subscriber = { version = "0.4.0", optional = true }
thiserror = "2.0.9"
tracing = "0.1.40"
tracing = { version = "0.1.40", features = ["release_max_level_warn"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-tracy = { version = "0.11.1", optional = true }
coderabbitai[bot] commented 2025-06-28 14:53:14 -04:00 (Migrated from github.com)
Review

🛠️ Refactor suggestion

Pin git dependencies to specific commits for reproducible builds.

Using branch names without commit hashes can lead to non-reproducible builds as branches can be updated.

Add specific commit hashes or tags to ensure reproducible builds:

-bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "non_default_wait_frame_system" }
+bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", rev = "COMMIT_HASH" }
🤖 Prompt for AI Agents
In Cargo.toml around lines 45 to 48, the git dependencies are specified using
branch names, which can cause non-reproducible builds because branches may
change over time. To fix this, replace the branch specifications with exact
commit hashes or tags for each dependency to ensure builds are reproducible and
consistent.
_🛠️ Refactor suggestion_ **Pin git dependencies to specific commits for reproducible builds.** Using branch names without commit hashes can lead to non-reproducible builds as branches can be updated. Add specific commit hashes or tags to ensure reproducible builds: ```diff -bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "non_default_wait_frame_system" } +bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", rev = "COMMIT_HASH" } ``` <details> <summary>🤖 Prompt for AI Agents</summary> ``` In Cargo.toml around lines 45 to 48, the git dependencies are specified using branch names, which can cause non-reproducible builds because branches may change over time. To fix this, replace the branch specifications with exact commit hashes or tags for each dependency to ensure builds are reproducible and consistent. ``` </details> <!-- This is an auto-generated comment by CodeRabbit --> <!-- fingerprinting:phantom:medusa:lion -->
@@ -79,7 +79,7 @@ mint = "0.5.9"
tokio = { version = "1.39.2", features = ["rt-multi-thread", "signal", "time"] }
# bevy
bevy = { version = "0.16", features = ["wayland", "bevy_remote", "mp3", "wav"] }
bevy = { version = "0.16", features = ["wayland", "mp3", "wav"] }
bevy_mod_xr = "0.3"
bevy_mod_openxr = "0.3"
# bevy_sk.git = "https://github.com/MalekiRe/bevy_sk"

View File

@@ -21,8 +21,6 @@ use bevy::gizmos::GizmoPlugin;
use bevy::gltf::GltfPlugin;
use bevy::input::InputPlugin;
use bevy::pbr::PbrPlugin;
use bevy::remote::RemotePlugin;
use bevy::remote::http::RemoteHttpPlugin;
use bevy::render::{RenderDebugFlags, RenderPlugin};
use bevy::scene::ScenePlugin;
use bevy::text::FontLoader;
@@ -378,7 +376,10 @@ fn bevy_loop(
app.add_schedule(pre_frame_wait);
app.insert_resource(ClearColor(Color::BLACK.with_alpha(0.0)));
app.insert_resource(ObjectRegistryRes(object_registry));
app.add_plugins((RemotePlugin::default(), RemoteHttpPlugin::default()));
#[cfg(feature = "bevy_debugging")]
{
app.add_plugins((RemotePlugin::default(), RemoteHttpPlugin::default()));
}
// the Stardust server plugins
// infra plugins
app.add_plugins(EntityHandlePlugin);

View File

@@ -27,7 +27,7 @@ pub struct LinesNodePlugin;
impl Plugin for LinesNodePlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, (build_line_mesh, update_visibillity).chain());
app.add_systems(Update, (build_line_mesh/* , update_visibillity */).chain());
}
}

View File

@@ -31,7 +31,7 @@ impl Plugin for ModelNodePlugin {
app.add_systems(Update, load_models);
app.add_systems(
PostUpdate,
(gen_model_parts, apply_materials, update_visibillity).chain(),
(gen_model_parts, apply_materials/* , update_visibillity */).chain(),
);
}
}

View File

@@ -1,4 +1,5 @@
use crate::{
BevyMaterial,
core::{
bevy_channel::{BevyChannel, BevyChannelReader},
client::Client,
@@ -9,10 +10,12 @@ use crate::{
resource::get_resource_file,
},
nodes::{
drawable::XAlign, spatial::{Spatial, SpatialNode}, Node
}, BevyMaterial,
Node,
drawable::XAlign,
spatial::{Spatial, SpatialNode},
},
};
use bevy::{platform::collections::HashMap, prelude::*};
use bevy::{platform::collections::HashMap, prelude::*, render::mesh::MeshAabb};
use bevy_mesh_text_3d::{
Align, Attrs, MeshTextPlugin, Settings as FontSettings, generate_meshes,
text_glyphs::TextGlyphs,
@@ -40,25 +43,7 @@ impl Plugin for TextNodePlugin {
SPAWN_TEXT.init(app);
app.init_resource::<MaterialRegistry>();
app.add_systems(Update, (spawn_text, update_visibillity).chain());
}
}
fn update_visibillity(mut cmds: Commands) {
for text in TEXT_REGISTRY.get_valid_contents().into_iter() {
let Some(entity) = text.entity.lock().as_deref().copied() else {
continue;
};
match text.spatial.node().map(|n| n.enabled()).unwrap_or(false) {
true => {
cmds.entity(entity)
.insert_recursive::<Children>(Visibility::Visible);
}
false => {
cmds.entity(entity)
.insert_recursive::<Children>(Visibility::Hidden);
}
}
app.add_systems(Update, spawn_text);
}
}
@@ -134,15 +119,25 @@ fn spawn_text(
if let Some(db) = old_db {
mem::swap(font_settings.font_system.db_mut(), db);
}
let Ok(meshes) = char_meshes.inspect_err(|err| error!("unable to create text meshes: {err}"))
let Ok(char_meshes) =
char_meshes.inspect_err(|err| error!("unable to create text meshes: {err}"))
else {
continue;
};
let dist = meshes
.iter()
.fold(f32::MAX, |dist, v| dist.min(v.transform.translation.x));
let dist = char_meshes.iter().fold(f32::MAX, |dist, v| {
dist.min(
v.transform.translation.x
- meshes
.get(&v.mesh)
.unwrap()
.compute_aabb()
.unwrap_or_default()
.half_extents
.x,
)
});
// TODO: text align
let letters = meshes
let letters = char_meshes
.into_iter()
.map(|v| {
cmds.spawn((

View File

@@ -6,6 +6,8 @@ mod method;
mod pointer;
mod tip;
use bevy::tasks::ComputeTaskPool;
use bevy::tasks::ParallelSlice;
pub use handler::*;
pub use method::*;
use tracing::debug_span;
@@ -120,55 +122,60 @@ pub fn process_input() {
};
node.enabled()
});
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
let _span = debug_span!("handle input handler").entered();
for method_alias in handler.method_aliases.get_aliases() {
method_alias.set_enabled(false);
}
INPUT_HANDLER_REGISTRY
.get_valid_contents()
.into_iter()
.par_splat_map(ComputeTaskPool::get(), None, |_, handlers| {
for handler in handlers {
let _span = debug_span!("handle input handler").entered();
for method_alias in handler.method_aliases.get_aliases() {
method_alias.set_enabled(false);
}
let Some(handler_node) = handler.spatial.node() else {
continue;
};
if !handler_node.enabled() {
continue;
}
if let Some(handler_field_node) = handler.field.spatial.node() {
if !handler_field_node.enabled() {
continue;
let Some(handler_node) = handler.spatial.node() else {
continue;
};
if !handler_node.enabled() {
continue;
}
if let Some(handler_field_node) = handler.field.spatial.node() {
if !handler_field_node.enabled() {
continue;
}
};
let ser_span = debug_span!("serializing input").entered();
let (methods, datas) = methods
.clone()
// filter out methods without the handler in their handler order
.filter(|a| {
a.handler_order
.lock()
.iter()
.any(|h| h.ptr_eq(&Arc::downgrade(&handler)))
})
// filter out methods without the proper alias
.filter_map(|m| {
Some((
handler
.method_aliases
.get_from_original_node(m.spatial.node.clone())?,
m,
))
})
// make sure the input method alias is enabled
.inspect(|(a, _)| {
a.set_enabled(true);
})
// serialize the data
.map(|(a, m)| (a.clone(), m.serialize(a.get_id(), &handler)))
.unzip::<_, _, Vec<_>, Vec<_>>();
drop(ser_span);
let _span = debug_span!("client input").entered();
let _ = input_handler_client::input(&handler_node, &methods, &datas);
}
};
let ser_span = debug_span!("serializing input").entered();
let (methods, datas) = methods
.clone()
// filter out methods without the handler in their handler order
.filter(|a| {
a.handler_order
.lock()
.iter()
.any(|h| h.ptr_eq(&Arc::downgrade(&handler)))
})
// filter out methods without the proper alias
.filter_map(|m| {
Some((
handler
.method_aliases
.get_from_original_node(m.spatial.node.clone())?,
m,
))
})
// make sure the input method alias is enabled
.inspect(|(a, _)| {
a.set_enabled(true);
})
// serialize the data
.map(|(a, m)| (a.clone(), m.serialize(a.get_id(), &handler)))
.unzip::<_, _, Vec<_>, Vec<_>>();
drop(ser_span);
let _span = debug_span!("client input").entered();
let _ = input_handler_client::input(&handler_node, &methods, &datas);
}
});
for method in methods {
method.cull_capture_attempts();
}

View File

@@ -18,6 +18,7 @@ use mint::Vector3;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use std::fmt::Debug;
coderabbitai[bot] commented 2025-06-28 14:53:15 -04:00 (Migrated from github.com)
Review

🛠️ Refactor suggestion

Consider implementing entity cleanup for dead spatial references.

The comment raises a valid concern. When the spatial weak reference can't be upgraded, the entity is orphaned and should be despawned to prevent resource leaks.

 let Some(spatial) = spatial_node.0.upgrade() else {
-    // should we despawn the entity?
+    cmds.entity(entity).despawn_recursive();
     return;
 };

Note: This would require adding Commands to the system parameters.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/nodes/spatial/mod.rs around lines 41 to 44, the code currently returns
early when the spatial weak reference cannot be upgraded, potentially leaving
orphaned entities. To fix this, modify the system to accept Commands as a
parameter and use it to despawn the entity when the spatial reference is dead,
ensuring proper cleanup and preventing resource leaks.
_🛠️ Refactor suggestion_ **Consider implementing entity cleanup for dead spatial references.** The comment raises a valid concern. When the spatial weak reference can't be upgraded, the entity is orphaned and should be despawned to prevent resource leaks. ```diff let Some(spatial) = spatial_node.0.upgrade() else { - // should we despawn the entity? + cmds.entity(entity).despawn_recursive(); return; }; ``` Note: This would require adding `Commands` to the system parameters. > Committable suggestion skipped: line range outside the PR's diff. <details> <summary>🤖 Prompt for AI Agents</summary> ``` In src/nodes/spatial/mod.rs around lines 41 to 44, the code currently returns early when the spatial weak reference cannot be upgraded, potentially leaving orphaned entities. To fix this, modify the system to accept Commands as a parameter and use it to despawn the entity when the spatial reference is dead, ensuring proper cleanup and preventing resource leaks. ``` </details> <!-- This is an auto-generated comment by CodeRabbit --> <!-- fingerprinting:phantom:medusa:lion -->
use std::sync::atomic::Ordering;
use std::sync::{Arc, OnceLock, Weak};
use std::{f32, ptr};
@@ -32,17 +33,42 @@ impl Plugin for SpatialNodePlugin {
}
fn update_spatial_nodes(
mut query: Query<(&mut BevyTransform, &SpatialNode, Option<&ChildOf>)>,
mut query: Query<(
&mut BevyTransform,
&SpatialNode,
Option<&ChildOf>,
&mut Visibility,
)>,
parent_query: Query<&GlobalTransform>,
) {
query
.par_iter_mut()
.for_each(|(mut transform, spatial_node, child_of)| {
.for_each(|(mut transform, spatial_node, child_of, mut vis)| {
let _span = debug_span!("updating spatial node").entered();
let Some(spatial) = spatial_node.0.upgrade() else {
// should we despawn the entity?
return;
};
let mat4 = spatial.global_transform();
if spatial
.node()
.is_some_and(|v| !v.enabled.load(Ordering::Relaxed))
{
if !matches!(*vis, Visibility::Hidden) {
*vis = Visibility::Hidden;
}
return;
}
let mat4 =
debug_span!("getting global transform").in_scope(|| spatial.global_transform());
let (scale, _, _) = mat4.to_scale_rotation_translation();
match (*vis, scale == Vec3::ZERO) {
(Visibility::Inherited | Visibility::Visible, true) => {
*vis = Visibility::Hidden;
}
(Visibility::Hidden, false) => {
*vis = Visibility::Inherited;
}
_ => {}
}
match child_of {
Some(child_of) => {
let Ok(parent) = parent_query.get(child_of.0) else {
@@ -60,7 +86,7 @@ fn update_spatial_nodes(
}
#[derive(Clone, Component, Debug)]
#[require(BevyTransform)]
#[require(BevyTransform, Visibility)]
pub struct SpatialNode(pub Weak<Spatial>);
stardust_xr_server_codegen::codegen_spatial_protocol!();

View File

@@ -39,6 +39,7 @@ use std::{
str::FromStr,
sync::Arc,
};
use tracing::instrument;
use zbus::Connection;
pub struct ControllerPlugin;
const CURSOR_MODEL_PATH: &str = "/tmp/stardust_server/models/cursor.glb";
@@ -152,9 +153,11 @@ fn update(
controllers.right.set_enabled(false);
return;
};
session
.sync_actions(&[ActiveActionSet::new(&actions.set)])
.unwrap();
debug_span!("sync actions").in_scope(|| {
session
.sync_actions(&[ActiveActionSet::new(&actions.set)])
.unwrap();
});
let time = state.predicted_display_time;
// stupid bevy gltf loading issue (rotated 180 degrees on the y axis)
controllers
@@ -299,6 +302,7 @@ impl SkController {
space: None,
})
}
#[instrument(level = "debug", skip(self))]
pub fn set_enabled(&self, enabled: bool) {
if let Some(node) = self.input.spatial.node() {
node.set_enabled(enabled);
@@ -321,6 +325,7 @@ impl SkController {
let Some(space) = self.space.as_ref() else {
return;
};
let _span = debug_span!("locate space").entered();
let Ok(location) = session
.locate_space(space, &ref_space, time)
.inspect_err(|err| error!("error while locating controller space: {err}"))
@@ -333,6 +338,7 @@ impl SkController {
| SpaceLocationFlags::ORIENTATION_VALID
| SpaceLocationFlags::ORIENTATION_TRACKED,
);
drop(_span);
self.set_enabled(enabled);
if enabled {
let world_transform = Mat4::from(Affine3A::from(location.pose.to_xr_pose()));
@@ -377,6 +383,7 @@ impl SkController {
.map(|v| v.current_state)
.unwrap_or_default()
}
let _span = debug_span!("apply datamap").entered();
self.datamap = ControllerDatamap {
select: get(session, path, &actions.trigger),
middle: get(session, path, &actions.stick_click) as u32 as f32,
@@ -385,6 +392,7 @@ impl SkController {
scroll: get(session, path, &actions.stick).to_vec2(),
};
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
drop(_span);
let distance_calculator = |space: &Arc<Spatial>, _data: &InputDataType, field: &Field| {
Some(field.distance(space, [0.0; 3].into()).abs())