Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions _release-content/release-notes/resources_as_components.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Resources as Components
authors: ["@Trashtalk", "@cart"]
pull_requests: [20934, 22910, 22911, 22919, 22930]
authors: ["@Trashtalk", "@cart", "@specificprotagonist"]
pull_requests: [20934, 22910, 22911, 22919, 22930, 24058]
---

Resources are very similar to Components: they are both data that can be stored in the ECS and queried.
Expand Down
2 changes: 1 addition & 1 deletion benches/benches/bevy_ecs/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ criterion_main!(
bundles::benches,
change_detection::benches,
components::benches,
resources::benches,
empty_archetypes::benches,
entity_cloning::benches,
events::benches,
iteration::benches,
fragmentation::benches,
observers::benches,
resources::benches,
scheduling::benches,
world::benches,
param::benches,
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_ecs/macro_logic/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,8 @@ pub enum StorageTy {
Table,
/// Sparse set storage
SparseSet,
/// Resource, not choosable from component derive macro
Resource,
}

/// Derived required component from the `#[require]` attribute.
Expand Down Expand Up @@ -654,6 +656,7 @@ fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream {
let storage_type = match ty {
StorageTy::Table => Ident::new("Table", Span::call_site()),
StorageTy::SparseSet => Ident::new("SparseSet", Span::call_site()),
StorageTy::Resource => Ident::new("Resource", Span::call_site()),
};

quote! { #bevy_ecs_path::component::StorageType::#storage_type }
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/macros/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn derive_resource(ast: &mut DeriveInput) -> TokenStream {
Ok(value) => value,
Err(e) => return e.into_compile_error(),
};
derive_component.storage = StorageTy::SparseSet;
derive_component.storage = StorageTy::Resource;

let struct_name = &ast.ident;
let (_, type_generics, _) = &ast.generics.split_for_impl();
Expand Down
86 changes: 73 additions & 13 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,20 +390,20 @@ pub struct Archetype {
}

impl Archetype {
/// `table_components` and `sparse_set_components` must be sorted
/// `table_components` must be sorted
pub(crate) fn new(
components: &Components,
component_index: &mut ComponentIndex,
observers: &Observers,
id: ArchetypeId,
table_id: TableId,
table_components: impl Iterator<Item = ComponentId>,
sparse_set_components: impl Iterator<Item = ComponentId>,
non_table_components: impl Iterator<Item = ComponentId>,
) -> Self {
let (min_table, _) = table_components.size_hint();
let (min_sparse, _) = sparse_set_components.size_hint();
let (min_non_table, _) = non_table_components.size_hint();
let mut flags = ArchetypeFlags::empty();
let mut archetype_components = SparseSet::with_capacity(min_table + min_sparse);
let mut archetype_components = SparseSet::with_capacity(min_table + min_non_table);
for (idx, component_id) in table_components.enumerate() {
// SAFETY: We are creating an archetype that includes this component so it must exist
let info = unsafe { components.get_info_unchecked(component_id) };
Expand All @@ -424,22 +424,23 @@ impl Archetype {
.insert(id, ArchetypeRecord { column: Some(idx) });
}

for component_id in sparse_set_components {
for component_id in non_table_components {
// SAFETY: We are creating an archetype that includes this component so it must exist
let info = unsafe { components.get_info_unchecked(component_id) };
info.update_archetype_flags(&mut flags);
observers.update_archetype_flags(component_id, &mut flags);
archetype_components.insert(
component_id,
ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
storage_type: info.storage_type(),
},
);
component_index
.entry(component_id)
.or_default()
.insert(id, ArchetypeRecord { column: None });
}

Self {
id,
table_id,
Expand Down Expand Up @@ -510,6 +511,19 @@ impl Archetype {
.map(|(id, _)| *id)
}

/// Gets an iterator of all of the components not stored in [`Table`]s.
///
/// All of the IDs are unique.
///
/// [`Table`]: crate::storage::Table
#[inline]
pub fn non_table_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components
.iter()
.filter(|(_, component)| component.storage_type != StorageType::Table)
.map(|(id, _)| *id)
}

/// Gets an iterator of all of the components stored in [`ComponentSparseSet`]s.
///
/// All of the IDs are unique.
Expand All @@ -523,6 +537,19 @@ impl Archetype {
.map(|(id, _)| *id)
}

/// Gets an iterator of all of the components stored in [`ResourceStorages`].
///
/// All of the IDs are unique.
///
/// [`ResourceStorages`]: crate::storage::ResourceStorages
#[inline]
pub fn resource_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components
.iter()
.filter(|(_, component)| component.storage_type == StorageType::Resource)
.map(|(id, _)| *id)
}

/// Returns a slice of all of the components in the archetype.
///
/// All of the IDs are unique.
Expand Down Expand Up @@ -754,10 +781,12 @@ impl ArchetypeGeneration {
}
}

/// Components must be sorted
/// (which allows `ArchetypeComponents` to be used as an archetype's identity).
#[derive(Hash, PartialEq, Eq)]
struct ArchetypeComponents {
table_components: Box<[ComponentId]>,
sparse_set_components: Box<[ComponentId]>,
non_table_components: Box<[ComponentId]>,
}

/// Maps a [`ComponentId`] to the list of [`Archetypes`]([`Archetype`]) that contain the [`Component`](crate::component::Component),
Expand Down Expand Up @@ -856,6 +885,37 @@ impl Archetypes {
self.archetypes.get(id.index())
}

/// # Safety
/// - all ids must be valid and pairwise unequal
pub(crate) unsafe fn get_disjoint_unchecked_mut(
&mut self,
id_a: ArchetypeId,
id_b: ArchetypeId,
id_c: Option<ArchetypeId>,
) -> (&mut Archetype, &mut Archetype, Option<&mut Archetype>) {
match id_c {
Some(id_c) => {
// SAFETY: Same preconditions
let [a, b, c] = unsafe {
self.archetypes.get_disjoint_unchecked_mut([
id_a.index(),
id_b.index(),
id_c.index(),
])
};
(a, b, Some(c))
}
None => {
// SAFETY: Same preconditions
let [a, b] = unsafe {
self.archetypes
.get_disjoint_unchecked_mut([id_a.index(), id_b.index()])
};
(a, b, None)
}
}
}

/// Tries to fetch mutable references to two disjoint archetypes.
///
/// Returns `(&mut Archetype, None)` if the same [`ArchetypeId`] was provided twice.
Expand Down Expand Up @@ -896,21 +956,21 @@ impl Archetypes {
/// Specifically, it returns a tuple where the first element
/// is the [`ArchetypeId`] that the given inputs belong to, and the second element is a boolean indicating whether a new archetype was created.
///
/// `table_components` and `sparse_set_components` must be sorted
/// `table_components` and `non_table_components` must be sorted
///
/// # Safety
/// [`TableId`] must exist in tables
/// `table_components` and `sparse_set_components` must exist in `components`
/// `table_components` and `non_table_components` must exist in `components`
pub(crate) unsafe fn get_id_or_insert(
&mut self,
components: &Components,
observers: &Observers,
table_id: TableId,
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
non_table_components: Vec<ComponentId>,
) -> (ArchetypeId, bool) {
let archetype_identity = ArchetypeComponents {
sparse_set_components: sparse_set_components.into_boxed_slice(),
non_table_components: non_table_components.into_boxed_slice(),
table_components: table_components.into_boxed_slice(),
};

Expand All @@ -921,7 +981,7 @@ impl Archetypes {
Entry::Vacant(vacant) => {
let ArchetypeComponents {
table_components,
sparse_set_components,
non_table_components,
} = vacant.key();
let id = ArchetypeId::new(archetypes.len());
archetypes.push(Archetype::new(
Expand All @@ -931,7 +991,7 @@ impl Archetypes {
id,
table_id,
table_components.iter().copied(),
sparse_set_components.iter().copied(),
non_table_components.iter().copied(),
));
vacant.insert(id);
(id, true)
Expand Down
Loading
Loading