Resource and ResourceMut pointers

This commit is contained in:
Carter Anderson 2020-04-29 16:32:19 -07:00
parent 45a710fe6a
commit 2d3903299b
6 changed files with 410 additions and 153 deletions

View file

@ -1,10 +1,8 @@
use legion::{ use legion::{
filter::EntityFilter,
prelude::{ prelude::{
into_resource_for_each_system, into_resource_system, IntoQuery, ResourceSet, Resources, Resources,
Runnable, Schedulable, World, Runnable, Schedulable, World,
}, },
query::{DefaultFilter, View},
}; };
pub enum System { pub enum System {
Schedulable(Box<dyn Schedulable>), Schedulable(Box<dyn Schedulable>),
@ -31,25 +29,4 @@ where
fn from(system: T) -> Self { fn from(system: T) -> Self {
System::ThreadLocalFn(Box::new(system)) System::ThreadLocalFn(Box::new(system))
} }
} }
impl System {
pub fn resource_for_each<'a, Q, F, R, X>(name: &'static str, system: F) -> Self
where
Q: IntoQuery + DefaultFilter<Filter = R>,
<Q as View<'a>>::Iter: Iterator<Item = Q> + 'a,
F: FnMut(&mut X, Q) + Send + Sync + 'static,
R: EntityFilter + Sync + 'static,
X: ResourceSet<PreparedResources = X> + 'static,
{
into_resource_for_each_system(name, system).into()
}
pub fn resource<'a, F, X>(name: &'static str, system: F) -> Self
where
F: FnMut(&mut X) + Send + Sync + 'static,
X: ResourceSet<PreparedResources = X> + 'static,
{
into_resource_system(name, system).into()
}
}

View file

@ -9,6 +9,7 @@ mod system_fn_types;
pub use bit_set; pub use bit_set;
pub use system::*; pub use system::*;
pub use system_fn::*; pub use system_fn::*;
pub use system_fn_types::{ResourceMut, Resource};
pub mod prelude { pub mod prelude {
pub use crate::{ pub use crate::{
@ -17,11 +18,12 @@ pub mod prelude {
into_resource_system, into_resource_system,
// aliased preparedread and preparedwrite used by system_fn // aliased preparedread and preparedwrite used by system_fn
resource::{ resource::{
PreparedRead as Resource, PreparedWrite as ResourceMut, ResourceSet, Resources, ResourceSet, Resources,
}, },
schedule::{Executor, Runnable, Schedulable, Schedule}, schedule::{Executor, Runnable, Schedulable, Schedule},
IntoSystem, IntoSystem,
System, System,
SystemBuilder, SystemBuilder,
Resource, ResourceMut,
}; };
} }

View file

@ -630,7 +630,7 @@ pub struct SubWorld {
} }
impl SubWorld { impl SubWorld {
unsafe fn new( pub(super) unsafe fn new(
world: &World, world: &World,
access: &Access<ComponentTypeId>, access: &Access<ComponentTypeId>,
archetypes: &ArchetypeAccess, archetypes: &ArchetypeAccess,

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
resource::{PreparedRead, Resource, ResourceSet, ResourceTypeId}, resource::{ResourceSet, ResourceTypeId},
schedule::{ArchetypeAccess, Schedulable}, schedule::{ArchetypeAccess, Schedulable},
Access, System, SystemAccess, SystemFnWrapper, SystemQuery, Access, SystemAccess, SystemQuery, system_fn_types::{FuncSystem, FuncSystemFnWrapper},
}; };
use bit_set::BitSet; use bit_set::BitSet;
use fxhash::FxHashMap; use fxhash::FxHashMap;
@ -20,9 +20,9 @@ pub fn into_resource_for_each_system<'a, Q, F, R, X>(
where where
Q: IntoQuery + DefaultFilter<Filter = R>, Q: IntoQuery + DefaultFilter<Filter = R>,
<Q as View<'a>>::Iter: Iterator<Item = Q> + 'a, <Q as View<'a>>::Iter: Iterator<Item = Q> + 'a,
F: FnMut(&mut X, Q) + Send + Sync + 'static, F: FnMut(X, Q) + Send + Sync + 'static,
R: EntityFilter + Sync + 'static, R: EntityFilter + Sync + 'static,
X: ResourceSet<PreparedResources = X> + 'static, X: ResourceSet<PreparedResources = X> + 'static + Clone,
{ {
let mut resource_access: Access<ResourceTypeId> = Access::default(); let mut resource_access: Access<ResourceTypeId> = Access::default();
resource_access.reads.extend(X::read_types().iter()); resource_access.reads.extend(X::read_types().iter());
@ -32,19 +32,19 @@ where
component_access.reads.extend(Q::read_types().iter()); component_access.reads.extend(Q::read_types().iter());
component_access.writes.extend(Q::write_types().iter()); component_access.writes.extend(Q::write_types().iter());
let run_fn = SystemFnWrapper( let run_fn = FuncSystemFnWrapper(
move |_, move |_,
world, world,
resources: &mut X, resources: X,
query: &mut SystemQuery<Q, <Q as DefaultFilter>::Filter>| { query: &mut SystemQuery<Q, <Q as DefaultFilter>::Filter>| {
for components in query.iter_mut(world) { for components in query.iter_mut(world) {
system(resources, components); system(resources.clone(), components);
} }
}, },
PhantomData, PhantomData,
); );
Box::new(System { Box::new(FuncSystem {
name: name.into(), name: name.into(),
queries: AtomicRefCell::new(Q::query()), queries: AtomicRefCell::new(Q::query()),
access: SystemAccess { access: SystemAccess {
@ -61,7 +61,7 @@ where
pub fn into_resource_system<'a, F, X>(name: &'static str, mut system: F) -> Box<dyn Schedulable> pub fn into_resource_system<'a, F, X>(name: &'static str, mut system: F) -> Box<dyn Schedulable>
where where
F: FnMut(&mut X) + Send + Sync + 'static, F: FnMut(X) + Send + Sync + 'static,
X: ResourceSet<PreparedResources = X> + 'static, X: ResourceSet<PreparedResources = X> + 'static,
{ {
let mut resource_access: Access<ResourceTypeId> = Access::default(); let mut resource_access: Access<ResourceTypeId> = Access::default();
@ -69,14 +69,14 @@ where
resource_access.writes.extend(X::write_types().iter()); resource_access.writes.extend(X::write_types().iter());
let component_access: Access<ComponentTypeId> = Access::default(); let component_access: Access<ComponentTypeId> = Access::default();
let run_fn = SystemFnWrapper( let run_fn = FuncSystemFnWrapper(
move |_, _, resources: &mut X, _| { move |_, _, resources: X, _| {
system(resources); system(resources);
}, },
PhantomData, PhantomData,
); );
Box::new(System { Box::new(FuncSystem {
name: name.into(), name: name.into(),
queries: AtomicRefCell::new(()), queries: AtomicRefCell::new(()),
access: SystemAccess { access: SystemAccess {
@ -98,72 +98,17 @@ where
fn into_system(self, name: &'static str) -> Box<dyn Schedulable>; fn into_system(self, name: &'static str) -> Box<dyn Schedulable>;
} }
// impl<F, X: Resource + Send + Sync + 'static, A: Component, B: Component> IntoSystem<(X,), (A, B)> for F
// where
// F: for<'a> FnMut(&X, Ref<'a, A>, Ref<'a, B>) + Send + Sync + 'static,
// {
// fn into_system(mut self, name: &'static str) -> Box<dyn Schedulable> {
// let mut resource_access: Access<ResourceTypeId> = Access::default();
// resource_access
// .reads
// .extend(<PreparedRead<X>>::read_types().iter());
// resource_access
// .writes
// .extend(<PreparedRead<X>>::write_types().iter());
// let mut component_access: Access<ComponentTypeId> = Access::default();
// component_access
// .reads
// .extend(<(Ref<A>, Ref<B>) as View>::read_types().iter());
// component_access
// .writes
// .extend(<(Ref<A>, Ref<B>) as View>::write_types().iter());
// let run_fn = SystemFnWrapper(
// move |_,
// world,
// x: &mut PreparedRead<X>,
// query: &mut SystemQuery<
// (Ref<A>, Ref<B>),
// EntityFilterTuple<
// And<(ComponentFilter<A>, ComponentFilter<B>)>,
// And<(Passthrough, Passthrough)>,
// And<(Passthrough, Passthrough)>,
// >,
// >| {
// for (a, b) in query.iter_mut(world) {
// self(&*x, a, b);
// }
// },
// PhantomData,
// );
// Box::new(System {
// name: name.into(),
// queries: AtomicRefCell::new(<(Ref<A>, Ref<B>)>::query()),
// access: SystemAccess {
// resources: resource_access,
// components: component_access,
// tags: Access::default(),
// },
// archetypes: ArchetypeAccess::Some(BitSet::default()),
// _resources: PhantomData::<PreparedRead<X>>,
// command_buffer: FxHashMap::default(),
// run_fn: AtomicRefCell::new(run_fn),
// })
// }
// }
impl< impl<
'a, 'a,
F, F,
X: Resource + Send + Sync + 'static, X: ResourceSet<PreparedResources = X> + 'static + Clone,
A: for<'b> View<'b> + DefaultFilter<Filter = AF> + ViewElement, A: for<'b> View<'b> + DefaultFilter<Filter = AF> + ViewElement,
AF: EntityFilter + Sync + 'static, AF: EntityFilter + Sync + 'static,
B: for<'b> View<'b> + DefaultFilter<Filter = BF> + ViewElement, B: for<'b> View<'b> + DefaultFilter<Filter = BF> + ViewElement,
BF: EntityFilter + Sync + 'static, BF: EntityFilter + Sync + 'static,
> IntoSystem<'a, (X,), (A, B)> for F > IntoSystem<'a, (X,), (A, B)> for F
where where
F: FnMut(&X, A, B) + Send + Sync + 'static, F: FnMut(X, A, B) + Send + Sync + 'static,
<A as View<'a>>::Iter: Iterator<Item = A>, <A as View<'a>>::Iter: Iterator<Item = A>,
<B as View<'a>>::Iter: Iterator<Item = B>, <B as View<'a>>::Iter: Iterator<Item = B>,
{ {
@ -171,10 +116,10 @@ where
let mut resource_access: Access<ResourceTypeId> = Access::default(); let mut resource_access: Access<ResourceTypeId> = Access::default();
resource_access resource_access
.reads .reads
.extend(<PreparedRead<X>>::read_types().iter()); .extend(<X>::read_types().iter());
resource_access resource_access
.writes .writes
.extend(<PreparedRead<X>>::write_types().iter()); .extend(<X>::write_types().iter());
let mut component_access: Access<ComponentTypeId> = Access::default(); let mut component_access: Access<ComponentTypeId> = Access::default();
component_access component_access
.reads .reads
@ -183,10 +128,10 @@ where
.writes .writes
.extend(<(A, B) as View>::write_types().iter()); .extend(<(A, B) as View>::write_types().iter());
let run_fn = SystemFnWrapper( let run_fn = FuncSystemFnWrapper(
move |_, move |_,
world, world,
x: &mut PreparedRead<X>, x: X::PreparedResources,
query: &mut SystemQuery< query: &mut SystemQuery<
(A, B), (A, B),
EntityFilterTuple< EntityFilterTuple<
@ -205,13 +150,13 @@ where
>, >,
>| { >| {
for (a, b) in query.iter_mut(world) { for (a, b) in query.iter_mut(world) {
self(&*x, a, b); self(x.clone(), a, b);
} }
}, },
PhantomData, PhantomData,
); );
Box::new(System { Box::new(FuncSystem {
name: name.into(), name: name.into(),
queries: AtomicRefCell::new(<(A, B)>::query()), queries: AtomicRefCell::new(<(A, B)>::query()),
access: SystemAccess { access: SystemAccess {
@ -220,7 +165,7 @@ where
tags: Access::default(), tags: Access::default(),
}, },
archetypes: ArchetypeAccess::Some(BitSet::default()), archetypes: ArchetypeAccess::Some(BitSet::default()),
_resources: PhantomData::<PreparedRead<X>>, _resources: PhantomData::<X::PreparedResources>,
command_buffer: FxHashMap::default(), command_buffer: FxHashMap::default(),
run_fn: AtomicRefCell::new(run_fn), run_fn: AtomicRefCell::new(run_fn),
}) })
@ -242,10 +187,10 @@ macro_rules! impl_system {
let resource_access: Access<ResourceTypeId> = Access::default(); let resource_access: Access<ResourceTypeId> = Access::default();
let component_access: Access<ComponentTypeId> = component_access!(($($view),+)); let component_access: Access<ComponentTypeId> = component_access!(($($view),+));
let run_fn = SystemFnWrapper( let run_fn = FuncSystemFnWrapper(
move |_, move |_,
world, world,
_: &mut (), _: (),
query: &mut system_query!($($view, $filter),+) query: &mut system_query!($($view, $filter),+)
, ,
| { | {
@ -256,7 +201,7 @@ macro_rules! impl_system {
PhantomData, PhantomData,
); );
Box::new(System { Box::new(FuncSystem {
name: name.into(), name: name.into(),
queries: AtomicRefCell::new(query!($($view),+)), queries: AtomicRefCell::new(query!($($view),+)),
access: SystemAccess { access: SystemAccess {
@ -357,7 +302,7 @@ mod tests {
use crate::{ use crate::{
into_resource_for_each_system, into_resource_for_each_system,
resource::{PreparedRead, PreparedWrite, Resources}, resource::{PreparedRead, PreparedWrite, Resources},
IntoSystem, IntoSystem, system_fn_types::{ResourceMut, Resource},
}; };
use legion_core::{ use legion_core::{
borrow::{Ref, RefMut}, borrow::{Ref, RefMut},
@ -375,30 +320,53 @@ mod tests {
#[test] #[test]
fn test_into_system() { fn test_into_system() {
// fn read_system(a: &A, x: Ref<X>, y: Ref<Y>) {
// println!("{} {} {}", a.0, x.0, y.0);
// }
// fn read_system(x: Ref<X>) {
// println!("{}", x.0);
// }
fn read_system(x: Ref<X>, y: Ref<Y>, mut z: RefMut<A>) {
z.0 += 1;
println!("{} {} {}", x.0, y.0, z.0);
}
let mut world = World::new(); let mut world = World::new();
let mut resources = Resources::default(); let mut resources = Resources::default();
resources.insert(A(0)); resources.insert(A(0));
world.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]); world.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]);
let mut system = read_system.into_system("hi"); // fn single_read_system(x: Ref<X>) {
// println!("{}", x.0);
// }
// let mut system = single_read_system.into_system("hi");
// system.run(&mut world, &mut resources);
// fn read_write_system(x: Ref<X>, y: Ref<Y>, mut z: RefMut<A>) {
// z.0 += 1;
// println!("{} {} {}", x.0, y.0, z.0);
// }
// (
// {
// |x: Resource<A>, y: Ref<Y>, mut z: RefMut<A>| {
// z.0 += 1;
// println!("{} {} {}", x.0, y.0, z.0);
// }}).into_system("bleh");
// let mut system = read_write_system.into_system("read_write");
// system.run(&mut world, &mut resources);
// fn resource_system(a: Resource<A>, x: Ref<X>, y: Ref<Y>) {
// println!("{} {} {}", a.0, x.0, y.0);
// }
// let mut system = resource_system.into_system("hi");
// system.run(&mut world, &mut resources);
fn resource_system_mut(mut a: ResourceMut<A>, x: Ref<X>, y: Ref<Y>) {
let hi = &mut a;
a.0 += 1;
println!("{} {} {}", a.0, x.0, y.0);
}
let mut system = resource_system_mut.into_system("hi");
system.run(&mut world, &mut resources); system.run(&mut world, &mut resources);
} }
#[test] #[test]
fn test_system_fn() { fn test_system_fn() {
fn read_write_system(_: &mut (), (_x, mut y): (Ref<X>, RefMut<Y>)) { y.0 += 1; } fn read_write_system(_: (), (_x, mut y): (Ref<X>, RefMut<Y>)) { y.0 += 1; }
let mut world = World::new(); let mut world = World::new();
let mut resources = Resources::default(); let mut resources = Resources::default();
@ -410,34 +378,34 @@ mod tests {
#[test] #[test]
fn test_resource_system_fn() { fn test_resource_system_fn() {
fn my_system( // fn my_system(
(a, b): &mut (PreparedWrite<A>, PreparedRead<B>), // (a,): (Resource<A>,),
(x, mut y): (Ref<X>, RefMut<Y>), // (x, mut y): (Ref<X>, RefMut<Y>),
) { // ) {
assert_eq!(**b, B(1)); // assert_eq!(*a, A(1));
// assert_eq!(**b, B(0)); // // assert_eq!(**b, B(0));
if a.0 == 0 { // if a.0 == 0 {
assert_eq!(*x, X(2)); // assert_eq!(*x, X(2));
assert_eq!(*y, Y(3)); // assert_eq!(*y, Y(3));
} else if a.0 == 1 { // } else if a.0 == 1 {
assert_eq!(*x, X(4)); // assert_eq!(*x, X(4));
assert_eq!(*y, Y(5)); // assert_eq!(*y, Y(5));
y.0 += 1; // y.0 += 1;
assert_eq!(*y, Y(6)); // assert_eq!(*y, Y(6));
} else { // } else {
panic!("unexpected value"); // panic!("unexpected value");
} // }
a.0 += 1; // // a.0 += 1;
} // }
let mut world = World::new(); // let mut world = World::new();
let mut resources = Resources::default(); // let mut resources = Resources::default();
resources.insert(A(0)); // resources.insert(A(0));
resources.insert(B(1)); // resources.insert(B(1));
world.insert((), vec![(X(2), Y(3)), (X(4), Y(5))]); // world.insert((), vec![(X(2), Y(3)), (X(4), Y(5))]);
let mut my_system = into_resource_for_each_system("read_resources", my_system); // let mut my_system = into_resource_for_each_system("read_resources", my_system);
my_system.run(&mut world, &mut resources); // my_system.run(&mut world, &mut resources);
} }
} }

View file

@ -1,9 +1,186 @@
use crate::resource::{ use crate::{schedule::{ArchetypeAccess, Runnable}, resource::{
PreparedRead, PreparedWrite, Resource, ResourceSet, ResourceTypeId, Resources, PreparedRead, PreparedWrite, ResourceSet, ResourceTypeId, Resources, self
}; }, QuerySet, SystemId, SubWorld, SystemAccess};
use std::ops::{Deref, DerefMut}; use std::{marker::PhantomData, ops::{Deref, DerefMut}, hash::{Hasher, Hash}};
use legion_core::{world::{World, WorldId}, storage::ComponentTypeId, borrow::{AtomicRefCell, RefMut}, command::CommandBuffer};
use tracing::{debug, span, info, Level};
use fxhash::FxHashMap;
#[derive(Debug)]
pub struct Resource<'a, T: 'a> {
#[allow(dead_code)]
// held for drop impl
_marker: PhantomData<&'a ()>,
value: *const T,
}
impl<T: Resource> ResourceSet for PreparedRead<T> { unsafe impl<'a, T: resource::Resource> Send for Resource<'a, T> {}
unsafe impl<'a, T: resource::Resource> Sync for Resource<'a, T> {}
impl<'a, T: 'a> Clone for Resource<'a, T> {
#[inline(always)]
fn clone(&self) -> Self { Resource::new(self.value) }
}
impl<'a, T: 'a> Resource<'a, T> {
#[inline(always)]
fn new(resource: *const T) -> Self { Self { value: resource, _marker: PhantomData::default()} }
#[inline(always)]
pub fn map<K: 'a, F: FnMut(&T) -> &K>(&self, mut f: F) -> Resource<'a, K> {
Resource::new(f(&self))
}
}
impl<'a, T: 'a> Deref for Resource<'a, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target { unsafe { &*self.value } }
}
impl<'a, T: 'a> AsRef<T> for Resource<'a, T> {
#[inline(always)]
fn as_ref(&self) -> &T { unsafe { &*self.value } }
}
impl<'a, T: 'a> std::borrow::Borrow<T> for Resource<'a, T> {
#[inline(always)]
fn borrow(&self) -> &T { unsafe { &*self.value } }
}
impl<'a, T> PartialEq for Resource<'a, T>
where
T: 'a + PartialEq,
{
fn eq(&self, other: &Self) -> bool { self.value == other.value }
}
impl<'a, T> Eq for Resource<'a, T> where T: 'a + Eq {}
impl<'a, T> PartialOrd for Resource<'a, T>
where
T: 'a + PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<'a, T> Ord for Resource<'a, T>
where
T: 'a + Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.value.cmp(&other.value) }
}
impl<'a, T> Hash for Resource<'a, T>
where
T: 'a + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) { self.value.hash(state); }
}
impl<'a, T: resource::Resource> ResourceSet for Resource<'a, T> {
type PreparedResources = Resource<'a, T>;
unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
let resource = resources
.get::<T>()
.unwrap_or_else(|| panic!("Failed to fetch resource!: {}", std::any::type_name::<T>()));
Resource::new(resource.deref() as *const T)
}
fn read_types() -> Vec<ResourceTypeId> { vec![ResourceTypeId::of::<T>()] }
fn write_types() -> Vec<ResourceTypeId> { Vec::new() }
}
#[derive(Debug)]
pub struct ResourceMut<'a, T: 'a> {
// held for drop impl
_marker: PhantomData<&'a mut ()>,
value: *mut T,
}
unsafe impl<'a, T: resource::Resource> Send for ResourceMut<'a, T> {}
unsafe impl<'a, T: resource::Resource> Sync for ResourceMut<'a, T> {}
impl<'a, T: 'a> Clone for ResourceMut<'a, T> {
#[inline(always)]
fn clone(&self) -> Self { ResourceMut::new(self.value) }
}
impl<'a, T: 'a> ResourceMut<'a, T> {
#[inline(always)]
fn new(resource: *mut T) -> Self { Self { value: resource, _marker: PhantomData::default()} }
#[inline(always)]
pub fn map_into<K: 'a, F: FnMut(&mut T) -> K>(mut self, mut f: F) -> ResourceMut<'a, K> {
ResourceMut::new(&mut f(&mut self))
}
}
impl<'a, T: 'a> Deref for ResourceMut<'a, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target { unsafe { &*self.value } }
}
impl<'a, T: 'a> DerefMut for ResourceMut<'a, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.value } }
}
impl<'a, T: 'a> AsRef<T> for ResourceMut<'a, T> {
#[inline(always)]
fn as_ref(&self) -> &T { unsafe { &*self.value } }
}
impl<'a, T: 'a> std::borrow::Borrow<T> for ResourceMut<'a, T> {
#[inline(always)]
fn borrow(&self) -> &T { unsafe { &*self.value } }
}
impl<'a, T> PartialEq for ResourceMut<'a, T>
where
T: 'a + PartialEq,
{
fn eq(&self, other: &Self) -> bool { self.value == other.value }
}
impl<'a, T> Eq for ResourceMut<'a, T> where T: 'a + Eq {}
impl<'a, T> PartialOrd for ResourceMut<'a, T>
where
T: 'a + PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<'a, T> Ord for ResourceMut<'a, T>
where
T: 'a + Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.value.cmp(&other.value) }
}
impl<'a, T> Hash for ResourceMut<'a, T>
where
T: 'a + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) { self.value.hash(state); }
}
impl<'a, T: resource::Resource> ResourceSet for ResourceMut<'a, T> {
type PreparedResources = ResourceMut<'a, T>;
unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
let mut resource = resources
.get_mut::<T>()
.unwrap_or_else(|| panic!("Failed to fetch resource!: {}", std::any::type_name::<T>()));
ResourceMut::new(resource.deref_mut() as *mut T)
}
fn read_types() -> Vec<ResourceTypeId> { vec![ResourceTypeId::of::<T>()] }
fn write_types() -> Vec<ResourceTypeId> { Vec::new() }
}
impl<T: resource::Resource> ResourceSet for PreparedRead<T> {
type PreparedResources = PreparedRead<T>; type PreparedResources = PreparedRead<T>;
unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources { unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
@ -16,7 +193,7 @@ impl<T: Resource> ResourceSet for PreparedRead<T> {
fn write_types() -> Vec<ResourceTypeId> { Vec::new() } fn write_types() -> Vec<ResourceTypeId> { Vec::new() }
} }
impl<T: Resource> ResourceSet for PreparedWrite<T> { impl<T: resource::Resource> ResourceSet for PreparedWrite<T> {
type PreparedResources = PreparedWrite<T>; type PreparedResources = PreparedWrite<T>;
unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources { unsafe fn fetch_unchecked(resources: &Resources) -> Self::PreparedResources {
@ -28,3 +205,135 @@ impl<T: Resource> ResourceSet for PreparedWrite<T> {
fn read_types() -> Vec<ResourceTypeId> { Vec::new() } fn read_types() -> Vec<ResourceTypeId> { Vec::new() }
fn write_types() -> Vec<ResourceTypeId> { vec![ResourceTypeId::of::<T>()] } fn write_types() -> Vec<ResourceTypeId> { vec![ResourceTypeId::of::<T>()] }
} }
/// The concrete type which contains the system closure provided by the user. This struct should
/// not be instantiated directly, and instead should be created using `SystemBuilder`.
///
/// Implements `Schedulable` which is consumable by the `StageExecutor`, executing the closure.
///
/// Also handles caching of archetype information in a `BitSet`, as well as maintaining the provided
/// information about what queries this system will run and, as a result, its data access.
///
/// Queries are stored generically within this struct, and the `SystemQuery` types are generated
/// on each `run` call, wrapping the world and providing the set to the user in their closure.
pub struct FuncSystem<R, Q, F>
where
R: ResourceSet,
Q: QuerySet,
F: FuncSystemFn<
Resources = <R as ResourceSet>::PreparedResources,
Queries = <Q as QuerySet>::Queries,
>,
{
pub name: SystemId,
pub _resources: PhantomData<R>,
pub queries: AtomicRefCell<Q>,
pub run_fn: AtomicRefCell<F>,
pub archetypes: ArchetypeAccess,
// These are stored statically instead of always iterated and created from the
// query types, which would make allocations every single request
pub access: SystemAccess,
// We pre-allocate a command buffer for ourself. Writes are self-draining so we never have to rellocate.
pub command_buffer: FxHashMap<WorldId, AtomicRefCell<CommandBuffer>>,
}
impl<R, Q, F> Runnable for FuncSystem<R, Q, F>
where
R: ResourceSet,
Q: QuerySet,
F: FuncSystemFn<
Resources = <R as ResourceSet>::PreparedResources,
Queries = <Q as QuerySet>::Queries,
>,
{
fn name(&self) -> &SystemId { &self.name }
fn reads(&self) -> (&[ResourceTypeId], &[ComponentTypeId]) {
(&self.access.resources.reads, &self.access.components.reads)
}
fn writes(&self) -> (&[ResourceTypeId], &[ComponentTypeId]) {
(
&self.access.resources.writes,
&self.access.components.writes,
)
}
fn prepare(&mut self, world: &World) {
if let ArchetypeAccess::Some(bitset) = &mut self.archetypes {
self.queries.get_mut().filter_archetypes(world, bitset);
}
}
fn accesses_archetypes(&self) -> &ArchetypeAccess { &self.archetypes }
fn command_buffer_mut(&self, world: WorldId) -> Option<RefMut<CommandBuffer>> {
self.command_buffer.get(&world).map(|cmd| cmd.get_mut())
}
unsafe fn run_unsafe(&mut self, world: &World, resources: &Resources) {
let span = span!(Level::INFO, "System", system = %self.name);
let _guard = span.enter();
debug!("Initializing");
let resources = R::fetch_unchecked(resources);
let mut queries = self.queries.get_mut();
let mut prepared_queries = queries.prepare();
let mut world_shim = SubWorld::new(world, &self.access.components, &self.archetypes);
let cmd = self
.command_buffer
.entry(world.id())
.or_insert_with(|| AtomicRefCell::new(CommandBuffer::new(world)));
info!("Running");
let mut borrow = self.run_fn.get_mut();
borrow.deref_mut().run(
&mut cmd.get_mut(),
&mut world_shim,
resources,
&mut prepared_queries,
);
}
}
/// Supertrait used for defining systems. All wrapper objects for systems implement this trait.
///
/// This trait will generally not be used by users.
pub trait FuncSystemFn {
type Resources;
type Queries;
fn run(
&mut self,
commands: &mut CommandBuffer,
world: &mut SubWorld,
resources: Self::Resources,
queries: &mut Self::Queries,
);
}
pub struct FuncSystemFnWrapper<
R,
Q,
F: FnMut(&mut CommandBuffer, &mut SubWorld, R, &mut Q) + 'static,
>(pub F, pub PhantomData<(R, Q)>);
impl<F, R, Q> FuncSystemFn for FuncSystemFnWrapper<R, Q, F>
where
F: FnMut(&mut CommandBuffer, &mut SubWorld, R, &mut Q) + 'static,
{
type Resources = R;
type Queries = Q;
fn run(
&mut self,
commands: &mut CommandBuffer,
world: &mut SubWorld,
resources: Self::Resources,
queries: &mut Self::Queries,
) {
(self.0)(commands, world, resources, queries);
}
}

View file

@ -54,10 +54,11 @@ pub use legion::{
systems::{ systems::{
bit_set::BitSet, bit_set::BitSet,
resource::{ resource::{
PreparedRead as Resource, PreparedWrite as ResourceMut, ResourceSet, Resources, ResourceSet, Resources,
}, },
schedule::{Executor, Runnable, Schedulable, Schedule}, schedule::{Executor, Runnable, Schedulable, Schedule},
IntoSystem, SubWorld, SystemBuilder, IntoSystem, SubWorld, SystemBuilder,
Resource, ResourceMut,
}, },
world::{Universe, World}, world::{Universe, World},
}; };