Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/bundle/insert.rs
6849 views
1
use alloc::vec::Vec;
2
use bevy_ptr::{ConstNonNull, MovingPtr};
3
use core::ptr::NonNull;
4
5
use crate::{
6
archetype::{
7
Archetype, ArchetypeAfterBundleInsert, ArchetypeCreated, ArchetypeId, Archetypes,
8
ComponentStatus,
9
},
10
bundle::{ArchetypeMoveType, Bundle, BundleId, BundleInfo, DynamicBundle, InsertMode},
11
change_detection::MaybeLocation,
12
component::{Components, StorageType, Tick},
13
entity::{Entities, Entity, EntityLocation},
14
event::EntityComponentsTrigger,
15
lifecycle::{Add, Insert, Replace, ADD, INSERT, REPLACE},
16
observer::Observers,
17
query::DebugCheckedUnwrap as _,
18
relationship::RelationshipHookMode,
19
storage::{Storages, Table},
20
world::{unsafe_world_cell::UnsafeWorldCell, World},
21
};
22
23
// SAFETY: We have exclusive world access so our pointers can't be invalidated externally
24
pub(crate) struct BundleInserter<'w> {
25
world: UnsafeWorldCell<'w>,
26
bundle_info: ConstNonNull<BundleInfo>,
27
archetype_after_insert: ConstNonNull<ArchetypeAfterBundleInsert>,
28
table: NonNull<Table>,
29
archetype: NonNull<Archetype>,
30
archetype_move_type: ArchetypeMoveType,
31
change_tick: Tick,
32
}
33
34
impl<'w> BundleInserter<'w> {
35
#[inline]
36
pub(crate) fn new<T: Bundle>(
37
world: &'w mut World,
38
archetype_id: ArchetypeId,
39
change_tick: Tick,
40
) -> Self {
41
let bundle_id = world.register_bundle_info::<T>();
42
43
// SAFETY: We just ensured this bundle exists
44
unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) }
45
}
46
47
/// Creates a new [`BundleInserter`].
48
///
49
/// # Safety
50
/// - Caller must ensure that `bundle_id` exists in `world.bundles`.
51
#[inline]
52
pub(crate) unsafe fn new_with_id(
53
world: &'w mut World,
54
archetype_id: ArchetypeId,
55
bundle_id: BundleId,
56
change_tick: Tick,
57
) -> Self {
58
// SAFETY: We will not make any accesses to the command queue, component or resource data of this world
59
let bundle_info = world.bundles.get_unchecked(bundle_id);
60
let bundle_id = bundle_info.id();
61
let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype(
62
&mut world.archetypes,
63
&mut world.storages,
64
&world.components,
65
&world.observers,
66
archetype_id,
67
);
68
69
let inserter = if new_archetype_id == archetype_id {
70
let archetype = &mut world.archetypes[archetype_id];
71
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
72
let archetype_after_insert = unsafe {
73
archetype
74
.edges()
75
.get_archetype_after_bundle_insert_internal(bundle_id)
76
.debug_checked_unwrap()
77
};
78
let table_id = archetype.table_id();
79
let table = &mut world.storages.tables[table_id];
80
Self {
81
archetype_after_insert: archetype_after_insert.into(),
82
archetype: archetype.into(),
83
bundle_info: bundle_info.into(),
84
table: table.into(),
85
archetype_move_type: ArchetypeMoveType::SameArchetype,
86
change_tick,
87
world: world.as_unsafe_world_cell(),
88
}
89
} else {
90
let (archetype, new_archetype) =
91
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
92
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
93
let archetype_after_insert = unsafe {
94
archetype
95
.edges()
96
.get_archetype_after_bundle_insert_internal(bundle_id)
97
.debug_checked_unwrap()
98
};
99
let table_id = archetype.table_id();
100
let new_table_id = new_archetype.table_id();
101
if table_id == new_table_id {
102
let table = &mut world.storages.tables[table_id];
103
Self {
104
archetype_after_insert: archetype_after_insert.into(),
105
archetype: archetype.into(),
106
bundle_info: bundle_info.into(),
107
table: table.into(),
108
archetype_move_type: ArchetypeMoveType::NewArchetypeSameTable {
109
new_archetype: new_archetype.into(),
110
},
111
change_tick,
112
world: world.as_unsafe_world_cell(),
113
}
114
} else {
115
let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id);
116
Self {
117
archetype_after_insert: archetype_after_insert.into(),
118
archetype: archetype.into(),
119
bundle_info: bundle_info.into(),
120
table: table.into(),
121
archetype_move_type: ArchetypeMoveType::NewArchetypeNewTable {
122
new_archetype: new_archetype.into(),
123
new_table: new_table.into(),
124
},
125
change_tick,
126
world: world.as_unsafe_world_cell(),
127
}
128
}
129
};
130
131
if is_new_created {
132
inserter
133
.world
134
.into_deferred()
135
.trigger(ArchetypeCreated(new_archetype_id));
136
}
137
inserter
138
}
139
140
/// # Safety
141
/// - `entity` must currently exist in the source archetype for this inserter.
142
/// - `location` must be `entity`'s location in the archetype.
143
/// - `T` must match this [`BundleInserter`] type used to create
144
/// - If `T::Effect: !NoBundleEffect.`, then [`apply_effect`] must be called at most once on
145
/// `bundle` after this function before returning to user-space safe code.
146
/// - The value pointed to by `bundle` must not be accessed for anything other than [`apply_effect`]
147
/// or dropped.
148
///
149
/// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect
150
#[inline]
151
pub(crate) unsafe fn insert<T: DynamicBundle>(
152
&mut self,
153
entity: Entity,
154
location: EntityLocation,
155
bundle: MovingPtr<'_, T>,
156
insert_mode: InsertMode,
157
caller: MaybeLocation,
158
relationship_hook_mode: RelationshipHookMode,
159
) -> EntityLocation {
160
let bundle_info = self.bundle_info.as_ref();
161
let archetype_after_insert = self.archetype_after_insert.as_ref();
162
let archetype = self.archetype.as_ref();
163
164
// SAFETY: All components in the bundle are guaranteed to exist in the World
165
// as they must be initialized before creating the BundleInfo.
166
unsafe {
167
// SAFETY: Mutable references do not alias and will be dropped after this block
168
let mut deferred_world = self.world.into_deferred();
169
170
if insert_mode == InsertMode::Replace {
171
if archetype.has_replace_observer() {
172
// SAFETY: the REPLACE event_key corresponds to the Replace event's type
173
deferred_world.trigger_raw(
174
REPLACE,
175
&mut Replace { entity },
176
&mut EntityComponentsTrigger {
177
components: archetype_after_insert.existing(),
178
},
179
caller,
180
);
181
}
182
deferred_world.trigger_on_replace(
183
archetype,
184
entity,
185
archetype_after_insert.existing().iter().copied(),
186
caller,
187
relationship_hook_mode,
188
);
189
}
190
}
191
192
let table = self.table.as_mut();
193
194
// SAFETY: Archetype gets borrowed when running the on_replace observers above,
195
// so this reference can only be promoted from shared to &mut down here, after they have been ran
196
let archetype = self.archetype.as_mut();
197
198
let (new_archetype, new_location) = match &mut self.archetype_move_type {
199
ArchetypeMoveType::SameArchetype => {
200
// SAFETY: Mutable references do not alias and will be dropped after this block
201
let sparse_sets = {
202
let world = self.world.world_mut();
203
&mut world.storages.sparse_sets
204
};
205
206
bundle_info.write_components(
207
table,
208
sparse_sets,
209
archetype_after_insert,
210
archetype_after_insert.required_components.iter(),
211
entity,
212
location.table_row,
213
self.change_tick,
214
bundle,
215
insert_mode,
216
caller,
217
);
218
219
(archetype, location)
220
}
221
ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => {
222
let new_archetype = new_archetype.as_mut();
223
224
// SAFETY: Mutable references do not alias and will be dropped after this block
225
let (sparse_sets, entities) = {
226
let world = self.world.world_mut();
227
(&mut world.storages.sparse_sets, &mut world.entities)
228
};
229
230
let result = archetype.swap_remove(location.archetype_row);
231
if let Some(swapped_entity) = result.swapped_entity {
232
let swapped_location =
233
// SAFETY: If the swap was successful, swapped_entity must be valid.
234
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
235
entities.set(
236
swapped_entity.index(),
237
Some(EntityLocation {
238
archetype_id: swapped_location.archetype_id,
239
archetype_row: location.archetype_row,
240
table_id: swapped_location.table_id,
241
table_row: swapped_location.table_row,
242
}),
243
);
244
}
245
let new_location = new_archetype.allocate(entity, result.table_row);
246
entities.set(entity.index(), Some(new_location));
247
bundle_info.write_components(
248
table,
249
sparse_sets,
250
archetype_after_insert,
251
archetype_after_insert.required_components.iter(),
252
entity,
253
result.table_row,
254
self.change_tick,
255
bundle,
256
insert_mode,
257
caller,
258
);
259
260
(new_archetype, new_location)
261
}
262
ArchetypeMoveType::NewArchetypeNewTable {
263
new_archetype,
264
new_table,
265
} => {
266
let new_table = new_table.as_mut();
267
let new_archetype = new_archetype.as_mut();
268
269
// SAFETY: Mutable references do not alias and will be dropped after this block
270
let (archetypes_ptr, sparse_sets, entities) = {
271
let world = self.world.world_mut();
272
let archetype_ptr: *mut Archetype = world.archetypes.archetypes.as_mut_ptr();
273
(
274
archetype_ptr,
275
&mut world.storages.sparse_sets,
276
&mut world.entities,
277
)
278
};
279
let result = archetype.swap_remove(location.archetype_row);
280
if let Some(swapped_entity) = result.swapped_entity {
281
let swapped_location =
282
// SAFETY: If the swap was successful, swapped_entity must be valid.
283
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
284
entities.set(
285
swapped_entity.index(),
286
Some(EntityLocation {
287
archetype_id: swapped_location.archetype_id,
288
archetype_row: location.archetype_row,
289
table_id: swapped_location.table_id,
290
table_row: swapped_location.table_row,
291
}),
292
);
293
}
294
// PERF: store "non bundle" components in edge, then just move those to avoid
295
// redundant copies
296
let move_result = table.move_to_superset_unchecked(result.table_row, new_table);
297
let new_location = new_archetype.allocate(entity, move_result.new_row);
298
entities.set(entity.index(), Some(new_location));
299
300
// If an entity was moved into this entity's table spot, update its table row.
301
if let Some(swapped_entity) = move_result.swapped_entity {
302
let swapped_location =
303
// SAFETY: If the swap was successful, swapped_entity must be valid.
304
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
305
306
entities.set(
307
swapped_entity.index(),
308
Some(EntityLocation {
309
archetype_id: swapped_location.archetype_id,
310
archetype_row: swapped_location.archetype_row,
311
table_id: swapped_location.table_id,
312
table_row: result.table_row,
313
}),
314
);
315
316
if archetype.id() == swapped_location.archetype_id {
317
archetype
318
.set_entity_table_row(swapped_location.archetype_row, result.table_row);
319
} else if new_archetype.id() == swapped_location.archetype_id {
320
new_archetype
321
.set_entity_table_row(swapped_location.archetype_row, result.table_row);
322
} else {
323
// SAFETY: the only two borrowed archetypes are above and we just did collision checks
324
(*archetypes_ptr.add(swapped_location.archetype_id.index()))
325
.set_entity_table_row(swapped_location.archetype_row, result.table_row);
326
}
327
}
328
329
bundle_info.write_components(
330
new_table,
331
sparse_sets,
332
archetype_after_insert,
333
archetype_after_insert.required_components.iter(),
334
entity,
335
move_result.new_row,
336
self.change_tick,
337
bundle,
338
insert_mode,
339
caller,
340
);
341
342
(new_archetype, new_location)
343
}
344
};
345
346
let new_archetype = &*new_archetype;
347
// SAFETY: We have no outstanding mutable references to world as they were dropped
348
let mut deferred_world = unsafe { self.world.into_deferred() };
349
350
// SAFETY: All components in the bundle are guaranteed to exist in the World
351
// as they must be initialized before creating the BundleInfo.
352
unsafe {
353
deferred_world.trigger_on_add(
354
new_archetype,
355
entity,
356
archetype_after_insert.added().iter().copied(),
357
caller,
358
);
359
if new_archetype.has_add_observer() {
360
// SAFETY: the ADD event_key corresponds to the Add event's type
361
deferred_world.trigger_raw(
362
ADD,
363
&mut Add { entity },
364
&mut EntityComponentsTrigger {
365
components: archetype_after_insert.added(),
366
},
367
caller,
368
);
369
}
370
match insert_mode {
371
InsertMode::Replace => {
372
// Insert triggers for both new and existing components if we're replacing them.
373
deferred_world.trigger_on_insert(
374
new_archetype,
375
entity,
376
archetype_after_insert.inserted().iter().copied(),
377
caller,
378
relationship_hook_mode,
379
);
380
if new_archetype.has_insert_observer() {
381
// SAFETY: the INSERT event_key corresponds to the Insert event's type
382
deferred_world.trigger_raw(
383
INSERT,
384
&mut Insert { entity },
385
&mut EntityComponentsTrigger {
386
components: archetype_after_insert.inserted(),
387
},
388
caller,
389
);
390
}
391
}
392
InsertMode::Keep => {
393
// Insert triggers only for new components if we're not replacing them (since
394
// nothing is actually inserted).
395
deferred_world.trigger_on_insert(
396
new_archetype,
397
entity,
398
archetype_after_insert.added().iter().copied(),
399
caller,
400
relationship_hook_mode,
401
);
402
if new_archetype.has_insert_observer() {
403
// SAFETY: the INSERT event_key corresponds to the Insert event's type
404
deferred_world.trigger_raw(
405
INSERT,
406
&mut Insert { entity },
407
&mut EntityComponentsTrigger {
408
components: archetype_after_insert.added(),
409
},
410
caller,
411
);
412
}
413
}
414
}
415
}
416
417
new_location
418
}
419
420
#[inline]
421
pub(crate) fn entities(&mut self) -> &mut Entities {
422
// SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers
423
unsafe { &mut self.world.world_mut().entities }
424
}
425
}
426
427
impl BundleInfo {
428
/// Inserts a bundle into the given archetype and returns the resulting archetype and whether a new archetype was created.
429
/// This could be the same [`ArchetypeId`], in the event that inserting the given bundle
430
/// does not result in an [`Archetype`] change.
431
///
432
/// Results are cached in the [`Archetype`] graph to avoid redundant work.
433
///
434
/// # Safety
435
/// `components` must be the same components as passed in [`Self::new`]
436
pub(crate) unsafe fn insert_bundle_into_archetype(
437
&self,
438
archetypes: &mut Archetypes,
439
storages: &mut Storages,
440
components: &Components,
441
observers: &Observers,
442
archetype_id: ArchetypeId,
443
) -> (ArchetypeId, bool) {
444
if let Some(archetype_after_insert_id) = archetypes[archetype_id]
445
.edges()
446
.get_archetype_after_bundle_insert(self.id)
447
{
448
return (archetype_after_insert_id, false);
449
}
450
let mut new_table_components = Vec::new();
451
let mut new_sparse_set_components = Vec::new();
452
let mut bundle_status = Vec::with_capacity(self.explicit_components_len());
453
let mut added_required_components = Vec::new();
454
let mut added = Vec::new();
455
let mut existing = Vec::new();
456
457
let current_archetype = &mut archetypes[archetype_id];
458
for component_id in self.iter_explicit_components() {
459
if current_archetype.contains(component_id) {
460
bundle_status.push(ComponentStatus::Existing);
461
existing.push(component_id);
462
} else {
463
bundle_status.push(ComponentStatus::Added);
464
added.push(component_id);
465
// SAFETY: component_id exists
466
let component_info = unsafe { components.get_info_unchecked(component_id) };
467
match component_info.storage_type() {
468
StorageType::Table => new_table_components.push(component_id),
469
StorageType::SparseSet => new_sparse_set_components.push(component_id),
470
}
471
}
472
}
473
474
for (index, component_id) in self.iter_required_components().enumerate() {
475
if !current_archetype.contains(component_id) {
476
added_required_components.push(self.required_component_constructors[index].clone());
477
added.push(component_id);
478
// SAFETY: component_id exists
479
let component_info = unsafe { components.get_info_unchecked(component_id) };
480
match component_info.storage_type() {
481
StorageType::Table => {
482
new_table_components.push(component_id);
483
}
484
StorageType::SparseSet => {
485
new_sparse_set_components.push(component_id);
486
}
487
}
488
}
489
}
490
491
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
492
let edges = current_archetype.edges_mut();
493
// The archetype does not change when we insert this bundle.
494
edges.cache_archetype_after_bundle_insert(
495
self.id,
496
archetype_id,
497
bundle_status,
498
added_required_components,
499
added,
500
existing,
501
);
502
(archetype_id, false)
503
} else {
504
let table_id;
505
let table_components;
506
let sparse_set_components;
507
// The archetype changes when we insert this bundle. Prepare the new archetype and storages.
508
{
509
let current_archetype = &archetypes[archetype_id];
510
table_components = if new_table_components.is_empty() {
511
// If there are no new table components, we can keep using this table.
512
table_id = current_archetype.table_id();
513
current_archetype.table_components().collect()
514
} else {
515
new_table_components.extend(current_archetype.table_components());
516
// Sort to ignore order while hashing.
517
new_table_components.sort_unstable();
518
// SAFETY: all component ids in `new_table_components` exist
519
table_id = unsafe {
520
storages
521
.tables
522
.get_id_or_insert(&new_table_components, components)
523
};
524
525
new_table_components
526
};
527
528
sparse_set_components = if new_sparse_set_components.is_empty() {
529
current_archetype.sparse_set_components().collect()
530
} else {
531
new_sparse_set_components.extend(current_archetype.sparse_set_components());
532
// Sort to ignore order while hashing.
533
new_sparse_set_components.sort_unstable();
534
new_sparse_set_components
535
};
536
};
537
// SAFETY: ids in self must be valid
538
let (new_archetype_id, is_new_created) = archetypes.get_id_or_insert(
539
components,
540
observers,
541
table_id,
542
table_components,
543
sparse_set_components,
544
);
545
546
// Add an edge from the old archetype to the new archetype.
547
archetypes[archetype_id]
548
.edges_mut()
549
.cache_archetype_after_bundle_insert(
550
self.id,
551
new_archetype_id,
552
bundle_status,
553
added_required_components,
554
added,
555
existing,
556
);
557
(new_archetype_id, is_new_created)
558
}
559
}
560
}
561
562