Skip to content

Commit 3bf7c6d

Browse files
authored
feat: linarize most bached simulation kernels by switching to batch-interleaved formats (#37)
* perf(rbd): batch-interleaved flat buffer layout with demand-sized constraint arenas * test(rbd): add a batched-stacks parity probe * refactor(rbd): remove the never-maintained dof_values mirror * perf(rbd): positional contact slots, per-pair manifold reduction, contact-to-multibody index * chore(rbd): update docs * feat(rbd): use stronger typing in shaders when possible * chore: CI fixes
1 parent 280b637 commit 3bf7c6d

48 files changed

Lines changed: 3988 additions & 2528 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/state.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ impl NexusCapacities {
6464
self
6565
}
6666

67+
pub fn rbd_mb_contact_constraints(mut self, capacity: u32) -> Self {
68+
self.rbd.mb_contact_constraints_capacity = capacity;
69+
self
70+
}
71+
6772
#[cfg(feature = "mpm")]
6873
pub fn mpm_grid_size(mut self, num_chunks: u32) -> Self {
6974
self.mpm.grid_size = num_chunks;
@@ -109,6 +114,8 @@ pub struct NexusCounts {
109114
pub multibody_dofs: usize,
110115
pub collision_pairs: usize,
111116
pub collision_pairs_capacity: usize,
117+
pub mb_contact_constraints: usize,
118+
pub mb_contact_constraints_capacity: usize,
112119
pub particles: usize,
113120
}
114121

@@ -342,6 +349,13 @@ impl NexusState {
342349
self.capacities.rbd.collisions_capacity = capacity.max(1);
343350
}
344351

352+
/// Sets the per-batch multibody contact-constraint slot budget (see
353+
/// [`RbdCapacities::mb_contact_constraints_capacity`]). Takes effect on the
354+
/// next state (re)build.
355+
pub fn set_rbd_mb_contact_constraints_capacity(&mut self, capacity: u32) {
356+
self.capacities.rbd.mb_contact_constraints_capacity = capacity.max(1);
357+
}
358+
345359
/// Sets the number of rigid-body solver steps advanced per
346360
/// [`NexusPipeline::simulate`](crate::pipeline::NexusPipeline::simulate) call (default 1). Acts as a simulation-speed control.
347361
pub fn set_rbd_steps_per_frame(&mut self, steps: u32) {
@@ -373,6 +387,11 @@ impl NexusState {
373387
if let Some(rbd) = self.rbd.as_ref() {
374388
c.collision_pairs = rbd.collision_pairs_len() as usize;
375389
c.collision_pairs_capacity = rbd.collision_pairs_capacity() as usize;
390+
#[cfg(feature = "dim3")]
391+
{
392+
c.mb_contact_constraints = rbd.mb_contact_constraints_len() as usize;
393+
c.mb_contact_constraints_capacity = rbd.mb_contact_constraints_capacity() as usize;
394+
}
376395
}
377396
#[cfg(feature = "mpm")]
378397
if let Some(mpm) = self.mpm.as_ref() {
@@ -640,13 +659,13 @@ impl NexusState {
640659
<= rbd.num_colliders_per_batch() as usize =>
641660
{
642661
let range = rbd.append_bodies(backend, &gpu_pairs)?;
643-
// Single environment: the per-batch local slot is the gpu_id.
662+
let nb = rbd.num_batches();
644663
for (i, (&handle, &coupling)) in handles.iter().zip(&couplings).enumerate() {
645664
self.rbd2gpu[0].insert(
646665
handle.0,
647666
GpuRigidBodyRef {
648667
coupling,
649-
gpu_id: range.start + i as u32,
668+
gpu_id: (range.start + i as u32) * nb,
650669
},
651670
);
652671
}
@@ -981,9 +1000,9 @@ impl NexusState {
9811000
// `gpu_id` is its *body* slot, not a collider slot, since a body may
9821001
// own several colliders. Body slots are assigned in the order
9831002
// `from_rapier` uses (the first time each parent body is seen while
984-
// iterating colliders) and are laid out env-major with stride
985-
// `num_colliders_per_batch`.
986-
let stride = rbd_state.num_colliders_per_batch();
1003+
// iterating colliders); the per-body buffers are batch-interleaved,
1004+
// so `gpu_id = local_slot * num_batches + env`.
1005+
let nb = rbd_state.num_batches();
9871006
for (env_idx, world) in self.rbd_envs.iter().enumerate() {
9881007
let mut body_slot: std::collections::HashMap<_, u32> =
9891008
std::collections::HashMap::new();
@@ -1012,7 +1031,7 @@ impl NexusState {
10121031
body_handle.0,
10131032
GpuRigidBodyRef {
10141033
coupling,
1015-
gpu_id: env_idx as u32 * stride + slot,
1034+
gpu_id: slot * nb + env_idx as u32,
10161035
},
10171036
);
10181037
}
@@ -1038,7 +1057,7 @@ impl NexusState {
10381057
body_handle.0,
10391058
GpuRigidBodyRef {
10401059
coupling,
1041-
gpu_id: env_idx as u32 * stride + slot,
1060+
gpu_id: slot * nb + env_idx as u32,
10421061
},
10431062
);
10441063
}

src_rbd/broad_phase/lbvh.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::math::Pose;
77
use crate::shaders::PaddedVector;
88
use crate::shaders::bounding_volumes::Aabb;
99
use crate::shaders::broad_phase::{
10-
CollisionPair, GpuBfComputeAabbs, GpuBfFindPairs, GpuLbvhBuild, GpuLbvhComputeDomain,
11-
GpuLbvhComputeMorton, GpuLbvhFindCollisionPairs, GpuLbvhInitDispatch, GpuLbvhRefitInternal,
10+
CollisionPair, GpuBfComputeAabbs, GpuBfFindPairs, GpuFlatListDispatch, GpuLbvhBuild,
11+
GpuLbvhComputeDomain, GpuLbvhComputeMorton, GpuLbvhFindCollisionPairs, GpuLbvhRefitInternal,
1212
GpuLbvhRefitLeaves, GpuLbvhResetCollisionPairs, LbvhNode,
1313
};
1414
use crate::shaders::shapes::Shape;
@@ -32,7 +32,9 @@ pub struct GpuLbvh {
3232
refit_internal: GpuLbvhRefitInternal,
3333
reset_collision_pairs: GpuLbvhResetCollisionPairs,
3434
find_collision_pairs: GpuLbvhFindCollisionPairs,
35-
lbvh_init_indirect_args: GpuLbvhInitDispatch,
35+
/// Writes the `[total/64, 1, 1]` indirect grid from the single global pair
36+
/// counter.
37+
flat_list_dispatch: GpuFlatListDispatch,
3638
// Kernels for brute-force broad-phase for small scenes
3739
// (typically, small scenes but many batches).
3840
bf_compute_aabbs: GpuBfComputeAabbs,
@@ -294,7 +296,7 @@ impl Lbvh {
294296

295297
self.shaders
296298
.reset_collision_pairs
297-
.call(pass, [num_batches, 1, 1], collision_pairs_len)?;
299+
.call(pass, [1u32, 1, 1], collision_pairs_len)?;
298300
self.shaders.find_collision_pairs.call(
299301
pass,
300302
[colliders_per_batch, num_batches, 1],
@@ -305,11 +307,12 @@ impl Lbvh {
305307
batch_indices,
306308
pair_filter,
307309
)?;
308-
self.shaders.lbvh_init_indirect_args.call(
310+
self.shaders.flat_list_dispatch.call(
309311
pass,
310-
256u32,
312+
1u32,
311313
collision_pairs_len,
312314
collision_pairs_indirect,
315+
batch_indices,
313316
)?;
314317
Ok(())
315318
}
@@ -351,7 +354,7 @@ impl Lbvh {
351354
)?;
352355
self.shaders
353356
.reset_collision_pairs
354-
.call(pass, [num_batches, 1, 1], collision_pairs_len)?;
357+
.call(pass, [1u32, 1, 1], collision_pairs_len)?;
355358
self.shaders.bf_find_pairs.call(
356359
pass,
357360
[active_per_batch * active_per_batch * num_batches, 1, 1],
@@ -363,12 +366,12 @@ impl Lbvh {
363366
pair_filter,
364367
sim_params,
365368
)?;
366-
// Single 256-lane workgroup: parallel max over the per-batch counts.
367-
self.shaders.lbvh_init_indirect_args.call(
369+
self.shaders.flat_list_dispatch.call(
368370
pass,
369-
256u32,
371+
1u32,
370372
collision_pairs_len,
371373
collision_pairs_indirect,
374+
batch_indices,
372375
)?;
373376
Ok(())
374377
}

0 commit comments

Comments
 (0)