lyra-engine/lyra-ecs/src/query/borrow.rs

316 lines
8.2 KiB
Rust
Raw Normal View History

use std::{marker::PhantomData, ptr::NonNull, cell::{Ref, RefMut}};
2023-11-25 23:43:11 +00:00
2023-12-26 19:12:53 +00:00
use crate::{world::World, ComponentColumn, DynTypeId, Tick};
2023-11-30 04:21:27 +00:00
2023-12-04 04:14:27 +00:00
use super::{Fetch, Query, AsQuery};
2023-11-25 23:43:11 +00:00
2023-11-27 02:05:35 +00:00
/// Fetcher for borrowing components from archetypes.
2023-11-25 23:43:11 +00:00
pub struct FetchBorrow<'a, T> {
col: &'a ComponentColumn,
2023-11-25 23:43:11 +00:00
size: usize,
_phantom: PhantomData<&'a T>
}
impl<'a, T> Fetch<'a> for FetchBorrow<'a, T>
where
T: 'a,
{
type Item = Ref<'a, T>;
2023-11-25 23:43:11 +00:00
fn dangling() -> Self {
unreachable!()
2023-11-25 23:43:11 +00:00
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let ptr = self.col.borrow_ptr();
Ref::map(ptr, |ptr| {
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.size))
.cast();
&*ptr.as_ptr()
})
2023-11-25 23:43:11 +00:00
}
}
2023-11-27 02:05:35 +00:00
/// A Query for borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
/// ```nobuild
2023-11-27 02:05:35 +00:00
/// for ts in world.view::<&T>() {
/// println!("Got a &T!");
2023-11-27 02:05:35 +00:00
/// }
/// ```
2023-11-25 23:43:11 +00:00
pub struct QueryBorrow<T> {
type_id: DynTypeId,
2023-11-25 23:43:11 +00:00
_phantom: PhantomData<T>
}
impl<T: 'static> Default for QueryBorrow<T> {
fn default() -> Self {
Self {
type_id: DynTypeId::of::<T>(),
_phantom: PhantomData,
}
}
}
2023-12-04 04:14:27 +00:00
impl<T> Copy for QueryBorrow<T> {}
impl<T> Clone for QueryBorrow<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> QueryBorrow<T> {
pub fn new() -> Self {
Self::default()
}
}
2023-11-25 23:43:11 +00:00
impl<T> Query for QueryBorrow<T>
where
T: 'static
{
type Item<'a> = Ref<'a, T>;
2023-11-25 23:43:11 +00:00
type Fetch<'a> = FetchBorrow<'a, T>;
2023-12-04 04:14:27 +00:00
fn new() -> Self {
QueryBorrow::<T>::new()
}
2023-11-25 23:43:11 +00:00
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
}
2023-12-26 19:12:53 +00:00
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick;
2023-11-25 23:43:11 +00:00
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
.expect("You ignored 'can_visit_archetype'!");
FetchBorrow {
col,
size: col.info.layout.size,
2023-11-25 23:43:11 +00:00
_phantom: PhantomData,
}
}
}
impl<T: 'static> AsQuery for QueryBorrow<T> {
type Query = Self;
}
impl<T: 'static> AsQuery for &T {
type Query = QueryBorrow<T>;
}
2023-11-27 02:05:35 +00:00
/// A fetcher for mutably borrowing components from archetypes.
2023-11-25 23:43:11 +00:00
pub struct FetchBorrowMut<'a, T> {
2023-12-26 19:12:53 +00:00
col: NonNull<ComponentColumn>,
2023-11-25 23:43:11 +00:00
size: usize,
2023-12-26 19:12:53 +00:00
tick: Tick,
2023-11-25 23:43:11 +00:00
_phantom: PhantomData<&'a T>
}
impl<'a, T> Fetch<'a> for FetchBorrowMut<'a, T>
where
T: 'a,
{
type Item = RefMut<'a, T>;
2023-11-25 23:43:11 +00:00
fn dangling() -> Self {
unreachable!()
2023-11-25 23:43:11 +00:00
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
2023-12-26 19:12:53 +00:00
let col = unsafe { self.col.as_mut() };
col.entity_ticks[entity.0 as usize] = self.tick;
let ptr = col.borrow_mut_ptr();
RefMut::map(ptr, |ptr| {
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.size))
.cast();
&mut *ptr.as_ptr()
})
2023-11-25 23:43:11 +00:00
}
}
2023-11-27 02:05:35 +00:00
/// A Query for mutably borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&mut T`, you can use this query like this:
/// ```nobuild
2023-11-27 02:05:35 +00:00
/// for ts in world.view::<&mut T>() {
/// println!("Got an &mut T!");
2023-11-27 02:05:35 +00:00
/// }
/// ```
2023-11-25 23:43:11 +00:00
pub struct QueryBorrowMut<T> {
type_id: DynTypeId,
2023-11-25 23:43:11 +00:00
_phantom: PhantomData<T>
}
impl<T: 'static> Default for QueryBorrowMut<T> {
fn default() -> Self {
Self {
type_id: DynTypeId::of::<T>(),
_phantom: PhantomData,
}
}
}
2023-12-04 04:14:27 +00:00
impl<T> Copy for QueryBorrowMut<T> {}
impl<T> Clone for QueryBorrowMut<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> QueryBorrowMut<T> {
pub fn new() -> Self {
Self::default()
}
}
2023-11-25 23:43:11 +00:00
impl<T> Query for QueryBorrowMut<T>
where
T: 'static
{
type Item<'a> = RefMut<'a, T>;
2023-11-25 23:43:11 +00:00
type Fetch<'a> = FetchBorrowMut<'a, T>;
2023-12-26 19:12:53 +00:00
const MUTATES: bool = true;
2023-12-04 04:14:27 +00:00
fn new() -> Self {
QueryBorrowMut::<T>::new()
}
2023-11-25 23:43:11 +00:00
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
}
2023-12-26 19:12:53 +00:00
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
2023-11-25 23:43:11 +00:00
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
.expect("You ignored 'can_visit_archetype'!");
2023-12-26 19:12:53 +00:00
let layout_size = col.info.layout.size;
let col = NonNull::from(col);
2023-12-26 19:12:53 +00:00
// TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated.
// the fetcher needs to tick the entities tick in the archetype
2023-11-25 23:43:11 +00:00
FetchBorrowMut {
col,
2023-12-26 19:12:53 +00:00
size: layout_size,
tick,
2023-11-25 23:43:11 +00:00
_phantom: PhantomData,
}
}
}
impl<T: 'static> AsQuery for QueryBorrowMut<T> {
type Query = Self;
}
impl<T: 'static> AsQuery for &mut T {
type Query = QueryBorrowMut<T>;
}
2023-11-25 23:43:11 +00:00
#[cfg(test)]
mod tests {
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
2023-11-25 23:43:11 +00:00
use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::{View, Fetch}, tests::Vec2, bundle::Bundle, DynTypeId, Tick};
2023-11-25 23:43:11 +00:00
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
2023-11-25 23:43:11 +00:00
/// Creates a world with two entities, one at Vec(10, 50) and
/// the other at Vec2(25, 30).
fn prepare_world() -> World {
let mut world = World::new();
world.spawn((Vec2::new(10.0, 50.0),));
world.spawn((Vec2::new(25.0, 30.0),));
world
}
#[test]
fn borrow_query() {
let world = prepare_world();
let borrow = QueryBorrow::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
2023-11-25 23:43:11 +00:00
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
2023-11-30 04:21:27 +00:00
let v = View::new(&world, borrow, archetypes);
2023-11-25 23:43:11 +00:00
for e in v.into_iter() {
println!("Found entity at {:?}", e);
}
}
#[test]
fn borrow_mut_query() {
let world = prepare_world();
let borrow = QueryBorrowMut::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
2023-11-25 23:43:11 +00:00
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
2023-11-30 04:21:27 +00:00
let v = View::new(&world, borrow, archetypes);
2023-11-25 23:43:11 +00:00
let mut orig = vec![];
for mut v in v.into_iter() {
2023-11-25 23:43:11 +00:00
orig.push(v.clone());
v.x += 10.0;
v.y += 10.0;
}
// Now make sure the changes were actually made
let borrow = QueryBorrow::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
2023-11-25 23:43:11 +00:00
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
2023-11-30 04:21:27 +00:00
let v = View::new(&world, borrow, archetypes);
2023-11-25 23:43:11 +00:00
for (new, orig) in v.into_iter().zip(orig.iter()) {
assert!(new.x - orig.x == 10.0);
assert!(new.y - orig.y == 10.0);
}
println!("They were modified!");
}
#[test]
fn only_one_mutable_borrow() {
let info = (Vec2::new(0.0, 0.0),).info();
let mut a = Archetype::from_bundle_info(ArchetypeId(0), info);
for i in 0..10 {
a.add_entity(Entity {
id: EntityId(i),
generation: 0,
2023-12-26 19:12:53 +00:00
}, (Vec2::rand(),), &Tick::default());
}
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> {
2023-12-26 19:12:53 +00:00
col: NonNull::from(col),
tick: Tick::default(),
size: size_of::<Vec2>(),
_phantom: PhantomData,
};
let item = unsafe { bmut.get_item(crate::ArchetypeEntityId(0)) };
// ensure only one mutable borrow can be done at a time
assert!(col.try_borrow_mut_ptr().is_err());
drop(item);
assert!(col.try_borrow_mut_ptr().is_ok());
}
2023-11-25 23:43:11 +00:00
}