Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation for ECS internals #3366

Closed
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::{
ops::{Index, IndexMut},
};

/// The unique identifier of an [`Archetype`]
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ArchetypeId(usize);

Expand All @@ -33,16 +34,22 @@ impl ArchetypeId {
}
}

/// Records whether a component was modified by addition or mutation
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
pub enum ComponentStatus {
Added,
Mutated,
}

/// Stores the information needed to add a bundle to the archetype graph.
pub struct AddBundle {
pub archetype_id: ArchetypeId,
pub bundle_status: Vec<ComponentStatus>,
}

/// The connections from one [`Archetype`] to its neighbours
///
/// Used to acccelerate add and remove operations.
/// Edges, like archetypes, are never cleaned up.
#[derive(Default)]
pub struct Edges {
pub add_bundle: SparseArray<BundleId, AddBundle>,
Expand Down Expand Up @@ -116,6 +123,9 @@ pub(crate) struct ArchetypeComponentInfo {
pub(crate) archetype_component_id: ArchetypeComponentId,
}

/// A unique set of components stored within the [`World`](crate::world::World)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With sparse set components, these can contain many sets of component, although each set is uniquely assigned to a single archetype.

///
/// Archetypes are used internally to accelerate iteration over queries
pub struct Archetype {
id: ArchetypeId,
entities: Vec<Entity>,
Expand Down Expand Up @@ -335,6 +345,9 @@ pub struct ArchetypeIdentity {
sparse_set_components: Cow<'static, [ComponentId]>,
}

/// The unique identifier for a component within a particular archetype
///
/// These are unique across archetypes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ArchetypeComponentId(usize);

Expand All @@ -361,6 +374,7 @@ impl SparseSetIndex for ArchetypeComponentId {
}
}

/// The collection of [`Archetype`] stored in the [`World`](crate::world::World)
pub struct Archetypes {
pub(crate) archetypes: Vec<Archetype>,
pub(crate) archetype_component_count: usize,
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ macro_rules! tuple_impl {

all_tuples!(tuple_impl, 0, 15, C);

/// The unique identifer of a type that implements [Bundle]
#[derive(Debug, Clone, Copy)]
pub struct BundleId(usize);

Expand All @@ -150,6 +151,7 @@ impl SparseSetIndex for BundleId {
}
}

/// The metadata of a [Bundle] and its constituent components
pub struct BundleInfo {
pub(crate) id: BundleId,
pub(crate) component_ids: Vec<ComponentId>,
Expand Down Expand Up @@ -572,6 +574,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
}
}

/// A central storage of [Bundle]s
#[derive(Default)]
pub struct Bundles {
bundle_infos: Vec<BundleInfo>,
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ pub trait Component: Send + Sync + 'static {
type Storage: ComponentStorage;
}

/// A [ComponentStorage] strategy that prioritizes efficient iteration
pub struct TableStorage;

/// A [ComponentStorage] strategy that prioritizes efficient insertion and removal
pub struct SparseStorage;

/// The strategy used to store a [Component] within the [World](crate::world::World)
///
/// This trait is sealed, and cannot be implemented externally.
pub trait ComponentStorage: sealed::Sealed {
// because the trait is sealed, those items are private API.
const STORAGE_TYPE: StorageType;
Expand Down Expand Up @@ -90,6 +96,7 @@ impl Default for StorageType {
}
}

/// Stores metadata that identify and describe a specific [Component]
#[derive(Debug)]
pub struct ComponentInfo {
id: ComponentId,
Expand Down Expand Up @@ -137,6 +144,10 @@ impl ComponentInfo {
}
}

/// Unique identifier for a component (or resource) type.
///
/// Used to lookup storage information in ['Components'](crate::component::Components).
/// These are assigned sequentially, beginning at 0.
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ComponentId(usize);

Expand All @@ -163,6 +174,9 @@ impl SparseSetIndex for ComponentId {
}
}

/// Functional metadata for a component (or resource) type
///
/// Used for control flow in ECS internals to ensure the correct behavior is followed.
#[derive(Debug)]
pub struct ComponentDescriptor {
name: String,
Expand Down Expand Up @@ -232,6 +246,14 @@ impl ComponentDescriptor {
}
}

/// The ['World'](crate::world::World)'s collection of component and resource types.
///
/// Can be accessed via ['World::components'](crate::world::World::components)
///
/// Stores metadata about component storage layout.
/// ['ComponentId'](crate::component::ComponentId)'s can be produced from
/// ['TypeId'](std::any::TypeId) using ['get_id'](Self::get_id).
/// These can then be used to access the corresponding ['ComponentInfo'](crate::component::ComponentInfo).
#[derive(Debug, Default)]
pub struct Components {
components: Vec<ComponentInfo>,
Expand Down Expand Up @@ -344,6 +366,7 @@ impl Components {
}
}

/// Stores the last time a [Component] or [Resource] was added or changed
#[derive(Clone, Debug)]
pub struct ComponentTicks {
pub(crate) added: u32,
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/entity/map_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub trait MapEntities {
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError>;
}

/// A map storing a map from one set of entities to a new set
///
/// This is useful for reflection, scenes, and hierarchies,
/// particularly when used with the [MapEntities] trait
#[derive(Default, Debug)]
pub struct EntityMap {
map: HashMap<Entity, Entity>,
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct Entity {
pub(crate) id: u32,
}

/// The result of attempting to allocate an entity using [Entity::alloc_at_without_replacement]
pub enum AllocAtWithoutReplacement {
Exists(EntityLocation),
DidNotExist,
Expand Down Expand Up @@ -158,6 +159,7 @@ impl<'a> Iterator for ReserveEntitiesIterator<'a> {

impl<'a> core::iter::ExactSizeIterator for ReserveEntitiesIterator<'a> {}

/// The central collection of [Entity]s found within a [World](crate::world::World).
#[derive(Debug, Default)]
pub struct Entities {
pub meta: Vec<EntityMeta>,
Expand Down Expand Up @@ -404,6 +406,9 @@ impl Entities {
.map_or(false, |e| e.generation() == entity.generation)
}

/// Despawns all entities, removing them from the [World](crate::world::World)
///
/// All data will be lost, but the size of the underlying vector storages will not be reset.
pub fn clear(&mut self) {
self.meta.clear();
self.pending.clear();
Expand Down Expand Up @@ -516,6 +521,7 @@ impl Entities {
}
}

/// The position within [Entities] of a particular [Entity]
#[derive(Copy, Clone, Debug)]
pub struct EntityMeta {
pub generation: u32,
Expand Down
46 changes: 29 additions & 17 deletions crates/bevy_ecs/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct EventInstance<T> {
}

#[derive(Debug)]
enum State {
enum DoubleBufferState {
A,
B,
}
Expand All @@ -74,6 +74,9 @@ enum State {
/// If events are not handled by the end of the frame after they are updated, they will be
/// dropped silently.
///
/// If you require more complex event management, consider working with `ResMut<Events>` directly,
/// and using a [ManualEventReader] to track which events your system has seen.
///
/// # Example
/// ```
/// use bevy_ecs::event::Events;
Expand Down Expand Up @@ -124,7 +127,7 @@ pub struct Events<T> {
a_start_event_count: usize,
b_start_event_count: usize,
event_count: usize,
state: State,
state: DoubleBufferState,
}

impl<T> Default for Events<T> {
Expand All @@ -135,7 +138,7 @@ impl<T> Default for Events<T> {
event_count: 0,
events_a: Vec::new(),
events_b: Vec::new(),
state: State::A,
state: DoubleBufferState::A,
}
}
}
Expand Down Expand Up @@ -173,6 +176,15 @@ impl<'w, 's, T: Resource> EventWriter<'w, 's, T> {
}
}

/// A simple event iterator which can be manually controlled.
///
/// In most cases, an [EventReader] will better meet your needs,
/// as it both tracks which events a system has seen and fetches the relevant data.
/// However, [EventReader] and [EventWriter] have conflicting accesses, and so cannot be used in the same system.
///
/// To get around this, you can store a [ManualEventReader] in a [Local] to help track events seen
/// when working directly with `ResMut<Events<T>>`,
/// allowing you to read and write events from within the same system.
pub struct ManualEventReader<T> {
last_event_count: usize,
_marker: PhantomData<T>,
Expand Down Expand Up @@ -222,7 +234,7 @@ fn internal_event_reader<'a, T>(
};
*last_event_count = events.event_count;
match events.state {
State::A => events
DoubleBufferState::A => events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
Expand All @@ -236,7 +248,7 @@ fn internal_event_reader<'a, T>(
.iter()
.map(map_instance_event_with_id),
),
State::B => events
DoubleBufferState::B => events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
Expand Down Expand Up @@ -283,8 +295,8 @@ impl<T: Resource> Events<T> {
let event_instance = EventInstance { event_id, event };

match self.state {
State::A => self.events_a.push(event_instance),
State::B => self.events_b.push(event_instance),
DoubleBufferState::A => self.events_a.push(event_instance),
DoubleBufferState::B => self.events_b.push(event_instance),
}

self.event_count += 1;
Expand All @@ -311,14 +323,14 @@ impl<T: Resource> Events<T> {
/// called once per frame/update.
pub fn update(&mut self) {
match self.state {
State::A => {
DoubleBufferState::A => {
self.events_b.clear();
self.state = State::B;
self.state = DoubleBufferState::B;
self.b_start_event_count = self.event_count;
}
State::B => {
DoubleBufferState::B => {
self.events_a.clear();
self.state = State::A;
self.state = DoubleBufferState::A;
self.a_start_event_count = self.event_count;
}
}
Expand Down Expand Up @@ -355,12 +367,12 @@ impl<T: Resource> Events<T> {

let map = |i: EventInstance<T>| i.event;
match self.state {
State::A => self
DoubleBufferState::A => self
.events_b
.drain(..)
.map(map)
.chain(self.events_a.drain(..).map(map)),
State::B => self
DoubleBufferState::B => self
.events_a
.drain(..)
.map(map)
Expand All @@ -376,8 +388,8 @@ impl<T: Resource> Events<T> {
/// happen after this call and before the next `update()` call will be dropped.
pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator<Item = &T> {
match self.state {
State::A => self.events_a.iter().map(map_instance_event),
State::B => self.events_b.iter().map(map_instance_event),
DoubleBufferState::A => self.events_a.iter().map(map_instance_event),
DoubleBufferState::B => self.events_b.iter().map(map_instance_event),
}
}
}
Expand All @@ -398,8 +410,8 @@ impl<T> std::iter::Extend<T> for Events<T> {
});

match self.state {
State::A => self.events_a.extend(events),
State::B => self.events_b.extend(events),
DoubleBufferState::A => self.events_a.extend(events),
DoubleBufferState::B => self.events_b.extend(events),
}

trace!(
Expand Down
18 changes: 14 additions & 4 deletions crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ use crate::storage::SparseSetIndex;
use fixedbitset::FixedBitSet;
use std::marker::PhantomData;

/// `Access` keeps track of read and write accesses to values within a collection.
/// [Access] keeps track of read and write accesses to values within a collection.
///
/// This is used for ensuring systems are executed soundly.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Access<T: SparseSetIndex> {
/// Is read-access to the entire [World](crate::world::World) required?
reads_all: bool,
/// A combined set of T read and write accesses.
/// A combined set of read and write accesses for T.
reads_and_writes: FixedBitSet,
writes: FixedBitSet,
marker: PhantomData<T>,
_marker: PhantomData<T>,
}

impl<T: SparseSetIndex> Default for Access<T> {
Expand All @@ -20,7 +21,7 @@ impl<T: SparseSetIndex> Default for Access<T> {
reads_all: false,
reads_and_writes: Default::default(),
writes: Default::default(),
marker: PhantomData,
_marker: PhantomData,
}
}
}
Expand Down Expand Up @@ -129,6 +130,10 @@ impl<T: SparseSetIndex> Access<T> {
}
}

/// An [Access] which is filtered by various [With](crate::query::With) and [Without](crate::query::Without) query filters.
///
/// Used to schedule systems efficiently and verify non-conflicting accesses to entities,
/// as entities cannot both have and not-have a component.
#[derive(Clone, Eq, PartialEq)]
pub struct FilteredAccess<T: SparseSetIndex> {
access: Access<T>,
Expand Down Expand Up @@ -188,6 +193,11 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
}
}

/// The combined [Access] of a system parameter or system,
/// used to schedule systems soundly.
///
/// A [FilteredAccessSet] is constructed iteratively, by combining the [FilteredAccess] of
/// its constituents using [FilteredAccessSet::add](Self::add).
pub struct FilteredAccessSet<T: SparseSetIndex> {
combined_access: Access<T>,
filtered_accesses: Vec<FilteredAccess<T>>,
Expand Down
Loading