bevy_ecs/schedule/
auto_insert_apply_deferred.rs1use alloc::{boxed::Box, collections::BTreeSet, vec::Vec};
2
3use bevy_platform::{collections::HashMap, hash::FixedHasher};
4use indexmap::IndexSet;
5
6use crate::{
7 schedule::{graph::Dag, SystemKey, SystemSetKey},
8 system::{IntoSystem, System},
9 world::World,
10};
11
12use super::{
13 is_apply_deferred, ApplyDeferred, DiGraph, Direction, NodeId, ScheduleBuildError,
14 ScheduleBuildPass, ScheduleGraph,
15};
16
17#[derive(Debug, Default)]
27pub struct AutoInsertApplyDeferredPass {
28 no_sync_edges: BTreeSet<(NodeId, NodeId)>,
30 auto_sync_node_ids: HashMap<u32, SystemKey>,
31}
32
33pub struct IgnoreDeferred;
35
36impl AutoInsertApplyDeferredPass {
37 fn get_sync_point(&mut self, graph: &mut ScheduleGraph, distance: u32) -> SystemKey {
40 self.auto_sync_node_ids
41 .get(&distance)
42 .copied()
43 .unwrap_or_else(|| {
44 let key = self.add_auto_sync(graph);
45 self.auto_sync_node_ids.insert(distance, key);
46 key
47 })
48 }
49 fn add_auto_sync(&mut self, graph: &mut ScheduleGraph) -> SystemKey {
51 let key = graph
52 .systems
53 .insert(Box::new(IntoSystem::into_system(ApplyDeferred)), Vec::new());
54
55 graph.ambiguous_with_all.insert(NodeId::System(key));
58
59 key
60 }
61}
62
63impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
64 type EdgeOptions = IgnoreDeferred;
65
66 fn add_dependency(&mut self, from: NodeId, to: NodeId, options: Option<&Self::EdgeOptions>) {
67 if options.is_some() {
68 self.no_sync_edges.insert((from, to));
69 }
70 }
71
72 fn build(
73 &mut self,
74 _world: &mut World,
75 graph: &mut ScheduleGraph,
76 dependency_flattened: &mut Dag<SystemKey>,
77 ) -> Result<(), ScheduleBuildError> {
78 let mut sync_point_graph = dependency_flattened.graph().clone();
79 let (topo, flat_dependency) = dependency_flattened
80 .toposort_and_graph()
81 .map_err(ScheduleBuildError::FlatDependencySort)?;
82
83 fn set_has_conditions(graph: &ScheduleGraph, set: SystemSetKey) -> bool {
84 graph.system_sets.has_conditions(set)
85 || graph
86 .hierarchy()
87 .graph()
88 .edges_directed(NodeId::Set(set), Direction::Incoming)
89 .any(|(parent, _)| {
90 parent
91 .as_set()
92 .is_some_and(|p| set_has_conditions(graph, p))
93 })
94 }
95
96 fn system_has_conditions(graph: &ScheduleGraph, key: SystemKey) -> bool {
97 graph.systems.has_conditions(key)
98 || graph
99 .hierarchy()
100 .graph()
101 .edges_directed(NodeId::System(key), Direction::Incoming)
102 .any(|(parent, _)| {
103 parent
104 .as_set()
105 .is_some_and(|p| set_has_conditions(graph, p))
106 })
107 }
108
109 let mut system_has_conditions_cache = HashMap::<SystemKey, bool>::default();
110 let mut is_valid_explicit_sync_point = |key: SystemKey| {
111 is_apply_deferred(&graph.systems[key])
112 && !*system_has_conditions_cache
113 .entry(key)
114 .or_insert_with(|| system_has_conditions(graph, key))
115 };
116
117 let mut distances_and_pending_sync: HashMap<SystemKey, (u32, bool)> =
122 HashMap::with_capacity_and_hasher(topo.len(), Default::default());
123
124 let mut distance_to_explicit_sync_node: HashMap<u32, SystemKey> = HashMap::default();
126
127 for &key in topo {
129 let (node_distance, mut node_needs_sync) = distances_and_pending_sync
130 .get(&key)
131 .copied()
132 .unwrap_or_default();
133
134 if is_valid_explicit_sync_point(key) {
135 distance_to_explicit_sync_node.insert(node_distance, key);
140
141 node_needs_sync = false;
144 } else if !node_needs_sync {
145 node_needs_sync = graph.systems[key].has_deferred();
148 }
149
150 for target in flat_dependency.neighbors_directed(key, Direction::Outgoing) {
151 let (target_distance, target_pending_sync) =
152 distances_and_pending_sync.entry(target).or_default();
153
154 let mut edge_needs_sync = node_needs_sync;
155 if node_needs_sync
156 && !graph.systems[target].is_exclusive()
157 && self
158 .no_sync_edges
159 .contains(&(NodeId::System(key), NodeId::System(target)))
160 {
161 *target_pending_sync = true;
165 edge_needs_sync = false;
166 }
167
168 let mut weight = 0;
169 if edge_needs_sync || is_valid_explicit_sync_point(target) {
170 weight = 1;
174 }
175
176 *target_distance = (node_distance + weight).max(*target_distance);
178 }
179 }
180
181 for &key in topo {
184 let (node_distance, _) = distances_and_pending_sync
185 .get(&key)
186 .copied()
187 .unwrap_or_default();
188
189 for target in flat_dependency.neighbors_directed(key, Direction::Outgoing) {
190 let (target_distance, _) = distances_and_pending_sync
191 .get(&target)
192 .copied()
193 .unwrap_or_default();
194
195 if node_distance == target_distance {
196 continue;
198 }
199
200 if is_apply_deferred(&graph.systems[target]) {
201 continue;
204 }
205
206 let sync_point = distance_to_explicit_sync_node
207 .get(&target_distance)
208 .copied()
209 .unwrap_or_else(|| self.get_sync_point(graph, target_distance));
210
211 sync_point_graph.add_edge(key, sync_point);
212 sync_point_graph.add_edge(sync_point, target);
213
214 sync_point_graph.remove_edge(key, target);
216 }
217 }
218
219 **dependency_flattened = sync_point_graph;
220 Ok(())
221 }
222
223 fn collapse_set(
224 &mut self,
225 set: SystemSetKey,
226 systems: &IndexSet<SystemKey, FixedHasher>,
227 dependency_flattening: &DiGraph<NodeId>,
228 ) -> impl Iterator<Item = (NodeId, NodeId)> {
229 if systems.is_empty() {
230 for a in dependency_flattening.neighbors_directed(NodeId::Set(set), Direction::Incoming)
232 {
233 for b in
234 dependency_flattening.neighbors_directed(NodeId::Set(set), Direction::Outgoing)
235 {
236 if self.no_sync_edges.contains(&(a, NodeId::Set(set)))
237 && self.no_sync_edges.contains(&(NodeId::Set(set), b))
238 {
239 self.no_sync_edges.insert((a, b));
240 }
241 }
242 }
243 } else {
244 for a in dependency_flattening.neighbors_directed(NodeId::Set(set), Direction::Incoming)
245 {
246 for &sys in systems {
247 if self.no_sync_edges.contains(&(a, NodeId::Set(set))) {
248 self.no_sync_edges.insert((a, NodeId::System(sys)));
249 }
250 }
251 }
252
253 for b in dependency_flattening.neighbors_directed(NodeId::Set(set), Direction::Outgoing)
254 {
255 for &sys in systems {
256 if self.no_sync_edges.contains(&(NodeId::Set(set), b)) {
257 self.no_sync_edges.insert((NodeId::System(sys), b));
258 }
259 }
260 }
261 }
262 core::iter::empty()
263 }
264}