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
19 changes: 19 additions & 0 deletions src_rbd/broad_phase/narrow_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use crate::math::Pose;
use crate::queries::GpuIndexedContact;
use crate::shaders::PaddedVector;
#[cfg(feature = "dim3")]
use crate::shaders::broad_phase::GpuReduceContacts;
use crate::shaders::broad_phase::{
CollisionPair, GpuInitPfmPfmDispatch, GpuNarrowPhaseInitContactsDispatch, GpuNarrowPhasePfmPfm,
GpuNarrowPhaseShapeShape, GpuNarrowPhaseShapeShapeDeferred, GpuResetNarrowPhase,
Expand All @@ -22,6 +24,8 @@ pub struct GpuNarrowPhase {
/// `pfm_pairs` work-list. Split from `narrow_phase` to fit 8 storage buffers.
narrow_phase_deferred: GpuNarrowPhaseShapeShapeDeferred,
narrow_phase_pfm_pfm: GpuNarrowPhasePfmPfm,
#[cfg(feature = "dim3")]
reduce_contacts: GpuReduceContacts,
init_pfm_pfm_indirect_args: GpuInitPfmPfmDispatch,
init_contacts_indirect_args: GpuNarrowPhaseInitContactsDispatch,
}
Expand All @@ -48,6 +52,9 @@ impl GpuNarrowPhase {
batch_indices: &Tensor<crate::shaders::utils::BatchIndices>,
collider_parent: &Tensor<u32>,
collider_materials: &Tensor<crate::shaders::queries::ColliderMaterial>,
// Optional: merge each collider pair's manifolds into one before the
// solvers see them. `false` skips the kernel entirely.
reduce_contacts: bool,
) -> Result<(), GpuBackendError> {
let num_batches = contacts_len.len() as u32;
self.reset_narrow_phase
Expand Down Expand Up @@ -98,6 +105,18 @@ impl GpuNarrowPhase {
collider_parent,
collider_materials,
)?;
#[cfg(feature = "dim3")]
if reduce_contacts {
self.reduce_contacts.call(
pass,
[1u32, num_batches, 1],
contacts,
contacts_len,
batch_indices,
)?;
}
#[cfg(not(feature = "dim3"))]
let _ = reduce_contacts;
self.init_contacts_indirect_args
.call(pass, 1u32, contacts_len, contacts_indirect)?;

Expand Down
5 changes: 5 additions & 0 deletions src_rbd/pipeline/rbd_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pub struct RbdPipeline {
coloring: GpuColoring,
warmstart: GpuWarmstart,
reduce: Reduce,
/// Optional (default `false`): merge each collider pair's manifolds
/// (e.g. per-triangle trimesh contacts) into one before the solvers.
pub contact_reduction: bool,
}

impl RbdPipeline {
Expand All @@ -54,6 +57,7 @@ impl RbdPipeline {
coloring: GpuColoring::from_backend(backend)?,
warmstart: GpuWarmstart::from_backend(backend)?,
reduce: Reduce::from_backend(backend)?,
contact_reduction: false,
})
}

Expand Down Expand Up @@ -197,6 +201,7 @@ impl RbdPipeline {
&state.batch_indices,
&state.collider_parent,
&state.collider_materials,
self.contact_reduction,
)?;

drop(pass);
Expand Down
83 changes: 83 additions & 0 deletions src_rbd_shaders/broad_phase/narrow_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::queries::{
ColliderMaterial, ContactManifold, IndexedManifold, ball_ball, ball_convex, convex_ball,
cuboid_cuboid, pfm_pfm,
};
#[cfg(feature = "dim3")]
use crate::queries::{ContactPoint, MAX_MANIFOLD_POINTS, manifold_reduction};
use crate::shapes::{
Capsule, Polyline, SHAPE_TYPE_BALL, SHAPE_TYPE_CAPSULE, SHAPE_TYPE_CONE, SHAPE_TYPE_CUBOID,
SHAPE_TYPE_CYLINDER, SHAPE_TYPE_POLYLINE, SHAPE_TYPE_TRIMESH, Shape, TriMesh,
Expand Down Expand Up @@ -70,6 +72,87 @@ pub fn gpu_narrow_phase_init_contacts_dispatch(
*indirect_args.at_mut(2) = 1;
}

/// Optional contact reduction: compacts each batch's contacts in place by
/// merging all manifolds of a collider pair (e.g. per-triangle trimesh
/// contacts, which share one `colliders` key and one collider-A local frame)
/// into a single `MAX_MANIFOLD_POINTS` manifold via `manifold_reduction`,
/// keeping the deeper manifold's normal. The first record of a pair is kept
/// verbatim, so single-manifold pairs are bit-identical to the unreduced
/// path. Approximations: one normal per merged manifold, greedy merging in
/// emission order. Grid `[1, num_batches, 1]`, serial per batch.
#[cfg(feature = "dim3")]
#[spirv_bindgen]
#[spirv(compute(threads(1)))]
pub fn gpu_reduce_contacts(
#[spirv(workgroup_id)] workgroup_id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] contacts: &mut [IndexedManifold],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] contacts_len: &mut [u32],
#[spirv(uniform, descriptor_set = 0, binding = 2)] batch_ids: &BatchIndices,
) {
let batch_id = workgroup_id.y;
let capacity = batch_ids.contacts_batch_capacity as usize;
let mut contacts = batch_ids.contact_batch_mut(batch_id, contacts);
let n = (contacts_len.read(batch_id as usize) as usize).min(capacity);

let mut w = 0usize; // write cursor — always ≤ read cursor, in-place safe
for i in 0..n {
let im = contacts[i];
let mut merged = false;
for j in 0..w {
let out = contacts[j];
if out.colliders.x == im.colliders.x && out.colliders.y == im.colliders.y {
// Pool the two manifolds' points (same collider-A local frame).
let na = (out.contact.len as usize).min(MAX_MANIFOLD_POINTS);
let nb = (im.contact.len as usize).min(MAX_MANIFOLD_POINTS);
let mut cand = [ContactPoint::default(); 8];
for k in 0..na {
cand.write(k, out.contact.points_a.read(k));
}
for k in 0..nb {
cand.write(na + k, im.contact.points_a.read(k));
}
// Normal of whichever manifold holds the deepest point.
let mut deep_out = out.contact.points_a.at(0).dist;
for k in 1..na {
let d = out.contact.points_a.at(k).dist;
if d < deep_out {
deep_out = d;
}
}
let mut deep_in = im.contact.points_a.at(0).dist;
for k in 1..nb {
let d = im.contact.points_a.at(k).dist;
if d < deep_in {
deep_in = d;
}
}
let normal = if deep_in < deep_out {
im.contact.normal_a
} else {
out.contact.normal_a
};
let mut reduced = manifold_reduction(&cand, (na + nb) as u32, normal);
// `manifold_reduction` fills points/len only.
reduced.normal_a = normal;
let mut kept = out;
kept.contact = reduced;
contacts[j] = kept;
merged = true;
break;
}
}
if !merged {
contacts[w] = im;
w += 1;
}
}
// Compacted count; plain store, single writer per batch. (Loop shell per
// the `gpu_reset_narrow_phase` rustgpu-triviality workaround.)
for _ in 0..1 {
contacts_len.write(batch_id as usize, w as u32);
}
}

const PREDICTION: f32 = 2.0e-3; // TODO: make the prediction configurable.

/// Narrow phase, pass 1 of 2: analytic shape-shape contacts for ball / cuboid
Expand Down
5 changes: 5 additions & 0 deletions src_rbd_shaders/queries/polygonal_feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ mod dim2 {
// 3D Implementation
// ====================

/// Re-export of the ≤8 → ≤4 keep-deepest-then-spread contact selector for the
/// optional contact-reduction pass (see `gpu_reduce_contacts`).
#[cfg(feature = "dim3")]
pub use dim3::manifold_reduction;

#[cfg(feature = "dim3")]
mod dim3 {
use super::*;
Expand Down
Loading