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] - Add TextureAtlas stress test based on many_sprites and sprite_sheet examples #5087

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,16 @@ description = "A heavy sprite rendering workload to benchmark your system with B
category = "Stress Tests"
wasm = true

[[example]]
name = "many_animated_sprites"
path = "examples/stress_tests/many_animated_sprites.rs"

[package.metadata.example.many_animated_sprites]
name = "Many Animated Sprites"
description = "Displays many animated sprites in a grid arragement with slight offsets to their animation timers. Used for performance testing."
category = "Stress Tests"
wasm = true

[[example]]
name = "many_cubes"
path = "examples/stress_tests/many_cubes.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ cargo run --release --example <example name>
Example | Description
--- | ---
[Bevymark](../examples/stress_tests/bevymark.rs) | A heavy sprite rendering workload to benchmark your system with Bevy
[Many Animated Sprites](../examples/stress_tests/many_animated_sprites.rs) | Displays many animated sprites in a grid arragement with slight offsets to their animation timers. Used for performance testing.
[Many Cubes](../examples/stress_tests/many_cubes.rs) | Simple benchmark to test per-entity draw overhead. Run with the `sphere` argument to test frustum culling
[Many Foxes](../examples/stress_tests/many_foxes.rs) | Loads an animated fox model and spawns lots of them. Good for testing skinned mesh performance. Takes an unsigned integer argument for the number of foxes to spawn. Defaults to 1000
[Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights
Expand Down
136 changes: 136 additions & 0 deletions examples/stress_tests/many_animated_sprites.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//! Renders a lot of animated sprites to allow performance testing.
//!
//! It sets up many animated sprites in different sizes and rotations, and at different scales in the world,
//! and moves the camera over them to see how well frustum culling works.
//!
//! To measure performance realistically, be sure to run this in release mode.
//! `cargo run --example many_animated_sprites --release`

use std::time::Duration;

use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
math::Quat,
prelude::*,
render::camera::Camera,
};

use rand::Rng;

const CAMERA_SPEED: f32 = 1000.0;

fn main() {
App::new()
// Since this is also used as a benchmark, we want it to display performance data.
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_sprite)
.add_system(print_sprite_count)
.add_system(move_camera.after(print_sprite_count))
.run();
}

fn setup(
mut commands: Commands,
assets: Res<AssetServer>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
let mut rng = rand::thread_rng();

let tile_size = Vec2::splat(64.0);
let map_size = Vec2::splat(320.0);

let half_x = (map_size.x / 2.0) as i32;
let half_y = (map_size.y / 2.0) as i32;

let texture_handle = assets.load("textures/rpg/chars/gabe/gabe-idle-run.png");
let texture_atlas = TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1);
let texture_atlas_handle = texture_atlases.add(texture_atlas);

// Spawns the camera
commands
.spawn()
.insert_bundle(Camera2dBundle::default())
.insert(Transform::from_xyz(0.0, 0.0, 1000.0));

// Builds and spawns the sprites
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);
let mut timer = Timer::from_seconds(0.1, true);
timer.set_elapsed(Duration::from_secs_f32(rng.gen::<f32>()));

commands
.spawn_bundle(SpriteSheetBundle {
texture_atlas: texture_atlas_handle.clone(),
transform: Transform {
translation,
rotation,
scale,
},
sprite: TextureAtlasSprite {
custom_size: Some(tile_size),
..default()
},
..default()
})
.insert(AnimationTimer(timer));
}
}
}

// System for rotating and translating the camera
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(Quat::from_rotation_z(time.delta_seconds() * 0.5));
*camera_transform = *camera_transform
* Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
}

#[derive(Component, Deref, DerefMut)]
struct AnimationTimer(Timer);

fn animate_sprite(
time: Res<Time>,
texture_atlases: Res<Assets<TextureAtlas>>,
mut query: Query<(
&mut AnimationTimer,
&mut TextureAtlasSprite,
&Handle<TextureAtlas>,
)>,
) {
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
timer.tick(time.delta());
if timer.just_finished() {
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
sprite.index = (sprite.index + 1) % texture_atlas.textures.len();
}
}
}

#[derive(Deref, DerefMut)]
struct PrintingTimer(Timer);

impl Default for PrintingTimer {
fn default() -> Self {
Self(Timer::from_seconds(1.0, true))
}
}

// System for printing the number of sprites on every tick of the timer
fn print_sprite_count(
time: Res<Time>,
mut timer: Local<PrintingTimer>,
sprites: Query<&TextureAtlasSprite>,
) {
timer.tick(time.delta());

if timer.just_finished() {
info!("Sprites: {}", sprites.iter().count(),);
}
}