Skip to content
Open
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
11 changes: 11 additions & 0 deletions crates/parry2d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,18 @@ smallvec = { workspace = true }
foldhash = { workspace = true }

[dev-dependencies]
criterion = "0.8"
simba = { workspace = true, features = ["wide"] }
oorandom = { workspace = true }
ptree = { workspace = true }
rand = { workspace = true }

[[bench]]
name = "contact_manifolds"
path = "benches/contact_manifolds.rs"
harness = false

[[bench]]
name = "gjk_epa"
path = "benches/gjk_epa.rs"
harness = false
5 changes: 5 additions & 0 deletions crates/parry2d-f64/benches/contact_manifolds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! f64 variant of the parry2d contact-manifold benchmarks (shared body,
//! aliased crate).
extern crate parry2d_f64 as parry2d;

include!("../../parry2d/benches/contact_manifolds.rs");
4 changes: 4 additions & 0 deletions crates/parry2d-f64/benches/gjk_epa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! f64 variant of the parry2d GJK/EPA benchmarks (shared body, aliased crate).
extern crate parry2d_f64 as parry2d;

include!("../../parry2d/benches/gjk_epa.rs");
11 changes: 11 additions & 0 deletions crates/parry2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ foldhash = { workspace = true }
encase = { workspace = true, optional = true }

[dev-dependencies]
criterion = "0.8"
simba = { workspace = true, features = ["wide"] }
oorandom = { workspace = true }
ptree = { workspace = true }
Expand Down Expand Up @@ -186,3 +187,13 @@ doc-scrape-examples = true
name = "time_of_impact_query2d"
path = "examples/time_of_impact_query2d.rs"
doc-scrape-examples = true

[[bench]]
name = "contact_manifolds"
path = "benches/contact_manifolds.rs"
harness = false

[[bench]]
name = "gjk_epa"
path = "benches/gjk_epa.rs"
harness = false
201 changes: 201 additions & 0 deletions crates/parry2d/benches/contact_manifolds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Criterion benchmarks for steady-state 2D contact-manifold updates,
// mirroring the parry3d suite: dedicated convex pairs, the generic PFM
// fallback, and trimesh-vs-convex persistent manifolds. The body is written
// against `parry2d::math::Real` so the parry2d-f64 crate reuses it verbatim.

use criterion::{criterion_group, criterion_main, Criterion};
use parry2d::math::{Pose, Real, Vector};
use parry2d::query::{
ContactManifold, ContactManifoldsWorkspace, DefaultQueryDispatcher, PersistentQueryDispatcher,
};
use parry2d::shape::{Ball, Capsule, ConvexPolygon, Cuboid, TriMesh};
use std::hint::black_box;
use std::time::Duration;

type Manifold = ContactManifold<(), ()>;

fn drifting_pose(step: u32) -> Pose {
let t = step as Real * 0.02;
Pose::translation(t.sin() * 0.03, 1.2 + t.cos() * 0.02)
}

fn hexagon(radius: Real) -> ConvexPolygon {
let pts: Vec<Vector> = (0..6)
.map(|i| {
let a = i as Real * core::f64::consts::PI as Real / 3.0;
Vector::new(a.cos() * radius, a.sin() * radius)
})
.collect();
ConvexPolygon::from_convex_polyline(pts).unwrap()
}

fn bench_convex_pairs(c: &mut Criterion) {
let dispatcher = DefaultQueryDispatcher;
let prediction = 0.1;

let mut g = c.benchmark_group("contact_manifold_convex_convex");

g.bench_function("cuboid_cuboid", |b| {
let c1 = Cuboid::new(Vector::new(1.0, 1.0));
let c2 = Cuboid::new(Vector::new(0.8, 0.8));
let mut manifold = Manifold::new();
let mut step = 0u32;
b.iter(|| {
step = step.wrapping_add(1);
let pos12 = drifting_pose(step) * Pose::translation(0.0, 0.35);
dispatcher
.contact_manifold_convex_convex(
&pos12,
&c1,
&c2,
None,
None,
prediction,
&mut manifold,
)
.unwrap();
black_box(manifold.points.len())
})
});

// Hexagon/capsule: generic support-map fallback (GJK + EPA on penetration).
g.bench_function("pfm_hexagon_capsule", |b| {
let poly = hexagon(1.0);
let cap = Capsule::new_y(0.6, 0.3);
let mut manifold = Manifold::new();
let mut step = 0u32;
b.iter(|| {
step = step.wrapping_add(1);
let pos12 = drifting_pose(step);
dispatcher
.contact_manifold_convex_convex(
&pos12,
&poly,
&cap,
None,
None,
prediction,
&mut manifold,
)
.unwrap();
black_box(manifold.points.len())
})
});

g.bench_function("capsule_capsule", |b| {
let cap1 = Capsule::new_y(0.8, 0.4);
let cap2 = Capsule::new_y(0.6, 0.3);
let mut manifold = Manifold::new();
let mut step = 0u32;
b.iter(|| {
step = step.wrapping_add(1);
let pos12 = drifting_pose(step) * Pose::translation(0.0, -0.2);
dispatcher
.contact_manifold_convex_convex(
&pos12,
&cap1,
&cap2,
None,
None,
prediction,
&mut manifold,
)
.unwrap();
black_box(manifold.points.len())
})
});

g.finish();
}

/// A triangulated band: a sine-curve top edge over a flat bottom edge.
fn make_terrain(columns: u32) -> TriMesh {
let n = columns as usize;
let mut vertices = Vec::with_capacity((n + 1) * 2);
let mut indices = Vec::with_capacity(n * 2);
for ix in 0..=n {
let x = ix as Real / n as Real * 40.0 - 20.0;
let y = (x * 0.8).sin() * 0.4;
vertices.push(Vector::new(x, y)); // top: 2*ix
vertices.push(Vector::new(x, -1.5)); // bottom: 2*ix + 1
}
for ix in 0..n {
let top0 = (ix * 2) as u32;
let bot0 = top0 + 1;
let top1 = top0 + 2;
let bot1 = top0 + 3;
indices.push([top0, bot0, top1]);
indices.push([bot0, bot1, top1]);
}
TriMesh::new(vertices, indices).unwrap()
}

fn bench_trimesh_shape(c: &mut Criterion) {
let dispatcher = DefaultQueryDispatcher;
let prediction = 0.1;
let terrain = make_terrain(512); // 1024 triangles

let mut g = c.benchmark_group("contact_manifolds_trimesh");

g.bench_function("trimesh_vs_ball", |b| {
let ball = Ball::new(0.7);
let mut manifolds: Vec<Manifold> = Vec::new();
let mut workspace: Option<ContactManifoldsWorkspace> = None;
let mut step = 0u32;
b.iter(|| {
step = step.wrapping_add(1);
let t = step as Real * 0.02;
let pos12 = Pose::translation(t.sin() * 0.5, 0.55);
dispatcher
.contact_manifolds(
&pos12,
&terrain,
&ball,
prediction,
&mut manifolds,
&mut workspace,
)
.unwrap();
black_box(manifolds.len())
})
});

g.bench_function("trimesh_vs_cuboid", |b| {
let cuboid = Cuboid::new(Vector::new(0.6, 0.6));
let mut manifolds: Vec<Manifold> = Vec::new();
let mut workspace: Option<ContactManifoldsWorkspace> = None;
let mut step = 0u32;
b.iter(|| {
step = step.wrapping_add(1);
let t = step as Real * 0.02;
let pos12 = Pose::translation(t.sin() * 0.5, 0.5);
dispatcher
.contact_manifolds(
&pos12,
&terrain,
&cuboid,
prediction,
&mut manifolds,
&mut workspace,
)
.unwrap();
black_box(manifolds.len())
})
});

g.finish();
}

fn config() -> Criterion {
Criterion::default()
.warm_up_time(Duration::from_millis(800))
.measurement_time(Duration::from_secs(3))
.sample_size(60)
}

criterion_group! {
name = benches;
config = config();
targets = bench_convex_pairs, bench_trimesh_shape
}
criterion_main!(benches);
59 changes: 59 additions & 0 deletions crates/parry2d/benches/gjk_epa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Criterion benchmarks for 2D GJK/EPA contact queries between smooth convex
// shapes (support-map path), mirroring the parry3d suite. Written against
// `parry2d::math::Real` so parry2d-f64 reuses the body verbatim.

use criterion::{criterion_group, criterion_main, Criterion};
use parry2d::math::{Pose, Real, Vector};
use parry2d::query;
use parry2d::shape::{Capsule, ConvexPolygon};
use std::hint::black_box;
use std::time::Duration;

fn hexagon(radius: Real) -> ConvexPolygon {
let pts: Vec<Vector> = (0..6)
.map(|i| {
let a = i as Real * core::f64::consts::PI as Real / 3.0;
Vector::new(a.cos() * radius, a.sin() * radius)
})
.collect();
ConvexPolygon::from_convex_polyline(pts).unwrap()
}

fn bench_gjk_epa(c: &mut Criterion) {
let poly = hexagon(1.0);
let cap = Capsule::new_y(0.8, 0.4);
let identity = Pose::identity();

let mut g = c.benchmark_group("gjk_epa_contact");

g.bench_function("gjk_separated", |b| {
let pos2 = Pose::translation(2.9, 0.4);
b.iter(|| black_box(query::contact(&identity, &poly, &pos2, &cap, 1.5).unwrap()))
});

g.bench_function("gjk_shallow_penetration", |b| {
let pos2 = Pose::translation(1.32, 0.05);
b.iter(|| black_box(query::contact(&identity, &poly, &pos2, &cap, 0.1).unwrap()))
});

g.bench_function("epa_deep_penetration", |b| {
let pos2 = Pose::translation(0.35, 0.1);
b.iter(|| black_box(query::contact(&identity, &poly, &pos2, &cap, 0.1).unwrap()))
});

g.finish();
}

fn config() -> Criterion {
Criterion::default()
.warm_up_time(Duration::from_millis(500))
.measurement_time(Duration::from_secs(2))
.sample_size(80)
}

criterion_group! {
name = benches;
config = config();
targets = bench_gjk_epa
}
criterion_main!(benches);
11 changes: 11 additions & 0 deletions crates/parry3d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,21 @@ ordered-float = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
criterion = "0.8"
oorandom = { workspace = true }
ptree = { workspace = true }
rand = { workspace = true }

[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
features = ["wavefront"]

[[bench]]
name = "contact_manifolds"
path = "benches/contact_manifolds.rs"
harness = false

[[bench]]
name = "gjk_epa"
path = "benches/gjk_epa.rs"
harness = false
6 changes: 6 additions & 0 deletions crates/parry3d-f64/benches/contact_manifolds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! f64 variant of the parry3d contact-manifold benchmarks: the bench body is
//! shared with parry3d (it is written against `parry3d::math::Real`) and the
//! extern-crate alias below rebinds it to the f64 build.
extern crate parry3d_f64 as parry3d;

include!("../../parry3d/benches/contact_manifolds.rs");
4 changes: 4 additions & 0 deletions crates/parry3d-f64/benches/gjk_epa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! f64 variant of the parry3d GJK/EPA benchmarks (shared body, aliased crate).
extern crate parry3d_f64 as parry3d;

include!("../../parry3d/benches/gjk_epa.rs");
23 changes: 23 additions & 0 deletions crates/parry3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,29 @@ rand = { workspace = true }
kiss3d = { workspace = true }
rand_isaac = { workspace = true }
web-time = { workspace = true }
criterion = "0.8"

# Declaring any [[bench]] disables bench auto-discovery, so the pre-existing
# nightly libtest bench (`benches/all.rs`) is declared explicitly to keep it
# available unchanged.
[[bench]]
name = "all"
path = "benches/all.rs"

[[bench]]
name = "contact_manifolds"
path = "benches/contact_manifolds.rs"
harness = false

[[bench]]
name = "gjk_epa"
path = "benches/gjk_epa.rs"
harness = false

[[bench]]
name = "bvh_queries"
path = "benches/bvh_queries.rs"
harness = false

[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
Expand Down
Loading