Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/bundle/info.rs
9550 views
1
use alloc::{boxed::Box, vec, vec::Vec};
2
use bevy_platform::{
3
collections::{HashMap, HashSet},
4
hash::FixedHasher,
5
};
6
use bevy_ptr::{MovingPtr, OwningPtr};
7
use bevy_utils::TypeIdMap;
8
use core::{any::TypeId, ptr::NonNull};
9
use indexmap::{IndexMap, IndexSet};
10
11
use crate::{
12
archetype::{Archetype, BundleComponentStatus, ComponentStatus},
13
bundle::{Bundle, DynamicBundle},
14
change_detection::{MaybeLocation, Tick},
15
component::{
16
ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, StorageType,
17
},
18
entity::Entity,
19
query::DebugCheckedUnwrap as _,
20
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
21
};
22
23
/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].
24
///
25
/// [`World`]: crate::world::World
26
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
27
pub struct BundleId(usize);
28
29
impl BundleId {
30
/// Returns the index of the associated [`Bundle`] type.
31
///
32
/// Note that this is unique per-world, and should not be reused across them.
33
#[inline]
34
pub fn index(self) -> usize {
35
self.0
36
}
37
}
38
39
impl SparseSetIndex for BundleId {
40
#[inline]
41
fn sparse_set_index(&self) -> usize {
42
self.index()
43
}
44
45
#[inline]
46
fn get_sparse_set_index(value: usize) -> Self {
47
Self(value)
48
}
49
}
50
51
/// What to do on insertion if a component already exists.
52
#[derive(Clone, Copy, Eq, PartialEq)]
53
pub enum InsertMode {
54
/// Any existing components of a matching type will be overwritten.
55
Replace,
56
/// Any existing components of a matching type will be left unchanged.
57
Keep,
58
}
59
60
/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].
61
///
62
/// [`World`]: crate::world::World
63
pub struct BundleInfo {
64
pub(super) id: BundleId,
65
66
/// The list of all components contributed by the bundle (including Required Components). This is in
67
/// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]`
68
///
69
/// # Safety
70
/// Every ID in this list must be valid within the World that owns the [`BundleInfo`],
71
/// must have its storage initialized (i.e. columns created in tables, sparse set created),
72
/// and the range (0..`explicit_components_len`) must be in the same order as the source bundle
73
/// type writes its components in.
74
pub(super) contributed_component_ids: Box<[ComponentId]>,
75
76
/// The list of constructors for all required components indirectly contributed by this bundle.
77
pub(super) required_component_constructors: Box<[RequiredComponentConstructor]>,
78
}
79
80
impl BundleInfo {
81
/// Create a new [`BundleInfo`].
82
///
83
/// # Safety
84
///
85
/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
86
/// and must be in the same order as the source bundle type writes its components in.
87
unsafe fn new(
88
bundle_type_name: &'static str,
89
storages: &mut Storages,
90
components: &Components,
91
mut component_ids: Vec<ComponentId>,
92
id: BundleId,
93
) -> BundleInfo {
94
let explicit_component_ids = component_ids
95
.iter()
96
.copied()
97
.collect::<IndexSet<_, FixedHasher>>();
98
99
// check for duplicates
100
if explicit_component_ids.len() != component_ids.len() {
101
// TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
102
let mut seen = <HashSet<_>>::default();
103
let mut dups = Vec::new();
104
for id in component_ids {
105
if !seen.insert(id) {
106
dups.push(id);
107
}
108
}
109
110
let names = dups
111
.into_iter()
112
.map(|id| {
113
// SAFETY: the caller ensures component_id is valid.
114
unsafe { components.get_info_unchecked(id).name() }
115
})
116
.collect::<Vec<_>>();
117
118
panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
119
}
120
121
let mut depth_first_components = IndexMap::<_, _, FixedHasher>::default();
122
for &component_id in &component_ids {
123
// SAFETY: caller has verified that all ids are valid
124
let info = unsafe { components.get_info_unchecked(component_id) };
125
126
for (&required_id, required_component) in &info.required_components().all {
127
depth_first_components
128
.entry(required_id)
129
.or_insert_with(|| required_component.clone());
130
}
131
132
storages.prepare_component(info);
133
}
134
135
let required_components = depth_first_components
136
.into_iter()
137
.filter(|&(required_id, _)| !explicit_component_ids.contains(&required_id))
138
.inspect(|&(required_id, _)| {
139
// SAFETY: These ids came out of the passed `components`, so they must be valid.
140
storages.prepare_component(unsafe { components.get_info_unchecked(required_id) });
141
component_ids.push(required_id);
142
})
143
.map(|(_, required_component)| required_component.constructor)
144
.collect::<Box<_>>();
145
146
// SAFETY: The caller ensures that component_ids:
147
// - is valid for the associated world
148
// - has had its storage initialized
149
// - is in the same order as the source bundle type
150
BundleInfo {
151
id,
152
contributed_component_ids: component_ids.into(),
153
required_component_constructors: required_components,
154
}
155
}
156
157
/// Returns a value identifying the associated [`Bundle`] type.
158
#[inline]
159
pub const fn id(&self) -> BundleId {
160
self.id
161
}
162
163
/// Returns the length of the explicit components part of the [`contributed_components`](Self::contributed_components) list.
164
#[inline]
165
pub(super) fn explicit_components_len(&self) -> usize {
166
self.contributed_component_ids.len() - self.required_component_constructors.len()
167
}
168
169
/// Returns the [ID](ComponentId) of each component explicitly defined in this bundle (ex: Required Components are excluded).
170
///
171
/// For all components contributed by this bundle (including Required Components), see [`BundleInfo::contributed_components`]
172
#[inline]
173
pub fn explicit_components(&self) -> &[ComponentId] {
174
&self.contributed_component_ids[0..self.explicit_components_len()]
175
}
176
177
/// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are
178
/// explicitly provided by the bundle.
179
#[inline]
180
pub fn required_components(&self) -> &[ComponentId] {
181
&self.contributed_component_ids[self.explicit_components_len()..]
182
}
183
184
/// Returns the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.
185
///
186
/// For only components explicitly defined in this bundle, see [`BundleInfo::explicit_components`]
187
#[inline]
188
pub fn contributed_components(&self) -> &[ComponentId] {
189
&self.contributed_component_ids
190
}
191
192
/// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components).
193
/// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`]
194
#[inline]
195
pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
196
self.explicit_components().iter().copied()
197
}
198
199
/// Returns an iterator over the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.
200
///
201
/// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`]
202
#[inline]
203
pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
204
self.contributed_components().iter().copied()
205
}
206
207
/// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are
208
/// explicitly provided by the bundle.
209
pub fn iter_required_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
210
self.required_components().iter().copied()
211
}
212
213
/// This writes components from a given [`Bundle`] to the given entity.
214
///
215
/// # Safety
216
///
217
/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component
218
/// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added).
219
///
220
/// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status
221
/// should be `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`.
222
///
223
/// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`](crate::archetype::SpawnBundleStatus)
224
/// should be used, which will report `Added` vs `Existing` status based on the current archetype's structure.
225
///
226
/// When spawning a bundle, [`SpawnBundleStatus`](crate::archetype::SpawnBundleStatus) can be used instead,
227
/// which removes the need to look up the [`ArchetypeAfterBundleInsert`](crate::archetype::ArchetypeAfterBundleInsert)
228
/// in the archetype graph, which requires ownership of the entity's current archetype.
229
///
230
/// Regardless of how this is used, [`apply_effect`] must be called at most once on `bundle` after this function is
231
/// called if `T::Effect: !NoBundleEffect` before returning to user-space safe code before returning to user-space safe code.
232
/// This is currently only doable via use of [`MovingPtr::partial_move`].
233
///
234
/// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the
235
/// `entity`, `bundle` must match this [`BundleInfo`]'s type
236
///
237
/// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect
238
#[inline]
239
pub(super) unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>(
240
&self,
241
table: &mut Table,
242
sparse_sets: &mut SparseSets,
243
bundle_component_status: &S,
244
required_components: impl Iterator<Item = &'a RequiredComponentConstructor>,
245
entity: Entity,
246
table_row: TableRow,
247
change_tick: Tick,
248
bundle: MovingPtr<'_, T>,
249
insert_mode: InsertMode,
250
caller: MaybeLocation,
251
) {
252
// NOTE: get_components calls this closure on each component in "bundle order".
253
// bundle_info.component_ids are also in "bundle order"
254
let mut bundle_component = 0;
255
T::get_components(bundle, &mut |storage_type, component_ptr| {
256
let component_id = *self
257
.contributed_component_ids
258
.get_unchecked(bundle_component);
259
// SAFETY: bundle_component is a valid index for this bundle
260
let status = unsafe { bundle_component_status.get_status(bundle_component) };
261
match storage_type {
262
StorageType::Table => {
263
let column =
264
// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
265
// the target table contains the component.
266
unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
267
match (status, insert_mode) {
268
(ComponentStatus::Added, _) => {
269
column.initialize(table_row, component_ptr, change_tick, caller);
270
}
271
(ComponentStatus::Existing, InsertMode::Replace) => {
272
column.replace(table_row, component_ptr, change_tick, caller);
273
}
274
(ComponentStatus::Existing, InsertMode::Keep) => {
275
if let Some(drop_fn) = table.get_drop_for(component_id) {
276
drop_fn(component_ptr);
277
}
278
}
279
}
280
}
281
StorageType::SparseSet => {
282
let sparse_set =
283
// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
284
// a sparse set exists for the component.
285
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
286
match (status, insert_mode) {
287
(ComponentStatus::Added, _) | (_, InsertMode::Replace) => {
288
sparse_set.insert(entity, component_ptr, change_tick, caller);
289
}
290
(ComponentStatus::Existing, InsertMode::Keep) => {
291
if let Some(drop_fn) = sparse_set.get_drop() {
292
drop_fn(component_ptr);
293
}
294
}
295
}
296
}
297
}
298
bundle_component += 1;
299
});
300
301
for required_component in required_components {
302
required_component.initialize(
303
table,
304
sparse_sets,
305
change_tick,
306
table_row,
307
entity,
308
caller,
309
);
310
}
311
}
312
313
/// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called
314
/// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`].
315
///
316
/// # Safety
317
///
318
/// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match
319
/// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized
320
/// component matching `component_id`.
321
///
322
/// This method _should not_ be called outside of [`BundleInfo::write_components`].
323
/// For more information, read the [`BundleInfo::write_components`] safety docs.
324
/// This function inherits the safety requirements defined there.
325
pub(crate) unsafe fn initialize_required_component(
326
table: &mut Table,
327
sparse_sets: &mut SparseSets,
328
change_tick: Tick,
329
table_row: TableRow,
330
entity: Entity,
331
component_id: ComponentId,
332
storage_type: StorageType,
333
component_ptr: OwningPtr,
334
caller: MaybeLocation,
335
) {
336
{
337
match storage_type {
338
StorageType::Table => {
339
let column =
340
// SAFETY: If component_id is in required_components, BundleInfo::new requires that
341
// the target table contains the component.
342
unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
343
column.initialize(table_row, component_ptr, change_tick, caller);
344
}
345
StorageType::SparseSet => {
346
let sparse_set =
347
// SAFETY: If component_id is in required_components, BundleInfo::new requires that
348
// a sparse set exists for the component.
349
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
350
sparse_set.insert(entity, component_ptr, change_tick, caller);
351
}
352
}
353
}
354
}
355
}
356
357
/// The type of archetype move (or lack thereof) that will result from a bundle
358
/// being inserted into an entity.
359
pub(crate) enum ArchetypeMoveType {
360
/// If the entity already has all of the components that are being inserted,
361
/// its archetype won't change.
362
SameArchetype,
363
/// If only [`sparse set`](StorageType::SparseSet) components are being added,
364
/// the entity's archetype will change while keeping the same table.
365
NewArchetypeSameTable { new_archetype: NonNull<Archetype> },
366
/// If any [`table-stored`](StorageType::Table) components are being added,
367
/// both the entity's archetype and table will change.
368
NewArchetypeNewTable {
369
new_archetype: NonNull<Archetype>,
370
new_table: NonNull<Table>,
371
},
372
}
373
374
/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.
375
#[derive(Default)]
376
pub struct Bundles {
377
bundle_infos: Vec<BundleInfo>,
378
/// Cache static [`BundleId`]
379
bundle_ids: TypeIdMap<BundleId>,
380
/// Cache bundles, which contains both explicit and required components of [`Bundle`]
381
contributed_bundle_ids: TypeIdMap<BundleId>,
382
/// Cache dynamic [`BundleId`] with multiple components
383
dynamic_bundle_ids: HashMap<Box<[ComponentId]>, BundleId>,
384
dynamic_bundle_storages: HashMap<BundleId, Vec<StorageType>>,
385
/// Cache optimized dynamic [`BundleId`] with single component
386
dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,
387
dynamic_component_storages: HashMap<BundleId, StorageType>,
388
}
389
390
impl Bundles {
391
/// The total number of [`Bundle`] registered in [`Storages`].
392
pub fn len(&self) -> usize {
393
self.bundle_infos.len()
394
}
395
396
/// Returns true if no [`Bundle`] registered in [`Storages`].
397
pub fn is_empty(&self) -> bool {
398
self.len() == 0
399
}
400
401
/// Iterate over [`BundleInfo`].
402
pub fn iter(&self) -> impl Iterator<Item = &BundleInfo> {
403
self.bundle_infos.iter()
404
}
405
406
/// Gets the metadata associated with a specific type of bundle.
407
/// Returns `None` if the bundle is not registered with the world.
408
#[inline]
409
pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> {
410
self.bundle_infos.get(bundle_id.index())
411
}
412
413
/// Gets the value identifying a specific type of bundle.
414
/// Returns `None` if the bundle does not exist in the world,
415
/// or if `type_id` does not correspond to a type of bundle.
416
#[inline]
417
pub fn get_id(&self, type_id: TypeId) -> Option<BundleId> {
418
self.bundle_ids.get(&type_id).cloned()
419
}
420
421
/// Registers a new [`BundleInfo`] for a statically known type.
422
///
423
/// Also registers all the components in the bundle.
424
///
425
/// # Safety
426
///
427
/// `components` and `storages` must be from the same [`World`] as `self`.
428
///
429
/// [`World`]: crate::world::World
430
#[deny(unsafe_op_in_unsafe_fn)]
431
pub(crate) unsafe fn register_info<T: Bundle>(
432
&mut self,
433
components: &mut ComponentsRegistrator,
434
storages: &mut Storages,
435
) -> BundleId {
436
let bundle_infos = &mut self.bundle_infos;
437
*self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
438
let component_ids = T::component_ids(components).collect::<Vec<_>>();
439
let id = BundleId(bundle_infos.len());
440
let bundle_info =
441
// SAFETY: T::component_id ensures:
442
// - its info was created
443
// - appropriate storage for it has been initialized.
444
// - it was created in the same order as the components in T
445
unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };
446
bundle_infos.push(bundle_info);
447
id
448
})
449
}
450
451
/// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.
452
///
453
/// Also registers all the components in the bundle.
454
///
455
/// # Safety
456
///
457
/// `components` and `storages` must be from the same [`World`] as `self`.
458
///
459
/// [`World`]: crate::world::World
460
#[deny(unsafe_op_in_unsafe_fn)]
461
pub(crate) unsafe fn register_contributed_bundle_info<T: Bundle>(
462
&mut self,
463
components: &mut ComponentsRegistrator,
464
storages: &mut Storages,
465
) -> BundleId {
466
if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
467
id
468
} else {
469
// SAFETY: as per the guarantees of this function, components and
470
// storages are from the same world as self
471
let explicit_bundle_id = unsafe { self.register_info::<T>(components, storages) };
472
473
// SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this
474
let id = unsafe {
475
let (ptr, len) = {
476
// SAFETY: `explicit_bundle_id` is valid and defined above
477
let contributed = self
478
.get_unchecked(explicit_bundle_id)
479
.contributed_components();
480
(contributed.as_ptr(), contributed.len())
481
};
482
// SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as
483
// part of init_dynamic_info. No mutable references will be created and the allocation will remain valid.
484
self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len))
485
};
486
self.contributed_bundle_ids.insert(TypeId::of::<T>(), id);
487
id
488
}
489
}
490
491
/// # Safety
492
/// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`.
493
pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo {
494
self.bundle_infos.get_unchecked(id.0)
495
}
496
497
/// # Safety
498
/// This [`BundleId`] must have been initialized with a single [`Component`](crate::component::Component)
499
/// (via [`init_component_info`](Self::init_dynamic_info))
500
pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType {
501
*self
502
.dynamic_component_storages
503
.get(&id)
504
.debug_checked_unwrap()
505
}
506
507
/// # Safety
508
/// This [`BundleId`] must have been initialized with multiple [`Component`](crate::component::Component)s
509
/// (via [`init_dynamic_info`](Self::init_dynamic_info))
510
pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec<StorageType> {
511
self.dynamic_bundle_storages
512
.get_mut(&id)
513
.debug_checked_unwrap()
514
}
515
516
/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`].
517
///
518
/// # Panics
519
///
520
/// Panics if any of the provided [`ComponentId`]s do not exist in the
521
/// provided [`Components`].
522
pub(crate) fn init_dynamic_info(
523
&mut self,
524
storages: &mut Storages,
525
components: &Components,
526
component_ids: &[ComponentId],
527
) -> BundleId {
528
let bundle_infos = &mut self.bundle_infos;
529
530
// Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry`
531
let (_, bundle_id) = self
532
.dynamic_bundle_ids
533
.raw_entry_mut()
534
.from_key(component_ids)
535
.or_insert_with(|| {
536
let (id, storages) = initialize_dynamic_bundle(
537
bundle_infos,
538
storages,
539
components,
540
Vec::from(component_ids),
541
);
542
// SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.
543
unsafe {
544
self.dynamic_bundle_storages
545
.insert_unique_unchecked(id, storages);
546
}
547
(component_ids.into(), id)
548
});
549
*bundle_id
550
}
551
552
/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component.
553
///
554
/// # Panics
555
///
556
/// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].
557
pub(crate) fn init_component_info(
558
&mut self,
559
storages: &mut Storages,
560
components: &Components,
561
component_id: ComponentId,
562
) -> BundleId {
563
let bundle_infos = &mut self.bundle_infos;
564
let bundle_id = self
565
.dynamic_component_bundle_ids
566
.entry(component_id)
567
.or_insert_with(|| {
568
let (id, storage_type) = initialize_dynamic_bundle(
569
bundle_infos,
570
storages,
571
components,
572
vec![component_id],
573
);
574
self.dynamic_component_storages.insert(id, storage_type[0]);
575
id
576
});
577
*bundle_id
578
}
579
}
580
581
/// Asserts that all components are part of [`Components`]
582
/// and initializes a [`BundleInfo`].
583
fn initialize_dynamic_bundle(
584
bundle_infos: &mut Vec<BundleInfo>,
585
storages: &mut Storages,
586
components: &Components,
587
component_ids: Vec<ComponentId>,
588
) -> (BundleId, Vec<StorageType>) {
589
// Assert component existence
590
let storage_types = component_ids.iter().map(|&id| {
591
components.get_info(id).unwrap_or_else(|| {
592
panic!(
593
"init_dynamic_info called with component id {id:?} which doesn't exist in this world"
594
)
595
}).storage_type()
596
}).collect();
597
598
let id = BundleId(bundle_infos.len());
599
let bundle_info =
600
// SAFETY: `component_ids` are valid as they were just checked
601
unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };
602
bundle_infos.push(bundle_info);
603
604
(id, storage_types)
605
}
606
607