rapier3d/dynamics/solver/
interaction_groups.rs1use crate::dynamics::{IslandManager, JointGraphEdge, JointIndex, RigidBodySet};
2use crate::geometry::{ContactManifold, ContactManifoldIndex};
3
4#[cfg(feature = "simd-is-enabled")]
5use {
6 crate::math::{SIMD_LAST_INDEX, SIMD_WIDTH},
7 vec_map::VecMap,
8};
9
10#[cfg(feature = "parallel")]
11use crate::dynamics::{MultibodyJointSet, RigidBodyHandle};
12
13#[cfg(feature = "parallel")]
14pub(crate) trait PairInteraction {
15 fn body_pair(&self) -> (Option<RigidBodyHandle>, Option<RigidBodyHandle>);
16}
17#[cfg(feature = "simd-is-enabled")]
18use crate::dynamics::RigidBodyType;
19
20#[cfg(feature = "parallel")]
21impl PairInteraction for &mut ContactManifold {
22 fn body_pair(&self) -> (Option<RigidBodyHandle>, Option<RigidBodyHandle>) {
23 (self.data.rigid_body1, self.data.rigid_body2)
24 }
25}
26
27#[cfg(feature = "parallel")]
28impl PairInteraction for JointGraphEdge {
29 fn body_pair(&self) -> (Option<RigidBodyHandle>, Option<RigidBodyHandle>) {
30 (Some(self.weight.body1), Some(self.weight.body2))
31 }
32}
33
34#[cfg(feature = "parallel")]
35#[allow(dead_code)] pub(crate) struct ParallelInteractionGroups {
37 bodies_color: Vec<u128>, interaction_indices: Vec<usize>, interaction_colors: Vec<usize>, sorted_interactions: Vec<usize>,
41 groups: Vec<usize>,
42}
43
44#[cfg(feature = "parallel")]
45#[allow(dead_code)] impl ParallelInteractionGroups {
47 pub fn new() -> Self {
48 Self {
49 bodies_color: Vec::new(),
50 interaction_indices: Vec::new(),
51 interaction_colors: Vec::new(),
52 sorted_interactions: Vec::new(),
53 groups: Vec::new(),
54 }
55 }
56
57 pub fn group(&self, i: usize) -> &[usize] {
58 let range = self.groups[i]..self.groups[i + 1];
59 &self.sorted_interactions[range]
60 }
61
62 pub fn num_groups(&self) -> usize {
63 self.groups.len().saturating_sub(1)
64 }
65
66 pub fn group_interactions<Interaction: PairInteraction>(
67 &mut self,
68 island_id: usize,
69 islands: &IslandManager,
70 bodies: &RigidBodySet,
71 multibodies: &MultibodyJointSet,
72 interactions: &[Interaction],
73 interaction_indices: &[usize],
74 ) {
75 let num_island_bodies = islands.island(island_id).len();
76 self.bodies_color.clear();
77 self.interaction_indices.clear();
78 self.groups.clear();
79 self.sorted_interactions.clear();
80 self.interaction_colors.clear();
81
82 let mut color_len = [0; 128];
83 self.bodies_color.resize(num_island_bodies, 0u128);
84 self.interaction_indices
85 .extend_from_slice(interaction_indices);
86 self.interaction_colors.resize(interaction_indices.len(), 0);
87 let bcolors = &mut self.bodies_color;
88
89 for (interaction_id, color) in self
90 .interaction_indices
91 .iter()
92 .zip(self.interaction_colors.iter_mut())
93 {
94 let mut body_pair = interactions[*interaction_id].body_pair();
95 let is_fixed1 = body_pair.0.map(|b| bodies[b].is_fixed()).unwrap_or(true);
96 let is_fixed2 = body_pair.1.map(|b| bodies[b].is_fixed()).unwrap_or(true);
97
98 let representative = |handle: RigidBodyHandle| {
99 if let Some(link) = multibodies.rigid_body_link(handle).copied() {
100 let multibody = multibodies.get_multibody(link.multibody).unwrap();
101 multibody
102 .link(1) .or(multibody.link(0)) .map(|l| l.rigid_body)
105 .unwrap()
106 } else {
107 handle
108 }
109 };
110
111 body_pair = (
112 body_pair.0.map(representative),
113 body_pair.1.map(representative),
114 );
115
116 match (is_fixed1, is_fixed2) {
117 (false, false) => {
118 let rb1 = &bodies[body_pair.0.unwrap()];
119 let rb2 = &bodies[body_pair.1.unwrap()];
120 let color_mask =
121 bcolors[rb1.ids.active_set_id] | bcolors[rb2.ids.active_set_id];
122 *color = (!color_mask).trailing_zeros() as usize;
123 color_len[*color] += 1;
124 bcolors[rb1.ids.active_set_id] |= 1 << *color;
125 bcolors[rb2.ids.active_set_id] |= 1 << *color;
126 }
127 (true, false) => {
128 let rb2 = &bodies[body_pair.1.unwrap()];
129 let color_mask = bcolors[rb2.ids.active_set_id];
130 *color = 127 - (!color_mask).leading_zeros() as usize;
131 color_len[*color] += 1;
132 bcolors[rb2.ids.active_set_id] |= 1 << *color;
133 }
134 (false, true) => {
135 let rb1 = &bodies[body_pair.0.unwrap()];
136 let color_mask = bcolors[rb1.ids.active_set_id];
137 *color = 127 - (!color_mask).leading_zeros() as usize;
138 color_len[*color] += 1;
139 bcolors[rb1.ids.active_set_id] |= 1 << *color;
140 }
141 (true, true) => unreachable!(),
142 }
143 }
144
145 let mut sort_offsets = [0; 128];
146 let mut last_offset = 0;
147
148 for i in 0..128 {
149 if color_len[i] != 0 {
150 self.groups.push(last_offset);
151 sort_offsets[i] = last_offset;
152 last_offset += color_len[i];
153 }
154 }
155
156 self.sorted_interactions
157 .resize(interaction_indices.len(), 0);
158
159 for (interaction_id, color) in interaction_indices
160 .iter()
161 .zip(self.interaction_colors.iter())
162 {
163 self.sorted_interactions[sort_offsets[*color]] = *interaction_id;
164 sort_offsets[*color] += 1;
165 }
166
167 self.groups.push(self.sorted_interactions.len());
168 }
169}
170
171pub(crate) struct InteractionGroups {
172 #[cfg(feature = "simd-is-enabled")]
173 buckets: VecMap<([usize; SIMD_WIDTH], usize)>,
174 #[cfg(feature = "simd-is-enabled")]
175 body_masks: Vec<u128>,
176 pub simd_interactions: Vec<ContactManifoldIndex>,
177 pub nongrouped_interactions: Vec<ContactManifoldIndex>,
178}
179
180impl InteractionGroups {
181 pub fn new() -> Self {
182 Self {
183 #[cfg(feature = "simd-is-enabled")]
184 buckets: VecMap::new(),
185 #[cfg(feature = "simd-is-enabled")]
186 body_masks: Vec::new(),
187 simd_interactions: Vec::new(),
188 nongrouped_interactions: Vec::new(),
189 }
190 }
191
192 #[cfg(not(feature = "simd-is-enabled"))]
207 pub fn group_joints(
208 &mut self,
209 _island_id: usize,
210 _islands: &IslandManager,
211 _bodies: &RigidBodySet,
212 _interactions: &[JointGraphEdge],
213 interaction_indices: &[JointIndex],
214 ) {
215 self.nongrouped_interactions
216 .extend_from_slice(interaction_indices);
217 }
218
219 #[cfg(feature = "simd-is-enabled")]
220 #[profiling::function]
221 pub fn group_joints(
222 &mut self,
223 island_id: usize,
224 islands: &IslandManager,
225 bodies: &RigidBodySet,
226 interactions: &[JointGraphEdge],
227 interaction_indices: &[JointIndex],
228 ) {
229 #[cfg(feature = "dim3")]
233 const NUM_JOINT_TYPES: usize = 64;
234 #[cfg(feature = "dim2")]
235 const NUM_JOINT_TYPES: usize = 8;
236
237 let mut joint_type_conflicts = [0u128; NUM_JOINT_TYPES];
240
241 self.body_masks
247 .resize(islands.island(island_id).len(), 0u128);
248
249 let mut occupied_mask = 0u128;
252
253 for interaction_i in interaction_indices {
254 let interaction = &interactions[*interaction_i].weight;
255
256 let rb1 = &bodies[interaction.body1];
257 let rb2 = &bodies[interaction.body2];
258
259 let is_fixed1 = !rb1.is_dynamic_or_kinematic();
260 let is_fixed2 = !rb2.is_dynamic_or_kinematic();
261
262 if is_fixed1 && is_fixed2 {
263 continue;
264 }
265
266 if !interaction.data.supports_simd_constraints() {
267 self.nongrouped_interactions.push(*interaction_i);
269 continue;
270 }
271
272 let ijoint = interaction.data.locked_axes.bits() as usize;
273 let i1 = rb1.ids.active_set_id;
274 let i2 = rb2.ids.active_set_id;
275 let conflicts = self.body_masks.get(i1).copied().unwrap_or_default()
276 | self.body_masks.get(i2).copied().unwrap_or_default()
277 | joint_type_conflicts[ijoint];
278 let conflictfree_targets = !(conflicts & occupied_mask); let conflictfree_occupied_targets = conflictfree_targets & occupied_mask;
280
281 let target_index = if conflictfree_occupied_targets != 0 {
282 conflictfree_occupied_targets.trailing_zeros()
284 } else {
285 conflictfree_targets.trailing_zeros()
286 };
287
288 if target_index == 128 {
289 self.nongrouped_interactions.push(*interaction_i);
293 continue;
294 }
295
296 let target_mask_bit = 1 << target_index;
297
298 let bucket = self
299 .buckets
300 .entry(target_index as usize)
301 .or_insert_with(|| ([0; SIMD_WIDTH], 0));
302
303 if bucket.1 == SIMD_LAST_INDEX {
304 (bucket.0)[SIMD_LAST_INDEX] = *interaction_i;
306 self.simd_interactions.extend_from_slice(&bucket.0);
307 bucket.1 = 0;
308 occupied_mask &= !target_mask_bit;
309
310 for k in 0..NUM_JOINT_TYPES {
311 joint_type_conflicts[k] &= !target_mask_bit;
312 }
313 } else {
314 (bucket.0)[bucket.1] = *interaction_i;
315 bucket.1 += 1;
316 occupied_mask |= target_mask_bit;
317
318 for k in 0..ijoint {
319 joint_type_conflicts[k] |= target_mask_bit;
320 }
321 for k in ijoint + 1..NUM_JOINT_TYPES {
322 joint_type_conflicts[k] |= target_mask_bit;
323 }
324 }
325
326 if !is_fixed1 {
329 self.body_masks[i1] |= target_mask_bit;
330 }
331
332 if !is_fixed2 {
333 self.body_masks[i2] |= target_mask_bit;
334 }
335 }
336
337 self.nongrouped_interactions.extend(
338 self.buckets
339 .values()
340 .flat_map(|e| e.0.iter().take(e.1).copied()),
341 );
342 self.buckets.clear();
343 self.body_masks.iter_mut().for_each(|e| *e = 0);
344
345 assert!(
346 self.simd_interactions.len() % SIMD_WIDTH == 0,
347 "Invalid SIMD contact grouping."
348 );
349
350 }
356
357 pub fn clear_groups(&mut self) {
358 self.simd_interactions.clear();
359 self.nongrouped_interactions.clear();
360 }
361
362 #[cfg(not(feature = "simd-is-enabled"))]
363 pub fn group_manifolds(
364 &mut self,
365 _island_id: usize,
366 _islands: &IslandManager,
367 _bodies: &RigidBodySet,
368 _interactions: &[&mut ContactManifold],
369 interaction_indices: &[ContactManifoldIndex],
370 ) {
371 self.nongrouped_interactions
372 .extend_from_slice(interaction_indices);
373 }
374
375 #[cfg(feature = "simd-is-enabled")]
376 pub fn group_manifolds(
377 &mut self,
378 island_id: usize,
379 islands: &IslandManager,
380 bodies: &RigidBodySet,
381 interactions: &[&mut ContactManifold],
382 interaction_indices: &[ContactManifoldIndex],
383 ) {
384 self.body_masks
391 .resize(islands.island(island_id).len(), 0u128);
392
393 let mut occupied_mask = 0u128;
396 let max_interaction_points = interaction_indices
397 .iter()
398 .map(|i| interactions[*i].data.num_active_contacts())
399 .max()
400 .unwrap_or(1);
401
402 for k in 1..=max_interaction_points {
406 for interaction_i in interaction_indices {
407 let interaction = &interactions[*interaction_i];
408
409 if interaction.data.num_active_contacts() != k {
412 continue;
413 }
414
415 let (status1, active_set_id1) = if let Some(rb1) = interaction.data.rigid_body1 {
416 let rb1 = &bodies[rb1];
417 (rb1.body_type, rb1.ids.active_set_id as u32)
418 } else {
419 (RigidBodyType::Fixed, u32::MAX)
420 };
421 let (status2, active_set_id2) = if let Some(rb2) = interaction.data.rigid_body2 {
422 let rb2 = &bodies[rb2];
423 (rb2.body_type, rb2.ids.active_set_id as u32)
424 } else {
425 (RigidBodyType::Fixed, u32::MAX)
426 };
427
428 let is_fixed1 = !status1.is_dynamic_or_kinematic();
429 let is_fixed2 = !status2.is_dynamic_or_kinematic();
430
431 if is_fixed1 && is_fixed2 {
433 continue;
434 }
435
436 let i1 = active_set_id1;
437 let i2 = active_set_id2;
438 let mask1 = if !is_fixed1 {
439 self.body_masks[i1 as usize]
440 } else {
441 0
442 };
443 let mask2 = if !is_fixed2 {
444 self.body_masks[i2 as usize]
445 } else {
446 0
447 };
448 let conflicts = mask1 | mask2;
449 let conflictfree_targets = !(conflicts & occupied_mask); let conflictfree_occupied_targets = conflictfree_targets & occupied_mask;
451
452 let target_index = if conflictfree_occupied_targets != 0 {
453 conflictfree_occupied_targets.trailing_zeros()
455 } else {
456 conflictfree_targets.trailing_zeros()
457 };
458
459 if target_index == 128 {
460 self.nongrouped_interactions.push(*interaction_i);
464 continue;
465 }
466
467 let target_mask_bit = 1 << target_index;
468
469 let bucket = self
470 .buckets
471 .entry(target_index as usize)
472 .or_insert_with(|| ([0; SIMD_WIDTH], 0));
473
474 if bucket.1 == SIMD_LAST_INDEX {
475 (bucket.0)[SIMD_LAST_INDEX] = *interaction_i;
477 self.simd_interactions.extend_from_slice(&bucket.0);
478 bucket.1 = 0;
479 occupied_mask &= !target_mask_bit;
480 } else {
481 (bucket.0)[bucket.1] = *interaction_i;
482 bucket.1 += 1;
483 occupied_mask |= target_mask_bit;
484 }
485
486 if !is_fixed1 {
489 self.body_masks[i1 as usize] |= target_mask_bit;
490 }
491
492 if !is_fixed2 {
493 self.body_masks[i2 as usize] |= target_mask_bit;
494 }
495 }
496
497 self.nongrouped_interactions.extend(
498 self.buckets
499 .values()
500 .flat_map(|e| e.0.iter().take(e.1).copied()),
501 );
502 self.buckets.clear();
503 self.body_masks.iter_mut().for_each(|e| *e = 0);
504 occupied_mask = 0u128;
505 }
506
507 assert!(
508 self.simd_interactions.len() % SIMD_WIDTH == 0,
509 "Invalid SIMD contact grouping."
510 );
511 }
512}