docs/bevy/examples/stress_tests/many_meshlet_materials.rs 3.7 K · 132 lines · raw · history

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 println!("Meshlet Stress Test");
35 println!(
36 "Grid size: {}x{} ({} instances)",
37 args.grid_size,
38 args.grid_size,
39 args.grid_size * args.grid_size
40 );
41 println!(
42 "Materials: {}",
43 if args.unique_materials {
44 "UNIQUE"
45 } else {
46 "SHARED"
47 }
48 );
49
50 App::new()
51 .add_plugins((
52 DefaultPlugins.set(WindowPlugin {
53 primary_window: Some(Window {
54 present_mode: PresentMode::AutoNoVsync,
55 resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
56 ..default()
57 }),
58 ..default()
59 }),
60 FrameTimeDiagnosticsPlugin::default(),
61 LogDiagnosticsPlugin::default(),
62 MeshletPlugin {
63 cluster_buffer_slots: 8192,
64 },
65 ))
66 .insert_resource(WinitSettings::continuous())
67 .insert_resource(args)
68 .add_systems(Startup, setup)
69 .run();
70 }
71
72 fn setup(
73 mut commands: Commands,
74 args: Res<Args>,
75 asset_server: Res<AssetServer>,
76 mut materials: ResMut<Assets<StandardMaterial>>,
77 ) {
78 warn!(include_str!("warning_string.txt"));
79
80 let meshlet_handle = asset_server.load(ASSET_URL);
81
82 let n = args.grid_size;
83 let spacing = 2.0;
84
85 commands.spawn((
86 Camera3d::default(),
87 Transform::from_xyz(0.0, n as f32, n as f32 * 1.5).looking_at(Vec3::ZERO, Vec3::Y),
88 Msaa::Off,
89 ));
90
91 commands.spawn((
92 DirectionalLight {
93 illuminance: 3000.0,
94 shadow_maps_enabled: true,
95 ..default()
96 },
97 Transform::from_rotation(Quat::from_euler(
98 EulerRot::ZYX,
99 0.0,
100 1.0,
101 -std::f32::consts::FRAC_PI_4,
102 )),
103 ));
104
105 let shared_material = materials.add(StandardMaterial {
106 base_color: Color::WHITE,
107 ..default()
108 });
109
110 for x in 0..n {
111 for z in 0..n {
112 let material = if args.unique_materials {
113 materials.add(StandardMaterial {
114 base_color: Color::srgb(x as f32 / n as f32, 0.5, z as f32 / n as f32),
115 ..default()
116 })
117 } else {
118 shared_material.clone()
119 };
120
121 commands.spawn((
122 MeshletMesh3d(meshlet_handle.clone()),
123 MeshMaterial3d(material),
124 Transform::from_xyz(
125 x as f32 * spacing - n as f32,
126 0.0,
127 z as f32 * spacing - n as f32,
128 ),
129 ));
130 }
131 }
132 }