aboutsummaryrefslogtreecommitdiff
path: root/src/dynamics/island_set.rs
diff options
context:
space:
mode:
authorCrozet Sébastien <developer@crozet.re>2021-02-04 18:04:50 +0100
committerCrozet Sébastien <developer@crozet.re>2021-02-04 18:04:50 +0100
commite585e262c468646e681aa1aec38d45cb7df2c082 (patch)
tree50ffe3f943061bbfcb8000f5f92d797dc52f0d35 /src/dynamics/island_set.rs
parent09b867d0be5378f249a3dc4722527ed2e0233645 (diff)
downloadrapier-perf_experiments.tar.gz
rapier-perf_experiments.tar.bz2
rapier-perf_experiments.zip
Rename island_set2 -> island_setperf_experiments
Diffstat (limited to 'src/dynamics/island_set.rs')
-rw-r--r--src/dynamics/island_set.rs383
1 files changed, 231 insertions, 152 deletions
diff --git a/src/dynamics/island_set.rs b/src/dynamics/island_set.rs
index a0da207..71dc9eb 100644
--- a/src/dynamics/island_set.rs
+++ b/src/dynamics/island_set.rs
@@ -1,114 +1,69 @@
+use crate::data::Coarena;
use crate::dynamics::{RigidBody, RigidBodyHandle, RigidBodySet};
-use crate::geometry::NarrowPhase;
+use crate::geometry::{ColliderSet, NarrowPhase};
use crate::utils;
-use downcast_rs::__std::collections::VecDeque;
+use std::collections::VecDeque;
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct Island {
bodies: Vec<RigidBodyHandle>,
-
- // Number of bodies that are awake on this island.
- // If this is equal to zero, ten the whole island is asleep.
- wake_up_count: usize,
- // Index in the island_set.active_islands vector.
- active_island_id: usize,
- dirty: bool,
}
impl Island {
pub fn new() -> Self {
- Self {
- bodies: vec![],
- wake_up_count: 0,
- active_island_id: crate::INVALID_USIZE,
- dirty: false,
- }
+ Self { bodies: vec![] }
}
pub fn len(&self) -> usize {
self.bodies.len()
}
- pub fn is_sleeping(&self) -> bool {
- self.wake_up_count == 0
- }
-
- pub fn active_island_id(&self) -> usize {
- self.active_island_id
- }
-
pub fn bodies(&self) -> &[RigidBodyHandle] {
- &self.bodies[..]
+ &self.bodies
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct IslandSet {
- active_islands: Vec<usize>,
+ // We can't store this in the rigid-bodies because that would
+ // cause borrowing issues during the traversal.
+ traversal_body_timestamps: Coarena<u64>,
islands: Vec<Island>,
- to_update: VecDeque<usize>,
+ active_island: usize,
+ islands_to_extract: VecDeque<RigidBodyHandle>,
+ islands_extraction_queue: Vec<RigidBodyHandle>,
+ // NOTE: this value must never be 0, otherwise some
+ // invariants used during the traversal can break.
+ traversal_timestamp: u64,
}
impl IslandSet {
pub fn new() -> Self {
IslandSet {
- active_islands: vec![],
- islands: vec![],
- to_update: VecDeque::new(),
+ traversal_body_timestamps: Coarena::new(),
+ islands: vec![Island::new()],
+ active_island: 0,
+ islands_to_extract: VecDeque::new(),
+ islands_extraction_queue: vec![],
+ traversal_timestamp: 1,
}
}
- pub fn islands(&self) -> &[Island] {
- &self.islands[..]
- }
-
pub fn num_active_islands(&self) -> usize {
- self.active_islands.len()
- }
-
- pub fn active_island(&self, i: usize) -> &Island {
- &self.islands[self.active_islands[i]]
- }
-
- pub fn active_bodies<'a>(&'a self) -> impl Iterator<Item = RigidBodyHandle> + 'a {
- let islands = &self.islands;
- self.active_islands
- .iter()
- .copied()
- .flat_map(move |i| islands[i].bodies.iter())
- .copied()
+ 1
}
- pub fn contact_stopped(
- &mut self,
- bodies: &RigidBodySet,
- h1: RigidBodyHandle,
- _h2: RigidBodyHandle,
- ) {
- // // NOTE: we don't actually need h2 because they are both on the same island anyway.
- // // Yet we keep the `h2` argument to show that this function properly take into account
- // // both bodies.
- // if let Some(island_id) = bodies.get(h1).map(|b| b.island_id) {
- // if let Some(island) = self.islands.get_mut(island_id) {
- // if !island.dirty {
- // island.dirty = true;
- // self.to_update.push_back(island_id);
- // }
- // }
- // }
+ pub fn is_island_sleeping(&self, island_id: usize) -> bool {
+ island_id != self.active_island
}
- pub fn incremental_update(&mut self, narrow_phase: &NarrowPhase) {
- // if let Some(island) = self.to_update.pop_front() {
- // // Next island to update.
- // }
+ pub fn active_bodies(&self) -> &[RigidBodyHandle] {
+ &self.islands[self.active_island].bodies
}
- pub fn is_island_sleeping(&self, island_id: usize) -> bool {
- self.islands[island_id].is_sleeping()
- }
+ pub fn incremental_update(&mut self, narrow_phase: &NarrowPhase) {}
pub fn add_rigid_body(&mut self, handle: RigidBodyHandle, body: &mut RigidBody) {
assert_eq!(body.island_id, crate::INVALID_USIZE);
@@ -117,85 +72,239 @@ impl IslandSet {
return;
}
- let new_island_id = handle.into_raw_parts().0;
+ self.traversal_body_timestamps
+ .ensure_element_exists(handle.0, 0);
- if self.islands.len() <= new_island_id {
- self.islands.resize(new_island_id + 1, Island::new());
+ if body.can_sleep() {
+ body.island_id = self.islands.len();
+ body.island_offset = 0;
+ self.islands.push(Island {
+ bodies: vec![handle],
+ })
+ } else {
+ let mut active_island = &mut self.islands[self.active_island];
+ body.island_id = self.active_island;
+ body.island_offset = active_island.bodies.len();
+ active_island.bodies.push(handle);
}
+ }
- body.island_id = new_island_id;
- body.island_offset = self.islands[new_island_id].bodies.len();
- self.islands[new_island_id].bodies.push(handle);
+ pub fn contact_started(
+ &mut self,
+ bodies: &mut RigidBodySet,
+ mut h1: RigidBodyHandle,
+ mut h2: RigidBodyHandle,
+ ) {
+ let island1 = bodies[h1].island_id;
+ let island2 = bodies[h2].island_id;
- // NOTE: `body_sleep_state_changed` must be called afterwards.
+ self.wake_up_island(bodies, island1);
+ self.wake_up_island(bodies, island2);
}
- pub fn body_sleep_state_changed(&mut self, body: &RigidBody) {
- // Non-dynamic bodies never take part in the island computation
- // since they don't transmit any forces.
- if !body.is_dynamic() {
- return;
+ pub fn body_sleep_state_changed(&mut self, bodies: &mut RigidBodySet, handle: RigidBodyHandle) {
+ let body = &mut bodies[handle];
+ let island_id = body.island_id;
+
+ if island_id == crate::INVALID_USIZE {
+ self.add_rigid_body(handle, body);
+ } else if island_id == self.active_island && body.can_sleep() {
+ // The body is sleeping now.
+ self.islands_to_extract.push_back(handle);
+ } else if island_id != self.active_island && !body.can_sleep() {
+ // The body is awake now.
+ self.wake_up_island(bodies, island_id);
}
+ }
- let island = &mut self.islands[body.island_id];
-
- if body.can_sleep() {
- island.wake_up_count -= 1;
+ #[inline(always)]
+ fn push_contacting_bodies(
+ handle: RigidBodyHandle,
+ bodies: &mut RigidBodySet,
+ colliders: &ColliderSet,
+ narrow_phase: &NarrowPhase,
+ queue: &mut Vec<RigidBodyHandle>,
+ traversal_body_timestamps: &mut Coarena<u64>,
+ first_traversal_timestamp: u64,
+ traversal_timestamp: u64,
+ ) -> bool {
+ for collider_handle in &bodies[handle].colliders {
+ if let Some(contacts) = narrow_phase.contacts_with(*collider_handle) {
+ for inter in contacts {
+ let other = crate::utils::select_other((inter.0, inter.1), *collider_handle);
+ let other_body_handle = colliders[other].parent;
+ let other_body = &bodies[other_body_handle];
+
+ if other_body.is_dynamic() {
+ if !other_body.can_sleep() {
+ return false;
+ }
+
+ let other_body_timestamp =
+ &mut traversal_body_timestamps[other_body_handle.0];
+
+ if *other_body_timestamp >= first_traversal_timestamp
+ && *other_body_timestamp < traversal_timestamp
+ {
+ // We already saw this body during another traversal this frame.
+ // So we don't need to continue any further.
+ return false;
+ }
+
+ if *other_body_timestamp != traversal_timestamp {
+ *other_body_timestamp = traversal_timestamp;
+ queue.push(other_body_handle);
+ }
+ }
+ }
+ }
+ }
- if island.wake_up_count == 0 {
- // The island is sleeping now.
- // Remove it from the active set.
- let active_island_id_to_remove = island.active_island_id;
- island.active_island_id = crate::INVALID_USIZE;
- self.active_islands.swap_remove(active_island_id_to_remove);
+ true
+ }
- if let Some(moved_island) = self.active_islands.get(active_island_id_to_remove) {
- self.islands[*moved_island].active_island_id = active_island_id_to_remove;
+ pub fn update_sleeping_islands(
+ &mut self,
+ bodies: &mut RigidBodySet,
+ colliders: &mut ColliderSet,
+ narrow_phase: &NarrowPhase,
+ ) {
+ let first_traversal_timestamp = self.traversal_timestamp + 1;
+ let mut island_found = false;
+
+ while let Some(handle) = self.islands_to_extract.pop_front() {
+ self.traversal_timestamp += 1;
+ island_found = self.extract_sleeping_island(
+ bodies,
+ colliders,
+ narrow_phase,
+ handle,
+ first_traversal_timestamp,
+ ) || island_found;
+
+ // Make sure our `traversal_timestamp` remains correct
+ // even if it overflows (can happen in very long-running
+ // simulations).
+ if self.traversal_timestamp == u64::MAX - 1 {
+ // Stop the work now because the overflowing timestamp
+ // will cause issues with the traversal. Forcibly reset all
+ // the timestamps and continue next frame.
+ for body in bodies.iter_mut_internal() {
+ body.1.traversal_timestamp = 0;
}
+ self.traversal_timestamp = 1;
+ break;
}
- } else {
- if island.wake_up_count == 0 {
- // The islands is waking up.
- // Add it to the active set.
- assert_eq!(island.active_island_id, crate::INVALID_USIZE);
- island.active_island_id = self.active_islands.len();
- self.active_islands.push(body.island_id);
- }
-
- island.wake_up_count += 1;
}
- if self.active_islands.len() == 0 {
- dbg!("Hurray!");
+ if island_found {
+ // Now we need to remove from the active set all the bodies
+ // we moved to a sleeping island.
+ let mut i = 0;
+ let mut new_len = 0;
+ let active_island = &mut self.islands[self.active_island];
+
+ while i < active_island.bodies.len() {
+ let handle = active_island.bodies[i];
+ let body = &mut bodies[handle];
+
+ if body.island_id == self.active_island {
+ // This body still belongs to this island.
+ // Update its offset.
+ body.island_offset = new_len;
+ active_island.bodies[new_len] = handle;
+ new_len += 1;
+ }
+
+ i += 1;
+ }
+
+ active_island.bodies.truncate(new_len);
}
}
- pub fn contact_started(
+ fn extract_sleeping_island(
&mut self,
bodies: &mut RigidBodySet,
- mut h1: RigidBodyHandle,
- mut h2: RigidBodyHandle,
- ) {
- let body1 = &bodies[h1];
- let body2 = &bodies[h2];
+ colliders: &mut ColliderSet,
+ narrow_phase: &NarrowPhase,
+ handle: RigidBodyHandle,
+ first_traversal_timestamp: u64,
+ ) -> bool {
+ // Attempt to extract a sleeping island by traversing the
+ // contact graph starting with the given rigid-body.
+ if let Some(body) = bodies.get(handle) {
+ if body.is_dynamic() && body.can_sleep() {
+ // Perform a breadth-first search of the contact graph starting
+ // with this body. We stop the search when an active body is
+ // found (meaning that the island cannot sleep), or if we
+ // extracted the whole island.
+ // We do a breadth-first search in order to increase our chances
+ // to find a non-sleeping body quickly: if this body just started
+ // sleeping, chances are that a non-sleeping body is close.
+ self.islands_extraction_queue.clear();
+ self.islands_extraction_queue.push(handle);
+ self.traversal_body_timestamps[handle.0] = self.traversal_timestamp;
+ let mut i = 0;
+
+ while i < self.islands_extraction_queue.len() {
+ let handle = self.islands_extraction_queue[i];
+
+ let continue_traversal = Self::push_contacting_bodies(
+ handle,
+ bodies,
+ colliders,
+ narrow_phase,
+ &mut self.islands_extraction_queue,
+ &mut self.traversal_body_timestamps,
+ first_traversal_timestamp,
+ self.traversal_timestamp,
+ );
+
+ if !continue_traversal {
+ // This island cannot sleep yet.
+ return false;
+ }
+
+ i += 1;
+ }
- if !body1.is_dynamic() || !body2.is_dynamic() {
- // Non-dynamic bodies don't transmit forces.
- // So we can ignore their contact for island computation.
- return;
+ // If we reached this point, then we successfully found a sleeping island.
+ for (k, handle) in self.islands_extraction_queue.iter().enumerate() {
+ let body = &mut bodies[*handle];
+ body.island_id = self.islands.len();
+ body.island_offset = k;
+ }
+
+ // FIXME: recycle old islands.
+ self.islands.push(Island {
+ bodies: std::mem::replace(&mut self.islands_extraction_queue, Vec::new()),
+ });
+
+ return true;
+ }
}
- assert_ne!(body1.island_id, crate::INVALID_USIZE);
- assert_ne!(body2.island_id, crate::INVALID_USIZE);
+ false
+ }
+
+ pub fn wake_up_island(&mut self, bodies: &mut RigidBodySet, island_id: usize) {
+ if island_id == crate::INVALID_USIZE {
+ return;
+ }
- let mut island_id1 = body1.island_id;
- let mut island_id2 = body2.island_id;
+ let mut island_id1 = self.active_island;
+ let mut island_id2 = island_id;
if island_id1 != island_id2 {
let (mut island1, mut island2) =
utils::get2_mut(&mut self.islands, island_id1, island_id2);
// Make sure island1 points to the biggest island.
+ // The typical scenario is that only a few object are awaking
+ // one big island. So in this typical scenario, we actually want
+ // to merge the active island into the sleeping island, and then
+ // mark the sleeping island as active.
if island2.len() > island1.len() {
std::mem::swap(&mut island1, &mut island2);
std::mem::swap(&mut island_id1, &mut island_id2);
@@ -213,37 +322,7 @@ impl IslandSet {
// Islands can get big so let's save some memory.
island2.bodies = vec![];
-
- // Update the wake-up count and determine if the island is awake.
- // Note that if both `wake_up_count` are zero, then the island
- // won't end up in the active island set (which is what we want).
- let island2_was_sleeping = island2.is_sleeping();
-
- if island1.is_sleeping() && !island2_was_sleeping {
- // Add the first island to the active island set.
- assert_eq!(island1.active_island_id, crate::INVALID_USIZE);
- island1.active_island_id = self.active_islands.len();
- self.active_islands.push(island_id1);
- }
-
- island1.wake_up_count += island2.wake_up_count;
- island2.wake_up_count = 0;
-
- assert!(island1.wake_up_count != 0 || island1.active_island_id == crate::INVALID_USIZE);
-
- if !island2_was_sleeping {
- // The second island will be emptied, so we
- // can remove it from the set of active islands.
- let active_island_id_to_remove = island2.active_island_id;
- island2.active_island_id = crate::INVALID_USIZE;
- self.active_islands.swap_remove(active_island_id_to_remove);
-
- if let Some(moved_island) = self.active_islands.get(active_island_id_to_remove) {
- self.islands[*moved_island].active_island_id = active_island_id_to_remove;
- }
- } else {
- assert_eq!(island2.active_island_id, crate::INVALID_USIZE);
- }
+ self.active_island = island_id1;
}
}
}