//! Hierarchy and transform propagation stress test. //! //! Running this example: //! //! ``` //! cargo r --release --example transform_hierarchy //! ``` //! //! | Configuration | Description | //! | -------------------- | ----------------------------------------------------------------- | //! | `large_tree` | A fairly wide and deep tree. | //! | `wide_tree` | A shallow but very wide tree. | //! | `deep_tree` | A deep but not very wide tree. | //! | `chain` | A chain. 2500 levels deep. | //! | `update_leaves` | Same as `large_tree`, but only leaves are updated. | //! | `update_shallow` | Same as `large_tree`, but only the first few levels are updated. | //! | `humanoids_active` | 4000 active humanoid rigs. | //! | `humanoids_inactive` | 4000 humanoid rigs. Only 10 are active. | //! | `humanoids_mixed` | 2000 active and 2000 inactive humanoid rigs. | use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, prelude::*, window::ExitCondition, }; use rand::Rng; /// pre-defined test configurations with name const CONFIGS: [(&str, Cfg); 9] = [ ( "large_tree", Cfg { test_case: TestCase::NonUniformTree { depth: 18, branch_width: 8, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "wide_tree", Cfg { test_case: TestCase::Tree { depth: 3, branch_width: 500, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "deep_tree", Cfg { test_case: TestCase::NonUniformTree { depth: 25, branch_width: 2, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "chain", Cfg { test_case: TestCase::Tree { depth: 2500, branch_width: 1, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "update_leaves", Cfg { test_case: TestCase::Tree { depth: 18, branch_width: 2, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 17, max_depth: u32::MAX, }, }, ), ( "update_shallow", Cfg { test_case: TestCase::Tree { depth: 18, branch_width: 2, }, update_filter: UpdateFilter { probability: 0.5, min_depth: 0, max_depth: 8, }, }, ), ( "humanoids_active", Cfg { test_case: TestCase::Humanoids { active: 4000, inactive: 0, }, update_filter: UpdateFilter { probability: 1.0, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "humanoids_inactive", Cfg { test_case: TestCase::Humanoids { active: 10, inactive: 3990, }, update_filter: UpdateFilter { probability: 1.0, min_depth: 0, max_depth: u32::MAX, }, }, ), ( "humanoids_mixed", Cfg { test_case: TestCase::Humanoids { active: 2000, inactive: 2000, }, update_filter: UpdateFilter { probability: 1.0, min_depth: 0, max_depth: u32::MAX, }, }, ), ]; fn print_available_configs() { println!("available configurations:"); for (name, _) in CONFIGS { println!(" {name}"); } } fn main() { // parse cli argument and find the selected test configuration let cfg: Cfg = match std::env::args().nth(1) { Some(arg) => match CONFIGS.iter().find(|(name, _)| *name == arg) { Some((name, cfg)) => { println!("test configuration: {name}"); cfg.clone() } None => { println!("test configuration \"{arg}\" not found.\n"); print_available_configs(); return; } }, None => { println!("missing argument: \n"); print_available_configs(); return; } }; println!("\n{cfg:#?}"); App::new() .insert_resource(cfg) .add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: None, exit_condition: ExitCondition::DontExit, ..default() }), FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin::default(), )) .add_systems(Startup, setup) // Updating transforms *must* be done before `PostUpdate` // or the hierarchy will momentarily be in an invalid state. .add_systems(Update, update) .run(); } /// test configuration #[derive(Resource, Debug, Clone)] struct Cfg { /// which test case should be inserted test_case: TestCase, /// which entities should be updated update_filter: UpdateFilter, } #[allow(unused)] #[derive(Debug, Clone)] enum TestCase { /// a uniform tree, exponentially growing with depth Tree { /// total depth depth: u32, /// number of children per node branch_width: u32, }, /// a non uniform tree (one side is deeper than the other) /// creates significantly less nodes than `TestCase::Tree` with the same parameters NonUniformTree { /// the maximum depth depth: u32, /// max number of children per node branch_width: u32, }, /// one or multiple humanoid rigs Humanoids { /// number of active instances (uses the specified [`UpdateFilter`]) active: u32, /// number of inactive instances (always inactive) inactive: u32, }, } /// a filter to restrict which nodes are updated #[derive(Debug, Clone)] struct UpdateFilter { /// starting depth (inclusive) min_depth: u32, /// end depth (inclusive) max_depth: u32, /// probability of a node to get updated (evaluated at insertion time, not during update) /// 0 (never) .. 1 (always) probability: f32, } /// update component with some per-component value #[derive(Component)] struct UpdateValue(f32); /// update positions system fn update(time: Res