Skip to content
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
6 changes: 6 additions & 0 deletions crates/rapier2d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ serde-serialize = [
enhanced-determinism = ["simba/libm_force", "parry2d-f64/enhanced-determinism"]
debug-render = []
profiler = ["dep:web-time"] # Enables the internal profiler.
gpu-acceleration = ["dep:wgpu", "dep:bytemuck", "dep:pollster"] # Enables GPU compute via WGPU

# Feature used for debugging only.
debug-disable-legitimate-fe-exceptions = []
Expand Down Expand Up @@ -89,6 +90,11 @@ thiserror = "2"
profiling = "1.0"
static_assertions = "1"

# GPU acceleration dependencies
wgpu = { version = "22", optional = true }
bytemuck = { version = "1.14", optional = true, features = ["derive"] }
pollster = { version = "0.3", optional = true }

[dev-dependencies]
bincode = "1"
serde_json = "1"
Expand Down
12 changes: 12 additions & 0 deletions crates/rapier2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ serde-serialize = [
enhanced-determinism = ["simba/libm_force", "parry2d/enhanced-determinism"]
debug-render = []
profiler = ["dep:web-time"] # Enables the internal profiler.
gpu-acceleration = ["dep:wgpu", "dep:bytemuck", "dep:pollster"] # Enables GPU compute via WGPU

# Feature used for debugging only.
debug-disable-legitimate-fe-exceptions = []
Expand Down Expand Up @@ -92,8 +93,19 @@ static_assertions = "1"
# TODO: should be re-exported from simba
wide = "0.7.1"

# GPU acceleration dependencies
wgpu = { version = "22", optional = true }
bytemuck = { version = "1.14", optional = true, features = ["derive"] }
pollster = { version = "0.3", optional = true }

[dev-dependencies]
bincode = "1"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
oorandom = { version = "11", default-features = false }
criterion = { version = "0.5", features = ["html_reports"] }

[[bench]]
name = "gpu_benchmarks"
harness = false
required-features = ["gpu-acceleration"]
Empty file.
6 changes: 6 additions & 0 deletions crates/rapier3d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ serde-serialize = [
enhanced-determinism = ["simba/libm_force", "parry3d-f64/enhanced-determinism"]
debug-render = []
profiler = ["dep:web-time"] # Enables the internal profiler.
gpu-acceleration = ["dep:wgpu", "dep:bytemuck", "dep:pollster"] # Enables GPU compute via WGPU

# Feature used for debugging only.
debug-disable-legitimate-fe-exceptions = []
Expand Down Expand Up @@ -92,6 +93,11 @@ thiserror = "2"
profiling = "1.0"
static_assertions = "1"

# GPU acceleration dependencies
wgpu = { version = "22", optional = true }
bytemuck = { version = "1.14", optional = true, features = ["derive"] }
pollster = { version = "0.3", optional = true }

[dev-dependencies]
bincode = "1"
serde_json = "1"
Expand Down
12 changes: 12 additions & 0 deletions crates/rapier3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ serde-serialize = [
enhanced-determinism = ["simba/libm_force", "parry3d/enhanced-determinism"]
debug-render = []
profiler = ["dep:web-time"] # Enables the internal profiler.
gpu-acceleration = ["dep:wgpu", "dep:bytemuck", "dep:pollster"] # Enables GPU compute via WGPU

# Feature used for debugging only.
debug-disable-legitimate-fe-exceptions = []
Expand Down Expand Up @@ -96,8 +97,19 @@ static_assertions = "1"
# TODO: should be re-exported from simba
wide = "0.7.1"

# GPU acceleration dependencies
wgpu = { version = "22", optional = true }
bytemuck = { version = "1.14", optional = true, features = ["derive"] }
pollster = { version = "0.3", optional = true }

[dev-dependencies]
bincode = "1"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
oorandom = { version = "11", default-features = false }
criterion = { version = "0.5", features = ["html_reports"] }

[[bench]]
name = "gpu_benchmarks"
harness = true
required-features = ["gpu-acceleration"]
195 changes: 195 additions & 0 deletions crates/rapier3d/benches/gpu_benchmarks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//! GPU vs CPU benchmarks: Direct comparison with summary table.
//!
//! Compares CPU and GPU performance for physics operations
//! and prints an easy-to-read comparison table.

use rapier3d::prelude::*;
use rapier3d::gpu::{GpuContext, BufferManager};
use std::time::Instant;

/// Test scales: body counts
const SCALES: &[usize] = &[10, 50, 100, 500, 1_000, 5_000, 10_000];
const ITERATIONS: usize = 10; // Number of iterations to average

struct BenchmarkResult {
scale: usize,
cpu_time_us: f64,
gpu_time_us: f64,
speedup: f64,
}

/// Helper to create a scene with N bodies in a grid.
fn create_test_bodies(count: usize) -> RigidBodySet {
let mut bodies = RigidBodySet::new();
let side = (count as f32).cbrt().ceil() as usize;
let spacing = 2.5;

for i in 0..side {
for j in 0..side {
for k in 0..side {
if bodies.len() >= count {
break;
}

let pos = Vector::new(
i as Real * spacing,
j as Real * spacing,
k as Real * spacing,
);

let rb = RigidBodyBuilder::dynamic()
.translation(pos)
.linvel(Vector::new(
(i as Real - side as Real / 2.0) * 0.1,
-1.0,
(k as Real - side as Real / 2.0) * 0.1,
))
.build();

bodies.insert(rb);
}
if bodies.len() >= count {
break;
}
}
if bodies.len() >= count {
break;
}
}

bodies
}

fn benchmark_cpu_integration(bodies: &RigidBodySet, iterations: usize) -> f64 {
let dt = 1.0 / 60.0;
let mut bodies_copy = bodies.clone();

let start = Instant::now();
for _ in 0..iterations {
for (_handle, body) in bodies_copy.iter_mut() {
if body.is_dynamic() {
let mut pos = body.position().clone();
let linvel = body.linvel();

pos.translation.x += linvel.x * dt;
pos.translation.y += linvel.y * dt;
pos.translation.z += linvel.z * dt;

body.set_position(pos, false);
}
}
}
let elapsed = start.elapsed();

elapsed.as_micros() as f64 / iterations as f64
}

fn benchmark_gpu_transfer(bodies: &RigidBodySet, buffer_manager: &BufferManager, gpu_buffer: &mut rapier3d::gpu::RigidBodyGpuBuffer, iterations: usize) -> f64 {
let start = Instant::now();
for _ in 0..iterations {
buffer_manager.upload_rigid_bodies(bodies, gpu_buffer);
let (_positions, _velocities) = buffer_manager.download_rigid_bodies(gpu_buffer);
}
let elapsed = start.elapsed();

elapsed.as_micros() as f64 / iterations as f64
}

fn print_results_table(results: &[BenchmarkResult]) {
println!("\n╔═══════════════════════════════════════════════════════════════════════════════╗");
println!("║ CPU vs GPU PERFORMANCE COMPARISON ║");
println!("╠═══════════════════════════════════════════════════════════════════════════════╣");
println!("║ Bodies │ CPU Time │ GPU Time │ Speedup │ Winner ║");
println!("╠══════════╪════════════════╪════════════════╪═════════════╪═══════════════════╣");

for result in results {
let winner = if result.speedup > 1.0 {
format!("GPU {:>6.2}x faster", result.speedup)
} else if result.speedup < 0.95 {
format!("CPU {:>6.2}x faster", 1.0 / result.speedup)
} else {
" ~Same speed ".to_string()
};

let cpu_str = format_time(result.cpu_time_us);
let gpu_str = format_time(result.gpu_time_us);

println!("║ {:>8} │ {:>14} │ {:>14} │ {:>11.2}x │ {:>17} ║",
result.scale,
cpu_str,
gpu_str,
result.speedup,
winner);
}

println!("╚══════════╧════════════════╧════════════════╧═════════════╧═══════════════════╝");
println!("\nNote: GPU times include CPU↔GPU transfer overhead.");
println!(" Actual GPU compute will be added in Phase 2.");
}

fn format_time(us: f64) -> String {
if us < 1_000.0 {
format!("{:>10.2} µs", us)
} else if us < 1_000_000.0 {
format!("{:>10.2} ms", us / 1_000.0)
} else {
format!("{:>10.2} s", us / 1_000_000.0)
}
}

fn main() {
println!("╔═══════════════════════════════════════════════════════════════════════════════╗");
println!("║ RAPIER GPU ACCELERATION BENCHMARK ║");
println!("╚═══════════════════════════════════════════════════════════════════════════════╝\n");

println!("Initializing GPU...");
let gpu_setup = match GpuContext::new() {
Ok(ctx) => {
println!("✓ GPU initialized: {}", ctx.adapter.get_info().name);
let buffer_manager = BufferManager::new(ctx.device, ctx.queue);
Some(buffer_manager)
},
Err(e) => {
println!("✗ GPU not available: {:?}", e);
println!(" Running CPU-only benchmarks...\n");
None
}
};

let mut results = Vec::new();

for &scale in SCALES {
print!("Benchmarking {} bodies... ", scale);
std::io::Write::flush(&mut std::io::stdout()).unwrap();

let bodies = create_test_bodies(scale);

// Benchmark CPU
let cpu_time = benchmark_cpu_integration(&bodies, ITERATIONS);

// Benchmark GPU if available
let gpu_time = if let Some(ref buffer_manager) = gpu_setup {
let mut gpu_buffer = buffer_manager.create_rigid_body_buffer(bodies.len());
benchmark_gpu_transfer(&bodies, buffer_manager, &mut gpu_buffer, ITERATIONS)
} else {
0.0
};

let speedup = if gpu_time > 0.0 {
cpu_time / gpu_time
} else {
0.0
};

results.push(BenchmarkResult {
scale,
cpu_time_us: cpu_time,
gpu_time_us: gpu_time,
speedup,
});

println!("Done!");
}

print_results_table(&results);
}
4 changes: 4 additions & 0 deletions examples3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ path = "./all_examples3.rs"
name = "harness_capsules3"
path = "./harness_capsules3.rs"

[[bin]]
name = "gpu_benchmark"
path = "./gpu_benchmark.rs"

#[lib]
#crate-type = ["cdylib", "rlib"]
#path = "./all_examples3_wasm.rs"
Loading