1use crate::resource::{Resource, ResourceSet, ResourceTypeId, Resources};
2use crate::schedule::{Runnable, Schedulable};
3use bit_set::BitSet;
4use derivative::Derivative;
5use fxhash::FxHashMap;
6use legion_core::{
7 borrow::{AtomicRefCell, RefMut},
8 command::CommandBuffer,
9 cons::{ConsAppend, ConsFlatten},
10 filter::EntityFilter,
11 index::ArchetypeIndex,
12 permission::Permissions,
13 query::{Query, Read, View, Write},
14 storage::{Component, ComponentTypeId, TagTypeId},
15 subworld::{ArchetypeAccess, SubWorld},
16 world::{World, WorldId},
17};
18use std::any::TypeId;
19use std::borrow::Cow;
20use std::marker::PhantomData;
21use tracing::{debug, info, span, Level};
22
23#[derive(Derivative, Debug, Clone)]
25#[derivative(Default(bound = ""))]
26pub struct SystemAccess {
27 pub resources: Permissions<ResourceTypeId>,
28 pub components: Permissions<ComponentTypeId>,
29 pub tags: Permissions<TagTypeId>,
30}
31
32pub trait QuerySet: Send + Sync {
36 fn filter_archetypes(&mut self, world: &World, archetypes: &mut BitSet);
39}
40
41macro_rules! impl_queryset_tuple {
42 ($($ty: ident),*) => {
43 paste::item! {
44 #[allow(unused_parens, non_snake_case)]
45 impl<$([<$ty V>], [<$ty F>], )*> QuerySet for ($(Query<[<$ty V>], [<$ty F>]>, )*)
46 where
47 $([<$ty V>]: for<'v> View<'v>,)*
48 $([<$ty F>]: EntityFilter + Send + Sync,)*
49 {
50 fn filter_archetypes(&mut self, world: &World, bitset: &mut BitSet) {
51 let ($($ty,)*) = self;
52
53 $(
54 let storage = world.storage();
55 $ty.filter.iter_archetype_indexes(storage).for_each(|ArchetypeIndex(id)| { bitset.insert(id); });
56 )*
57 }
58 }
59 }
60 };
61}
62
63impl QuerySet for () {
64 fn filter_archetypes(&mut self, _: &World, _: &mut BitSet) {}
65}
66
67impl<AV, AF> QuerySet for Query<AV, AF>
68where
69 AV: for<'v> View<'v>,
70 AF: EntityFilter + Send + Sync,
71{
72 fn filter_archetypes(&mut self, world: &World, bitset: &mut BitSet) {
73 let storage = world.storage();
74 self.filter
75 .iter_archetype_indexes(storage)
76 .for_each(|ArchetypeIndex(id)| {
77 bitset.insert(id);
78 });
79 }
80}
81
82impl_queryset_tuple!(A);
83impl_queryset_tuple!(A, B);
84impl_queryset_tuple!(A, B, C);
85impl_queryset_tuple!(A, B, C, D);
86impl_queryset_tuple!(A, B, C, D, E);
87impl_queryset_tuple!(A, B, C, D, E, F);
88impl_queryset_tuple!(A, B, C, D, E, F, G);
89impl_queryset_tuple!(A, B, C, D, E, F, G, H);
90impl_queryset_tuple!(A, B, C, D, E, F, G, H, I);
91impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J);
92impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K);
93impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
94impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
95impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
96impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
97impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
98impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
99impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
100impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
101impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
102impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
103impl_queryset_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
104impl_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);
105impl_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);
106impl_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);
107impl_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);
108
109#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110pub struct SystemId {
111 name: Cow<'static, str>,
112 type_id: TypeId,
113}
114
115struct Unspecified;
116
117impl SystemId {
118 pub fn of<T: 'static>(name: Option<String>) -> Self {
119 Self {
120 name: name
121 .unwrap_or_else(|| std::any::type_name::<T>().to_string())
122 .into(),
123 type_id: TypeId::of::<T>(),
124 }
125 }
126}
127
128impl std::fmt::Display for SystemId {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 write!(f, "{}", self.name)
131 }
132}
133
134impl<T: Into<Cow<'static, str>>> From<T> for SystemId {
135 fn from(name: T) -> SystemId {
136 SystemId {
137 name: name.into(),
138 type_id: TypeId::of::<Unspecified>(),
139 }
140 }
141}
142
143pub struct System<R, Q, F>
154where
155 R: ResourceSet,
156 Q: QuerySet,
157 F: SystemFn<Resources = <R as ResourceSet>::PreparedResources, Queries = Q>,
158{
159 name: SystemId,
160 _resources: PhantomData<R>,
161 queries: AtomicRefCell<Q>,
162 run_fn: AtomicRefCell<F>,
163 archetypes: ArchetypeAccess,
164
165 access: SystemAccess,
168
169 command_buffer: FxHashMap<WorldId, AtomicRefCell<CommandBuffer>>,
171}
172
173impl<R, Q, F> Runnable for System<R, Q, F>
174where
175 R: ResourceSet,
176 Q: QuerySet,
177 F: SystemFn<Resources = <R as ResourceSet>::PreparedResources, Queries = Q>,
178{
179 fn name(&self) -> &SystemId { &self.name }
180
181 fn reads(&self) -> (&[ResourceTypeId], &[ComponentTypeId]) {
182 (
183 self.access.resources.reads(),
184 self.access.components.reads(),
185 )
186 }
187 fn writes(&self) -> (&[ResourceTypeId], &[ComponentTypeId]) {
188 (
189 self.access.resources.writes(),
190 self.access.components.writes(),
191 )
192 }
193
194 fn prepare(&mut self, world: &World) {
195 if let ArchetypeAccess::Some(bitset) = &mut self.archetypes {
196 self.queries.get_mut().filter_archetypes(world, bitset);
197 }
198 }
199
200 fn accesses_archetypes(&self) -> &ArchetypeAccess { &self.archetypes }
201
202 fn command_buffer_mut(&self, world: WorldId) -> Option<RefMut<CommandBuffer>> {
203 self.command_buffer.get(&world).map(|cmd| cmd.get_mut())
204 }
205
206 unsafe fn run_unsafe(&mut self, world: &World, resources: &Resources) {
207 let span = span!(Level::INFO, "System", system = %self.name);
208 let _guard = span.enter();
209
210 debug!("Initializing");
211 let mut resources = R::fetch_unchecked(resources);
212 let mut queries = self.queries.get_mut();
213 let mut world_shim =
215 SubWorld::new_unchecked(world, &self.access.components, &self.archetypes);
216 let cmd = self
217 .command_buffer
218 .entry(world.id())
219 .or_insert_with(|| AtomicRefCell::new(CommandBuffer::new(world)));
220
221 info!(permissions = ?self.access, archetypes = ?self.archetypes, "Running");
222 use std::ops::DerefMut;
223 let mut borrow = self.run_fn.get_mut();
224 borrow.deref_mut().run(
225 &mut cmd.get_mut(),
226 &mut world_shim,
227 &mut resources,
228 queries.deref_mut(),
230 );
231 }
232}
233
234pub trait SystemFn {
238 type Resources;
239 type Queries;
240
241 fn run(
242 &mut self,
243 commands: &mut CommandBuffer,
244 world: &mut SubWorld,
245 resources: &mut Self::Resources,
246 queries: &mut Self::Queries,
247 );
248}
249
250struct SystemFnWrapper<R, Q, F: FnMut(&mut CommandBuffer, &mut SubWorld, &mut R, &mut Q) + 'static>(
251 F,
252 PhantomData<(R, Q)>,
253);
254
255impl<F, R, Q> SystemFn for SystemFnWrapper<R, Q, F>
256where
257 F: FnMut(&mut CommandBuffer, &mut SubWorld, &mut R, &mut Q) + 'static,
258{
259 type Resources = R;
260 type Queries = Q;
261
262 fn run(
263 &mut self,
264 commands: &mut CommandBuffer,
265 world: &mut SubWorld,
266 resources: &mut Self::Resources,
267 queries: &mut Self::Queries,
268 ) {
269 (self.0)(commands, world, resources, queries);
270 }
271}
272
273pub struct SystemBuilder<Q = (), R = ()> {
308 name: SystemId,
309
310 queries: Q,
311 resources: R,
312
313 resource_access: Permissions<ResourceTypeId>,
314 component_access: Permissions<ComponentTypeId>,
315 access_all_archetypes: bool,
316}
317
318impl SystemBuilder<(), ()> {
319 pub fn new<T: Into<SystemId>>(name: T) -> Self {
324 Self {
325 name: name.into(),
326 queries: (),
327 resources: (),
328 resource_access: Permissions::default(),
329 component_access: Permissions::default(),
330 access_all_archetypes: false,
331 }
332 }
333}
334
335impl<Q, R> SystemBuilder<Q, R>
336where
337 Q: 'static + Send + ConsFlatten,
338 R: 'static + Send + ConsFlatten,
339{
340 pub fn with_query<V, F>(
346 mut self,
347 query: Query<V, F>,
348 ) -> SystemBuilder<<Q as ConsAppend<Query<V, F>>>::Output, R>
349 where
350 V: for<'a> View<'a>,
351 F: 'static + EntityFilter,
352 Q: ConsAppend<Query<V, F>>,
353 {
354 self.component_access.add(V::requires_permissions());
355
356 SystemBuilder {
357 name: self.name,
358 queries: ConsAppend::append(self.queries, query),
359 resources: self.resources,
360 resource_access: self.resource_access,
361 component_access: self.component_access,
362 access_all_archetypes: self.access_all_archetypes,
363 }
364 }
365
366 pub fn read_resource<T>(mut self) -> SystemBuilder<Q, <R as ConsAppend<Read<T>>>::Output>
371 where
372 T: 'static + Resource,
373 R: ConsAppend<Read<T>>,
374 <R as ConsAppend<Read<T>>>::Output: ConsFlatten,
375 {
376 self.resource_access.push_read(ResourceTypeId::of::<T>());
377
378 SystemBuilder {
379 name: self.name,
380 queries: self.queries,
381 resources: ConsAppend::append(self.resources, Read::<T>::default()),
382 resource_access: self.resource_access,
383 component_access: self.component_access,
384 access_all_archetypes: self.access_all_archetypes,
385 }
386 }
387
388 pub fn write_resource<T>(mut self) -> SystemBuilder<Q, <R as ConsAppend<Write<T>>>::Output>
393 where
394 T: 'static + Resource,
395 R: ConsAppend<Write<T>>,
396 <R as ConsAppend<Write<T>>>::Output: ConsFlatten,
397 {
398 self.resource_access.push(ResourceTypeId::of::<T>());
399
400 SystemBuilder {
401 name: self.name,
402 queries: self.queries,
403 resources: ConsAppend::append(self.resources, Write::<T>::default()),
404 resource_access: self.resource_access,
405 component_access: self.component_access,
406 access_all_archetypes: self.access_all_archetypes,
407 }
408 }
409
410 pub fn read_component<T>(mut self) -> Self
421 where
422 T: Component,
423 {
424 self.component_access.push_read(ComponentTypeId::of::<T>());
425 self.access_all_archetypes = true;
426
427 self
428 }
429
430 pub fn write_component<T>(mut self) -> Self
441 where
442 T: Component,
443 {
444 self.component_access.push(ComponentTypeId::of::<T>());
445 self.access_all_archetypes = true;
446
447 self
448 }
449
450 pub fn build<F>(self, run_fn: F) -> Box<dyn Schedulable>
456 where
457 <R as ConsFlatten>::Output: ResourceSet + Send + Sync,
458 <Q as ConsFlatten>::Output: QuerySet + Send + Sync,
459 <<R as ConsFlatten>::Output as ResourceSet>::PreparedResources: Send + Sync,
460 F: FnMut(
461 &mut CommandBuffer,
462 &mut SubWorld,
463 &mut <<R as ConsFlatten>::Output as ResourceSet>::PreparedResources,
464 &mut <Q as ConsFlatten>::Output,
465 ) + Send
466 + Sync
467 + 'static,
468 {
469 let run_fn = SystemFnWrapper(run_fn, PhantomData);
470 Box::new(System {
471 name: self.name,
472 run_fn: AtomicRefCell::new(run_fn),
473 _resources: PhantomData::<<R as ConsFlatten>::Output>,
474 queries: AtomicRefCell::new(self.queries.flatten()),
475 archetypes: if self.access_all_archetypes {
476 ArchetypeAccess::All
477 } else {
478 ArchetypeAccess::Some(BitSet::default())
479 },
480 access: SystemAccess {
481 resources: self.resource_access,
482 components: self.component_access,
483 tags: Permissions::default(),
484 },
485 command_buffer: FxHashMap::default(),
486 })
487 }
488
489 pub fn build_thread_local<F>(self, run_fn: F) -> Box<dyn Runnable>
494 where
495 <R as ConsFlatten>::Output: ResourceSet + Send + Sync,
496 <Q as ConsFlatten>::Output: QuerySet,
497 F: FnMut(
498 &mut CommandBuffer,
499 &mut SubWorld,
500 &mut <<R as ConsFlatten>::Output as ResourceSet>::PreparedResources,
501 &mut <Q as ConsFlatten>::Output,
502 ) + 'static,
503 {
504 let run_fn = SystemFnWrapper(run_fn, PhantomData);
505 Box::new(System {
506 name: self.name,
507 run_fn: AtomicRefCell::new(run_fn),
508 _resources: PhantomData::<<R as ConsFlatten>::Output>,
509 queries: AtomicRefCell::new(self.queries.flatten()),
510 archetypes: if self.access_all_archetypes {
511 ArchetypeAccess::All
512 } else {
513 ArchetypeAccess::Some(BitSet::default())
514 },
515 access: SystemAccess {
516 resources: self.resource_access,
517 components: self.component_access,
518 tags: Permissions::default(),
519 },
520 command_buffer: FxHashMap::default(),
521 })
522 }
523}
524
525#[cfg(test)]
526mod tests {
527 use super::*;
528 use crate::schedule::*;
529 use legion_core::prelude::*;
530 use std::collections::HashMap;
531 use std::sync::{Arc, Mutex};
532
533 #[derive(Clone, Copy, Debug, PartialEq)]
534 struct Pos(f32, f32, f32);
535 #[derive(Clone, Copy, Debug, PartialEq)]
536 struct Vel(f32, f32, f32);
537
538 #[derive(Default)]
539 struct TestResource(pub i32);
540 #[derive(Default)]
541 struct TestResourceTwo(pub i32);
542 #[derive(Default)]
543 struct TestResourceThree(pub i32);
544 #[derive(Default)]
545 struct TestResourceFour(pub i32);
546
547 #[derive(Clone, Copy, Debug, PartialEq)]
548 struct TestComp(f32, f32, f32);
549 #[derive(Clone, Copy, Debug, PartialEq)]
550 struct TestCompTwo(f32, f32, f32);
551 #[derive(Clone, Copy, Debug, PartialEq)]
552 struct TestCompThree(f32, f32, f32);
553
554 #[test]
555 fn builder_schedule_execute() {
556 let _ = tracing_subscriber::fmt::try_init();
557
558 let universe = Universe::new();
559 let mut world = universe.create_world();
560
561 let mut resources = Resources::default();
562 resources.insert(TestResource(123));
563 resources.insert(TestResourceTwo(123));
564
565 let components = vec![
566 (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
567 (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
568 ];
569
570 let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
571
572 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
573 if let Some((pos, rot)) = components.get(i) {
574 expected.insert(*e, (*pos, *rot));
575 }
576 }
577
578 #[derive(Debug, Eq, PartialEq)]
579 pub enum TestSystems {
580 TestSystemOne,
581 TestSystemTwo,
582 TestSystemThree,
583 TestSystemFour,
584 }
585
586 let runs = Arc::new(Mutex::new(Vec::new()));
587
588 let system_one_runs = runs.clone();
589 let system_one = SystemBuilder::<()>::new("TestSystem1")
590 .read_resource::<TestResource>()
591 .with_query(Read::<Pos>::query())
592 .with_query(Write::<Vel>::query())
593 .build(move |_commands, _world, _resource, _queries| {
594 tracing::trace!("system_one");
595 system_one_runs
596 .lock()
597 .unwrap()
598 .push(TestSystems::TestSystemOne);
599 });
600
601 let system_two_runs = runs.clone();
602 let system_two = SystemBuilder::<()>::new("TestSystem2")
603 .write_resource::<TestResourceTwo>()
604 .with_query(Read::<Vel>::query())
605 .build(move |_commands, _world, _resource, _queries| {
606 tracing::trace!("system_two");
607 system_two_runs
608 .lock()
609 .unwrap()
610 .push(TestSystems::TestSystemTwo);
611 });
612
613 let system_three_runs = runs.clone();
614 let system_three = SystemBuilder::<()>::new("TestSystem3")
615 .read_resource::<TestResourceTwo>()
616 .with_query(Read::<Vel>::query())
617 .build(move |_commands, _world, _resource, _queries| {
618 tracing::trace!("system_three");
619 system_three_runs
620 .lock()
621 .unwrap()
622 .push(TestSystems::TestSystemThree);
623 });
624 let system_four_runs = runs.clone();
625 let system_four = SystemBuilder::<()>::new("TestSystem4")
626 .write_resource::<TestResourceTwo>()
627 .with_query(Read::<Vel>::query())
628 .build(move |_commands, _world, _resource, _queries| {
629 tracing::trace!("system_four");
630 system_four_runs
631 .lock()
632 .unwrap()
633 .push(TestSystems::TestSystemFour);
634 });
635
636 let order = vec![
637 TestSystems::TestSystemOne,
638 TestSystems::TestSystemTwo,
639 TestSystems::TestSystemThree,
640 TestSystems::TestSystemFour,
641 ];
642
643 let systems = vec![system_one, system_two, system_three, system_four];
644
645 let mut executor = Executor::new(systems);
646 executor.execute(&mut world, &mut resources);
647
648 assert_eq!(*(runs.lock().unwrap()), order);
649 }
650
651 #[test]
652 fn builder_create_and_execute() {
653 let _ = tracing_subscriber::fmt::try_init();
654
655 let universe = Universe::new();
656 let mut world = universe.create_world();
657
658 let mut resources = Resources::default();
659 resources.insert(TestResource(123));
660
661 let components = vec![
662 (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
663 (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
664 ];
665
666 let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
667
668 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
669 if let Some((pos, rot)) = components.get(i) {
670 expected.insert(*e, (*pos, *rot));
671 }
672 }
673
674 let mut system = SystemBuilder::<()>::new("TestSystem")
675 .read_resource::<TestResource>()
676 .with_query(Read::<Pos>::query())
677 .with_query(Read::<Vel>::query())
678 .build(move |_commands, world, resource, queries| {
679 assert_eq!(resource.0, 123);
680 let mut count = 0;
681 {
682 for (entity, pos) in queries.0.iter_entities(world) {
683 assert_eq!(expected.get(&entity).unwrap().0, *pos);
684 count += 1;
685 }
686 }
687
688 assert_eq!(components.len(), count);
689 });
690 system.prepare(&world);
691 system.run(&mut world, &mut resources);
692 }
693
694 #[test]
695 fn fnmut_stateful_system_test() {
696 let _ = tracing_subscriber::fmt::try_init();
697
698 let universe = Universe::new();
699 let mut world = universe.create_world();
700
701 let mut resources = Resources::default();
702 resources.insert(TestResource(123));
703
704 let components = vec![
705 (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
706 (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
707 ];
708
709 let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
710
711 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
712 if let Some((pos, rot)) = components.get(i) {
713 expected.insert(*e, (*pos, *rot));
714 }
715 }
716
717 let mut system = SystemBuilder::<()>::new("TestSystem")
718 .read_resource::<TestResource>()
719 .with_query(Read::<Pos>::query())
720 .with_query(Read::<Vel>::query())
721 .build(move |_, _, _, _| {});
722
723 system.prepare(&world);
724 system.run(&mut world, &mut resources);
725 }
726
727 #[test]
728 fn system_mutate_archetype() {
729 let _ = tracing_subscriber::fmt::try_init();
730
731 let universe = Universe::new();
732 let mut world = universe.create_world();
733 let mut resources = Resources::default();
734
735 #[derive(Default, Clone, Copy)]
736 pub struct Balls(u32);
737
738 let components = vec![
739 (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
740 (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
741 ];
742
743 let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
744
745 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
746 if let Some((pos, rot)) = components.get(i) {
747 expected.insert(*e, (*pos, *rot));
748 }
749 }
750
751 let expected_copy = expected.clone();
752 let mut system = SystemBuilder::<()>::new("TestSystem")
753 .with_query(<(Read<Pos>, Read<Vel>)>::query())
754 .build(move |_, world, _, query| {
755 let mut count = 0;
756 {
757 for (entity, (pos, vel)) in query.iter_entities(world) {
758 assert_eq!(expected_copy.get(&entity).unwrap().0, *pos);
759 assert_eq!(expected_copy.get(&entity).unwrap().1, *vel);
760 count += 1;
761 }
762 }
763
764 assert_eq!(components.len(), count);
765 });
766
767 system.prepare(&world);
768 system.run(&mut world, &mut resources);
769
770 world
771 .add_component(*(expected.keys().nth(0).unwrap()), Balls::default())
772 .unwrap();
773
774 system.prepare(&world);
775 system.run(&mut world, &mut resources);
776 }
777
778 #[test]
779 fn system_mutate_archetype_buffer() {
780 let _ = tracing_subscriber::fmt::try_init();
781
782 let universe = Universe::new();
783 let mut world = universe.create_world();
784 let mut resources = Resources::default();
785
786 #[derive(Default, Clone, Copy)]
787 pub struct Balls(u32);
788
789 let components = (0..30000)
790 .map(|_| (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)))
791 .collect::<Vec<_>>();
792
793 let mut expected = HashMap::<Entity, (Pos, Vel)>::new();
794
795 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
796 if let Some((pos, rot)) = components.get(i) {
797 expected.insert(*e, (*pos, *rot));
798 }
799 }
800
801 let expected_copy = expected.clone();
802 let mut system = SystemBuilder::<()>::new("TestSystem")
803 .with_query(<(Read<Pos>, Read<Vel>)>::query())
804 .build(move |command_buffer, world, _, query| {
805 let mut count = 0;
806 {
807 for (entity, (pos, vel)) in query.iter_entities(world) {
808 assert_eq!(expected_copy.get(&entity).unwrap().0, *pos);
809 assert_eq!(expected_copy.get(&entity).unwrap().1, *vel);
810 count += 1;
811
812 command_buffer.add_component(entity, Balls::default());
813 }
814 }
815
816 assert_eq!(components.len(), count);
817 });
818
819 system.prepare(&world);
820 system.run(&mut world, &mut resources);
821
822 system
823 .command_buffer_mut(world.id())
824 .unwrap()
825 .write(&mut world);
826
827 system.prepare(&world);
828 system.run(&mut world, &mut resources);
829 }
830
831 #[test]
832 #[cfg(feature = "par-schedule")]
833 fn par_res_write() {
834 use std::sync::atomic::{AtomicUsize, Ordering};
835 let _ = tracing_subscriber::fmt::try_init();
836
837 #[derive(Default)]
838 struct AtomicRes(AtomicRefCell<AtomicUsize>);
839
840 let universe = Universe::new();
841 let mut world = universe.create_world();
842
843 let mut resources = Resources::default();
844 resources.insert(AtomicRes::default());
845
846 let system1 = SystemBuilder::<()>::new("TestSystem1")
847 .write_resource::<AtomicRes>()
848 .with_query(Read::<Pos>::query())
849 .with_query(Read::<Vel>::query())
850 .build(move |_, _, resource, _| {
851 resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
852 });
853
854 let system2 = SystemBuilder::<()>::new("TestSystem2")
855 .write_resource::<AtomicRes>()
856 .with_query(Read::<Pos>::query())
857 .with_query(Read::<Vel>::query())
858 .build(move |_, _, resource, _| {
859 resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
860 });
861
862 let system3 = SystemBuilder::<()>::new("TestSystem3")
863 .write_resource::<AtomicRes>()
864 .with_query(Read::<Pos>::query())
865 .with_query(Read::<Vel>::query())
866 .build(move |_, _, resource, _| {
867 resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
868 });
869
870 let pool = rayon::ThreadPoolBuilder::new()
871 .num_threads(8)
872 .build()
873 .unwrap();
874
875 tracing::debug!(
876 reads = ?system1.reads(),
877 writes = ?system1.writes(),
878 "System access"
879 );
880
881 let systems = vec![system1, system2, system3];
882 let mut executor = Executor::new(systems);
883 pool.install(|| {
884 for _ in 0..1000 {
885 executor.execute(&mut world, &mut resources);
886 }
887 });
888
889 assert_eq!(
890 resources
891 .get::<AtomicRes>()
892 .unwrap()
893 .0
894 .get()
895 .load(Ordering::SeqCst),
896 3 * 1000,
897 );
898 }
899
900 #[test]
901 #[cfg(feature = "par-schedule")]
902 fn par_res_readwrite() {
903 use std::sync::atomic::{AtomicUsize, Ordering};
904 let _ = tracing_subscriber::fmt::try_init();
905
906 #[derive(Default)]
907 struct AtomicRes(AtomicRefCell<AtomicUsize>);
908
909 let universe = Universe::new();
910 let mut world = universe.create_world();
911
912 let mut resources = Resources::default();
913 resources.insert(AtomicRes::default());
914
915 let system1 = SystemBuilder::<()>::new("TestSystem1")
916 .read_resource::<AtomicRes>()
917 .with_query(Read::<Pos>::query())
918 .with_query(Read::<Vel>::query())
919 .build(move |_, _, resource, _| {
920 resource.0.get().fetch_add(1, Ordering::SeqCst);
921 });
922
923 let system2 = SystemBuilder::<()>::new("TestSystem2")
924 .write_resource::<AtomicRes>()
925 .with_query(Read::<Pos>::query())
926 .with_query(Read::<Vel>::query())
927 .build(move |_, _, resource, _| {
928 resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
929 });
930
931 let system3 = SystemBuilder::<()>::new("TestSystem3")
932 .write_resource::<AtomicRes>()
933 .with_query(Read::<Pos>::query())
934 .with_query(Read::<Vel>::query())
935 .build(move |_, _, resource, _| {
936 resource.0.get_mut().fetch_add(1, Ordering::SeqCst);
937 });
938
939 let pool = rayon::ThreadPoolBuilder::new()
940 .num_threads(8)
941 .build()
942 .unwrap();
943
944 tracing::debug!(
945 reads = ?system1.reads(),
946 writes = ?system1.writes(),
947 "System access"
948 );
949
950 let systems = vec![system1, system2, system3];
951 let mut executor = Executor::new(systems);
952 pool.install(|| {
953 for _ in 0..1000 {
954 executor.execute(&mut world, &mut resources);
955 }
956 });
957 }
958
959 #[test]
960 #[cfg(feature = "par-schedule")]
961 #[allow(clippy::float_cmp)]
962 fn par_comp_readwrite() {
963 let _ = tracing_subscriber::fmt::try_init();
964
965 let universe = Universe::new();
966 let mut world = universe.create_world();
967
968 #[derive(Clone, Copy, Debug, PartialEq)]
969 struct Comp1(f32, f32, f32);
970 #[derive(Clone, Copy, Debug, PartialEq)]
971 struct Comp2(f32, f32, f32);
972
973 let components = vec![
974 (Comp1(69., 69., 69.), Comp2(69., 69., 69.)),
975 (Comp1(69., 69., 69.), Comp2(69., 69., 69.)),
976 ];
977
978 let mut expected = HashMap::<Entity, (Comp1, Comp2)>::new();
979
980 for (i, e) in world.insert((), components.clone()).iter().enumerate() {
981 if let Some((pos, rot)) = components.get(i) {
982 expected.insert(*e, (*pos, *rot));
983 }
984 }
985
986 let system1 = SystemBuilder::<()>::new("TestSystem1")
987 .with_query(<(Read<Comp1>, Read<Comp2>)>::query())
988 .build(move |_, world, _, query| {
989 query.iter(world).for_each(|(one, two)| {
990 assert_eq!(one.0, 69.);
991 assert_eq!(one.1, 69.);
992 assert_eq!(one.2, 69.);
993
994 assert_eq!(two.0, 69.);
995 assert_eq!(two.1, 69.);
996 assert_eq!(two.2, 69.);
997 });
998 });
999
1000 let system2 = SystemBuilder::<()>::new("TestSystem2")
1001 .with_query(<(Write<Comp1>, Read<Comp2>)>::query())
1002 .build(move |_, world, _, query| {
1003 query.iter_mut(world).for_each(|(mut one, two)| {
1004 one.0 = 456.;
1005 one.1 = 456.;
1006 one.2 = 456.;
1007
1008 assert_eq!(two.0, 69.);
1009 assert_eq!(two.1, 69.);
1010 assert_eq!(two.2, 69.);
1011 });
1012 });
1013
1014 let system3 = SystemBuilder::<()>::new("TestSystem3")
1015 .with_query(<(Write<Comp1>, Write<Comp2>)>::query())
1016 .build(move |_, world, _, query| {
1017 query.iter_mut(world).for_each(|(mut one, mut two)| {
1018 assert_eq!(one.0, 456.);
1019 assert_eq!(one.1, 456.);
1020 assert_eq!(one.2, 456.);
1021
1022 assert_eq!(two.0, 69.);
1023 assert_eq!(two.1, 69.);
1024 assert_eq!(two.2, 69.);
1025
1026 one.0 = 789.;
1027 one.1 = 789.;
1028 one.2 = 789.;
1029
1030 two.0 = 789.;
1031 two.1 = 789.;
1032 two.2 = 789.;
1033 });
1034 });
1035
1036 let system4 = SystemBuilder::<()>::new("TestSystem4")
1037 .with_query(<(Read<Comp1>, Read<Comp2>)>::query())
1038 .build(move |_, world, _, query| {
1039 query.iter(world).for_each(|(one, two)| {
1040 assert_eq!(one.0, 789.);
1041 assert_eq!(one.1, 789.);
1042 assert_eq!(one.2, 789.);
1043
1044 assert_eq!(two.0, 789.);
1045 assert_eq!(two.1, 789.);
1046 assert_eq!(two.2, 789.);
1047 });
1048 });
1049
1050 let system5 = SystemBuilder::<()>::new("TestSystem5")
1051 .with_query(<(Write<Comp1>, Write<Comp2>)>::query())
1052 .build(move |_, world, _, query| {
1053 query.iter_mut(world).for_each(|(mut one, mut two)| {
1054 assert_eq!(one.0, 789.);
1055 assert_eq!(one.1, 789.);
1056 assert_eq!(one.2, 789.);
1057
1058 assert_eq!(two.0, 789.);
1059 assert_eq!(two.1, 789.);
1060 assert_eq!(two.2, 789.);
1061
1062 one.0 = 69.;
1063 one.1 = 69.;
1064 one.2 = 69.;
1065
1066 two.0 = 69.;
1067 two.1 = 69.;
1068 two.2 = 69.;
1069 });
1070 });
1071
1072 let pool = rayon::ThreadPoolBuilder::new()
1073 .num_threads(8)
1074 .build()
1075 .unwrap();
1076
1077 tracing::debug!(
1078 reads = ?system1.reads(),
1079 writes = ?system1.writes(),
1080 "System access"
1081 );
1082
1083 let systems = vec![system1, system2, system3, system4, system5];
1084 let mut executor = Executor::new(systems);
1085 pool.install(|| {
1086 for _ in 0..1000 {
1087 executor.execute(&mut world, &mut Resources::default());
1088 }
1089 });
1090 }
1091
1092 #[test]
1093 fn split_world() {
1094 let mut world = World::new();
1095
1096 let system = SystemBuilder::new("split worlds")
1097 .with_query(Write::<usize>::query())
1098 .with_query(Write::<bool>::query())
1099 .build(|_, world, _, (query_a, query_b)| {
1100 let (mut left, mut right) = world.split_for_query(&query_a);
1101 for _ in query_a.iter_mut(&mut left) {
1102 let _ = query_b.iter_mut(&mut right);
1103 }
1104 });
1105
1106 let mut schedule = Schedule::builder().add_system(system).build();
1107 schedule.execute(&mut world, &mut Resources::default());
1108 }
1109
1110 #[test]
1111 fn split_world2() {
1112 let system = SystemBuilder::new("system")
1113 .with_query(<(Read<usize>, Write<isize>)>::query())
1114 .write_component::<bool>()
1115 .build_thread_local(move |_, world, _, query| {
1116 let (_, mut world) = world.split_for_query(&query);
1117 let (_, _) = world.split::<Write<bool>>();
1118 });
1119
1120 let mut schedule = Schedule::builder().add_thread_local(system).build();
1121
1122 let mut world = World::new();
1123 schedule.execute(&mut world, &mut Resources::default());
1124 }
1125
1126 #[test]
1127 fn overlapped_reads() {
1128 let _ = tracing_subscriber::fmt::try_init();
1129
1130 #[derive(Debug)]
1131 struct Money(f64);
1132 #[derive(Debug)]
1133 struct Health(f64);
1134 struct Food(f64);
1135
1136 let universe = Universe::new();
1137 let mut world = universe.create_world();
1138
1139 world.insert((), vec![(Money(5.0), Food(5.0))]);
1140
1141 world.insert(
1142 (),
1143 vec![
1144 (Money(4.0), Health(3.0)),
1145 (Money(4.0), Health(3.0)),
1146 (Money(4.0), Health(3.0)),
1147 ],
1148 );
1149
1150 let show_me_the_money = SystemBuilder::new("money_show")
1151 .with_query(<(Read<Money>, Read<Food>)>::query())
1152 .build(|_, world, _, query| {
1153 for (money, _food) in query.iter(world) {
1154 info!("Look at my money {:?}", money);
1155 }
1156 });
1157
1158 let health_conscious = SystemBuilder::new("healthy")
1159 .with_query(<(Read<Money>, Read<Health>)>::query())
1160 .build(|_, world, _, query| {
1161 for (_money, health) in query.iter(world) {
1162 info!("So healthy {:?}", health);
1163 }
1164 });
1165
1166 let mut schedule = Schedule::builder()
1167 .add_system(show_me_the_money)
1168 .flush()
1169 .add_system(health_conscious)
1170 .flush()
1171 .build();
1172
1173 let mut resources = Resources::default();
1174 schedule.execute(&mut world, &mut resources);
1175 }
1176}