diff --git a/Cargo.toml b/Cargo.toml index 69f7b7de..01b43b09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["bevy_rapier2d", "bevy_rapier3d"] +members = ["bevy_rapier2d", "bevy_rapier3d", "bevy_rapier_benches3d"] resolver = "2" [profile.dev] diff --git a/bevy_rapier3d/Cargo.toml b/bevy_rapier3d/Cargo.toml index b0b5a495..58690f76 100644 --- a/bevy_rapier3d/Cargo.toml +++ b/bevy_rapier3d/Cargo.toml @@ -60,7 +60,17 @@ bevy = { version = "0.14", default-features = false, features = [ ] } approx = "0.5.1" glam = { version = "0.27", features = ["approx"] } +divan = "0.1" +bevy_rapier_benches3d = { version = "0.1", path = "../bevy_rapier_benches3d" } [package.metadata.docs.rs] # Enable all the features when building the docs on docs.rs features = ["debug-render-3d", "serde-serialize"] + +[[bench]] +name = "cubes" +harness = false + +[[bench]] +name = "many_pyramids3" +harness = false diff --git a/bevy_rapier3d/benches/common.rs b/bevy_rapier3d/benches/common.rs new file mode 100644 index 00000000..86229fc4 --- /dev/null +++ b/bevy_rapier3d/benches/common.rs @@ -0,0 +1,12 @@ +use bevy::app::App; +use bevy_rapier_benches3d::common::{default_app, wait_app_start}; + +pub fn bench_app_updates(bencher: divan::Bencher, setup: impl Fn(&mut App)) { + let mut app = default_app(); + setup(&mut app); + wait_app_start(&mut app); + + bencher.bench_local(|| { + app.update(); + }); +} diff --git a/bevy_rapier3d/benches/cubes.rs b/bevy_rapier3d/benches/cubes.rs new file mode 100644 index 00000000..6095783f --- /dev/null +++ b/bevy_rapier3d/benches/cubes.rs @@ -0,0 +1,52 @@ +//! Translated from avian benchmark. +//! +//! + +mod common; + +use common::bench_app_updates; + +use bevy::prelude::*; +use bevy_rapier3d::math::*; +use bevy_rapier3d::prelude::*; + +fn setup_cubes(app: &mut App, size: u32) { + app.add_systems(Startup, move |mut commands: Commands| { + commands.spawn(( + RigidBody::Fixed, + Transform::from_translation(-2.0 * Vect::Z), + Collider::cuboid(100.0, 1.0, 100.0), + )); + for x in 0..size { + for z in 0..size { + commands.spawn(( + RigidBody::Dynamic, + Transform::from_translation(Vec3::new(x as f32, 2.0, z as f32)), + Collider::cuboid(1.0, 1.0, 1.0), + )); + } + } + }); +} + +#[divan::bench(sample_count = 60, sample_size = 1)] +fn cubes_3x3(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_cubes(app, 3)) +} +#[divan::bench(sample_count = 60, sample_size = 1)] +fn cubes_5x5(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_cubes(app, 5)) +} +#[divan::bench(sample_count = 60, sample_size = 1)] +fn cubes_10x10(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_cubes(app, 10)) +} +#[divan::bench(sample_count = 60, sample_size = 1)] +fn cubes_20x20(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_cubes(app, 20)) +} + +fn main() { + // Run registered benchmarks. + divan::main(); +} diff --git a/bevy_rapier3d/benches/many_pyramids3.rs b/bevy_rapier3d/benches/many_pyramids3.rs new file mode 100644 index 00000000..e4ca49a4 --- /dev/null +++ b/bevy_rapier3d/benches/many_pyramids3.rs @@ -0,0 +1,28 @@ +//! Translated from rapier benchmark. +//! +//! + +mod common; + +use bevy_rapier_benches3d::pyramids::setup_pyramids; +use common::bench_app_updates; + +#[divan::bench(sample_count = 60, sample_size = 1)] +fn pyramid_1_with_height_2(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_pyramids(app, 1, 2)); +} + +#[divan::bench(sample_count = 60, sample_size = 1)] +fn pyramid_1_with_height_20(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_pyramids(app, 1, 20)); +} + +#[divan::bench(sample_count = 60, sample_size = 1)] +fn pyramid_2_with_height_20(bencher: divan::Bencher) { + bench_app_updates(bencher, |app| setup_pyramids(app, 2, 20)); +} + +fn main() { + // Run registered benchmarks. + divan::main(); +} diff --git a/bevy_rapier_benches3d/Cargo.toml b/bevy_rapier_benches3d/Cargo.toml new file mode 100644 index 00000000..2bb0caba --- /dev/null +++ b/bevy_rapier_benches3d/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "bevy_rapier_benches3d" +version = "0.1.0" +description = "Custom benchmarks for bevy_rapier." +readme = "./README.md" +license = "Apache-2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rapier3d = { features = ["profiler"], version = "0.21" } +bevy_rapier3d = { version = "0.27.0-rc.1", path = "../bevy_rapier3d" } +bevy = { version = "0.14.0-rc.3", default-features = false } diff --git a/bevy_rapier_benches3d/README.md b/bevy_rapier_benches3d/README.md new file mode 100644 index 00000000..7079cd61 --- /dev/null +++ b/bevy_rapier_benches3d/README.md @@ -0,0 +1,24 @@ +# bevy_rapier custom benches + +`bevy_rapier_benches3d` 's objective is to measure timings with detailed information +without spending too much time running multiple times expensive benchmarks. + +It is implemented as a standalone binary, running different scenes setup, gathering information +and outputs them at the end. + +```sh +cargo run --release --bin bench +``` + +## cargo bench + +For short-lived benchmarks based on statistical analysis, +benchmarks can be run through the [divan](https://github.com/nvzqz/divan) bench harness. + +```sh +cargo bench -p bevy_rapier3d +``` + +## Other resources + +- [Bevy profiling](https://github.com/bevyengine/bevy/blob/main/docs/profiling.md) diff --git a/bevy_rapier_benches3d/src/bin/bench.rs b/bevy_rapier_benches3d/src/bin/bench.rs new file mode 100644 index 00000000..e864541b --- /dev/null +++ b/bevy_rapier_benches3d/src/bin/bench.rs @@ -0,0 +1,14 @@ +use bevy_rapier_benches3d::{custom_bencher, pyramids::setup_pyramids}; + +fn pyramid_1_with_height_2() { + custom_bencher(1000, |app| setup_pyramids(app, 1, 2)); +} + +fn pyramid_2_with_height_20() { + custom_bencher(100, |app| setup_pyramids(app, 3, 20)); +} + +fn main() { + pyramid_1_with_height_2(); + pyramid_2_with_height_20(); +} diff --git a/bevy_rapier_benches3d/src/common.rs b/bevy_rapier_benches3d/src/common.rs new file mode 100644 index 00000000..9a2f5f6f --- /dev/null +++ b/bevy_rapier_benches3d/src/common.rs @@ -0,0 +1,48 @@ +use bevy::{ + app::PluginsState, + prelude::*, + render::{ + settings::{RenderCreation, WgpuSettings}, + RenderPlugin, + }, + scene::ScenePlugin, + time::TimeUpdateStrategy, +}; +use bevy_rapier3d::prelude::*; + +pub fn default_app() -> App { + let mut app = App::new(); + + app.add_plugins(( + WindowPlugin::default(), + MinimalPlugins, + AssetPlugin::default(), + ScenePlugin, + RenderPlugin { + render_creation: RenderCreation::Automatic(WgpuSettings { + backends: None, + ..Default::default() + }), + ..Default::default() + }, + ImagePlugin::default(), + HierarchyPlugin, + TransformPlugin, + RapierPhysicsPlugin::<()>::default(), + )); + + // 60 physics + app.insert_resource(TimeUpdateStrategy::ManualDuration( + std::time::Duration::from_secs_f32(1f32 / 60f32), + )); + app +} + +pub fn wait_app_start(app: &mut App) { + while app.plugins_state() != PluginsState::Ready { + bevy::tasks::tick_global_task_pools_on_main_thread(); + } + + app.finish(); + app.cleanup(); +} diff --git a/bevy_rapier_benches3d/src/lib.rs b/bevy_rapier_benches3d/src/lib.rs new file mode 100644 index 00000000..3e3220aa --- /dev/null +++ b/bevy_rapier_benches3d/src/lib.rs @@ -0,0 +1,42 @@ +//! Translated from rapier benchmark. +//! +//! + +pub mod common; +pub mod pyramids; + +use common::default_app; +use common::wait_app_start; + +use bevy::prelude::*; +use bevy_rapier3d::plugin::RapierContext; + +pub fn custom_bencher(steps: usize, setup: impl Fn(&mut App)) { + let mut app = default_app(); + setup(&mut app); + wait_app_start(&mut app); + + let mut timer_total = rapier3d::counters::Timer::new(); + let mut timer_full_update = rapier3d::counters::Timer::new(); + let mut rapier_step_times = vec![]; + let mut total_update_times = vec![]; + timer_total.start(); + for _ in 0..steps { + timer_full_update.start(); + app.update(); + timer_full_update.pause(); + let elapsed_time = timer_full_update.time() as f32; + let rc = app.world().resource::(); + rapier_step_times.push(rc.pipeline.counters.step_time.time() as f32); + total_update_times.push(elapsed_time); + } + timer_total.pause(); + let average_total = total_update_times.iter().sum::() / total_update_times.len() as f32; + println!("average total time: {} ms", average_total); + let average_rapier_step = + rapier_step_times.iter().sum::() / rapier_step_times.len() as f32; + println!("average rapier step time: {} ms", average_rapier_step); + let average_rapier_overhead = average_total - average_rapier_step; + println!("average bevy overhead: {} ms", average_rapier_overhead); + println!("total time: {} ms", timer_total.time()); +} diff --git a/bevy_rapier_benches3d/src/pyramids.rs b/bevy_rapier_benches3d/src/pyramids.rs new file mode 100644 index 00000000..363538a9 --- /dev/null +++ b/bevy_rapier_benches3d/src/pyramids.rs @@ -0,0 +1,64 @@ +use bevy::prelude::*; +use bevy_rapier3d::dynamics::RigidBody; +use bevy_rapier3d::geometry::Collider; +use bevy_rapier3d::math::Vect; + +pub fn create_pyramid(commands: &mut Commands, offset: Vect, stack_height: usize, rad: f32) { + let shift = rad * 2.0; + + for i in 0usize..stack_height { + for j in i..stack_height { + let fj = j as f32; + let fi = i as f32; + let x = (fi * shift / 2.0) + (fj - fi) * shift; + let y = fi * shift; + + // Build the rigid body. + commands.spawn(( + RigidBody::Dynamic, + Transform::from_translation(Vec3::new(x, y, 0.0) + offset), + Collider::cuboid(1.0, 1.0, 1.0), + )); + } + } +} + +pub fn setup_pyramids(app: &mut App, pyramid_count: usize, stack_height: usize) { + app.add_systems(Startup, move |mut commands: Commands| { + let rad = 0.5; + let spacing = 4.0; + + /* + * Ground + */ + let ground_size = 50.0; + let ground_height = 0.1; + + commands.spawn(( + RigidBody::Fixed, + Transform::from_translation(Vect::new(0.0, -ground_height, 0.0)), + Collider::cuboid( + ground_size, + ground_height, + pyramid_count as f32 * spacing / 2.0 + ground_size, + ), + )); + + /* + * Create the pyramids + */ + for pyramid_index in 0..pyramid_count { + let bottomy = rad; + create_pyramid( + &mut commands, + Vect::new( + 0.0, + bottomy, + (pyramid_index as f32 - pyramid_count as f32 / 2.0) * spacing, + ), + stack_height, + rad, + ); + } + }); +}