Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/bundle/remove.rs
6849 views
1
use alloc::vec::Vec;
2
use bevy_ptr::ConstNonNull;
3
use core::ptr::NonNull;
4
5
use crate::{
6
archetype::{Archetype, ArchetypeCreated, ArchetypeId, Archetypes},
7
bundle::{Bundle, BundleId, BundleInfo},
8
change_detection::MaybeLocation,
9
component::{ComponentId, Components, StorageType},
10
entity::{Entity, EntityLocation},
11
event::EntityComponentsTrigger,
12
lifecycle::{Remove, Replace, REMOVE, REPLACE},
13
observer::Observers,
14
relationship::RelationshipHookMode,
15
storage::{SparseSets, Storages, Table},
16
world::{unsafe_world_cell::UnsafeWorldCell, World},
17
};
18
19
// SAFETY: We have exclusive world access so our pointers can't be invalidated externally
20
pub(crate) struct BundleRemover<'w> {
21
world: UnsafeWorldCell<'w>,
22
bundle_info: ConstNonNull<BundleInfo>,
23
old_and_new_table: Option<(NonNull<Table>, NonNull<Table>)>,
24
old_archetype: NonNull<Archetype>,
25
new_archetype: NonNull<Archetype>,
26
pub(crate) relationship_hook_mode: RelationshipHookMode,
27
}
28
29
impl<'w> BundleRemover<'w> {
30
/// Creates a new [`BundleRemover`], if such a remover would do anything.
31
///
32
/// If `require_all` is true, the [`BundleRemover`] is only created if the entire bundle is present on the archetype.
33
///
34
/// # Safety
35
/// Caller must ensure that `archetype_id` is valid
36
#[inline]
37
#[deny(unsafe_op_in_unsafe_fn)]
38
pub(crate) unsafe fn new<T: Bundle>(
39
world: &'w mut World,
40
archetype_id: ArchetypeId,
41
require_all: bool,
42
) -> Option<Self> {
43
let bundle_id = world.register_bundle_info::<T>();
44
45
// SAFETY: we initialized this bundle_id in `init_info`, and caller ensures archetype is valid.
46
unsafe { Self::new_with_id(world, archetype_id, bundle_id, require_all) }
47
}
48
49
/// Creates a new [`BundleRemover`], if such a remover would do anything.
50
///
51
/// If `require_all` is true, the [`BundleRemover`] is only created if the entire bundle is present on the archetype.
52
///
53
/// # Safety
54
/// Caller must ensure that `bundle_id` exists in `world.bundles` and `archetype_id` is valid.
55
#[inline]
56
pub(crate) unsafe fn new_with_id(
57
world: &'w mut World,
58
archetype_id: ArchetypeId,
59
bundle_id: BundleId,
60
require_all: bool,
61
) -> Option<Self> {
62
let bundle_info = world.bundles.get_unchecked(bundle_id);
63
// SAFETY: Caller ensures archetype and bundle ids are correct.
64
let (new_archetype_id, is_new_created) = unsafe {
65
bundle_info.remove_bundle_from_archetype(
66
&mut world.archetypes,
67
&mut world.storages,
68
&world.components,
69
&world.observers,
70
archetype_id,
71
!require_all,
72
)
73
};
74
let new_archetype_id = new_archetype_id?;
75
76
if new_archetype_id == archetype_id {
77
return None;
78
}
79
80
let (old_archetype, new_archetype) =
81
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
82
83
let tables = if old_archetype.table_id() == new_archetype.table_id() {
84
None
85
} else {
86
let (old, new) = world
87
.storages
88
.tables
89
.get_2_mut(old_archetype.table_id(), new_archetype.table_id());
90
Some((old.into(), new.into()))
91
};
92
93
let remover = Self {
94
bundle_info: bundle_info.into(),
95
new_archetype: new_archetype.into(),
96
old_archetype: old_archetype.into(),
97
old_and_new_table: tables,
98
world: world.as_unsafe_world_cell(),
99
relationship_hook_mode: RelationshipHookMode::Run,
100
};
101
if is_new_created {
102
remover
103
.world
104
.into_deferred()
105
.trigger(ArchetypeCreated(new_archetype_id));
106
}
107
Some(remover)
108
}
109
110
/// This can be passed to [`remove`](Self::remove) as the `pre_remove` function if you don't want to do anything before removing.
111
pub fn empty_pre_remove(
112
_: &mut SparseSets,
113
_: Option<&mut Table>,
114
_: &Components,
115
_: &[ComponentId],
116
) -> (bool, ()) {
117
(true, ())
118
}
119
120
/// Performs the removal.
121
///
122
/// `pre_remove` should return a bool for if the components still need to be dropped.
123
///
124
/// # Safety
125
/// The `location` must have the same archetype as the remover.
126
#[inline]
127
pub(crate) unsafe fn remove<T: 'static>(
128
&mut self,
129
entity: Entity,
130
location: EntityLocation,
131
caller: MaybeLocation,
132
pre_remove: impl FnOnce(
133
&mut SparseSets,
134
Option<&mut Table>,
135
&Components,
136
&[ComponentId],
137
) -> (bool, T),
138
) -> (EntityLocation, T) {
139
// Hooks
140
// SAFETY: all bundle components exist in World
141
unsafe {
142
// SAFETY: We only keep access to archetype/bundle data.
143
let mut deferred_world = self.world.into_deferred();
144
let bundle_components_in_archetype = || {
145
self.bundle_info
146
.as_ref()
147
.iter_explicit_components()
148
.filter(|component_id| self.old_archetype.as_ref().contains(*component_id))
149
};
150
if self.old_archetype.as_ref().has_replace_observer() {
151
let components = bundle_components_in_archetype().collect::<Vec<_>>();
152
// SAFETY: the REPLACE event_key corresponds to the Replace event's type
153
deferred_world.trigger_raw(
154
REPLACE,
155
&mut Replace { entity },
156
&mut EntityComponentsTrigger {
157
components: &components,
158
},
159
caller,
160
);
161
}
162
deferred_world.trigger_on_replace(
163
self.old_archetype.as_ref(),
164
entity,
165
bundle_components_in_archetype(),
166
caller,
167
self.relationship_hook_mode,
168
);
169
if self.old_archetype.as_ref().has_remove_observer() {
170
let components = bundle_components_in_archetype().collect::<Vec<_>>();
171
// SAFETY: the REMOVE event_key corresponds to the Remove event's type
172
deferred_world.trigger_raw(
173
REMOVE,
174
&mut Remove { entity },
175
&mut EntityComponentsTrigger {
176
components: &components,
177
},
178
caller,
179
);
180
}
181
deferred_world.trigger_on_remove(
182
self.old_archetype.as_ref(),
183
entity,
184
bundle_components_in_archetype(),
185
caller,
186
);
187
}
188
189
// SAFETY: We still have the cell, so this is unique, it doesn't conflict with other references, and we drop it shortly.
190
let world = unsafe { self.world.world_mut() };
191
192
let (needs_drop, pre_remove_result) = pre_remove(
193
&mut world.storages.sparse_sets,
194
self.old_and_new_table
195
.as_ref()
196
// SAFETY: There is no conflicting access for this scope.
197
.map(|(old, _)| unsafe { &mut *old.as_ptr() }),
198
&world.components,
199
self.bundle_info.as_ref().explicit_components(),
200
);
201
202
// Handle sparse set removes
203
for component_id in self.bundle_info.as_ref().iter_explicit_components() {
204
if self.old_archetype.as_ref().contains(component_id) {
205
world.removed_components.write(component_id, entity);
206
207
// Make sure to drop components stored in sparse sets.
208
// Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
209
if let Some(StorageType::SparseSet) =
210
self.old_archetype.as_ref().get_storage_type(component_id)
211
{
212
world
213
.storages
214
.sparse_sets
215
.get_mut(component_id)
216
// Set exists because the component existed on the entity
217
.unwrap()
218
// If it was already forgotten, it would not be in the set.
219
.remove(entity);
220
}
221
}
222
}
223
224
// Handle archetype change
225
let remove_result = self
226
.old_archetype
227
.as_mut()
228
.swap_remove(location.archetype_row);
229
// if an entity was moved into this entity's archetype row, update its archetype row
230
if let Some(swapped_entity) = remove_result.swapped_entity {
231
let swapped_location = world.entities.get(swapped_entity).unwrap();
232
233
world.entities.set(
234
swapped_entity.index(),
235
Some(EntityLocation {
236
archetype_id: swapped_location.archetype_id,
237
archetype_row: location.archetype_row,
238
table_id: swapped_location.table_id,
239
table_row: swapped_location.table_row,
240
}),
241
);
242
}
243
244
// Handle table change
245
let new_location = if let Some((mut old_table, mut new_table)) = self.old_and_new_table {
246
let move_result = if needs_drop {
247
// SAFETY: old_table_row exists
248
unsafe {
249
old_table
250
.as_mut()
251
.move_to_and_drop_missing_unchecked(location.table_row, new_table.as_mut())
252
}
253
} else {
254
// SAFETY: old_table_row exists
255
unsafe {
256
old_table.as_mut().move_to_and_forget_missing_unchecked(
257
location.table_row,
258
new_table.as_mut(),
259
)
260
}
261
};
262
263
// SAFETY: move_result.new_row is a valid position in new_archetype's table
264
let new_location = unsafe {
265
self.new_archetype
266
.as_mut()
267
.allocate(entity, move_result.new_row)
268
};
269
270
// if an entity was moved into this entity's table row, update its table row
271
if let Some(swapped_entity) = move_result.swapped_entity {
272
let swapped_location = world.entities.get(swapped_entity).unwrap();
273
274
world.entities.set(
275
swapped_entity.index(),
276
Some(EntityLocation {
277
archetype_id: swapped_location.archetype_id,
278
archetype_row: swapped_location.archetype_row,
279
table_id: swapped_location.table_id,
280
table_row: location.table_row,
281
}),
282
);
283
world.archetypes[swapped_location.archetype_id]
284
.set_entity_table_row(swapped_location.archetype_row, location.table_row);
285
}
286
287
new_location
288
} else {
289
// The tables are the same
290
self.new_archetype
291
.as_mut()
292
.allocate(entity, location.table_row)
293
};
294
295
// SAFETY: The entity is valid and has been moved to the new location already.
296
unsafe {
297
world.entities.set(entity.index(), Some(new_location));
298
}
299
300
(new_location, pre_remove_result)
301
}
302
}
303
304
impl BundleInfo {
305
/// Removes a bundle from the given archetype and returns the resulting archetype and whether a new archetype was created.
306
/// (or `None` if the removal was invalid).
307
/// This could be the same [`ArchetypeId`], in the event that removing the given bundle
308
/// does not result in an [`Archetype`] change.
309
///
310
/// Results are cached in the [`Archetype`] graph to avoid redundant work.
311
///
312
/// If `intersection` is false, attempting to remove a bundle with components not contained in the
313
/// current archetype will fail, returning `None`.
314
///
315
/// If `intersection` is true, components in the bundle but not in the current archetype
316
/// will be ignored.
317
///
318
/// # Safety
319
/// `archetype_id` must exist and components in `bundle_info` must exist
320
pub(crate) unsafe fn remove_bundle_from_archetype(
321
&self,
322
archetypes: &mut Archetypes,
323
storages: &mut Storages,
324
components: &Components,
325
observers: &Observers,
326
archetype_id: ArchetypeId,
327
intersection: bool,
328
) -> (Option<ArchetypeId>, bool) {
329
// Check the archetype graph to see if the bundle has been
330
// removed from this archetype in the past.
331
let archetype_after_remove_result = {
332
let edges = archetypes[archetype_id].edges();
333
if intersection {
334
edges.get_archetype_after_bundle_remove(self.id())
335
} else {
336
edges.get_archetype_after_bundle_take(self.id())
337
}
338
};
339
let (result, is_new_created) = if let Some(result) = archetype_after_remove_result {
340
// This bundle removal result is cached. Just return that!
341
(result, false)
342
} else {
343
let mut next_table_components;
344
let mut next_sparse_set_components;
345
let next_table_id;
346
{
347
let current_archetype = &mut archetypes[archetype_id];
348
let mut removed_table_components = Vec::new();
349
let mut removed_sparse_set_components = Vec::new();
350
for component_id in self.iter_explicit_components() {
351
if current_archetype.contains(component_id) {
352
// SAFETY: bundle components were already initialized by bundles.get_info
353
let component_info = unsafe { components.get_info_unchecked(component_id) };
354
match component_info.storage_type() {
355
StorageType::Table => removed_table_components.push(component_id),
356
StorageType::SparseSet => {
357
removed_sparse_set_components.push(component_id);
358
}
359
}
360
} else if !intersection {
361
// A component in the bundle was not present in the entity's archetype, so this
362
// removal is invalid. Cache the result in the archetype graph.
363
current_archetype
364
.edges_mut()
365
.cache_archetype_after_bundle_take(self.id(), None);
366
return (None, false);
367
}
368
}
369
370
// Sort removed components so we can do an efficient "sorted remove".
371
// Archetype components are already sorted.
372
removed_table_components.sort_unstable();
373
removed_sparse_set_components.sort_unstable();
374
next_table_components = current_archetype.table_components().collect();
375
next_sparse_set_components = current_archetype.sparse_set_components().collect();
376
sorted_remove(&mut next_table_components, &removed_table_components);
377
sorted_remove(
378
&mut next_sparse_set_components,
379
&removed_sparse_set_components,
380
);
381
382
next_table_id = if removed_table_components.is_empty() {
383
current_archetype.table_id()
384
} else {
385
// SAFETY: all components in next_table_components exist
386
unsafe {
387
storages
388
.tables
389
.get_id_or_insert(&next_table_components, components)
390
}
391
};
392
}
393
394
let (new_archetype_id, is_new_created) = archetypes.get_id_or_insert(
395
components,
396
observers,
397
next_table_id,
398
next_table_components,
399
next_sparse_set_components,
400
);
401
(Some(new_archetype_id), is_new_created)
402
};
403
let current_archetype = &mut archetypes[archetype_id];
404
// Cache the result in an edge.
405
if intersection {
406
current_archetype
407
.edges_mut()
408
.cache_archetype_after_bundle_remove(self.id(), result);
409
} else {
410
current_archetype
411
.edges_mut()
412
.cache_archetype_after_bundle_take(self.id(), result);
413
}
414
(result, is_new_created)
415
}
416
}
417
418
fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
419
let mut remove_index = 0;
420
source.retain(|value| {
421
while remove_index < remove.len() && *value > remove[remove_index] {
422
remove_index += 1;
423
}
424
425
if remove_index < remove.len() {
426
*value != remove[remove_index]
427
} else {
428
true
429
}
430
});
431
}
432
433
#[cfg(test)]
434
mod tests {
435
use alloc::vec;
436
437
#[test]
438
fn sorted_remove() {
439
let mut a = vec![1, 2, 3, 4, 5, 6, 7];
440
let b = vec![1, 2, 3, 5, 7];
441
super::sorted_remove(&mut a, &b);
442
443
assert_eq!(a, vec![4, 6]);
444
445
let mut a = vec![1];
446
let b = vec![1];
447
super::sorted_remove(&mut a, &b);
448
449
assert_eq!(a, vec![]);
450
451
let mut a = vec![1];
452
let b = vec![2];
453
super::sorted_remove(&mut a, &b);
454
455
assert_eq!(a, vec![1]);
456
}
457
}
458
459