From ef2d6a6a77d5c7b696c839b401fb2d9a49d0f695 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Thu, 20 Mar 2025 18:34:10 +0000 Subject: [PATCH 01/13] Added `many_morph_targets` stress test. --- Cargo.toml | 11 ++ examples/stress_tests/many_morph_targets.rs | 124 ++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 examples/stress_tests/many_morph_targets.rs diff --git a/Cargo.toml b/Cargo.toml index af2d9d356cb59..7666127bf13a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3011,6 +3011,17 @@ description = "Loads an animated fox model and spawns lots of them. Good for tes category = "Stress Tests" wasm = true +[[example]] +name = "many_morph_targets" +path = "examples/stress_tests/many_morph_targets.rs" +doc-scrape-examples = true + +[package.metadata.example.many_morph_targets] +name = "Many Morph Targets" +description = "Simple benchmark to test rendering many meshes with animated morph targets." +category = "Stress Tests" +wasm = true + [[example]] name = "many_glyphs" path = "examples/stress_tests/many_glyphs.rs" diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs new file mode 100644 index 0000000000000..968cd64c0030f --- /dev/null +++ b/examples/stress_tests/many_morph_targets.rs @@ -0,0 +1,124 @@ +//! TODO + +use argh::FromArgs; +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + prelude::*, + scene::SceneInstanceReady, + window::{PresentMode, WindowResolution}, + winit::{UpdateMode, WinitSettings}, +}; +use std::f32::consts::PI; + +/// TODO +#[derive(FromArgs, Resource)] +struct Args { + /// TODO + #[argh(option, default = "1024")] + count: usize, +} + +fn main() { + // `from_env` panics on the web + #[cfg(not(target_arch = "wasm32"))] + let args: Args = argh::from_env(); + #[cfg(target_arch = "wasm32")] + let args = Args::from_args(&[], &[]).unwrap(); + + App::new() + .add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + title: "Many Morph Targets".to_string(), + present_mode: PresentMode::AutoNoVsync, + resolution: WindowResolution::new(1920.0, 1080.0) + .with_scale_factor_override(1.0), + ..default() + }), + ..Default::default() + }), + FrameTimeDiagnosticsPlugin::default(), + LogDiagnosticsPlugin::default(), + )) + .insert_resource(WinitSettings { + focused_mode: UpdateMode::Continuous, + unfocused_mode: UpdateMode::Continuous, + }) + .insert_resource(AmbientLight { + brightness: 1000.0, + ..Default::default() + }) + .insert_resource(args) + .add_systems(Startup, setup) + .run(); +} + +#[derive(Component)] +struct AnimationToPlay(Handle); + +fn setup(asset_server: Res, args: Res, mut commands: Commands) { + let scene_handle = asset_server + .load(GltfAssetLabel::Scene(0).from_asset("models/animated/MorphStressTest.gltf")); + + let animation_handles = (0..3) + .map(|index| { + asset_server.load( + GltfAssetLabel::Animation(index).from_asset("models/animated/MorphStressTest.gltf"), + ) + }) + .collect::>(); + + let count = args.count.max(1); + let x_dim = ((count as f32).sqrt().ceil() as usize).max(1); + let y_dim = count.div_ceil(x_dim); + + for mesh_index in 0..count { + let animation_index = mesh_index.rem_euclid(animation_handles.len()); + let animation_handle = animation_handles[animation_index].clone(); + + let x = 2.5 + (5.0 * ((mesh_index.rem_euclid(x_dim) as f32) - ((x_dim as f32) * 0.5))); + let y = -2.2 - (3.0 * ((mesh_index.div_euclid(x_dim) as f32) - ((y_dim as f32) * 0.5))); + + commands + .spawn(( + AnimationToPlay(animation_handle), + SceneRoot(scene_handle.clone()), + Transform::from_xyz(x, y, 0.0), + )) + .observe(play_animation); + } + + commands.spawn(( + DirectionalLight::default(), + Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)), + )); + + commands.spawn(( + Camera3d::default(), + Transform::from_xyz(0.0, 0.0, (x_dim as f32) * 4.0).looking_at(Vec3::ZERO, Vec3::Y), + )); +} + +fn play_animation( + trigger: Trigger, + mut commands: Commands, + children: Query<&Children>, + animations_to_play: Query<&AnimationToPlay>, + mut players: Query<&mut AnimationPlayer>, + mut graphs: ResMut>, +) { + if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) { + for child in children.iter_descendants(trigger.target()) { + if let Ok(mut player) = players.get_mut(child) { + let (graph, animation_index) = + AnimationGraph::from_clip(animation_to_play.0.clone()); + + commands + .entity(child) + .insert(AnimationGraphHandle(graphs.add(graph))); + + player.play(animation_index).repeat(); + } + } + } +} From 68a0ef6f506eba6e6cdc69b95d3ac135e09351c6 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:17:06 +0000 Subject: [PATCH 02/13] Fixed example docs. --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 202c41a4f1d98..6c8f8668470b1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -502,6 +502,7 @@ Example | Description [Many Gizmos](../examples/stress_tests/many_gizmos.rs) | Test rendering of many gizmos [Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering. [Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights +[Many Morph Targets](../examples/stress_tests/many_morph_targets.rs) | Simple benchmark to test rendering many meshes with animated morph targets. [Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites. [Many Text2d](../examples/stress_tests/many_text2d.rs) | Displays many Text2d! Used for performance testing. [Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark From 827da1ef6789460186ba48b482c45097694379cc Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:21:13 +0000 Subject: [PATCH 03/13] Added comments. Changed animation playing to share graphs instead of creating one per mesh. --- examples/stress_tests/many_morph_targets.rs | 60 +++++++++++---------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 968cd64c0030f..91b6033e1697a 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -1,4 +1,4 @@ -//! TODO +//! Simple benchmark to test rendering many meshes with animated morph targets. use argh::FromArgs; use bevy::{ @@ -10,10 +10,10 @@ use bevy::{ }; use std::f32::consts::PI; -/// TODO +/// `many_morph_targets` stress test #[derive(FromArgs, Resource)] struct Args { - /// TODO + /// number of meshes. #[argh(option, default = "1024")] count: usize, } @@ -53,38 +53,48 @@ fn main() { .run(); } -#[derive(Component)] -struct AnimationToPlay(Handle); - -fn setup(asset_server: Res, args: Res, mut commands: Commands) { - let scene_handle = asset_server - .load(GltfAssetLabel::Scene(0).from_asset("models/animated/MorphStressTest.gltf")); +#[derive(Component, Clone)] +struct AnimationToPlay { + graph_handle: Handle, + index: AnimationNodeIndex, +} - let animation_handles = (0..3) - .map(|index| { - asset_server.load( - GltfAssetLabel::Animation(index).from_asset("models/animated/MorphStressTest.gltf"), - ) +fn setup( + args: Res, + asset_server: Res, + mut graphs: ResMut>, + mut commands: Commands, +) { + const ASSET_PATH: &str = "models/animated/MorphStressTest.gltf"; + + let scene = SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(ASSET_PATH))); + + let animations = (0..3) + .map(|gltf_index| { + let (graph, index) = AnimationGraph::from_clip( + asset_server.load(GltfAssetLabel::Animation(gltf_index).from_asset(ASSET_PATH)), + ); + AnimationToPlay { + graph_handle: graphs.add(graph), + index, + } }) .collect::>(); + // Arrange the meshes in a grid. + let count = args.count.max(1); let x_dim = ((count as f32).sqrt().ceil() as usize).max(1); let y_dim = count.div_ceil(x_dim); for mesh_index in 0..count { - let animation_index = mesh_index.rem_euclid(animation_handles.len()); - let animation_handle = animation_handles[animation_index].clone(); + let animation = animations[mesh_index.rem_euclid(animations.len())].clone(); let x = 2.5 + (5.0 * ((mesh_index.rem_euclid(x_dim) as f32) - ((x_dim as f32) * 0.5))); let y = -2.2 - (3.0 * ((mesh_index.div_euclid(x_dim) as f32) - ((y_dim as f32) * 0.5))); commands - .spawn(( - AnimationToPlay(animation_handle), - SceneRoot(scene_handle.clone()), - Transform::from_xyz(x, y, 0.0), - )) + .spawn((animation, scene.clone(), Transform::from_xyz(x, y, 0.0))) .observe(play_animation); } @@ -105,19 +115,15 @@ fn play_animation( children: Query<&Children>, animations_to_play: Query<&AnimationToPlay>, mut players: Query<&mut AnimationPlayer>, - mut graphs: ResMut>, ) { if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) { for child in children.iter_descendants(trigger.target()) { if let Ok(mut player) = players.get_mut(child) { - let (graph, animation_index) = - AnimationGraph::from_clip(animation_to_play.0.clone()); - commands .entity(child) - .insert(AnimationGraphHandle(graphs.add(graph))); + .insert(AnimationGraphHandle(animation_to_play.graph_handle.clone())); - player.play(animation_index).repeat(); + player.play(animation_to_play.index).repeat(); } } } From 878a55b9bec9728f4ff2a915d749b74c8ca91aa7 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 07:26:18 +0000 Subject: [PATCH 04/13] Vary the animation speed so that performance is more stable. --- examples/stress_tests/many_morph_targets.rs | 31 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 91b6033e1697a..480805dfa2ff2 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -8,6 +8,8 @@ use bevy::{ window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, }; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::f32::consts::PI; /// `many_morph_targets` stress test @@ -57,6 +59,16 @@ fn main() { struct AnimationToPlay { graph_handle: Handle, index: AnimationNodeIndex, + speed: f32, +} + +impl AnimationToPlay { + fn with_speed(&self, speed: f32) -> Self { + AnimationToPlay { + speed, + ..self.clone() + } + } } fn setup( @@ -69,6 +81,8 @@ fn setup( let scene = SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(ASSET_PATH))); + let mut rng = ChaCha8Rng::seed_from_u64(856673); + let animations = (0..3) .map(|gltf_index| { let (graph, index) = AnimationGraph::from_clip( @@ -77,6 +91,7 @@ fn setup( AnimationToPlay { graph_handle: graphs.add(graph), index, + speed: 1.0, } }) .collect::>(); @@ -93,8 +108,17 @@ fn setup( let x = 2.5 + (5.0 * ((mesh_index.rem_euclid(x_dim) as f32) - ((x_dim as f32) * 0.5))); let y = -2.2 - (3.0 * ((mesh_index.div_euclid(x_dim) as f32) - ((y_dim as f32) * 0.5))); + // Randomly vary the animation speed so that the number of morph targets + // active on each frame is more likely to be stable. + + let animation_speed = rng.r#gen::() + 0.5; + commands - .spawn((animation, scene.clone(), Transform::from_xyz(x, y, 0.0))) + .spawn(( + animation.with_speed(animation_speed), + scene.clone(), + Transform::from_xyz(x, y, 0.0), + )) .observe(play_animation); } @@ -123,7 +147,10 @@ fn play_animation( .entity(child) .insert(AnimationGraphHandle(animation_to_play.graph_handle.clone())); - player.play(animation_to_play.index).repeat(); + player + .play(animation_to_play.index) + .repeat() + .set_speed(animation_to_play.speed); } } } From de11824063d8381651c21f0ae60c0414bb217c43 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 07:26:59 +0000 Subject: [PATCH 05/13] Prefer `core` over `std`. --- examples/stress_tests/many_morph_targets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 480805dfa2ff2..48e0e957c40fd 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -8,9 +8,9 @@ use bevy::{ window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, }; +use core::f32::consts::PI; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; -use std::f32::consts::PI; /// `many_morph_targets` stress test #[derive(FromArgs, Resource)] From 135c168fb3378afe5fdcf2024f2a7c7146ba8d3a Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 07:56:41 +0000 Subject: [PATCH 06/13] Added option to make the weights animated or all zero/one. --- examples/stress_tests/many_morph_targets.rs | 55 ++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 48e0e957c40fd..9a1bf014b567c 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -8,16 +8,40 @@ use bevy::{ window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, }; -use core::f32::consts::PI; +use core::{f32::consts::PI, str::FromStr}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; +#[derive(PartialEq)] +enum ArgWeights { + Animated, + None, + All, +} + +impl FromStr for ArgWeights { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "animated" => Ok(Self::Animated), + "none" => Ok(Self::None), + "all" => Ok(Self::All), + _ => Err("Accepted values: 'animated', 'none', 'all'".into()), + } + } +} + /// `many_morph_targets` stress test #[derive(FromArgs, Resource)] struct Args { /// number of meshes. #[argh(option, default = "1024")] count: usize, + + /// weights. + #[argh(option, default = "ArgWeights::Animated")] + weights: ArgWeights, } fn main() { @@ -119,7 +143,8 @@ fn setup( scene.clone(), Transform::from_xyz(x, y, 0.0), )) - .observe(play_animation); + .observe(play_animation) + .observe(set_weights); } commands.spawn(( @@ -136,10 +161,15 @@ fn setup( fn play_animation( trigger: Trigger, mut commands: Commands, + args: Res, children: Query<&Children>, animations_to_play: Query<&AnimationToPlay>, mut players: Query<&mut AnimationPlayer>, ) { + if args.weights != ArgWeights::Animated { + return; + } + if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) { for child in children.iter_descendants(trigger.target()) { if let Ok(mut player) = players.get_mut(child) { @@ -155,3 +185,24 @@ fn play_animation( } } } + +fn set_weights( + trigger: Trigger, + args: Res, + children: Query<&Children>, + mut weight_components: Query<&mut MorphWeights>, +) { + let weight_value = match args.weights { + ArgWeights::None => Some(0.0), + ArgWeights::All => Some(1.0), + _ => None, + }; + + if let Some(weight_value) = weight_value { + for child in children.iter_descendants(trigger.target()) { + if let Ok(mut weight_component) = weight_components.get_mut(child) { + weight_component.weights_mut().fill(weight_value); + } + } + } +} From 7a2d21a79aea134fd8bfe4142f62edb0d7459773 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 08:19:21 +0000 Subject: [PATCH 07/13] Argument cleanup. --- examples/stress_tests/many_morph_targets.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 9a1bf014b567c..38d4287ebd82f 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -15,8 +15,8 @@ use rand_chacha::ChaCha8Rng; #[derive(PartialEq)] enum ArgWeights { Animated, - None, All, + None, } impl FromStr for ArgWeights { @@ -27,7 +27,7 @@ impl FromStr for ArgWeights { "animated" => Ok(Self::Animated), "none" => Ok(Self::None), "all" => Ok(Self::All), - _ => Err("Accepted values: 'animated', 'none', 'all'".into()), + _ => Err("must be 'animated', 'none', or 'all'".into()), } } } @@ -35,11 +35,11 @@ impl FromStr for ArgWeights { /// `many_morph_targets` stress test #[derive(FromArgs, Resource)] struct Args { - /// number of meshes. + /// number of meshes #[argh(option, default = "1024")] count: usize, - /// weights. + /// options: 'animated', 'all', 'none' #[argh(option, default = "ArgWeights::Animated")] weights: ArgWeights, } @@ -193,8 +193,8 @@ fn set_weights( mut weight_components: Query<&mut MorphWeights>, ) { let weight_value = match args.weights { - ArgWeights::None => Some(0.0), ArgWeights::All => Some(1.0), + ArgWeights::None => Some(0.0), _ => None, }; From 8d84752404b7c223b216915b12bb1234387ae0ff Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 08:23:11 +0000 Subject: [PATCH 08/13] Added option to override camera distance. --- examples/stress_tests/many_morph_targets.rs | 30 ++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 38d4287ebd82f..adb0f1366718f 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -32,6 +32,24 @@ impl FromStr for ArgWeights { } } +#[derive(PartialEq)] +enum ArgCamera { + Default, + Far, +} + +impl FromStr for ArgCamera { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "default" => Ok(Self::Default), + "far" => Ok(Self::Far), + _ => Err("must be 'default' or 'far'".into()), + } + } +} + /// `many_morph_targets` stress test #[derive(FromArgs, Resource)] struct Args { @@ -42,6 +60,10 @@ struct Args { /// options: 'animated', 'all', 'none' #[argh(option, default = "ArgWeights::Animated")] weights: ArgWeights, + + /// options: 'default', 'far' + #[argh(option, default = "ArgCamera::Default")] + camera: ArgCamera, } fn main() { @@ -152,9 +174,15 @@ fn setup( Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)), )); + let camera_distance = (x_dim as f32) + * match args.camera { + ArgCamera::Default => 4.0, + ArgCamera::Far => 100.0, + }; + commands.spawn(( Camera3d::default(), - Transform::from_xyz(0.0, 0.0, (x_dim as f32) * 4.0).looking_at(Vec3::ZERO, Vec3::Y), + Transform::from_xyz(0.0, 0.0, camera_distance).looking_at(Vec3::ZERO, Vec3::Y), )); } From 2464d55626e0305b272316915e14d71f504c33c6 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 08:24:54 +0000 Subject: [PATCH 09/13] Allow zero meshes. --- examples/stress_tests/many_morph_targets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index adb0f1366718f..9f9328f8d6d99 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -144,7 +144,7 @@ fn setup( // Arrange the meshes in a grid. - let count = args.count.max(1); + let count = args.count; let x_dim = ((count as f32).sqrt().ceil() as usize).max(1); let y_dim = count.div_ceil(x_dim); From 3145106a597ba92fa6e4e038ddc7ab395b0defc9 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:02:06 +0000 Subject: [PATCH 10/13] Renamed weight options to be clearer. Added comments. Pulled far camera back a bit more. --- examples/stress_tests/many_morph_targets.rs | 29 ++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 9f9328f8d6d99..586078d130804 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -12,11 +12,17 @@ use core::{f32::consts::PI, str::FromStr}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; +/// Controls the weight values. #[derive(PartialEq)] enum ArgWeights { + /// Weights will change over time and be a mix of zero and non-zero. Animated, - All, - None, + + /// Set all the weights to one. + One, + + /// Set all the weights to zero, effectively disabling morph targets. + Zero, } impl FromStr for ArgWeights { @@ -25,16 +31,21 @@ impl FromStr for ArgWeights { fn from_str(s: &str) -> Result { match s { "animated" => Ok(Self::Animated), - "none" => Ok(Self::None), - "all" => Ok(Self::All), - _ => Err("must be 'animated', 'none', or 'all'".into()), + "zero" => Ok(Self::Zero), + "one" => Ok(Self::One), + _ => Err("must be 'animated', 'one', or 'zero'".into()), } } } +/// Controls the camera. #[derive(PartialEq)] enum ArgCamera { + /// Keep all the meshes in view and at a reasonable size. Default, + + /// Zoom far out. This is used to reduce pixel shader costs and so emphasise + /// vertex shader costs. Far, } @@ -57,7 +68,7 @@ struct Args { #[argh(option, default = "1024")] count: usize, - /// options: 'animated', 'all', 'none' + /// options: 'animated', 'one', 'zero' #[argh(option, default = "ArgWeights::Animated")] weights: ArgWeights, @@ -177,7 +188,7 @@ fn setup( let camera_distance = (x_dim as f32) * match args.camera { ArgCamera::Default => 4.0, - ArgCamera::Far => 100.0, + ArgCamera::Far => 200.0, }; commands.spawn(( @@ -221,8 +232,8 @@ fn set_weights( mut weight_components: Query<&mut MorphWeights>, ) { let weight_value = match args.weights { - ArgWeights::All => Some(1.0), - ArgWeights::None => Some(0.0), + ArgWeights::One => Some(1.0), + ArgWeights::Zero => Some(0.0), _ => None, }; From f92735b4ad14f3ae7fa627c0ce3943e8cbe72335 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:31:20 +0000 Subject: [PATCH 11/13] Added `--weights tiny` to better isolate vertex shader costs. --- examples/stress_tests/many_morph_targets.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 586078d130804..6525a573b7070 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -21,8 +21,12 @@ enum ArgWeights { /// Set all the weights to one. One, - /// Set all the weights to zero, effectively disabling morph targets. + /// Set all the weights to zero, minimising vertex shader cost. Zero, + + /// Set all the weights to a very small value, so the pixel shader cost + /// should be similar to `Zero` but vertex shader cost the same as `One`. + Tiny, } impl FromStr for ArgWeights { @@ -33,7 +37,8 @@ impl FromStr for ArgWeights { "animated" => Ok(Self::Animated), "zero" => Ok(Self::Zero), "one" => Ok(Self::One), - _ => Err("must be 'animated', 'one', or 'zero'".into()), + "tiny" => Ok(Self::Tiny), + _ => Err("must be 'animated', 'one', `zero`, or 'tiny'".into()), } } } @@ -61,14 +66,14 @@ impl FromStr for ArgCamera { } } -/// `many_morph_targets` stress test +/// many_morph_targets stress test #[derive(FromArgs, Resource)] struct Args { /// number of meshes #[argh(option, default = "1024")] count: usize, - /// options: 'animated', 'one', 'zero' + /// options: 'animated', 'one', 'zero', 'tiny' #[argh(option, default = "ArgWeights::Animated")] weights: ArgWeights, @@ -234,6 +239,7 @@ fn set_weights( let weight_value = match args.weights { ArgWeights::One => Some(1.0), ArgWeights::Zero => Some(0.0), + ArgWeights::Tiny => Some(0.00001), _ => None, }; From 2623099a3d2d490c8c61c3436360a1371277e7f7 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:12:42 +0000 Subject: [PATCH 12/13] Fix clippy. --- examples/stress_tests/many_morph_targets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 6525a573b7070..412fb52af1700 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -66,7 +66,7 @@ impl FromStr for ArgCamera { } } -/// many_morph_targets stress test +/// `many_morph_targets` stress test #[derive(FromArgs, Resource)] struct Args { /// number of meshes From aebff7468e20ab95d4bd8ef6dd7ff26db9ab097e Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:20:11 +0000 Subject: [PATCH 13/13] Typos. --- examples/stress_tests/many_morph_targets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stress_tests/many_morph_targets.rs b/examples/stress_tests/many_morph_targets.rs index 412fb52af1700..dcf3c23211507 100644 --- a/examples/stress_tests/many_morph_targets.rs +++ b/examples/stress_tests/many_morph_targets.rs @@ -21,7 +21,7 @@ enum ArgWeights { /// Set all the weights to one. One, - /// Set all the weights to zero, minimising vertex shader cost. + /// Set all the weights to zero, minimizing vertex shader cost. Zero, /// Set all the weights to a very small value, so the pixel shader cost @@ -49,7 +49,7 @@ enum ArgCamera { /// Keep all the meshes in view and at a reasonable size. Default, - /// Zoom far out. This is used to reduce pixel shader costs and so emphasise + /// Zoom far out. This is used to reduce pixel shader costs and so emphasize /// vertex shader costs. Far, }