Skip to content

Commit

Permalink
drop sparse components in remove_bundle command
Browse files Browse the repository at this point in the history
  • Loading branch information
Frizi committed May 16, 2021
1 parent 17d092c commit 4e8f81a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 23 deletions.
5 changes: 1 addition & 4 deletions crates/bevy_ecs/src/storage/sparse_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,7 @@ impl ComponentSparseSet {
/// returned).
pub fn remove_and_forget(&mut self, entity: Entity) -> Option<*mut u8> {
self.sparse.remove(entity).map(|dense_index| {
// SAFE: unique access to ticks
unsafe {
(*self.ticks.get()).swap_remove(dense_index);
}
self.ticks.get_mut().swap_remove(dense_index);
self.entities.swap_remove(dense_index);
let is_last = dense_index == self.dense.len() - 1;
// SAFE: dense_index was just removed from `sparse`, which ensures that it is valid
Expand Down
40 changes: 38 additions & 2 deletions crates/bevy_ecs/src/system/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,29 @@ impl<T: Component> Command for RemoveResource<T> {
#[allow(clippy::float_cmp, clippy::approx_constant)]
mod tests {
use crate::{
component::{ComponentDescriptor, StorageType},
system::{CommandQueue, Commands},
world::World,
};
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};

#[derive(Clone, Debug)]
struct DropCk(Arc<AtomicUsize>);
impl DropCk {
fn new_pair() -> (Self, Arc<AtomicUsize>) {
let atomic = Arc::new(AtomicUsize::new(0));
(DropCk(atomic.clone()), atomic)
}
}

impl Drop for DropCk {
fn drop(&mut self) {
self.0.as_ref().fetch_add(1, Ordering::Relaxed);
}
}

#[test]
fn commands() {
Expand Down Expand Up @@ -454,10 +474,20 @@ mod tests {
#[test]
fn remove_components() {
let mut world = World::default();

struct DenseDropCk(DropCk);
world
.register_component(ComponentDescriptor::new::<DropCk>(StorageType::SparseSet))
.unwrap();

let mut command_queue = CommandQueue::default();
let (dense_dropck, dense_is_dropped) = DropCk::new_pair();
let dense_dropck = DenseDropCk(dense_dropck);
let (sparse_dropck, sparse_is_dropped) = DropCk::new_pair();

let entity = Commands::new(&mut command_queue, &world)
.spawn()
.insert_bundle((1u32, 2u64))
.insert_bundle((1u32, 2u64, dense_dropck, sparse_dropck))
.id();
command_queue.apply(&mut world);
let results_before = world
Expand All @@ -471,8 +501,14 @@ mod tests {
Commands::new(&mut command_queue, &world)
.entity(entity)
.remove::<u32>()
.remove_bundle::<(u32, u64)>();
.remove_bundle::<(u32, u64, DenseDropCk, DropCk)>();

assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 0);
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 0);
command_queue.apply(&mut world);
assert_eq!(dense_is_dropped.load(Ordering::Relaxed), 1);
assert_eq!(sparse_is_dropped.load(Ordering::Relaxed), 1);

let results_after = world
.query::<(&u32, &u64)>()
.iter(&world)
Expand Down
40 changes: 23 additions & 17 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ impl<'w> EntityMut<'w> {
T::from_components(|| {
let component_id = bundle_components.next().unwrap();
// SAFE: entity location is valid and table row is removed below
remove_component(
take_component(
components,
storages,
old_archetype,
Expand Down Expand Up @@ -406,17 +406,18 @@ impl<'w> EntityMut<'w> {
let entity = self.entity;
for component_id in bundle_info.component_ids.iter().cloned() {
if old_archetype.contains(component_id) {
// SAFE: entity location is valid and table row is removed below
unsafe {
remove_component(
components,
storages,
old_archetype,
removed_components,
component_id,
entity,
old_location,
);
removed_components
.get_or_insert_with(component_id, Vec::new)
.push(entity);

// Make sure to drop components stored in sparse sets.
// Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
if let Some(StorageType::SparseSet) = old_archetype.get_storage_type(component_id) {
storages
.sparse_sets
.get_mut(component_id)
.unwrap()
.remove(entity);
}
}
}
Expand Down Expand Up @@ -586,13 +587,18 @@ unsafe fn get_component_and_ticks(
}
}

/// Moves component data out of storage.
///
/// This function leaves the underlying memory unchanged, but the component behind
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
///
/// # Safety
// `entity_location` must be within bounds of the given archetype and `entity` must exist inside the
// archetype
/// The relevant table row must be removed separately
/// `component_id` must be valid
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype
/// - `component_id` must be valid
/// - The relevant table row **must be removed** by the caller once all components are taken
/// - caller is responsible to drop component data behind returned pointer
#[inline]
unsafe fn remove_component(
unsafe fn take_component(
components: &Components,
storages: &mut Storages,
archetype: &Archetype,
Expand Down

0 comments on commit 4e8f81a

Please sign in to comment.