From 12c8ece4183f117864bac362d181ea5906141c6f Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 13 Apr 2024 02:10:25 -0400 Subject: [PATCH] ecs: create a DynamicViewState that can be used to create a dynamic view without dealing with lifetimes --- lyra-ecs/src/query/dynamic/mod.rs | 23 +++-- lyra-ecs/src/query/dynamic/view.rs | 156 ++++++++++++++++++++++------- 2 files changed, 136 insertions(+), 43 deletions(-) diff --git a/lyra-ecs/src/query/dynamic/mod.rs b/lyra-ecs/src/query/dynamic/mod.rs index 7d0a343..fb5522d 100644 --- a/lyra-ecs/src/query/dynamic/mod.rs +++ b/lyra-ecs/src/query/dynamic/mod.rs @@ -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 /// 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`. You must ensure that the column +/// is not dropped while this is still alive. +pub struct FetchDynamicTypeUnsafe { + pub col: NonNull, pub info: ComponentInfo, } -impl<'a> Fetch<'a> for FetchDynamicType<'a> { +impl<'a> Fetch<'a> for FetchDynamicTypeUnsafe { type Item = DynamicType; 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 { - let ptr = self.col.borrow_ptr(); + let ptr = unsafe { self.col.as_ref() }.borrow_ptr(); let ptr = NonNull::new_unchecked(ptr.as_ptr() .add(entity.0 as usize * self.info.layout().size())); @@ -66,13 +71,13 @@ impl QueryDynamicType { 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()) .expect("You ignored 'can_visit_archetype'!"); - FetchDynamicType { - col, - info: self.info, + FetchDynamicTypeUnsafe { + col: NonNull::from(col), + info: self.info } } } \ No newline at end of file diff --git a/lyra-ecs/src/query/dynamic/view.rs b/lyra-ecs/src/query/dynamic/view.rs index c54e8b7..7dba417 100644 --- a/lyra-ecs/src/query/dynamic/view.rs +++ b/lyra-ecs/src/query/dynamic/view.rs @@ -1,18 +1,25 @@ 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> { - world: &'a World, - queries: Vec +/// Stores the state of a dynamic view. +/// +/// 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 } -impl<'a> DynamicView<'a> { - pub fn new(world: &'a World) -> Self { +impl DynamicViewState { + pub fn new() -> Self { Self { - world, queries: Vec::new(), } } @@ -20,45 +27,38 @@ impl<'a> DynamicView<'a> { pub fn push(&mut self, dyn_query: QueryDynamicType) { self.queries.push(dyn_query); } -} -impl<'a> IntoIterator for DynamicView<'a> { - type Item = Vec; - - type IntoIter = DynamicViewIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - let archetypes = self.world.archetypes.values().collect(); - - DynamicViewIter { - world: self.world, + pub fn into_iter(self) -> DynamicViewStateIter { + DynamicViewStateIter { queries: self.queries, fetchers: Vec::new(), - archetypes, next_archetype: 0, component_indices: 0..0, } } } -pub struct DynamicViewIter<'a> { - pub world: &'a World, +/// A view iterator on dynamic types. +/// +/// 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, - pub fetchers: Vec>, - pub archetypes: Vec<&'a Archetype>, - pub next_archetype: usize, - pub component_indices: Range, + fetchers: Vec, + next_archetype: usize, + component_indices: Range, } -impl<'a> Iterator for DynamicViewIter<'a> { - type Item = Vec; +impl DynamicViewStateIter { + pub fn next(&mut self, world: &World) -> Option> { + let archetypes = world.archetypes.values().collect::>(); - fn next(&mut self) -> Option { loop { if let Some(entity_index) = self.component_indices.next() { let mut fetch_res = vec![]; - //let fetcher = self.fetcher.as_mut().unwrap(); for fetcher in self.fetchers.iter_mut() { let entity_index = ArchetypeEntityId(entity_index); if !fetcher.can_visit_item(entity_index) { @@ -75,13 +75,13 @@ impl<'a> Iterator for DynamicViewIter<'a> { return Some(fetch_res); } else { - if self.next_archetype >= self.archetypes.len() { + if self.next_archetype >= archetypes.len() { return None; // ran out of archetypes to go through } let arch_id = self.next_archetype; 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() { continue; @@ -92,7 +92,7 @@ impl<'a> Iterator for DynamicViewIter<'a> { } 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(); 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; + + 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; + + fn next(&mut self) -> Option { + self.inner.next(&self.world) + } +} + #[cfg(test)] mod tests { use std::{alloc::Layout, ptr::NonNull}; 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::(); + 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::(); + 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::().as_ref() }; + assert_eq!(*component_data, 50); + } + } #[test] fn single_dynamic_view() {