entity_data/
system.rs

1pub(crate) mod component;
2
3use crate::entity::ArchetypeId;
4use crate::system::component::{
5    CompMutability, GenericComponentGlobalAccess, GlobalComponentAccess, GlobalComponentAccessMut,
6};
7use crate::{Component, EntityStorage, HashMap};
8use std::any::TypeId;
9use std::cell::{RefCell, UnsafeCell};
10use std::collections::hash_map;
11use std::pin::Pin;
12use std::vec;
13
14pub trait SystemHandler: Send + Sync {
15    fn run(&mut self, data: SystemAccess);
16}
17
18impl<F: FnMut(SystemAccess) + Send + Sync> SystemHandler for F {
19    fn run(&mut self, data: SystemAccess) {
20        self(data);
21    }
22}
23
24/// A system context.
25pub struct System<'a> {
26    handler: Box<&'a mut dyn SystemHandler>,
27    components: HashMap<TypeId, CompMutability>,
28}
29
30impl<'a> System<'a> {
31    /// Creates a system with data handler.
32    pub fn new(handler: &'a mut impl SystemHandler) -> Self {
33        Self {
34            handler: Box::new(handler),
35            components: Default::default(),
36        }
37    }
38
39    /// Makes component accessible from the system.
40    pub fn with<C: Component>(mut self) -> Self {
41        self.components.insert(TypeId::of::<C>(), false);
42        self
43    }
44
45    /// Makes component mutably accessible from the system.
46    pub fn with_mut<C: Component>(mut self) -> Self {
47        self.components.insert(TypeId::of::<C>(), true);
48        self
49    }
50}
51
52/// Represents all available components to a system.
53pub struct SystemAccess<'a> {
54    storage: &'a EntityStorage,
55    /// Whether new components can be added to `global_components` from the `storage`.
56    /// Safety: `storage` must be uniquely borrowed.
57    new_components_allowed: bool,
58    /// Maps component `TypeId`s to respective archetypes which contain this component.
59    global_components:
60        UnsafeCell<HashMap<TypeId, Pin<Box<RefCell<GenericComponentGlobalAccess<'a>>>>>>,
61}
62
63impl<'a> SystemAccess<'a> {
64    fn get_component(&self, ty: TypeId) -> Option<&RefCell<GenericComponentGlobalAccess<'a>>> {
65        let global_components = unsafe { &mut *self.global_components.get() };
66
67        match global_components.entry(ty) {
68            hash_map::Entry::Occupied(e) => Some(e.into_mut()),
69            hash_map::Entry::Vacant(e) => {
70                if !self.new_components_allowed {
71                    return None;
72                }
73
74                // Modifying the hashmap is safe because referenced values are wrapped in Pin<Box<>>.
75                let new = RefCell::new(GenericComponentGlobalAccess {
76                    filtered_archetype_ids: self
77                        .storage
78                        .component_to_archetypes_map
79                        .get(&ty)
80                        .unwrap_or(&vec![])
81                        .clone(),
82                    all_archetypes: &self.storage.archetypes,
83                    // Safety: mutability is allowed because `self.new_components_allowed` is true,
84                    // therefore `self.storage` must be uniquely borrowed.
85                    mutable: true,
86                });
87
88                Some(e.insert(Box::pin(new)))
89            }
90        }
91    }
92
93    /// Returns `ArchetypeId` corresponding to the specified `TypeId`.
94    pub fn type_id_to_archetype_id(&self, type_id: &TypeId) -> Option<ArchetypeId> {
95        self.storage.type_id_to_archetype_id(type_id)
96    }
97
98    /// Borrows the component.
99    /// Panics if the component is mutably borrowed or not available to this system.
100    pub fn component<C: Component>(&self) -> GlobalComponentAccess<'_, C> {
101        let ty = TypeId::of::<C>();
102
103        // This is safe because the mutable reference gets dropped afterwards.
104        let generic = self.get_component(ty).expect("Component must be available");
105
106        GlobalComponentAccess {
107            generic: generic
108                .try_borrow()
109                .expect("Component must not be mutably borrowed"),
110            _ty: Default::default(),
111        }
112    }
113
114    /// Mutably borrows the component.
115    /// Panics if the component is already borrowed or not available to this system.
116    pub fn component_mut<'b, C: Component>(&'b self) -> GlobalComponentAccessMut<'a, 'b, C> {
117        let generic = self
118            .get_component(TypeId::of::<C>())
119            .expect("Component must be available");
120
121        let guard = generic
122            .try_borrow_mut()
123            .expect("Component must not be borrowed");
124
125        if !guard.mutable {
126            panic!("Component is not allowed to be mutated");
127        }
128
129        GlobalComponentAccessMut {
130            generic: guard,
131            _ty: Default::default(),
132        }
133    }
134}
135
136#[cfg(feature = "rayon")]
137mod parallel {
138    use crate::system::component::CompMutability;
139    use crate::{HashMap, System};
140    use std::any::TypeId;
141    use std::collections::hash_map;
142    use std::mem;
143
144    #[derive(Debug)]
145    pub struct ParallelSystems {
146        pub systems: Vec<usize>,
147        pub all_components: HashMap<TypeId, CompMutability>,
148    }
149
150    impl ParallelSystems {
151        fn take(&mut self) -> Self {
152            Self {
153                systems: mem::replace(&mut self.systems, vec![]),
154                all_components: mem::replace(&mut self.all_components, Default::default()),
155            }
156        }
157
158        fn append(&mut self, other: Self) {
159            self.systems.extend(other.systems);
160
161            self.all_components.reserve(other.all_components.len());
162
163            for (ty, b_mutable) in &other.all_components {
164                match self.all_components.entry(*ty) {
165                    hash_map::Entry::Occupied(mut e) => {
166                        let a_mutable = e.get_mut();
167                        if !*a_mutable {
168                            e.insert(*b_mutable);
169                        }
170                    }
171                    hash_map::Entry::Vacant(e) => {
172                        e.insert(*b_mutable);
173                    }
174                }
175            }
176        }
177    }
178
179    pub fn systems_do_conflict(
180        a_components: &HashMap<TypeId, CompMutability>,
181        b_components: &HashMap<TypeId, CompMutability>,
182    ) -> bool {
183        a_components.iter().any(|(ty, mutable_a)| {
184            b_components
185                .get(ty)
186                .map_or(false, |mutable_b| *mutable_a || *mutable_b)
187        })
188    }
189
190    /// Partitions systems in parallel in such a way as to maximally utilize CPU.
191    pub fn partition_parallel_systems(systems: &[System]) -> Vec<ParallelSystems> {
192        // Component conflict resolution example:
193        // Components (*) in rows are mutated concurrently.
194        //
195        // Initial state:
196        //
197        //      C0 C1 C2 C3 C4
198        //  S0  -  *  -  -  -
199        //  S1  -  -  *  *  -
200        //  S2  -  *  -  -  *
201        //  S3  *  -  -  *  -
202        //  S4  *  *  -  -  *
203        //
204        //  potential permutations:
205        //  (src) S0   S1   S2   S3   S4  \/
206        // ------------------------------
207        //  (dst) S1   S0   S1   S0   S1
208        //        S3   S2   S3   S2
209        //             S4
210        //
211        // 1. First step result:
212        //
213        //      C0 C1 C2 C3 C4
214        //  S0       -  *  -  -  -
215        //  (S1,S4)  *  *  *  *  *
216        //  S2       -  *  -  -  *
217        //  S3       *  -  -  *  -
218        //
219        //  potential permutations:
220        //  S0   S1   S2   S3   S4
221        // ------------------------
222        //  S3        S3   S0
223        //                 S2
224        //
225        // 2. Second step result (conflict resolution complete):
226        //
227        //      C0 C1 C2 C3 C4
228        //  (S1,S4)  *  *  *  *  *
229        //  S2       -  *  -  -  *
230        //  (S3,S0)  *  *  -  *  -
231        //
232        //  no potential permutations left:
233        //  S1   S2   S3   S4   S5
234        // ------------------------
235
236        fn extract_potential_moves(systems: &[ParallelSystems], moves: &mut [Vec<usize>]) {
237            for ((i, sys), moves) in systems.iter().enumerate().zip(moves) {
238                if sys.systems.is_empty() {
239                    continue;
240                }
241
242                for (j, sys2) in systems.iter().enumerate() {
243                    if j == i || sys2.systems.is_empty() {
244                        continue;
245                    }
246
247                    let conflicting =
248                        systems_do_conflict(&sys.all_components, &sys2.all_components);
249
250                    if !conflicting {
251                        moves.push(j);
252                    }
253                }
254            }
255        }
256
257        let mut parallel_runs: Vec<_> = systems
258            .iter()
259            .enumerate()
260            .map(|(i, sys)| ParallelSystems {
261                systems: vec![i],
262                all_components: sys.components.clone(),
263            })
264            .collect();
265
266        let mut potential_moves = vec![Vec::<usize>::with_capacity(systems.len()); systems.len()];
267
268        loop {
269            for v in &mut potential_moves {
270                v.clear();
271            }
272            extract_potential_moves(&parallel_runs, &mut potential_moves);
273
274            if potential_moves.iter().all(|v| v.is_empty()) {
275                break;
276            }
277
278            let (min_i, min_moves) = potential_moves
279                .iter_mut()
280                .enumerate()
281                .filter(|(_, v)| !v.is_empty())
282                .min_by_key(|(_, v)| v.len())
283                .unwrap();
284
285            let mv_from = min_i;
286            let mv_to = min_moves.pop().unwrap();
287
288            let mv_systems = parallel_runs[mv_from].take();
289            parallel_runs[mv_to].append(mv_systems);
290        }
291
292        parallel_runs.retain(|v| !v.systems.is_empty());
293
294        parallel_runs
295    }
296}
297
298impl EntityStorage {
299    /// Safety: mutable borrows must be unique.
300    pub(crate) unsafe fn global_component_by_id(
301        &self,
302        ty: TypeId,
303        mutable: bool,
304    ) -> GenericComponentGlobalAccess<'_> {
305        let filtered_archetype_ids: Vec<usize> = self
306            .component_to_archetypes_map
307            .get(&ty)
308            .map_or(vec![], |v| v.clone());
309
310        GenericComponentGlobalAccess {
311            filtered_archetype_ids,
312            all_archetypes: &self.archetypes,
313            mutable,
314        }
315    }
316
317    /// Safety: the same component aren't allowed to be mutated on different threads simultaneously.
318    unsafe fn get_system_data(
319        &self,
320        components: &HashMap<TypeId, CompMutability>,
321    ) -> SystemAccess<'_> {
322        unsafe {
323            let global_components = components
324                .iter()
325                .map(|(&ty, mutable)| {
326                    (
327                        ty,
328                        Box::pin(RefCell::new(self.global_component_by_id(ty, *mutable))),
329                    )
330                })
331                .collect();
332
333            SystemAccess {
334                storage: self,
335                // `self` is not uniquely borrowed, so restrict access only to specified components.
336                new_components_allowed: false,
337                global_components: UnsafeCell::new(global_components),
338            }
339        }
340    }
341
342    /// Provides access to all components. Allows simultaneous mutable access to multiple components.
343    pub fn access(&mut self) -> SystemAccess<'_> {
344        SystemAccess {
345            storage: self,
346            // Safety: `self` is &mut, therefore this is valid.
347            new_components_allowed: true,
348            global_components: UnsafeCell::new(HashMap::with_capacity(
349                self.component_to_archetypes_map.len(),
350            )),
351        }
352    }
353
354    /// Dispatches systems sequentially. For parallel execution,
355    /// see [dispatch_par](Self::dispatch_par) (requires `rayon` feature).
356    ///
357    /// # Example
358    /// ```
359    /// use entity_data::{EntityId, EntityStorage, System, SystemHandler};
360    /// use entity_data::system::SystemAccess;
361    /// use macros::Archetype;
362    ///
363    /// #[derive(Default, Debug)]
364    /// struct Position {
365    ///     x: f32,
366    ///     y: f32,
367    /// }
368    ///
369    /// #[derive(Archetype)]
370    /// struct Dog {
371    ///     pos: Position,
372    /// }
373    ///
374    /// let mut storage = EntityStorage::new();
375    /// let dog0 = storage.add(Dog { pos: Default::default() });
376    /// let dog1 = storage.add(Dog { pos: Position { x: 3.0, y: 5.0 } });
377    ///
378    /// struct PositionsPrintSystem {
379    ///     to_process: Vec<EntityId>,
380    /// }
381    ///
382    /// impl SystemHandler for PositionsPrintSystem {
383    ///     fn run(&mut self, data: SystemAccess) {
384    ///         let positions = data.component::<Position>();
385    ///         for entity in &self.to_process {
386    ///             println!("{:?}", positions.get(entity));
387    ///         }
388    ///     }
389    /// }
390    ///
391    /// let mut sys = PositionsPrintSystem {
392    ///     to_process: vec![dog0, dog1]
393    /// };
394    /// storage.dispatch(&mut [System::new(&mut sys).with::<Position>()]);
395    /// ```
396    pub fn dispatch<'a>(&self, mut systems: impl AsMut<[System<'a>]>) {
397        for sys in systems.as_mut() {
398            let data = unsafe { self.get_system_data(&sys.components) };
399            sys.handler.run(data);
400        }
401    }
402
403    /// Dispatches systems in parallel if possible. Two systems won't execute in parallel if they
404    /// access the same component and one of the systems mutates this component.
405    #[cfg(feature = "rayon")]
406    pub fn dispatch_par<'a>(&self, mut systems: impl AsMut<[System<'a>]>) {
407        let systems = systems.as_mut();
408
409        if systems.is_empty() {
410            return;
411        }
412
413        let parallel_runs = parallel::partition_parallel_systems(systems);
414
415        rayon::scope(|s| {
416            for mut run in parallel_runs {
417                for sys_i in &mut run.systems {
418                    let system = &systems[*sys_i];
419
420                    // The cast from *const to *mut is safe because the slice itself is &mut.
421                    let system_mut: &mut System = unsafe { &mut *(system as *const _ as *mut _) };
422
423                    s.spawn(|_| {
424                        let data = unsafe { self.get_system_data(&system.components) };
425                        system_mut.handler.run(data);
426                    });
427                }
428            }
429        });
430    }
431}
432
433#[cfg(feature = "rayon")]
434#[test]
435fn test_optimization() {
436    #[derive(Copy, Clone)]
437    struct TestSystem {}
438
439    impl SystemHandler for TestSystem {
440        fn run(&mut self, _: SystemAccess) {}
441    }
442
443    // Initial:
444    //      C0 C1 C2 C3 C4
445    //  S0  -  *  -  -  -
446    //  S1  -  -  *  *  -
447    //  S2  -  *  -  -  *
448    //  S3  *  -  -  *  -
449    //  S4  *  *  -  -  *
450
451    // Result:
452    //           C0 C1 C2 C3 C4
453    //  (S1,S4)  *  *  *  *  *
454    //  S2       -  *  -  -  *
455    //  (S3,S0)  *  *  -  *  -
456
457    let mut test_sys0 = TestSystem {};
458    let mut test_sys1 = TestSystem {};
459    let mut test_sys2 = TestSystem {};
460    let mut test_sys3 = TestSystem {};
461    let mut test_sys4 = TestSystem {};
462
463    let sys0 = System::new(&mut test_sys0).with_mut::<i16>();
464    let sys1 = System::new(&mut test_sys1)
465        .with_mut::<i32>()
466        .with_mut::<i64>();
467    let sys2 = System::new(&mut test_sys2)
468        .with_mut::<i16>()
469        .with_mut::<u64>();
470    let sys3 = System::new(&mut test_sys3)
471        .with_mut::<i8>()
472        .with_mut::<i64>();
473    let sys4 = System::new(&mut test_sys4)
474        .with_mut::<i8>()
475        .with_mut::<i16>()
476        .with_mut::<u64>();
477
478    let mut systems = [sys0, sys1, sys2, sys3, sys4];
479    let parallel_runs = parallel::partition_parallel_systems(&mut systems);
480
481    assert_eq!(systems.len(), 5);
482    assert_eq!(parallel_runs.len(), 3);
483
484    assert_eq!(
485        &parallel_runs[0].systems.iter().cloned().collect::<Vec<_>>(),
486        &[1, 4]
487    );
488    assert_eq!(
489        &parallel_runs[1].systems.iter().cloned().collect::<Vec<_>>(),
490        &[2]
491    );
492    assert_eq!(
493        &parallel_runs[2].systems.iter().cloned().collect::<Vec<_>>(),
494        &[3, 0]
495    );
496
497    for run in &parallel_runs {
498        let conflicting = run.systems.iter().enumerate().any(|(i, sys0_id)| {
499            run.systems.iter().enumerate().any(|(j, sys1_id)| {
500                if i == j {
501                    return false;
502                }
503                parallel::systems_do_conflict(
504                    &systems[*sys0_id].components,
505                    &systems[*sys1_id].components,
506                )
507            })
508        });
509
510        assert_eq!(conflicting, false);
511    }
512}
513
514#[test]
515fn test_system_data_access() {
516    use crate::EntityId;
517
518    #[derive(Clone, crate::Archetype)]
519    struct Arch {
520        comp: i16,
521    }
522
523    #[derive(Copy, Clone)]
524    struct TestSystem {
525        entity: EntityId,
526    }
527
528    impl SystemHandler for TestSystem {
529        fn run(&mut self, data: SystemAccess) {
530            let mut comp = data.component_mut::<i16>();
531
532            let e_comp = comp.get_mut(&self.entity).unwrap();
533            assert_eq!(*e_comp, 123);
534            *e_comp = 321;
535        }
536    }
537
538    let mut storage = EntityStorage::new();
539    let entity = storage.add(Arch { comp: 123 });
540
541    let mut test_sys = TestSystem { entity };
542    let sys0 = System::new(&mut test_sys).with_mut::<i16>();
543
544    storage.dispatch(&mut [sys0]);
545
546    assert_eq!(*storage.get::<i16>(&entity).unwrap(), 321);
547}