Skip to main content

many_meshlet_materials/
many_meshlet_materials.rs

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
6use argh::FromArgs;
7use 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")]
17struct 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
28const ASSET_URL: &str =
29    "https://github.com/bevyengine/bevy_asset_files/raw/6dccaef517bde74d1969734703709aead7211dbc/meshlet/bunny.meshlet_mesh";
30
31fn 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
72fn 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}