diff --git a/Cargo.toml b/Cargo.toml index a9ba376a72..4b101ba572 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -277,7 +277,7 @@ trace_tracy_memory = [ ] # Tracing support -trace = ["bevy_internal/trace"] +trace = ["bevy_internal/trace", "dep:tracing"] # Basis Universal compressed texture support basis-universal = ["bevy_internal/basis-universal"] @@ -484,6 +484,7 @@ ghost_nodes = ["bevy_internal/ghost_nodes"] [dependencies] bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false } +tracing = { version = "0.1", default-features = false, optional = true } # Wasm does not support dynamic linking. [target.'cfg(not(target_family = "wasm"))'.dependencies] diff --git a/examples/stress_tests/many_components.rs b/examples/stress_tests/many_components.rs index 6234ca90e8..f047445f94 100644 --- a/examples/stress_tests/many_components.rs +++ b/examples/stress_tests/many_components.rs @@ -23,17 +23,21 @@ use bevy::{ }, log::LogPlugin, prelude::{App, In, IntoSystem, Query, Schedule, SystemParamBuilder, Update}, - ptr::OwningPtr, + ptr::{OwningPtr, PtrMut}, MinimalPlugins, }; use rand::prelude::{Rng, SeedableRng, SliceRandom}; use rand_chacha::ChaCha8Rng; -use std::{alloc::Layout, num::Wrapping}; +use std::{alloc::Layout, mem::ManuallyDrop, num::Wrapping}; +#[expect(unsafe_code, reason = "Reading dynamic components requires unsafe")] // A simple system that matches against several components and does some menial calculation to create // some non-trivial load. fn base_system(access_components: In>, mut query: Query) { + #[cfg(feature = "trace")] + let _span = tracing::info_span!("base_system", components = ?access_components.0, count = query.iter().len()).entered(); + for mut filtered_entity in &mut query { // We calculate Faulhaber's formula mod 256 with n = value and p = exponent. // See https://en.wikipedia.org/wiki/Faulhaber%27s_formula @@ -45,10 +49,6 @@ fn base_system(access_components: In>, mut query: Query() }; @@ -65,10 +65,6 @@ fn base_system(access_components: In>, mut query: Query(); @@ -79,6 +75,7 @@ fn base_system(access_components: In>, mut query: Query = (1..=num_components) .map(|i| { world.register_component_with_descriptor( - #[expect(unsafe_code, reason = "Used to register a bunch of fake components")] // SAFETY: - // we don't implement a drop function - // u8 is Sync and Send + // * We don't implement a drop function + // * u8 is Sync and Send unsafe { ComponentDescriptor::new_with_layout( format!("Component{}", i).to_string(), @@ -131,26 +127,41 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) { // spawn a bunch of entities for _ in 1..=num_entities { let num_components = rng.gen_range(1..10); - let components = component_ids.choose_multiple(&mut rng, num_components); + let components: Vec = component_ids + .choose_multiple(&mut rng, num_components) + .copied() + .collect(); let mut entity = world.spawn_empty(); - for &component_id in components { - let value: u8 = rng.gen_range(0..255); - OwningPtr::make(value, |ptr| { - #[expect( - unsafe_code, - reason = "Used to write to a fake component that we previously set up" - )] + // We use `ManuallyDrop` here as we need to avoid dropping the u8's when `values` is dropped + // since ownership of the values is passed to the world in `insert_by_ids`. + // But we do want to deallocate the memory when values is dropped. + let mut values: Vec> = components + .iter() + .map(|_id| ManuallyDrop::new(rng.gen_range(0..255))) + .collect(); + let ptrs: Vec = values + .iter_mut() + .map(|value| { // SAFETY: - // component_id is from the same world - // value is u8, so ptr is a valid reference for component_id - unsafe { - entity.insert_by_id(component_id, ptr); - } - }); + // * We don't read/write `values` binding after this and values are `ManuallyDrop`, + // so we have the right to drop/move the values + unsafe { PtrMut::from(value).promote() } + }) + .collect(); + // SAFETY: + // * component_id's are from the same world + // * `values` was initialized above, so references are valid + unsafe { + entity.insert_by_ids(&components, ptrs.into_iter()); } } + println!( + "Number of Archetype-Components: {}", + world.archetypes().archetype_components_len() + ); + // overwrite Update schedule in the app app.add_schedule(schedule); app.add_plugins(MinimalPlugins)