Skip to content

Instantly share code, notes, and snippets.

@Maximetinu
Last active August 7, 2023 16:56
Show Gist options
  • Save Maximetinu/0e889e60ed3cef9fbabb7c61eaf41a1b to your computer and use it in GitHub Desktop.
Save Maximetinu/0e889e60ed3cef9fbabb7c61eaf41a1b to your computer and use it in GitHub Desktop.
Example benchmark for Bevy 0.11 to replicate a performance bottleneck when updating too many individual materials at runtime
use bevy::{
prelude::*,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef},
sprite::*,
};
use bevy_internal::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
sprite::Material2d,
window::PresentMode,
};
use rand::Rng;
const CAMERA_SPEED: f32 = 1000.0;
fn main() {
App::new()
.add_plugins((
LogDiagnosticsPlugin::default(),
FrameTimeDiagnosticsPlugin,
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync,
..default()
}),
..default()
}),
Material2dPlugin::<CustomMaterial2d>::default(),
))
.add_systems(Startup, setup)
.add_systems(Update, (move_camera, mutate_materials))
.run();
}
fn random_color() -> Color {
let mut rng = rand::thread_rng();
Color::rgba(rng.gen(), rng.gen(), rng.gen(), rng.gen())
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<CustomMaterial2d>>,
asset_server: Res<AssetServer>,
) {
let mut rng = rand::thread_rng();
// Separation between the images
let tile_size = Vec2::splat(64.0);
// The amount of entities will be determined by this size
// 80 is 80 * 80 = 6400 entities
let map_size = Vec2::splat(80.0);
let half_x = (map_size.x / 2.0) as i32;
let half_y = (map_size.y / 2.0) as i32;
let sprite_handle = asset_server.load("branding/icon.png");
// Can create the mesh handle beforehand
let mesh_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::ONE)));
// But can't do the same for the materials
// This would have a better performance, but in my real world scenario, I can't,
// because the quad size and material parameters are dynamic and only known at runtime
// let material_handle_beforehand = materials.add(CustomMaterial2d {
// color: random_color(),
// color_texture: Some(sprite_handle.clone()),
// })
// Besides, I need to update the properties of the material of some of them dynamically,
// which I cannot know in advance. This is the reason for the bad performance
let mut sprites = vec![];
for y in -half_y..half_y {
for x in -half_x..half_x {
let position = Vec2::new(x as f32, y as f32);
let translation = (position * tile_size).extend(rng.gen::<f32>());
let rotation = Quat::from_rotation_z(rng.gen::<f32>());
let scale = Vec3::splat(rng.gen::<f32>() * 2.0) * 256.0;
sprites.push(MaterialMesh2dBundle {
mesh: mesh_handle.clone().into(),
transform: Transform {
translation,
rotation,
scale,
},
// material: material_handle_beforehand.clone(),
material: materials.add(CustomMaterial2d {
color: random_color(),
color_texture: Some(sprite_handle.clone()),
}),
..Default::default()
});
}
}
commands.spawn_batch(sprites);
commands.spawn(Camera2dBundle::default());
}
impl Material2d for CustomMaterial2d {
fn fragment_shader() -> ShaderRef {
"shaders/custom_material.wgsl".into()
}
}
#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)]
#[uuid = "b8f64602-437b-4ed4-a054-9595585fd74d"]
pub struct CustomMaterial2d {
#[uniform(0)]
color: Color,
#[texture(1)]
#[sampler(2)]
color_texture: Option<Handle<Image>>,
}
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
let mut camera_transform = camera_query.single_mut();
camera_transform.rotate_z(time.delta_seconds() * 0.5);
*camera_transform = *camera_transform
* Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
}
// Simulate materials mutation at runtime
// This is another reason for bad performance. With no mutation, performance is still good.
fn mutate_materials(
mesh2d_materials: Query<&Handle<CustomMaterial2d>>,
mut mats: ResMut<Assets<CustomMaterial2d>>,
) {
for quad_mat in mesh2d_materials.iter() {
let mat = mats.get_mut(quad_mat).unwrap();
mat.color = mat.color + Color::rgba(0.00001, 0.00001, 0.00001, 0.00001);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment