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