Skip to content

Commit ea953aa

Browse files
authored
Add a dense layout to the many_cubes stress test (#21745)
# Objective - Test the impact that many overlapping cubes, all within the camera frustum, have on visibility/occlusion calculations, especially with continuous rotation and shadows enabled. ## Solution - Add a dense cube layout using a static camera, and a toggle for individual cube rotation. ## Testing - I ran the test in several stages, each causing a considerable performance drop. - Stage 1: using just the dense layout. - Stage 2: using the dense layout with rotation or shadows enabled. - Stage 3: using the dense layout with both rotation and shadows enabled. ``` cargo run --release --example many_cubes -- --layout dense --rotate-cubes --shadows ``` --- ## Showcase <img width="1904" height="940" alt="image" src="https://github.com/user-attachments/assets/6fc811aa-81fa-4130-a6e0-0a017bc99f19" />
1 parent 0c669e5 commit ea953aa

File tree

1 file changed

+57
-5
lines changed

1 file changed

+57
-5
lines changed

examples/stress_tests/many_cubes.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bevy::{
1616
camera::visibility::{NoCpuCulling, NoFrustumCulling},
1717
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
1818
light::NotShadowCaster,
19-
math::{DVec2, DVec3},
19+
math::{ops::cbrt, DVec2, DVec3},
2020
prelude::*,
2121
render::{
2222
batching::NoAutomaticBatching,
@@ -72,16 +72,21 @@ struct Args {
7272
#[argh(switch)]
7373
shadows: bool,
7474

75+
/// whether to continuously rotate individual cubes.
76+
#[argh(switch)]
77+
rotate_cubes: bool,
78+
7579
/// animate the cube materials by updating the material from the cpu each frame
7680
#[argh(switch)]
7781
animate_materials: bool,
7882
}
7983

80-
#[derive(Default, Clone)]
84+
#[derive(Default, Clone, PartialEq)]
8185
enum Layout {
8286
Cube,
8387
#[default]
8488
Sphere,
89+
Dense,
8590
}
8691

8792
impl FromStr for Layout {
@@ -91,8 +96,9 @@ impl FromStr for Layout {
9196
match s {
9297
"cube" => Ok(Self::Cube),
9398
"sphere" => Ok(Self::Sphere),
99+
"dense" => Ok(Self::Dense),
94100
_ => Err(format!(
95-
"Unknown layout value: '{s}', valid options: 'cube', 'sphere'"
101+
"Unknown layout value: '{s}', valid options: 'cube', 'sphere', 'dense'"
96102
)),
97103
}
98104
}
@@ -123,7 +129,15 @@ fn main() {
123129
unfocused_mode: UpdateMode::Continuous,
124130
})
125131
.add_systems(Startup, setup)
126-
.add_systems(Update, (move_camera, print_mesh_count));
132+
.add_systems(Update, print_mesh_count);
133+
134+
if args.layout != Layout::Dense {
135+
app.add_systems(Update, move_camera);
136+
}
137+
138+
if args.rotate_cubes {
139+
app.add_systems(Update, rotate_cubes);
140+
}
127141

128142
if args.animate_materials {
129143
app.add_systems(Update, update_materials);
@@ -199,7 +213,7 @@ fn setup(
199213
NotShadowCaster,
200214
));
201215
}
202-
_ => {
216+
Layout::Cube => {
203217
// NOTE: This pattern is good for demonstrating that frustum culling is working correctly
204218
// as the number of visible meshes rises and falls depending on the viewing angle.
205219
let scale = 2.5;
@@ -247,6 +261,34 @@ fn setup(
247261
NotShadowCaster,
248262
));
249263
}
264+
Layout::Dense => {
265+
// NOTE: This pattern is good for demonstrating a dense configuration of cubes
266+
// overlapping each other, all within the camera frustum.
267+
let count = WIDTH * HEIGHT * 2;
268+
let size = cbrt(count as f32).round();
269+
let gap = 1.25;
270+
271+
let cubes = (0..count).map(move |i| {
272+
let x = i as f32 % size;
273+
let y = (i as f32 / size) % size;
274+
let z = i as f32 / (size * size);
275+
let pos = Vec3::new(x * gap, y * gap, z * gap);
276+
(
277+
Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
278+
MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
279+
Transform::from_translation(pos),
280+
)
281+
});
282+
283+
// camera
284+
commands.spawn((
285+
Camera3d::default(),
286+
Transform::from_xyz(100.0, 90.0, 100.0)
287+
.looking_at(Vec3::new(0.0, -10.0, 0.0), Vec3::Y),
288+
));
289+
290+
commands.spawn_batch(cubes);
291+
}
250292
}
251293

252294
commands.spawn((
@@ -294,6 +336,7 @@ fn init_materials(
294336
match args.layout {
295337
Layout::Cube => (WIDTH - WIDTH / 10) * (HEIGHT - HEIGHT / 10),
296338
Layout::Sphere => WIDTH * HEIGHT * 4,
339+
Layout::Dense => WIDTH * HEIGHT * 2,
297340
}
298341
} else {
299342
args.material_texture_count
@@ -500,6 +543,15 @@ fn update_materials(mut materials: ResMut<Assets<StandardMaterial>>, time: Res<T
500543
}
501544
}
502545

546+
fn rotate_cubes(
547+
mut query: Query<&mut Transform, (With<Mesh3d>, Without<NotShadowCaster>)>,
548+
time: Res<Time>,
549+
) {
550+
for mut transform in query.iter_mut() {
551+
transform.rotate_y(10.0 * time.delta_secs());
552+
}
553+
}
554+
503555
#[inline]
504556
fn fast_hue_to_rgb(hue: f32) -> Vec3 {
505557
(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 commit comments

Comments
 (0)