bevy_ecs/schedule/executor/
simple.rs

1#[cfg(feature = "trace")]
2use bevy_utils::tracing::info_span;
3use core::panic::AssertUnwindSafe;
4use fixedbitset::FixedBitSet;
5
6use crate::{
7    schedule::{
8        executor::is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
9    },
10    world::World,
11};
12
13use super::__rust_begin_short_backtrace;
14
15/// A variant of [`SingleThreadedExecutor`](crate::schedule::SingleThreadedExecutor) that calls
16/// [`apply_deferred`](crate::system::System::apply_deferred) immediately after running each system.
17#[derive(Default)]
18pub struct SimpleExecutor {
19    /// Systems sets whose conditions have been evaluated.
20    evaluated_sets: FixedBitSet,
21    /// Systems that have run or been skipped.
22    completed_systems: FixedBitSet,
23}
24
25impl SystemExecutor for SimpleExecutor {
26    fn kind(&self) -> ExecutorKind {
27        ExecutorKind::Simple
28    }
29
30    fn init(&mut self, schedule: &SystemSchedule) {
31        let sys_count = schedule.system_ids.len();
32        let set_count = schedule.set_ids.len();
33        self.evaluated_sets = FixedBitSet::with_capacity(set_count);
34        self.completed_systems = FixedBitSet::with_capacity(sys_count);
35    }
36
37    fn run(
38        &mut self,
39        schedule: &mut SystemSchedule,
40        world: &mut World,
41        _skip_systems: Option<&FixedBitSet>,
42    ) {
43        // If stepping is enabled, make sure we skip those systems that should
44        // not be run.
45        #[cfg(feature = "bevy_debug_stepping")]
46        if let Some(skipped_systems) = _skip_systems {
47            // mark skipped systems as completed
48            self.completed_systems |= skipped_systems;
49        }
50
51        for system_index in 0..schedule.systems.len() {
52            #[cfg(feature = "trace")]
53            let name = schedule.systems[system_index].name();
54            #[cfg(feature = "trace")]
55            let should_run_span = info_span!("check_conditions", name = &*name).entered();
56
57            let mut should_run = !self.completed_systems.contains(system_index);
58            for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
59                if self.evaluated_sets.contains(set_idx) {
60                    continue;
61                }
62
63                // evaluate system set's conditions
64                let set_conditions_met =
65                    evaluate_and_fold_conditions(&mut schedule.set_conditions[set_idx], world);
66
67                if !set_conditions_met {
68                    self.completed_systems
69                        .union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
70                }
71
72                should_run &= set_conditions_met;
73                self.evaluated_sets.insert(set_idx);
74            }
75
76            // evaluate system's conditions
77            let system_conditions_met =
78                evaluate_and_fold_conditions(&mut schedule.system_conditions[system_index], world);
79
80            should_run &= system_conditions_met;
81
82            let system = &mut schedule.systems[system_index];
83            if should_run {
84                let valid_params = system.validate_param(world);
85                should_run &= valid_params;
86            }
87
88            #[cfg(feature = "trace")]
89            should_run_span.exit();
90
91            // system has either been skipped or will run
92            self.completed_systems.insert(system_index);
93
94            if !should_run {
95                continue;
96            }
97
98            if is_apply_deferred(system) {
99                continue;
100            }
101
102            let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
103                __rust_begin_short_backtrace::run(&mut **system, world);
104            }));
105            if let Err(payload) = res {
106                eprintln!("Encountered a panic in system `{}`!", &*system.name());
107                std::panic::resume_unwind(payload);
108            }
109        }
110
111        self.evaluated_sets.clear();
112        self.completed_systems.clear();
113    }
114
115    fn set_apply_final_deferred(&mut self, _: bool) {
116        // do nothing. simple executor does not do a final sync
117    }
118}
119
120impl SimpleExecutor {
121    /// Creates a new simple executor for use in a [`Schedule`](crate::schedule::Schedule).
122    /// This calls each system in order and immediately calls [`System::apply_deferred`](crate::system::System::apply_deferred).
123    pub const fn new() -> Self {
124        Self {
125            evaluated_sets: FixedBitSet::new(),
126            completed_systems: FixedBitSet::new(),
127        }
128    }
129}
130
131fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
132    // not short-circuiting is intentional
133    #[allow(clippy::unnecessary_fold)]
134    conditions
135        .iter_mut()
136        .map(|condition| {
137            if !condition.validate_param(world) {
138                return false;
139            }
140            __rust_begin_short_backtrace::readonly_run(&mut **condition, world)
141        })
142        .fold(true, |acc, res| acc && res)
143}
144
145#[cfg(test)]
146#[test]
147fn skip_automatic_sync_points() {
148    // Schedules automatically insert apply_deferred systems, but these should
149    // not be executed as they only serve as markers and are not initialized
150    use crate::prelude::*;
151    let mut sched = Schedule::default();
152    sched.set_executor_kind(ExecutorKind::Simple);
153    sched.add_systems((|_: Commands| (), || ()).chain());
154    let mut world = World::new();
155    sched.run(&mut world);
156}