Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Slight perf improvements and tidy for bevymark #3765

Closed
wants to merge 15 commits into from
147 changes: 84 additions & 63 deletions examples/tools/bevymark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ use bevy::{
prelude::*,
window::PresentMode,
};
use rand::random;
use rand::{thread_rng, Rng};
use std::fmt::Write;

const BIRDS_PER_SECOND: u32 = 10000;
const _BASE_COLOR: Color = Color::rgb(5.0, 5.0, 5.0);
const GRAVITY: f32 = -9.8 * 100.0;
const MAX_VELOCITY: f32 = 750.;
const BIRD_SCALE: f32 = 0.15;
const HALF_BIRD_SIZE: f32 = 256. * BIRD_SCALE * 0.5;

struct BevyCounter {
pub count: u128,
pub count: usize,
pub color: Color,
}

Expand All @@ -23,13 +23,16 @@ struct Bird {
velocity: Vec3,
}

/// This example provides a 2D benchmark.
///
/// Usage: spawn more entities by clicking on the screen.
fn main() {
App::new()
.insert_resource(WindowDescriptor {
title: "BevyMark".to_string(),
width: 800.,
height: 600.,
present_mode: PresentMode::Mailbox,
present_mode: PresentMode::Immediate,
resizable: true,
..Default::default()
})
Expand All @@ -54,8 +57,8 @@ fn main() {
}

struct BirdScheduled {
wave: u128,
per_wave: u128,
wave: usize,
per_wave: usize,
}

fn scheduled_spawner(
Expand All @@ -73,77 +76,84 @@ fn scheduled_spawner(
scheduled.per_wave,
bird_texture.0.clone_weak(),
);
counter.color = Color::rgb_linear(random(), random(), random());

let mut rng = thread_rng();
counter.color = Color::rgb_linear(rng.gen(), rng.gen(), rng.gen());
scheduled.wave -= 1;
}
}

struct BirdTexture(Handle<Image>);

#[derive(Component)]
struct StatsText;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let texture = asset_server.load("branding/icon.png");

commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(UiCameraBundle::default());
commands.spawn_bundle(TextBundle {
text: Text {
sections: vec![
TextSection {
value: "Bird Count: ".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 0.0),
commands
.spawn_bundle(TextBundle {
SUPERCILEX marked this conversation as resolved.
Show resolved Hide resolved
text: Text {
sections: vec![
TextSection {
value: "Bird Count: ".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 0.0),
},
},
},
TextSection {
value: "".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 1.0),
TextSection {
value: "".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 1.0),
},
},
},
TextSection {
value: "\nAverage FPS: ".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 0.0),
TextSection {
value: "\nAverage FPS: ".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 0.0),
},
},
},
TextSection {
value: "".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 1.0),
TextSection {
value: "".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
color: Color::rgb(0.0, 1.0, 1.0),
},
},
],
..Default::default()
},
style: Style {
position_type: PositionType::Absolute,
position: Rect {
top: Val::Px(5.0),
left: Val::Px(5.0),
..Default::default()
},
],
..Default::default()
},
style: Style {
position_type: PositionType::Absolute,
position: Rect {
top: Val::Px(5.0),
left: Val::Px(5.0),
..Default::default()
},
..Default::default()
},
..Default::default()
});
})
.insert(StatsText);

commands.insert_resource(BirdTexture(texture));
commands.insert_resource(BirdScheduled {
per_wave: std::env::args()
.nth(1)
.and_then(|arg| arg.parse::<u128>().ok())
.and_then(|arg| arg.parse::<usize>().ok())
.unwrap_or_default(),
wave: std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<u128>().ok())
.and_then(|arg| arg.parse::<usize>().ok())
.unwrap_or(1),
});
}
Expand All @@ -157,11 +167,12 @@ fn mouse_handler(
mut counter: ResMut<BevyCounter>,
) {
if mouse_button_input.just_released(MouseButton::Left) {
counter.color = Color::rgb_linear(random(), random(), random());
let mut rng = thread_rng();
counter.color = Color::rgb_linear(rng.gen(), rng.gen(), rng.gen());
}

if mouse_button_input.pressed(MouseButton::Left) {
let spawn_count = (BIRDS_PER_SECOND as f64 * time.delta_seconds_f64()) as u128;
let spawn_count = (BIRDS_PER_SECOND as f64 * time.delta_seconds_f64()) as usize;
spawn_birds(
&mut commands,
&windows,
Expand All @@ -176,12 +187,14 @@ fn spawn_birds(
commands: &mut Commands,
windows: &Windows,
counter: &mut BevyCounter,
spawn_count: u128,
spawn_count: usize,
texture: Handle<Image>,
) {
let window = windows.get_primary().unwrap();
let bird_x = (window.width() as f32 / -2.) + HALF_BIRD_SIZE;
let bird_y = (window.height() as f32 / 2.) - HALF_BIRD_SIZE;
let mut rng = thread_rng();

for count in 0..spawn_count {
let bird_z = (counter.count + count) as f32 * 0.00001;
commands
Expand All @@ -200,7 +213,7 @@ fn spawn_birds(
})
.insert(Bird {
velocity: Vec3::new(
rand::random::<f32>() * MAX_VELOCITY - (MAX_VELOCITY * 0.5),
rng.gen::<f32>() * MAX_VELOCITY - (MAX_VELOCITY * 0.5),
0.,
0.,
),
Expand All @@ -210,19 +223,21 @@ fn spawn_birds(
}

fn movement_system(time: Res<Time>, mut bird_query: Query<(&mut Bird, &mut Transform)>) {
for (mut bird, mut transform) in bird_query.iter_mut() {
// `.for_each_mut` is faster than `.iter_mut`, but can't be chained like a normal iterator.
bird_query.for_each_mut(|(mut bird, mut transform)| {
SUPERCILEX marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that using the for_each pattern here is worth it, but we should explain to beginners that this is faster (and ideally link to an explanation why).

Copy link
Contributor Author

@SUPERCILEX SUPERCILEX Jan 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I'd actually love to know why this is faster. This PR added it and said it was faster but not why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a note, though I have no idea if it's correct. 😅

Copy link
Contributor

@rparrett rparrett Feb 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment on the correctness of that note, but I feel like something like

// `.for_each_mut` is faster than `.iter`, but can't be chained like a normal iterator.

would be enough. (based on text here https://docs.rs/bevy/latest/bevy/ecs/system/struct.Query.html#method.for_each_mut)

transform.translation.x += bird.velocity.x * time.delta_seconds();
transform.translation.y += bird.velocity.y * time.delta_seconds();
bird.velocity.y += GRAVITY * time.delta_seconds();
}
});
}

fn collision_system(windows: Res<Windows>, mut bird_query: Query<(&mut Bird, &Transform)>) {
let window = windows.get_primary().unwrap();
let half_width = window.width() as f32 * 0.5;
let half_height = window.height() as f32 * 0.5;

for (mut bird, transform) in bird_query.iter_mut() {
// `.for_each_mut` is faster than `.iter_mut`, but can't be chained like a normal iterator.
bird_query.for_each_mut(|(mut bird, transform)| {
let x_vel = bird.velocity.x;
let y_vel = bird.velocity.y;
let x_pos = transform.translation.x;
Expand All @@ -239,20 +254,26 @@ fn collision_system(windows: Res<Windows>, mut bird_query: Query<(&mut Bird, &Tr
if y_pos + HALF_BIRD_SIZE > half_height && y_vel > 0.0 {
bird.velocity.y = 0.0;
}
}
});
}

fn counter_system(
diagnostics: Res<Diagnostics>,
counter: Res<BevyCounter>,
mut query: Query<&mut Text>,
mut query: Query<&mut Text, With<StatsText>>,
) {
let mut text = query.single_mut();

// Re-use string buffers here and below (clear + write) to save an allocation
if counter.is_changed() {
text.sections[1].value.clear();
SUPERCILEX marked this conversation as resolved.
Show resolved Hide resolved
write!(text.sections[1].value, "{}", counter.count).unwrap();
}

if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
if let Some(average) = fps.average() {
for mut text in query.iter_mut() {
text.sections[1].value = format!("{}", counter.count);
text.sections[3].value = format!("{:.2}", average);
}
text.sections[3].value.clear();
write!(text.sections[3].value, "{:.2}", average).unwrap();
}
};
}