Skip to content

Commit

Permalink
Add many_materials stress test (bevyengine#17346)
Browse files Browse the repository at this point in the history
# Objective

- This PR adds a new stress test called `many_materials` to benchmark
the rendering performance of many animated materials.
- Fixes bevyengine#11588 
- This PR continues the work started in the previous PR bevyengine#11592, which
was closed due to inactivity.

## Solution

- Created a new example (`examples/stress_tests/many_materials.rs`) that
renders a grid of cubes with animated materials.
- The size of the grid can be configured using the `-n` command-line
argument (or `--grid-size`). The default grid size is 10x10.
- The materials animate by cycling through colors in the HSL color
space.

## Testing

- I have tested these changes locally on my Linux machine.
- Reviewers can test the changes by running the example with different
grid sizes and observing the performance (FPS, frame time).
- I have not tested on other platforms (macOS, Windows, wasm), but I
expect it to work as the code uses standard Bevy features.

---

## Showcase

<details>
  <summary>Click to view showcase</summary>


![image](https://github.com/user-attachments/assets/b15209d4-f832-402b-a527-58e5048971d1)

</details>
  • Loading branch information
CrazyRoka authored Jan 24, 2025
1 parent e20ac69 commit af3a84f
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,17 @@ description = "Displays many Text2d! Used for performance testing."
category = "Stress Tests"
wasm = true

[[example]]
name = "many_materials"
path = "examples/stress_tests/many_materials.rs"
doc-scrape-examples = true

[package.metadata.example.many_materials]
name = "Many Animated Materials"
description = "Benchmark to test rendering many animated materials"
category = "Stress Tests"
wasm = true

[[example]]
name = "transform_hierarchy"
path = "examples/stress_tests/transform_hierarchy.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ cargo run --release --example <example name>
Example | Description
--- | ---
[Bevymark](../examples/stress_tests/bevymark.rs) | A heavy sprite rendering workload to benchmark your system with Bevy
[Many Animated Materials](../examples/stress_tests/many_materials.rs) | Benchmark to test rendering many animated materials
[Many Animated Sprites](../examples/stress_tests/many_animated_sprites.rs) | Displays many animated sprites in a grid arrangement with slight offsets to their animation timers. Used for performance testing.
[Many Buttons](../examples/stress_tests/many_buttons.rs) | Test rendering of many UI elements
[Many Cameras & Lights](../examples/stress_tests/many_cameras_lights.rs) | Test rendering of many cameras and lights
Expand Down
103 changes: 103 additions & 0 deletions examples/stress_tests/many_materials.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! Benchmark to test rendering many animated materials
use argh::FromArgs;
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
window::{PresentMode, WindowPlugin, WindowResolution},
};
use std::f32::consts::PI;

#[derive(FromArgs, Resource)]
/// Command-line arguments for the `many_materials` stress test.
struct Args {
/// the size of the grid of materials to render (n x n)
#[argh(option, short = 'n', default = "10")]
grid_size: 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 {
resolution: WindowResolution::new(1920.0, 1080.0)
.with_scale_factor_override(1.0),
title: "many_materials".into(),
present_mode: PresentMode::AutoNoVsync,
..default()
}),
..default()
}),
FrameTimeDiagnosticsPlugin::default(),
LogDiagnosticsPlugin::default(),
))
.insert_resource(args)
.add_systems(Startup, setup)
.add_systems(Update, animate_materials)
.run();
}

fn setup(
mut commands: Commands,
args: Res<Args>,
mesh_assets: ResMut<Assets<Mesh>>,
material_assets: ResMut<Assets<StandardMaterial>>,
) {
let args = args.into_inner();
let material_assets = material_assets.into_inner();
let mesh_assets = mesh_assets.into_inner();
let n = args.grid_size;

// Camera
let w = n as f32;
commands.spawn((
Camera3d::default(),
Transform::from_xyz(w * 1.25, w + 1.0, w * 1.25)
.looking_at(Vec3::new(0.0, (w * -1.1) + 1.0, 0.0), Vec3::Y),
));

// Light
commands.spawn((
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
DirectionalLight {
illuminance: 3000.0,
shadows_enabled: true,
..default()
},
));

// Cubes
let mesh_handle = mesh_assets.add(Cuboid::from_size(Vec3::ONE));
for x in 0..n {
for z in 0..n {
commands.spawn((
Mesh3d(mesh_handle.clone()),
MeshMaterial3d(material_assets.add(Color::WHITE)),
Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)),
));
}
}
}

fn animate_materials(
material_handles: Query<&MeshMaterial3d<StandardMaterial>>,
time: Res<Time>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
for (i, material_handle) in material_handles.iter().enumerate() {
if let Some(material) = materials.get_mut(material_handle) {
let color = Color::hsl(
((i as f32 * 2.345 + time.elapsed_secs()) * 100.0) % 360.0,
1.0,
0.5,
);
material.base_color = color;
}
}
}

0 comments on commit af3a84f

Please sign in to comment.