legion_systems/
system.rs

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/// Structure describing the resource and component access conditions of the system.
24#[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
32/// This trait is for providing abstraction across tuples of queries for populating the type
33/// information in the system closure. This trait also provides access to the underlying query
34/// information.
35pub trait QuerySet: Send + Sync {
36    /// Returns the archetypes accessed by this collection of queries. This allows for caching
37    /// effiency and granularity for system dispatching.
38    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
143/// The concrete type which contains the system closure provided by the user.  This struct should
144/// not be instantiated directly, and instead should be created using `SystemBuilder`.
145///
146/// Implements `Schedulable` which is consumable by the `StageExecutor`, executing the closure.
147///
148/// Also handles caching of archetype information in a `BitSet`, as well as maintaining the provided
149/// information about what queries this system will run and, as a result, its data access.
150///
151/// Queries are stored generically within this struct, and the `SystemQuery` types are generated
152/// on each `run` call, wrapping the world and providing the set to the user in their closure.
153pub 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    // These are stored statically instead of always iterated and created from the
166    // query types, which would make allocations every single request
167    access: SystemAccess,
168
169    // We pre-allocate a command buffer for ourself. Writes are self-draining so we never have to rellocate.
170    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 prepared_queries = queries.prepare();
214        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            //&mut prepared_queries,
229            queries.deref_mut(),
230        );
231    }
232}
233
234/// Supertrait used for defining systems. All wrapper objects for systems implement this trait.
235///
236/// This trait will generally not be used by users.
237pub 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
273// This builder uses a Cons/Hlist implemented in cons.rs to generated the static query types
274// for this system. Access types are instead stored and abstracted in the top level vec here
275// so the underlying ResourceSet type functions from the queries don't need to allocate.
276// Otherwise, this leads to excessive alloaction for every call to reads/writes
277/// The core builder of `System` types, which are systems within Legion. Systems are implemented
278/// as singular closures for a given system - providing queries which should be cached for that
279/// system, as well as resource access and other metadata.
280/// ```rust
281/// # use legion_core::prelude::*;
282/// # use legion_systems::prelude::*;
283/// # #[derive(Copy, Clone, Debug, PartialEq)]
284/// # struct Position;
285/// # #[derive(Copy, Clone, Debug, PartialEq)]
286/// # struct Velocity;
287/// # #[derive(Copy, Clone, Debug, PartialEq)]
288/// # struct Model;
289/// #[derive(Copy, Clone, Debug, PartialEq)]
290/// struct Static;
291/// #[derive(Debug)]
292/// struct TestResource {}
293///
294///  let mut system_one = SystemBuilder::<()>::new("TestSystem")
295///            .read_resource::<TestResource>()
296///            .with_query(<(Read<Position>, Tagged<Model>)>::query()
297///                         .filter(!tag::<Static>() | changed::<Position>()))
298///            .build(move |commands, world, resource, queries| {
299///               let mut count = 0;
300///                {
301///                    for (entity, pos) in queries.iter_entities_mut(&mut *world) {
302///
303///                    }
304///                }
305///            });
306/// ```
307pub 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    /// Create a new system builder to construct a new system.
320    ///
321    /// Please note, the `name` argument for this method is just for debugging and visualization
322    /// purposes and is not logically used anywhere.
323    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    /// Defines a query to provide this system for its execution. Multiple queries can be provided,
341    /// and queries are cached internally for efficiency for filtering and archetype ID handling.
342    ///
343    /// It is best practice to define your queries here, to allow for the caching to take place.
344    /// These queries are then provided to the executing closure as a tuple of queries.
345    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    /// Flag this resource type as being read by this system.
367    ///
368    /// This will inform the dispatcher to not allow any writes access to this resource while
369    /// this system is running. Parralel reads still occur during execution.
370    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    /// Flag this resource type as being written by this system.
389    ///
390    /// This will inform the dispatcher to not allow any parallel access to this resource while
391    /// this system is running.
392    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    /// This performs a soft resource block on the component for writing. The dispatcher will
411    /// generally handle dispatching read and writes on components based on archetype, allowing
412    /// for more granular access and more parallelization of systems.
413    ///
414    /// Using this method will mark the entire component as read by this system, blocking writing
415    /// systems from accessing any archetypes which contain this component for the duration of its
416    /// execution.
417    ///
418    /// This type of access with `SubWorld` is provided for cases where sparse component access
419    /// is required and searching entire query spaces for entities is inefficient.
420    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    /// This performs a exclusive resource block on the component for writing. The dispatcher will
431    /// generally handle dispatching read and writes on components based on archetype, allowing
432    /// for more granular access and more parallelization of systems.
433    ///
434    /// Using this method will mark the entire component as written by this system, blocking other
435    /// systems from accessing any archetypes which contain this component for the duration of its
436    /// execution.
437    ///
438    /// This type of access with `SubWorld` is provided for cases where sparse component access
439    /// is required and searching entire query spaces for entities is inefficient.
440    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    /// Builds a standard legion `System`. A system is considered a closure for all purposes. This
451    /// closure is `FnMut`, allowing for capture of variables for tracking state for this system.
452    /// Instead of the classic OOP architecture of a system, this lets you still maintain state
453    /// across execution of the systems while leveraging the type semantics of closures for better
454    /// ergonomics.
455    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    /// Builds a system which is not `Schedulable`, as it is not thread safe (!Send and !Sync),
490    /// but still implements all the calling infrastructure of the `Runnable` trait. This provides
491    /// a way for legion consumers to leverage the `System` construction and type-handling of
492    /// this build for thread local systems which cannot leave the main initializing thread.
493    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}