Skip to content

Commit 9863967

Browse files
CrazyRokacartmockersf
authored
Add many_meshlet_materials stress test example (#23792)
# Objective - Introduce a dedicated example to measure performance overhead when dealing with a high number of meshlet instances and unique materials. ## Solution - Added a new example `many_meshlet_materials` that spawns a configurable grid of meshlet bunnies. ## Testing - `SystemInfo { os: "Linux (CachyOS Linux rolling)", kernel: "6.19.10-1-cachyos", cpu: "AMD Ryzen 7 5800H with Radeon Graphics", core_count: "8", memory: "15.0 GiB" }` - `AdapterInfo { name: "AMD Radeon Graphics (RADV RENOIR)", vendor: 4098, device: 5688, device_type: IntegratedGpu, device_pci_bus_id: "0000:03:00.0", driver: "radv", driver_info: "Mesa 26.0.3-arch2.2", backend: Vulkan, subgroup_min_size: 64, subgroup_max_size: 64, transient_saves_memory: false }` - `cargo run --features=meshlet,https --release --example many_meshlet_materials` --- ## Showcase Example works perfectly with 5x5 grid. <img width="1024" height="599" alt="image" src="https://github.com/user-attachments/assets/948b42b7-e88e-4a91-9fb7-f1c1b4d27e32" /> The issue begins when grid count increases to 10x10. Meshes start flickering. <img width="1024" height="598" alt="image" src="https://github.com/user-attachments/assets/055df47f-39dc-41bc-be05-1978ec352e3a" /> With 50x50 meshes, the whole screen flickers. <img width="1024" height="595" alt="image" src="https://github.com/user-attachments/assets/7d685a2b-7389-43fe-b04e-a5987136f459" /> When running with unique materials, performance significantly drops on my hardware. <img width="2224" height="1298" alt="image" src="https://github.com/user-attachments/assets/8d5b4677-7851-4234-9467-992f8db62a9d" /> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: François Mockers <francois.mockers@vleue.com>
1 parent b5f5a42 commit 9863967

3 files changed

Lines changed: 145 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,6 +3573,19 @@ description = "Displays many Text2d! Used for performance testing."
35733573
category = "Stress Tests"
35743574
wasm = true
35753575

3576+
[[example]]
3577+
name = "many_meshlet_materials"
3578+
path = "examples/stress_tests/many_meshlet_materials.rs"
3579+
doc-scrape-examples = true
3580+
required-features = ["meshlet", "https"]
3581+
3582+
[package.metadata.example.many_meshlet_materials]
3583+
name = "Many Meshlet Materials"
3584+
description = "Benchmark to test rendering many meshlet materials (experimental)"
3585+
category = "Stress Tests"
3586+
# Requires compute shaders and WGPU extensions, not supported by WebGL nor WebGPU.
3587+
wasm = false
3588+
35763589
[[example]]
35773590
name = "many_materials"
35783591
path = "examples/stress_tests/many_materials.rs"

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ Example | Description
553553
[Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering.
554554
[Many Gradients](../examples/stress_tests/many_gradients.rs) | Stress test for gradient rendering performance
555555
[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
556+
[Many Meshlet Materials](../examples/stress_tests/many_meshlet_materials.rs) | Benchmark to test rendering many meshlet materials (experimental)
556557
[Many Morph Targets](../examples/stress_tests/many_morph_targets.rs) | Simple benchmark to test rendering many meshes with animated morph targets.
557558
[Many Sprite Meshes](../examples/stress_tests/many_sprite_meshes.rs) | Displays many sprite meshes in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites.
558559
[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.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//! A stress test for the Meshlet pipeline specialization overhead.
2+
//!
3+
//! Run with `--unique-materials` to trigger the unconditional specialization bug.
4+
//! Run without it (shared material) to see the baseline performance.
5+
6+
use argh::FromArgs;
7+
use bevy::{
8+
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
9+
pbr::experimental::meshlet::{MeshletMesh3d, MeshletPlugin},
10+
prelude::*,
11+
window::{PresentMode, WindowResolution},
12+
winit::WinitSettings,
13+
};
14+
15+
#[derive(FromArgs, Resource)]
16+
#[argh(description = "Meshlet Material Stress Test")]
17+
struct Args {
18+
/// the grid size (e.g., 50 means 50x50 = 2500 meshlets)
19+
#[argh(option, short = 'n', default = "50")]
20+
grid_size: usize,
21+
22+
/// if set, every meshlet gets a unique material asset.
23+
/// This triggers the unconditional pipeline specialization bug in `prepare_material_meshlet_meshes`.
24+
#[argh(switch)]
25+
unique_materials: bool,
26+
}
27+
28+
const ASSET_URL: &str =
29+
"https://github.com/bevyengine/bevy_asset_files/raw/6dccaef517bde74d1969734703709aead7211dbc/meshlet/bunny.meshlet_mesh";
30+
31+
fn main() {
32+
let args: Args = argh::from_env();
33+
34+
warn!(include_str!("warning_string.txt"));
35+
println!("Meshlet Stress Test");
36+
println!(
37+
"Grid size: {}x{} ({} instances)",
38+
args.grid_size,
39+
args.grid_size,
40+
args.grid_size * args.grid_size
41+
);
42+
println!(
43+
"Materials: {}",
44+
if args.unique_materials {
45+
"UNIQUE"
46+
} else {
47+
"SHARED"
48+
}
49+
);
50+
51+
App::new()
52+
.add_plugins((
53+
DefaultPlugins.set(WindowPlugin {
54+
primary_window: Some(Window {
55+
present_mode: PresentMode::AutoNoVsync,
56+
resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
57+
..default()
58+
}),
59+
..default()
60+
}),
61+
FrameTimeDiagnosticsPlugin::default(),
62+
LogDiagnosticsPlugin::default(),
63+
MeshletPlugin {
64+
cluster_buffer_slots: 8192,
65+
},
66+
))
67+
.insert_resource(WinitSettings::continuous())
68+
.insert_resource(args)
69+
.add_systems(Startup, setup)
70+
.run();
71+
}
72+
73+
fn setup(
74+
mut commands: Commands,
75+
args: Res<Args>,
76+
asset_server: Res<AssetServer>,
77+
mut materials: ResMut<Assets<StandardMaterial>>,
78+
) {
79+
let meshlet_handle = asset_server.load(ASSET_URL);
80+
81+
let n = args.grid_size;
82+
let spacing = 2.0;
83+
84+
commands.spawn((
85+
Camera3d::default(),
86+
Transform::from_xyz(0.0, n as f32, n as f32 * 1.5).looking_at(Vec3::ZERO, Vec3::Y),
87+
Msaa::Off,
88+
));
89+
90+
commands.spawn((
91+
DirectionalLight {
92+
illuminance: 3000.0,
93+
shadow_maps_enabled: true,
94+
..default()
95+
},
96+
Transform::from_rotation(Quat::from_euler(
97+
EulerRot::ZYX,
98+
0.0,
99+
1.0,
100+
-std::f32::consts::FRAC_PI_4,
101+
)),
102+
));
103+
104+
let shared_material = materials.add(StandardMaterial {
105+
base_color: Color::WHITE,
106+
..default()
107+
});
108+
109+
for x in 0..n {
110+
for z in 0..n {
111+
let material = if args.unique_materials {
112+
materials.add(StandardMaterial {
113+
base_color: Color::srgb(x as f32 / n as f32, 0.5, z as f32 / n as f32),
114+
..default()
115+
})
116+
} else {
117+
shared_material.clone()
118+
};
119+
120+
commands.spawn((
121+
MeshletMesh3d(meshlet_handle.clone()),
122+
MeshMaterial3d(material),
123+
Transform::from_xyz(
124+
x as f32 * spacing - n as f32,
125+
0.0,
126+
z as f32 * spacing - n as f32,
127+
),
128+
));
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)