Skip to content

Commit

Permalink
Add option to animate materials in many_cubes (#17927)
Browse files Browse the repository at this point in the history
This adds an option to animate the materials in the `many_cubes` stress
test. Each material instance `base_color` is varied each frame.

This has been tested in conjunction with the
`--vary-material-data-per-instance` and `--material-texture-count`
options.

If `--vary-material-data-per-instance` is not used it will just update
the single material, otherwise it will update all of them. If
`--material-texture-count` is used the `base_color` is multiplied with
the texture so the effect is still visible.

Because this test is focused on the performance of updating material
data and not the performance of bevy's color system it uses its own
function (`fast_hue_to_rgb`) to quickly set the hue. This appeared to be
around 8x faster than using `base_color.set_hue(hue)` in the tight loop.
  • Loading branch information
DGriffin91 authored Feb 18, 2025
1 parent 73970d0 commit c818c92
Showing 1 changed file with 42 additions and 20 deletions.
62 changes: 42 additions & 20 deletions examples/stress_tests/many_cubes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ struct Args {
/// whether to enable directional light cascaded shadow mapping.
#[argh(switch)]
shadows: bool,

/// animate the cube materials by updating the material from the cpu each frame
#[argh(switch)]
animate_materials: bool,
}

#[derive(Default, Clone)]
Expand Down Expand Up @@ -100,28 +104,31 @@ fn main() {
#[cfg(target_arch = "wasm32")]
let args = Args::from_args(&[], &[]).unwrap();

App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync,
resolution: WindowResolution::new(1920.0, 1080.0)
.with_scale_factor_override(1.0),
..default()
}),
let mut app = App::new();
app.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync,
resolution: WindowResolution::new(1920.0, 1080.0).with_scale_factor_override(1.0),
..default()
}),
FrameTimeDiagnosticsPlugin::default(),
LogDiagnosticsPlugin::default(),
))
.insert_resource(WinitSettings {
focused_mode: UpdateMode::Continuous,
unfocused_mode: UpdateMode::Continuous,
})
.insert_resource(args)
.add_systems(Startup, setup)
.add_systems(Update, (move_camera, print_mesh_count))
.run();
..default()
}),
FrameTimeDiagnosticsPlugin::default(),
LogDiagnosticsPlugin::default(),
))
.insert_resource(WinitSettings {
focused_mode: UpdateMode::Continuous,
unfocused_mode: UpdateMode::Continuous,
})
.add_systems(Startup, setup)
.add_systems(Update, (move_camera, print_mesh_count));

if args.animate_materials {
app.add_systems(Update, update_materials);
}

app.insert_resource(args).run();
}

const WIDTH: usize = 200;
Expand Down Expand Up @@ -475,3 +482,18 @@ impl Default for PrintingTimer {
Self(Timer::from_seconds(1.0, TimerMode::Repeating))
}
}

fn update_materials(mut materials: ResMut<Assets<StandardMaterial>>, time: Res<Time>) {
let elapsed = time.elapsed_secs();
for (i, (_, material)) in materials.iter_mut().enumerate() {
let hue = (elapsed + i as f32 * 0.005).rem_euclid(1.0);
// This is much faster than using base_color.set_hue(hue), and in a tight loop it shows.
let color = fast_hue_to_rgb(hue);
material.base_color = Color::linear_rgb(color.x, color.y, color.z);
}
}

#[inline]
fn fast_hue_to_rgb(hue: f32) -> Vec3 {
(hue * 6.0 - vec3(3.0, 2.0, 4.0)).abs() * vec3(1.0, -1.0, -1.0) + vec3(-1.0, 2.0, 2.0)
}

0 comments on commit c818c92

Please sign in to comment.