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

Entity Relations #1627

Closed
wants to merge 2 commits into from
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: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ path = "examples/diagnostics/custom_diagnostic.rs"
name = "ecs_guide"
path = "examples/ecs/ecs_guide.rs"

[[example]]
name = "relations_grouping"
path = "examples/ecs/relations_grouping.rs"

[[example]]
name = "component_change_detection"
path = "examples/ecs/component_change_detection.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.5.0" }
bevy_utils = { path = "../bevy_utils", version = "0.5.0" }
bevy_ecs_macros = { path = "macros", version = "0.5.0" }

smallvec = { version = "1.4", features = ["serde"] }
async-channel = "1.4"
fixedbitset = "0.4"
fxhash = "0.2"
Expand Down
19 changes: 16 additions & 3 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
});
} else {
field_component_ids.push(quote! {
component_ids.push(components.get_or_insert_id::<#field_type>());
component_ids.push(
components
.component_id_or_insert::<#field_type>()
);
});
field_get_components.push(quote! {
func((&mut self.#field as *mut #field_type).cast::<u8>());
Expand Down Expand Up @@ -260,7 +263,17 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
let (#(#query,)*) = &mut self.0;
#(
#query.new_archetype(archetype);
for (target_filter, cache) in #query.target_filter_accesses.iter_mut() {
QueryState::<#query, #filter>::new_archetype(
&#query.fetch_state,
&#query.filter_state,
&mut #query.archetype_component_access,
&*target_filter,
cache,
archetype
);
}

system_meta
.archetype_component_access
.extend(&#query.archetype_component_access);
Expand All @@ -282,7 +295,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
world: &'a World,
change_tick: u32,
) -> Self::Item {
let (#(#query,)*) = &state.0;
let (#(#query,)*) = &mut state.0;
QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*))
}
}
Expand Down
205 changes: 160 additions & 45 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use bevy_utils::HashMap;
use bevy_utils::StableHashMap;

use crate::{
bundle::BundleId,
component::{ComponentId, StorageType},
entity::{Entity, EntityLocation},
storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId},
storage::{Column, SparseSet, SparseSetIndex, TableId},
};
use std::{
borrow::Cow,
collections::HashMap,
hash::Hash,
ops::{Index, IndexMut},
};
Expand Down Expand Up @@ -48,15 +50,41 @@ pub struct AddBundle {

#[derive(Default)]
pub struct Edges {
pub add_bundle: SparseArray<BundleId, AddBundle>,
pub remove_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
pub remove_bundle_intersection: SparseArray<BundleId, Option<ArchetypeId>>,
pub add_bundle: HashMap<BundleId, AddBundle>,
pub remove_bundle: HashMap<BundleId, Option<ArchetypeId>>,
pub remove_bundle_intersection: HashMap<BundleId, Option<ArchetypeId>>,
}

impl Edges {
pub fn debug_ram_usage(&self) -> usize {
use std::mem::size_of;

let mut size = size_of::<Edges>();

let usage_of_hm = |cap: usize, k, v| (cap * 11 / 10).next_power_of_two() * (k + v + 8);

size += usage_of_hm(
self.add_bundle.capacity(),
size_of::<BundleId>(),
size_of::<AddBundle>(),
);
size += usage_of_hm(
self.remove_bundle.capacity(),
size_of::<BundleId>(),
size_of::<Option<ArchetypeId>>(),
);
size += usage_of_hm(
self.remove_bundle_intersection.capacity(),
size_of::<BundleId>(),
size_of::<Option<ArchetypeId>>(),
);

size
}

#[inline]
pub fn get_add_bundle(&self, bundle_id: BundleId) -> Option<&AddBundle> {
self.add_bundle.get(bundle_id)
self.add_bundle.get(&bundle_id)
}

#[inline]
Expand All @@ -77,7 +105,7 @@ impl Edges {

#[inline]
pub fn get_remove_bundle(&self, bundle_id: BundleId) -> Option<Option<ArchetypeId>> {
self.remove_bundle.get(bundle_id).cloned()
self.remove_bundle.get(&bundle_id).cloned()
}

#[inline]
Expand All @@ -90,7 +118,7 @@ impl Edges {
&self,
bundle_id: BundleId,
) -> Option<Option<ArchetypeId>> {
self.remove_bundle_intersection.get(bundle_id).cloned()
self.remove_bundle_intersection.get(&bundle_id).cloned()
}

#[inline]
Expand Down Expand Up @@ -124,54 +152,103 @@ pub struct Archetype {
entities: Vec<Entity>,
edges: Edges,
table_info: TableInfo,
table_components: Cow<'static, [ComponentId]>,
sparse_set_components: Cow<'static, [ComponentId]>,
table_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
sparse_set_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
pub(crate) unique_components: SparseSet<ComponentId, Column>,
pub(crate) components: SparseSet<ComponentId, ArchetypeComponentInfo>,
pub(crate) relations: SparseSet<ComponentId, StableHashMap<Entity, ArchetypeComponentInfo>>,
}

impl Archetype {
pub fn debug_ram_usage(&self) -> (usize, usize) {
use std::mem::size_of;
let mut size = size_of::<Archetype>();

if let Cow::Owned(owned) = &self.table_components {
size += size_of::<(ComponentId, Option<Entity>)>() * owned.len();
}
if let Cow::Owned(owned) = &self.sparse_set_components {
size += size_of::<(ComponentId, Option<Entity>)>() * owned.len();
}

size += size_of::<ArchetypeComponentInfo>() * self.components.dense_len();
size += size_of::<ComponentId>() * self.components.indices_len();
size += size_of::<Option<usize>>() * self.components.sparse_len();

size += size_of::<ComponentId>() * self.relations.indices_len();
size += size_of::<Option<usize>>() * self.relations.sparse_len();

for v in self.relations.values() {
let cap = v.capacity();
let usage = (cap * 11 / 10).next_power_of_two()
* (size_of::<Entity>() + size_of::<ArchetypeComponentInfo>() + 8);
size += usage;
}

size += size_of::<Entity>() * self.entities.len();

(size, self.edges.debug_ram_usage())
}

pub fn new(
id: ArchetypeId,
table_id: TableId,
table_components: Cow<'static, [ComponentId]>,
sparse_set_components: Cow<'static, [ComponentId]>,
table_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
sparse_set_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
table_archetype_components: Vec<ArchetypeComponentId>,
sparse_set_archetype_components: Vec<ArchetypeComponentId>,
) -> Self {
// FIXME(Relationships) sort out this capacity weirdness
let mut components =
SparseSet::with_capacity(table_components.len() + sparse_set_components.len());
for (component_id, archetype_component_id) in
let mut relations = SparseSet::new();
for ((component_id, target), archetype_component_id) in
table_components.iter().zip(table_archetype_components)
{
components.insert(
*component_id,
ArchetypeComponentInfo {
storage_type: StorageType::Table,
archetype_component_id,
},
);
let arch_comp_info = ArchetypeComponentInfo {
storage_type: StorageType::Table,
archetype_component_id,
};

match target {
None => {
components.insert(*component_id, arch_comp_info);
}
Some(target) => {
let set = relations.get_or_insert_with(*component_id, StableHashMap::default);
set.insert(*target, arch_comp_info);
}
};
}

for (component_id, archetype_component_id) in sparse_set_components
for ((component_id, target), archetype_component_id) in sparse_set_components
.iter()
.zip(sparse_set_archetype_components)
{
components.insert(
*component_id,
ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
archetype_component_id,
},
);
let arch_comp_info = ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
archetype_component_id,
};

match target {
None => {
components.insert(*component_id, arch_comp_info);
}
Some(target) => {
let set = relations.get_or_insert_with(*component_id, StableHashMap::default);
set.insert(*target, arch_comp_info);
}
};
}

Self {
id,
table_info: TableInfo {
id: table_id,
entity_rows: Default::default(),
},
components,
relations,
table_components,
sparse_set_components,
unique_components: SparseSet::new(),
Expand Down Expand Up @@ -201,12 +278,12 @@ impl Archetype {
}

#[inline]
pub fn table_components(&self) -> &[ComponentId] {
pub fn table_components(&self) -> &[(ComponentId, Option<Entity>)] {
&self.table_components
}

#[inline]
pub fn sparse_set_components(&self) -> &[ComponentId] {
pub fn sparse_set_components(&self) -> &[(ComponentId, Option<Entity>)] {
&self.sparse_set_components
}

Expand All @@ -221,8 +298,17 @@ impl Archetype {
}

#[inline]
pub fn components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components.indices()
pub fn components(&self) -> impl Iterator<Item = (ComponentId, Option<Entity>)> + '_ {
self.components
.indices()
.map(|kind| (kind, None))
.chain(self.relations.indices().flat_map(move |component_id| {
self.relations
.get(component_id)
.unwrap()
.keys()
.map(move |target| (component_id, Some(*target)))
}))
}

#[inline]
Expand Down Expand Up @@ -289,25 +375,50 @@ impl Archetype {
}

#[inline]
pub fn contains(&self, component_id: ComponentId) -> bool {
self.components.contains(component_id)
pub fn contains(&self, component_id: ComponentId, target: Option<Entity>) -> bool {
match target {
None => self.components.contains(component_id),
Some(target) => self
.relations
.get(component_id)
.map(|set| set.contains_key(&target))
.unwrap_or(false),
}
}

// FIXME(Relationships) technically the target is unnecessary here as all `KindId` have the same storage type
#[inline]
pub fn get_storage_type(&self, component_id: ComponentId) -> Option<StorageType> {
self.components
.get(component_id)
.map(|info| info.storage_type)
pub fn get_storage_type(
&self,
component_id: ComponentId,
target: Option<Entity>,
) -> Option<StorageType> {
match target {
None => self.components.get(component_id),
Some(target) => self
.relations
.get(component_id)
.and_then(|set| set.get(&target)),
}
.map(|info| info.storage_type)
}

#[inline]
pub fn get_archetype_component_id(
&self,
component_id: ComponentId,
// FIXME(Relationships) treat archetype componnet id the same as component id maybe?? see other fixme
// then we oculd get rid of this `target` arg and same with fn above
target: Option<Entity>,
) -> Option<ArchetypeComponentId> {
self.components
.get(component_id)
.map(|info| info.archetype_component_id)
match target {
None => self.components.get(component_id),
Some(target) => self
.relations
.get(component_id)
.and_then(|set| set.get(&target)),
}
.map(|info| info.archetype_component_id)
}
}

Expand All @@ -329,8 +440,8 @@ impl ArchetypeGeneration {

#[derive(Hash, PartialEq, Eq)]
pub struct ArchetypeIdentity {
table_components: Cow<'static, [ComponentId]>,
sparse_set_components: Cow<'static, [ComponentId]>,
table_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
sparse_set_components: Cow<'static, [(ComponentId, Option<Entity>)]>,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -453,6 +564,10 @@ impl Archetypes {
a: ArchetypeId,
b: ArchetypeId,
) -> (&mut Archetype, &mut Archetype) {
if a.0 == b.0 {
panic!("both indexes were the same");
}

if a.index() > b.index() {
let (b_slice, a_slice) = self.archetypes.split_at_mut(a.index());
(&mut a_slice[0], &mut b_slice[b.index()])
Expand All @@ -475,8 +590,8 @@ impl Archetypes {
pub(crate) fn get_id_or_insert(
&mut self,
table_id: TableId,
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
table_components: Vec<(ComponentId, Option<Entity>)>,
sparse_set_components: Vec<(ComponentId, Option<Entity>)>,
) -> ArchetypeId {
let table_components = Cow::from(table_components);
let sparse_set_components = Cow::from(sparse_set_components);
Expand Down
Loading