Add option to animate materials in many_cubes (#17927)

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.
This commit is contained in:
Griffin 2025-02-18 14:39:27 -08:00 committed by GitHub
parent 73970d0c12
commit c818c92143
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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)]
@ -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;
@ -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)
}