ecs: create a DynamicViewState that can be used to create a dynamic view without dealing with lifetimes
ci/woodpecker/push/debug Pipeline failed Details

This commit is contained in:
SeanOMik 2024-04-13 02:10:25 -04:00
parent 60ec62c558
commit 12c8ece418
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
2 changed files with 136 additions and 43 deletions

View File

@ -19,16 +19,21 @@ impl DynamicType {
} }
} }
/// A struct that fetches some dynamic type.
/// A struct that fetches a dynamic type.
/// ///
/// Currently it can only fetch from archetypes, later it will be able to fetch dynamic /// Currently it can only fetch from archetypes, later it will be able to fetch dynamic
/// resources as well. Its meant to be a single Fetcher for all dynamic types. /// resources as well. Its meant to be a single Fetcher for all dynamic types.
pub struct FetchDynamicType<'a> { ///
pub col: &'a ComponentColumn, /// # Safety
/// Internally, this struct has a `NonNull<ComponentColumn>`. You must ensure that the column
/// is not dropped while this is still alive.
pub struct FetchDynamicTypeUnsafe {
pub col: NonNull<ComponentColumn>,
pub info: ComponentInfo, pub info: ComponentInfo,
} }
impl<'a> Fetch<'a> for FetchDynamicType<'a> { impl<'a> Fetch<'a> for FetchDynamicTypeUnsafe {
type Item = DynamicType; type Item = DynamicType;
fn dangling() -> Self { fn dangling() -> Self {
@ -36,7 +41,7 @@ impl<'a> Fetch<'a> for FetchDynamicType<'a> {
} }
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item { unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
let ptr = self.col.borrow_ptr(); let ptr = unsafe { self.col.as_ref() }.borrow_ptr();
let ptr = NonNull::new_unchecked(ptr.as_ptr() let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.info.layout().size())); .add(entity.0 as usize * self.info.layout().size()));
@ -66,13 +71,13 @@ impl QueryDynamicType {
archetype.has_column(self.info.type_id()) archetype.has_column(self.info.type_id())
} }
pub unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> FetchDynamicType<'a> { pub unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> FetchDynamicTypeUnsafe {
let col = archetype.get_column(self.info.type_id()) let col = archetype.get_column(self.info.type_id())
.expect("You ignored 'can_visit_archetype'!"); .expect("You ignored 'can_visit_archetype'!");
FetchDynamicType { FetchDynamicTypeUnsafe {
col, col: NonNull::from(col),
info: self.info, info: self.info
} }
} }
} }

View File

@ -1,18 +1,25 @@
use std::ops::Range; use std::ops::Range;
use crate::{World, Archetype, ArchetypeEntityId, ArchetypeId, query::Fetch}; use crate::{query::Fetch, Archetype, ArchetypeEntityId, ArchetypeId, World};
use super::{QueryDynamicType, FetchDynamicType, DynamicType}; use super::{DynamicType, FetchDynamicTypeUnsafe, QueryDynamicType};
pub struct DynamicView<'a> { /// Stores the state of a dynamic view.
world: &'a World, ///
queries: Vec<QueryDynamicType> /// See [`DynamicView`].
///
/// This backs [`DynamicView`] which you should probably use. The only reason you would use this
/// instead is if you cant borrow the world when storing this type, and its iterators.
/// [`DynamicViewState`] provides an 'iterator' of [`DynamicViewStateIter`], which requires you to
/// provide a world borrow on each `next` of the iterator. View [`DynamicViewStateIter`] for more
/// info.
pub struct DynamicViewState {
pub(crate) queries: Vec<QueryDynamicType>
} }
impl<'a> DynamicView<'a> { impl DynamicViewState {
pub fn new(world: &'a World) -> Self { pub fn new() -> Self {
Self { Self {
world,
queries: Vec::new(), queries: Vec::new(),
} }
} }
@ -20,45 +27,38 @@ impl<'a> DynamicView<'a> {
pub fn push(&mut self, dyn_query: QueryDynamicType) { pub fn push(&mut self, dyn_query: QueryDynamicType) {
self.queries.push(dyn_query); self.queries.push(dyn_query);
} }
}
impl<'a> IntoIterator for DynamicView<'a> { pub fn into_iter(self) -> DynamicViewStateIter {
type Item = Vec<DynamicType>; DynamicViewStateIter {
type IntoIter = DynamicViewIter<'a>;
fn into_iter(self) -> Self::IntoIter {
let archetypes = self.world.archetypes.values().collect();
DynamicViewIter {
world: self.world,
queries: self.queries, queries: self.queries,
fetchers: Vec::new(), fetchers: Vec::new(),
archetypes,
next_archetype: 0, next_archetype: 0,
component_indices: 0..0, component_indices: 0..0,
} }
} }
} }
pub struct DynamicViewIter<'a> { /// A view iterator on dynamic types.
pub world: &'a World, ///
/// You will likely want to use [`DynamicViewIter`] unless you need to store the iterator
/// without also borrowing from the world. [`DynamicViewStateIter`] doesn't
/// actually implement [`Iterator`] since it requires a `&World` to be provided to it
/// each time `next` is ran (see [`DynamicViewStateIter::next`]).
pub struct DynamicViewStateIter {
pub queries: Vec<QueryDynamicType>, pub queries: Vec<QueryDynamicType>,
pub fetchers: Vec<FetchDynamicType<'a>>, fetchers: Vec<FetchDynamicTypeUnsafe>,
pub archetypes: Vec<&'a Archetype>, next_archetype: usize,
pub next_archetype: usize, component_indices: Range<u64>,
pub component_indices: Range<u64>,
} }
impl<'a> Iterator for DynamicViewIter<'a> { impl DynamicViewStateIter {
type Item = Vec<DynamicType>; pub fn next(&mut self, world: &World) -> Option<Vec<DynamicType>> {
let archetypes = world.archetypes.values().collect::<Vec<_>>();
fn next(&mut self) -> Option<Self::Item> {
loop { loop {
if let Some(entity_index) = self.component_indices.next() { if let Some(entity_index) = self.component_indices.next() {
let mut fetch_res = vec![]; let mut fetch_res = vec![];
//let fetcher = self.fetcher.as_mut().unwrap();
for fetcher in self.fetchers.iter_mut() { for fetcher in self.fetchers.iter_mut() {
let entity_index = ArchetypeEntityId(entity_index); let entity_index = ArchetypeEntityId(entity_index);
if !fetcher.can_visit_item(entity_index) { if !fetcher.can_visit_item(entity_index) {
@ -75,13 +75,13 @@ impl<'a> Iterator for DynamicViewIter<'a> {
return Some(fetch_res); return Some(fetch_res);
} else { } else {
if self.next_archetype >= self.archetypes.len() { if self.next_archetype >= archetypes.len() {
return None; // ran out of archetypes to go through return None; // ran out of archetypes to go through
} }
let arch_id = self.next_archetype; let arch_id = self.next_archetype;
self.next_archetype += 1; self.next_archetype += 1;
let arch = unsafe { self.archetypes.get_unchecked(arch_id) }; let arch = unsafe { archetypes.get_unchecked(arch_id) };
if arch.entity_ids.is_empty() { if arch.entity_ids.is_empty() {
continue; continue;
@ -92,7 +92,7 @@ impl<'a> Iterator for DynamicViewIter<'a> {
} }
self.fetchers = self.queries.iter() self.fetchers = self.queries.iter()
.map(|q| unsafe { q.fetch(self.world, ArchetypeId(arch_id as u64), arch) } ) .map(|q| unsafe { q.fetch(world, ArchetypeId(arch_id as u64), arch) } )
.collect(); .collect();
self.component_indices = 0..arch.entity_ids.len() as u64; self.component_indices = 0..arch.entity_ids.len() as u64;
} }
@ -100,13 +100,101 @@ impl<'a> Iterator for DynamicViewIter<'a> {
} }
} }
/// A view of dynamic types (types that are not known to Rust).
///
/// This view gives you the ability to iterate over types that are unknown to Rust, which we call
/// dynamic types. This is great for embedding with a scripting language (*cough* *cough* WASM)
/// since Rust doesn't actually need to know the types of what its iterating over.
pub struct DynamicView<'a> {
world: &'a World,
inner: DynamicViewState,
}
impl<'a> DynamicView<'a> {
pub fn new(world: &'a World) -> Self {
Self {
world,
inner: DynamicViewState::new(),
}
}
pub fn push(&mut self, dyn_query: QueryDynamicType) {
self.inner.queries.push(dyn_query);
}
}
/// A view iterator on dynamic types.
///
/// This view gives you the ability to iterate over types that are completely unknown to Rust.
/// This works great for a embedding with a scripting language (*cough* *cough* WASM) since
/// Rust doesn't actually need to know the types of what its iterating over.
impl<'a> IntoIterator for DynamicView<'a> {
type Item = Vec<DynamicType>;
type IntoIter = DynamicViewIter<'a>;
fn into_iter(self) -> Self::IntoIter {
let archetypes = self.world.archetypes.values().collect();
DynamicViewIter {
world: self.world,
archetypes,
inner: self.inner.into_iter(),
}
}
}
pub struct DynamicViewIter<'a> {
pub world: &'a World,
pub archetypes: Vec<&'a Archetype>,
inner: DynamicViewStateIter,
}
impl<'a> Iterator for DynamicViewIter<'a> {
type Item = Vec<DynamicType>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(&self.world)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{alloc::Layout, ptr::NonNull}; use std::{alloc::Layout, ptr::NonNull};
use crate::{World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType}; use crate::{World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
use super::DynamicView; use super::{DynamicView, DynamicViewState};
#[test]
fn single_dynamic_view_state() {
let comp_layout = Layout::new::<u32>();
let comp_info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), comp_layout);
let mut dynamic_bundle = DynamicBundle::default();
let comp = 50u32;
let ptr = NonNull::from(&comp).cast::<u8>();
dynamic_bundle.push_unknown(ptr, comp_info.clone());
let mut world = World::new();
world.spawn(dynamic_bundle);
let query = QueryDynamicType::from_info(comp_info);
let mut view = DynamicViewState::new();
view.push(query);
let mut view_iter = view.into_iter();
while let Some(view_row) = view_iter.next(&world) {
assert_eq!(view_row.len(), 1);
let mut row_iter = view_row.iter();
let dynamic_type = row_iter.next().unwrap();
let component_data = unsafe { dynamic_type.ptr.cast::<u32>().as_ref() };
assert_eq!(*component_data, 50);
}
}
#[test] #[test]
fn single_dynamic_view() { fn single_dynamic_view() {