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
126 changes: 72 additions & 54 deletions examples/tools/bevymark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use bevy::{
diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
};
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);
SUPERCILEX marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -72,67 +73,74 @@ 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 {
Expand All @@ -156,7 +164,8 @@ 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) {
Expand All @@ -181,6 +190,8 @@ fn spawn_birds(
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 @@ -199,7 +210,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 @@ -209,19 +220,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() {
// Use for_each style iteration here and in the collision_system because it's faster thanks to
// better LLVM optimization opportunities (see transrangers) and hoisted branching.
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() {
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 @@ -238,20 +251,25 @@ 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();

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();
}
};
}