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.active_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 = bcolors[rb1.ids.active_set_offset as usize]
121 | bcolors[rb2.ids.active_set_offset as usize];
122 *color = (!color_mask).trailing_zeros() as usize;
123 color_len[*color] += 1;
124 bcolors[rb1.ids.active_set_offset as usize] |= 1 << *color;
125 bcolors[rb2.ids.active_set_offset as usize] |= 1 << *color;
126 }
127 (true, false) => {
128 let rb2 = &bodies[body_pair.1.unwrap()];
129 let color_mask = bcolors[rb2.ids.active_set_offset as usize];
130 *color = 127 - (!color_mask).leading_zeros() as usize;
131 color_len[*color] += 1;
132 bcolors[rb2.ids.active_set_offset as usize] |= 1 << *color;
133 }
134 (false, true) => {
135 let rb1 = &bodies[body_pair.0.unwrap()];
136 let color_mask = bcolors[rb1.ids.active_set_offset as usize];
137 *color = 127 - (!color_mask).leading_zeros() as usize;
138 color_len[*color] += 1;
139 bcolors[rb1.ids.active_set_offset as usize] |= 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.active_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_offset;
274 let i2 = rb2.ids.active_set_offset;
275 let conflicts = self
276 .body_masks
277 .get(i1 as usize)
278 .copied()
279 .unwrap_or_default()
280 | self
281 .body_masks
282 .get(i2 as usize)
283 .copied()
284 .unwrap_or_default()
285 | joint_type_conflicts[ijoint];
286 let conflictfree_targets = !(conflicts & occupied_mask); let conflictfree_occupied_targets = conflictfree_targets & occupied_mask;
288
289 let target_index = if conflictfree_occupied_targets != 0 {
290 conflictfree_occupied_targets.trailing_zeros()
292 } else {
293 conflictfree_targets.trailing_zeros()
294 };
295
296 if target_index == 128 {
297 self.nongrouped_interactions.push(*interaction_i);
301 continue;
302 }
303
304 let target_mask_bit = 1 << target_index;
305
306 let bucket = self
307 .buckets
308 .entry(target_index as usize)
309 .or_insert_with(|| ([0; SIMD_WIDTH], 0));
310
311 if bucket.1 == SIMD_LAST_INDEX {
312 (bucket.0)[SIMD_LAST_INDEX] = *interaction_i;
314 self.simd_interactions.extend_from_slice(&bucket.0);
315 bucket.1 = 0;
316 occupied_mask &= !target_mask_bit;
317
318 for k in 0..NUM_JOINT_TYPES {
319 joint_type_conflicts[k] &= !target_mask_bit;
320 }
321 } else {
322 (bucket.0)[bucket.1] = *interaction_i;
323 bucket.1 += 1;
324 occupied_mask |= target_mask_bit;
325
326 for k in 0..ijoint {
327 joint_type_conflicts[k] |= target_mask_bit;
328 }
329 for k in ijoint + 1..NUM_JOINT_TYPES {
330 joint_type_conflicts[k] |= target_mask_bit;
331 }
332 }
333
334 if !is_fixed1 {
337 self.body_masks[i1 as usize] |= target_mask_bit;
338 }
339
340 if !is_fixed2 {
341 self.body_masks[i2 as usize] |= target_mask_bit;
342 }
343 }
344
345 self.nongrouped_interactions.extend(
346 self.buckets
347 .values()
348 .flat_map(|e| e.0.iter().take(e.1).copied()),
349 );
350 self.buckets.clear();
351 self.body_masks.iter_mut().for_each(|e| *e = 0);
352
353 assert!(
354 self.simd_interactions.len() % SIMD_WIDTH == 0,
355 "Invalid SIMD contact grouping."
356 );
357
358 }
364
365 pub fn clear_groups(&mut self) {
366 self.simd_interactions.clear();
367 self.nongrouped_interactions.clear();
368 }
369
370 #[cfg(not(feature = "simd-is-enabled"))]
371 pub fn group_manifolds(
372 &mut self,
373 _island_id: usize,
374 _islands: &IslandManager,
375 _bodies: &RigidBodySet,
376 _interactions: &[&mut ContactManifold],
377 interaction_indices: &[ContactManifoldIndex],
378 ) {
379 self.nongrouped_interactions
380 .extend_from_slice(interaction_indices);
381 }
382
383 #[cfg(feature = "simd-is-enabled")]
384 pub fn group_manifolds(
385 &mut self,
386 island_id: usize,
387 islands: &IslandManager,
388 bodies: &RigidBodySet,
389 interactions: &[&mut ContactManifold],
390 interaction_indices: &[ContactManifoldIndex],
391 ) {
392 self.body_masks
399 .resize(islands.active_island(island_id).len(), 0u128);
400
401 let mut occupied_mask = 0u128;
404 let max_interaction_points = interaction_indices
405 .iter()
406 .map(|i| interactions[*i].data.num_active_contacts())
407 .max()
408 .unwrap_or(1);
409
410 for k in 1..=max_interaction_points {
414 for interaction_i in interaction_indices {
415 let interaction = &interactions[*interaction_i];
416
417 if interaction.data.num_active_contacts() != k {
420 continue;
421 }
422
423 let (status1, active_set_offset1) = if let Some(rb1) = interaction.data.rigid_body1
424 {
425 let rb1 = &bodies[rb1];
426 (rb1.body_type, rb1.ids.active_set_offset)
427 } else {
428 (RigidBodyType::Fixed, u32::MAX)
429 };
430 let (status2, active_set_offset2) = if let Some(rb2) = interaction.data.rigid_body2
431 {
432 let rb2 = &bodies[rb2];
433 (rb2.body_type, rb2.ids.active_set_offset)
434 } else {
435 (RigidBodyType::Fixed, u32::MAX)
436 };
437
438 let is_fixed1 = !status1.is_dynamic_or_kinematic();
439 let is_fixed2 = !status2.is_dynamic_or_kinematic();
440
441 if is_fixed1 && is_fixed2 {
443 continue;
444 }
445
446 let i1 = active_set_offset1;
447 let i2 = active_set_offset2;
448 let mask1 = if !is_fixed1 {
449 self.body_masks[i1 as usize]
450 } else {
451 0
452 };
453 let mask2 = if !is_fixed2 {
454 self.body_masks[i2 as usize]
455 } else {
456 0
457 };
458 let conflicts = mask1 | mask2;
459 let conflictfree_targets = !(conflicts & occupied_mask); let conflictfree_occupied_targets = conflictfree_targets & occupied_mask;
461
462 let target_index = if conflictfree_occupied_targets != 0 {
463 conflictfree_occupied_targets.trailing_zeros()
465 } else {
466 conflictfree_targets.trailing_zeros()
467 };
468
469 if target_index == 128 {
470 self.nongrouped_interactions.push(*interaction_i);
474 continue;
475 }
476
477 let target_mask_bit = 1 << target_index;
478
479 let bucket = self
480 .buckets
481 .entry(target_index as usize)
482 .or_insert_with(|| ([0; SIMD_WIDTH], 0));
483
484 if bucket.1 == SIMD_LAST_INDEX {
485 (bucket.0)[SIMD_LAST_INDEX] = *interaction_i;
487 self.simd_interactions.extend_from_slice(&bucket.0);
488 bucket.1 = 0;
489 occupied_mask &= !target_mask_bit;
490 } else {
491 (bucket.0)[bucket.1] = *interaction_i;
492 bucket.1 += 1;
493 occupied_mask |= target_mask_bit;
494 }
495
496 if !is_fixed1 {
499 self.body_masks[i1 as usize] |= target_mask_bit;
500 }
501
502 if !is_fixed2 {
503 self.body_masks[i2 as usize] |= target_mask_bit;
504 }
505 }
506
507 self.nongrouped_interactions.extend(
508 self.buckets
509 .values()
510 .flat_map(|e| e.0.iter().take(e.1).copied()),
511 );
512 self.buckets.clear();
513 self.body_masks.iter_mut().for_each(|e| *e = 0);
514 occupied_mask = 0u128;
515 }
516
517 assert!(
518 self.simd_interactions.len() % SIMD_WIDTH == 0,
519 "Invalid SIMD contact grouping."
520 );
521 }
522}