use crate::resource::{Resource, ResourceSet, ResourceTypeId, Resources};
use crate::schedule::{Runnable, Schedulable};
use bit_set::BitSet;
use derivative::Derivative;
use fxhash::FxHashMap;
use legion_core::{
borrow::{AtomicRefCell, RefMut},
command::CommandBuffer,
cons::{ConsAppend, ConsFlatten},
filter::EntityFilter,
index::ArchetypeIndex,
query::{Query, Read, View, Write},
storage::{Component, ComponentTypeId, TagTypeId},
subworld::{Access, ArchetypeAccess, SubWorld},
world::{World, WorldId},
};
use std::any::TypeId;
use std::borrow::Cow;
use std::marker::PhantomData;
use tracing::{debug, info, span, Level};
#[derive(Derivative, Debug, Clone)]
#[derivative(Default(bound = ""))]
pub struct SystemAccess {
pub resources: Access<ResourceTypeId>,
pub components: Access<ComponentTypeId>,
pub tags: Access<TagTypeId>,
}
pub trait QuerySet: Send + Sync {
fn filter_archetypes(&mut self, world: &World, archetypes: &mut BitSet);
}
macro_rules! impl_queryset_tuple {
($($ty: ident),*) => {
paste::item! {
#[allow(unused_parens, non_snake_case)]
impl<$([<$ty V>], [<$ty F>], )*> QuerySet for ($(Query<[<$ty V>], [<$ty F>]>, )*)
where
$([<$ty V>]: for<'v> View<'v>,)*
$([<$ty F>]: EntityFilter + Send + Sync,)*
{
fn filter_archetypes(&mut self, world: &World, bitset: &mut BitSet) {
let ($($ty,)*) = self;
$(
let storage = world.storage();
$ty.filter.iter_archetype_indexes(storage).for_each(|ArchetypeIndex(id)| { bitset.insert(id); });
)*
}
}
}
};
}
impl QuerySet for () {
fn filter_archetypes(&mut self, _: &World, _: &mut BitSet) {}
}
impl<AV, AF> QuerySet for Query<AV, AF>
where
AV: for<'v> View<'v>,
AF: EntityFilter + Send + Sync,
{
fn filter_archetypes(&mut self, world: &World, bitset: &mut BitSet) {
let storage = world.storage();
self.filter
.iter_archetype_indexes(storage)
.for_each(|ArchetypeIndex(id)| {
bitset.insert(id);
});
}
}
impl_queryset_tuple!(A);
impl_queryset_tuple!(A, B);
impl_queryset_tuple!(A, B, C);
impl_queryset_tuple!(A, B, C, D);
impl_queryset_tuple!(A, B, C, D, E);
impl_queryset_tuple!(A, B, C, D, E, F);
impl_queryset_tuple!(A, B, C, D, E, F, G);
impl_queryset_tuple!(A, B, C, D, E, F, G, H);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SystemId {
name: Cow<'static, str>,
type_id: TypeId,
}
struct Unspecified;
impl SystemId {
pub fn of<T: 'static>(name: Option<String>) -> Self {
Self {
name: name
.unwrap_or_else(|| std::any::type_name::<T>().to_string())
.into(),
type_id: TypeId::of::<T>(),
}
}
}
impl std::fmt::Display for SystemId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl<T: Into<Cow<'static, str>>> From<T> for SystemId {
fn from(name: T) -> SystemId {
SystemId {
name: name.into(),
type_id: TypeId::of::<Unspecified>(),
}
}
}
pub struct System<R, Q, F>
where
R: ResourceSet,
Q: QuerySet,
F: SystemFn<Resources = <R as ResourceSet>::PreparedResources, Queries = Q>,
{
name: SystemId,
_resources: PhantomData<R>,
queries: AtomicRefCell<Q>,
run_fn: AtomicRefCell<F>,
archetypes: ArchetypeAccess,
access: SystemAccess,
command_buffer: FxHashMap<WorldId, AtomicRefCell<CommandBuffer>>,
}
impl<R, Q, F> Runnable for System<R, Q, F>
where
R: ResourceSet,
Q: QuerySet,
F: SystemFn<Resources = <R as ResourceSet>::PreparedResources, Queries = Q>,
{
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 mut resources = R::fetch_unchecked(resources);
let mut queries = self.queries.get_mut();
let mut world_shim =
SubWorld::new_unchecked(world, &self.access.components, &self.archetypes);
let cmd = self
.command_buffer
.entry(world.id())
.or_insert_with(|| AtomicRefCell::new(CommandBuffer::new(world)));
info!("Running");
use std::ops::DerefMut;
let mut borrow = self.run_fn.get_mut();
borrow.deref_mut().run(
&mut cmd.get_mut(),
&mut world_shim,
&mut resources,
queries.deref_mut(),
);
}
}
pub trait SystemFn {
type Resources;
type Queries;
fn run(
&mut self,
commands: &mut CommandBuffer,
world: &mut SubWorld,
resources: &mut Self::Resources,
queries: &mut Self::Queries,
);
}
struct SystemFnWrapper<R, Q, F: FnMut(&mut CommandBuffer, &mut SubWorld, &mut R, &mut Q) + 'static>(
F,
PhantomData<(R, Q)>,
);
impl<F, R, Q> SystemFn for SystemFnWrapper<R, Q, F>
where
F: FnMut(&mut CommandBuffer, &mut SubWorld, &mut R, &mut Q) + 'static,
{
type Resources = R;
type Queries = Q;
fn run(
&mut self,
commands: &mut CommandBuffer,
world: &mut SubWorld,
resources: &mut Self::Resources,
queries: &mut Self::Queries,
) {
(self.0)(commands, world, resources, queries);
}
}
pub struct SystemBuilder<Q = (), R = ()> {
name: SystemId,
queries: Q,
resources: R,
resource_access: Access<ResourceTypeId>,
component_access: Access<ComponentTypeId>,
access_all_archetypes: bool,
}
impl SystemBuilder<(), ()> {
pub fn new<T: Into<SystemId>>(name: T) -> Self {
Self {
name: name.into(),
queries: (),
resources: (),
resource_access: Access::default(),
component_access: Access::default(),
access_all_archetypes: false,
}
}
}
impl<Q, R> SystemBuilder<Q, R>
where
Q: 'static + Send + ConsFlatten,
R: 'static + Send + ConsFlatten,
{
pub fn with_query<V, F>(
mut self,
query: Query<V, F>,
) -> SystemBuilder<<Q as ConsAppend<Query<V, F>>>::Output, R>
where
V: for<'a> View<'a>,
F: 'static + EntityFilter,
Q: ConsAppend<Query<V, F>>,
{
self.component_access.reads.extend(V::read_types().iter());
self.component_access.writes.extend(V::write_types().iter());
SystemBuilder {
name: self.name,
queries: ConsAppend::append(self.queries, query),
resources: self.resources,
resource_access: self.resource_access,
component_access: self.component_access,
access_all_archetypes: self.access_all_archetypes,
}
}
pub fn read_resource<T>(mut self) -> SystemBuilder<Q, <R as ConsAppend<Read<T>>>::Output>
where
T: 'static + Resource,
R: ConsAppend<Read<T>>,
<R as ConsAppend<Read<T>>>::Output: ConsFlatten,
{
self.resource_access.reads.push(ResourceTypeId::of::<T>());
SystemBuilder {
name: self.name,
queries: self.queries,
resources: ConsAppend::append(self.resources, Read::<T>::default()),
resource_access: self.resource_access,
component_access: self.component_access,
access_all_archetypes: self.access_all_archetypes,
}
}
pub fn write_resource<T>(mut self) -> SystemBuilder<Q, <R as ConsAppend<Write<T>>>::Output>
where
T: 'static + Resource,
R: ConsAppend<Write<T>>,
<R as ConsAppend<Write<T>>>::Output: ConsFlatten,
{
self.resource_access.writes.push(ResourceTypeId::of::<T>());
SystemBuilder {
name: self.name,
queries: self.queries,
resources: ConsAppend::append(self.resources, Write::<T>::default()),
resource_access: self.resource_access,
component_access: self.component_access,
access_all_archetypes: self.access_all_archetypes,
}
}
pub fn read_component<T>(mut self) -> Self
where
T: Component,
{
self.component_access.reads.push(ComponentTypeId::of::<T>());
self.access_all_archetypes = true;
self
}
pub fn write_component<T>(mut self) -> Self
where
T: Component,
{
self.component_access
.writes
.push(ComponentTypeId::of::<T>());
self.access_all_archetypes = true;
self
}
pub fn build<F>(self, run_fn: F) -> Box<dyn Schedulable>
where
<R as ConsFlatten>::Output: ResourceSet + Send + Sync,
<Q as ConsFlatten>::Output: QuerySet + Send + Sync,
<<R as ConsFlatten>::Output as ResourceSet>::PreparedResources: Send + Sync,
F: FnMut(
&mut CommandBuffer,
&mut SubWorld,
&mut <<R as ConsFlatten>::Output as ResourceSet>::PreparedResources,
&mut <Q as ConsFlatten>::Output,
) + Send
+ Sync
+ 'static,
{
let run_fn = SystemFnWrapper(run_fn, PhantomData);
Box::new(System {
name: self.name,
run_fn: AtomicRefCell::new(run_fn),
_resources: PhantomData::<<R as ConsFlatten>::Output>,
queries: AtomicRefCell::new(self.queries.flatten()),
archetypes: if self.access_all_archetypes {
ArchetypeAccess::All
} else {
ArchetypeAccess::Some(BitSet::default())
},
access: SystemAccess {
resources: self.resource_access,
components: self.component_access,
tags: Access::default(),
},
command_buffer: FxHashMap::default(),
})
}
pub fn build_thread_local<F>(self, run_fn: F) -> Box<dyn Runnable>
where
<R as ConsFlatten>::Output: ResourceSet + Send + Sync,
<Q as ConsFlatten>::Output: QuerySet,
F: FnMut(
&mut CommandBuffer,
&mut SubWorld,
&mut <<R as ConsFlatten>::Output as ResourceSet>::PreparedResources,
&mut <Q as ConsFlatten>::Output,
) + 'static,
{
let run_fn = SystemFnWrapper(run_fn, PhantomData);
Box::new(System {
name: self.name,
run_fn: AtomicRefCell::new(run_fn),
_resources: PhantomData::<<R as ConsFlatten>::Output>,
queries: AtomicRefCell::new(self.queries.flatten()),
archetypes: if self.access_all_archetypes {
ArchetypeAccess::All
} else {
ArchetypeAccess::Some(BitSet::default())
},
access: SystemAccess {
resources: self.resource_access,
components: self.component_access,
tags: Access::default(),
},
command_buffer: FxHashMap::default(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::schedule::*;
use legion_core::prelude::*;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Clone, Copy, Debug, PartialEq)]
struct Pos(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vel(f32, f32, f32);
#[derive(Default)]
struct TestResource(pub i32);
#[derive(Default)]
struct TestResourceTwo(pub i32);
#[derive(Default)]
struct TestResourceThree(pub i32);
#[derive(Default)]
struct TestResourceFour(pub i32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct TestComp(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct TestCompTwo(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct TestCompThree(f32, f32, f32);
#[test]
fn builder_schedule_execute() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
resources.insert(TestResource(123));
resources.insert(TestResourceTwo(123));
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum TestSystems {
TestSystemOne,
TestSystemTwo,
TestSystemThree,
TestSystemFour,
}
let runs = Arc::new(Mutex::new(Vec::new()));
let system_one_runs = runs.clone();
let system_one = SystemBuilder::<()>::new("TestSystem1")
.read_resource::<TestResource>()
.with_query(Read::<Pos>::query())
.with_query(Write::<Vel>::query())
.build(move |_commands, _world, _resource, _queries| {
tracing::trace!("system_one");
system_one_runs
.lock()
.unwrap()
.push(TestSystems::TestSystemOne);
});
let system_two_runs = runs.clone();
let system_two = SystemBuilder::<()>::new("TestSystem2")
.write_resource::<TestResourceTwo>()
.with_query(Read::<Vel>::query())
.build(move |_commands, _world, _resource, _queries| {
tracing::trace!("system_two");
system_two_runs
.lock()
.unwrap()
.push(TestSystems::TestSystemTwo);
});
let system_three_runs = runs.clone();
let system_three = SystemBuilder::<()>::new("TestSystem3")
.read_resource::<TestResourceTwo>()
.with_query(Read::<Vel>::query())
.build(move |_commands, _world, _resource, _queries| {
tracing::trace!("system_three");
system_three_runs
.lock()
.unwrap()
.push(TestSystems::TestSystemThree);
});
let system_four_runs = runs.clone();
let system_four = SystemBuilder::<()>::new("TestSystem4")
.write_resource::<TestResourceTwo>()
.with_query(Read::<Vel>::query())
.build(move |_commands, _world, _resource, _queries| {
tracing::trace!("system_four");
system_four_runs
.lock()
.unwrap()
.push(TestSystems::TestSystemFour);
});
let order = vec![
TestSystems::TestSystemOne,
TestSystems::TestSystemTwo,
TestSystems::TestSystemThree,
TestSystems::TestSystemFour,
];
let systems = vec![system_one, system_two, system_three, system_four];
let mut executor = Executor::new(systems);
executor.execute(&mut world, &mut resources);
assert_eq!(*(runs.lock().unwrap()), order);
}
#[test]
fn builder_create_and_execute() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
resources.insert(TestResource(123));
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
let mut system = SystemBuilder::<()>::new("TestSystem")
.read_resource::<TestResource>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_commands, world, resource, queries| {
assert_eq!(resource.0, 123);
let mut count = 0;
{
for (entity, pos) in queries.0.iter_entities(world) {
assert_eq!(expected.get(&entity).unwrap().0, *pos);
count += 1;
}
}
assert_eq!(components.len(), count);
});
system.prepare(&world);
system.run(&mut world, &mut resources);
}
#[test]
fn fnmut_stateful_system_test() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
resources.insert(TestResource(123));
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
let mut state = 0;
let mut system = SystemBuilder::<()>::new("TestSystem")
.read_resource::<TestResource>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, _, _| {
state += 1;
});
let _ = state;
system.prepare(&world);
system.run(&mut world, &mut resources);
}
#[test]
fn system_mutate_archetype() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
#[derive(Default, Clone, Copy)]
pub struct Balls(u32);
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
let expected_copy = expected.clone();
let mut system = SystemBuilder::<()>::new("TestSystem")
.with_query(<(Read<Pos>, Read<Vel>)>::query())
.build(move |_, world, _, query| {
let mut count = 0;
{
for (entity, (pos, vel)) in query.iter_entities(world) {
assert_eq!(expected_copy.get(&entity).unwrap().0, *pos);
assert_eq!(expected_copy.get(&entity).unwrap().1, *vel);
count += 1;
}
}
assert_eq!(components.len(), count);
});
system.prepare(&world);
system.run(&mut world, &mut resources);
world
.add_component(*(expected.keys().nth(0).unwrap()), Balls::default())
.unwrap();
system.prepare(&world);
system.run(&mut world, &mut resources);
}
#[test]
fn system_mutate_archetype_buffer() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
#[derive(Default, Clone, Copy)]
pub struct Balls(u32);
let components = (0..30000)
.map(|_| (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)))
.collect::<Vec<_>>();
let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
let expected_copy = expected.clone();
let mut system = SystemBuilder::<()>::new("TestSystem")
.with_query(<(Read<Pos>, Read<Vel>)>::query())
.build(move |command_buffer, world, _, query| {
let mut count = 0;
{
for (entity, (pos, vel)) in query.iter_entities(world) {
assert_eq!(expected_copy.get(&entity).unwrap().0, *pos);
assert_eq!(expected_copy.get(&entity).unwrap().1, *vel);
count += 1;
command_buffer.add_component(entity, Balls::default());
}
}
assert_eq!(components.len(), count);
});
system.prepare(&world);
system.run(&mut world, &mut resources);
system
.command_buffer_mut(world.id())
.unwrap()
.write(&mut world);
system.prepare(&world);
system.run(&mut world, &mut resources);
}
#[test]
#[cfg(feature = "par-schedule")]
fn par_res_write() {
use std::sync::atomic::{AtomicUsize, Ordering};
let _ = tracing_subscriber::fmt::try_init();
#[derive(Default)]
struct AtomicRes(AtomicRefCell<AtomicUsize>);
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
resources.insert(AtomicRes::default());
let system1 = SystemBuilder::<()>::new("TestSystem1")
.write_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
});
let system2 = SystemBuilder::<()>::new("TestSystem2")
.write_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
});
let system3 = SystemBuilder::<()>::new("TestSystem3")
.write_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
});
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(8)
.build()
.unwrap();
tracing::debug!(
reads = ?system1.reads(),
writes = ?system1.writes(),
"System access"
);
let systems = vec![system1, system2, system3];
let mut executor = Executor::new(systems);
pool.install(|| {
for _ in 0..1000 {
executor.execute(&mut world, &mut resources);
}
});
assert_eq!(
resources
.get::<AtomicRes>()
.unwrap()
.0
.get()
.load(Ordering::SeqCst),
3 * 1000,
);
}
#[test]
#[cfg(feature = "par-schedule")]
fn par_res_readwrite() {
use std::sync::atomic::{AtomicUsize, Ordering};
let _ = tracing_subscriber::fmt::try_init();
#[derive(Default)]
struct AtomicRes(AtomicRefCell<AtomicUsize>);
let universe = Universe::new();
let mut world = universe.create_world();
let mut resources = Resources::default();
resources.insert(AtomicRes::default());
let system1 = SystemBuilder::<()>::new("TestSystem1")
.read_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get().fetch_add(1, Ordering::SeqCst);
});
let system2 = SystemBuilder::<()>::new("TestSystem2")
.write_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
});
let system3 = SystemBuilder::<()>::new("TestSystem3")
.write_resource::<AtomicRes>()
.with_query(Read::<Pos>::query())
.with_query(Read::<Vel>::query())
.build(move |_, _, resource, _| {
resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
});
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(8)
.build()
.unwrap();
tracing::debug!(
reads = ?system1.reads(),
writes = ?system1.writes(),
"System access"
);
let systems = vec![system1, system2, system3];
let mut executor = Executor::new(systems);
pool.install(|| {
for _ in 0..1000 {
executor.execute(&mut world, &mut resources);
}
});
}
#[test]
#[cfg(feature = "par-schedule")]
#[allow(clippy::float_cmp)]
fn par_comp_readwrite() {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
#[derive(Clone, Copy, Debug, PartialEq)]
struct Comp1(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct Comp2(f32, f32, f32);
let components = vec![
(Comp1(69., 69., 69.), Comp2(69., 69., 69.)),
(Comp1(69., 69., 69.), Comp2(69., 69., 69.)),
];
let mut expected = HashMap::<Entity, (Comp1, Comp2)>::new();
for (i, e) in world.insert((), components.clone()).iter().enumerate() {
if let Some((pos, rot)) = components.get(i) {
expected.insert(*e, (*pos, *rot));
}
}
let system1 = SystemBuilder::<()>::new("TestSystem1")
.with_query(<(Read<Comp1>, Read<Comp2>)>::query())
.build(move |_, world, _, query| {
query.iter(world).for_each(|(one, two)| {
assert_eq!(one.0, 69.);
assert_eq!(one.1, 69.);
assert_eq!(one.2, 69.);
assert_eq!(two.0, 69.);
assert_eq!(two.1, 69.);
assert_eq!(two.2, 69.);
});
});
let system2 = SystemBuilder::<()>::new("TestSystem2")
.with_query(<(Write<Comp1>, Read<Comp2>)>::query())
.build(move |_, world, _, query| {
query.iter_mut(world).for_each(|(mut one, two)| {
one.0 = 456.;
one.1 = 456.;
one.2 = 456.;
assert_eq!(two.0, 69.);
assert_eq!(two.1, 69.);
assert_eq!(two.2, 69.);
});
});
let system3 = SystemBuilder::<()>::new("TestSystem3")
.with_query(<(Write<Comp1>, Write<Comp2>)>::query())
.build(move |_, world, _, query| {
query.iter_mut(world).for_each(|(mut one, mut two)| {
assert_eq!(one.0, 456.);
assert_eq!(one.1, 456.);
assert_eq!(one.2, 456.);
assert_eq!(two.0, 69.);
assert_eq!(two.1, 69.);
assert_eq!(two.2, 69.);
one.0 = 789.;
one.1 = 789.;
one.2 = 789.;
two.0 = 789.;
two.1 = 789.;
two.2 = 789.;
});
});
let system4 = SystemBuilder::<()>::new("TestSystem4")
.with_query(<(Read<Comp1>, Read<Comp2>)>::query())
.build(move |_, world, _, query| {
query.iter(world).for_each(|(one, two)| {
assert_eq!(one.0, 789.);
assert_eq!(one.1, 789.);
assert_eq!(one.2, 789.);
assert_eq!(two.0, 789.);
assert_eq!(two.1, 789.);
assert_eq!(two.2, 789.);
});
});
let system5 = SystemBuilder::<()>::new("TestSystem5")
.with_query(<(Write<Comp1>, Write<Comp2>)>::query())
.build(move |_, world, _, query| {
query.iter_mut(world).for_each(|(mut one, mut two)| {
assert_eq!(one.0, 789.);
assert_eq!(one.1, 789.);
assert_eq!(one.2, 789.);
assert_eq!(two.0, 789.);
assert_eq!(two.1, 789.);
assert_eq!(two.2, 789.);
one.0 = 69.;
one.1 = 69.;
one.2 = 69.;
two.0 = 69.;
two.1 = 69.;
two.2 = 69.;
});
});
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(8)
.build()
.unwrap();
tracing::debug!(
reads = ?system1.reads(),
writes = ?system1.writes(),
"System access"
);
let systems = vec![system1, system2, system3, system4, system5];
let mut executor = Executor::new(systems);
pool.install(|| {
for _ in 0..1000 {
executor.execute(&mut world, &mut Resources::default());
}
});
}
#[test]
fn split_world() {
let mut world = World::new();
let system = SystemBuilder::new("split worlds")
.with_query(Write::<usize>::query())
.with_query(Write::<bool>::query())
.build(|_, world, _, (query_a, query_b)| {
let (mut left, mut right) = world.split_for_query(&query_a);
for _ in query_a.iter_mut(&mut left) {
let _ = query_b.iter_mut(&mut right);
}
});
let mut schedule = Schedule::builder().add_system(system).build();
schedule.execute(&mut world, &mut Resources::default());
}
}