diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 408fae9b1d615..95a580c425790 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -661,6 +661,42 @@ impl ScheduleGraph { &self.conflicting_systems } + fn process_config( + &mut self, + config: NodeConfig, + collect_nodes: bool, + ) -> ProcessConfigsResult { + ProcessConfigsResult { + densely_chained: true, + nodes: collect_nodes + .then_some(T::process_config(self, config)) + .into_iter() + .collect(), + } + } + + fn apply_collective_conditions( + &mut self, + configs: &mut [NodeConfigs], + collective_conditions: Vec, + ) { + if !collective_conditions.is_empty() { + if let [config] = configs { + for condition in collective_conditions { + config.run_if_dyn(condition); + } + } else { + let set = self.create_anonymous_set(); + for config in configs.iter_mut() { + config.in_set_inner(set.intern()); + } + let mut set_config = SystemSetConfig::new(set.intern()); + set_config.conditions.extend(collective_conditions); + self.configure_set_inner(set_config).unwrap(); + } + } + } + /// Adds the config nodes to the graph. /// /// `collect_nodes` controls whether the `NodeId`s of the processed config nodes are stored in the returned [`ProcessConfigsResult`]. @@ -676,157 +712,75 @@ impl ScheduleGraph { collect_nodes: bool, ) -> ProcessConfigsResult { match configs { - NodeConfigs::NodeConfig(config) => { - let node_id = T::process_config(self, config); - if collect_nodes { - ProcessConfigsResult { - densely_chained: true, - nodes: vec![node_id], - } - } else { - ProcessConfigsResult { - densely_chained: true, - nodes: Vec::new(), - } - } - } + NodeConfigs::NodeConfig(config) => self.process_config(config, collect_nodes), NodeConfigs::Configs { mut configs, collective_conditions, chained, } => { - let more_than_one_entry = configs.len() > 1; - if !collective_conditions.is_empty() { - if more_than_one_entry { - let set = self.create_anonymous_set(); - for config in &mut configs { - config.in_set_inner(set.intern()); - } - let mut set_config = SystemSetConfig::new(set.intern()); - set_config.conditions.extend(collective_conditions); - self.configure_set_inner(set_config).unwrap(); - } else { - for condition in collective_conditions { - configs[0].run_if_dyn(condition); - } - } - } - let mut config_iter = configs.into_iter(); - let mut nodes_in_scope = Vec::new(); - let mut densely_chained = true; - if chained == Chain::Yes || chained == Chain::YesIgnoreDeferred { - let Some(prev) = config_iter.next() else { - return ProcessConfigsResult { - nodes: Vec::new(), - densely_chained: true, - }; + self.apply_collective_conditions(&mut configs, collective_conditions); + + let ignore_deferred = matches!(chained, Chain::YesIgnoreDeferred); + let chained = matches!(chained, Chain::Yes | Chain::YesIgnoreDeferred); + + // Densely chained if + // * chained and all configs in the chain are densely chained, or + // * unchained with a single densely chained config + let mut densely_chained = chained || configs.len() == 1; + let mut configs = configs.into_iter(); + let mut nodes = Vec::new(); + + let Some(first) = configs.next() else { + return ProcessConfigsResult { + nodes: Vec::new(), + densely_chained, }; - let mut previous_result = self.process_configs(prev, true); - densely_chained = previous_result.densely_chained; - for current in config_iter { - let current_result = self.process_configs(current, true); - densely_chained = densely_chained && current_result.densely_chained; - match ( - previous_result.densely_chained, - current_result.densely_chained, - ) { - // Both groups are "densely" chained, so we can simplify the graph by only - // chaining the last in the previous list to the first in the current list - (true, true) => { - let last_in_prev = previous_result.nodes.last().unwrap(); - let first_in_current = current_result.nodes.first().unwrap(); - self.dependency.graph.add_edge( - *last_in_prev, - *first_in_current, - (), - ); - - if chained == Chain::YesIgnoreDeferred { - self.no_sync_edges - .insert((*last_in_prev, *first_in_current)); - } - } - // The previous group is "densely" chained, so we can simplify the graph by only - // chaining the last item from the previous list to every item in the current list - (true, false) => { - let last_in_prev = previous_result.nodes.last().unwrap(); - for current_node in ¤t_result.nodes { - self.dependency.graph.add_edge( - *last_in_prev, - *current_node, - (), - ); - - if chained == Chain::YesIgnoreDeferred { - self.no_sync_edges.insert((*last_in_prev, *current_node)); - } - } - } - // The current list is currently "densely" chained, so we can simplify the graph by - // only chaining every item in the previous list to the first item in the current list - (false, true) => { - let first_in_current = current_result.nodes.first().unwrap(); - for previous_node in &previous_result.nodes { - self.dependency.graph.add_edge( - *previous_node, - *first_in_current, - (), - ); - - if chained == Chain::YesIgnoreDeferred { - self.no_sync_edges - .insert((*previous_node, *first_in_current)); - } - } - } - // Neither of the lists are "densely" chained, so we must chain every item in the first - // list to every item in the second list - (false, false) => { - for previous_node in &previous_result.nodes { - for current_node in ¤t_result.nodes { - self.dependency.graph.add_edge( - *previous_node, - *current_node, - (), - ); - - if chained == Chain::YesIgnoreDeferred { - self.no_sync_edges - .insert((*previous_node, *current_node)); - } - } + }; + let mut previous_result = self.process_configs(first, collect_nodes || chained); + densely_chained &= previous_result.densely_chained; + + for current in configs { + let current_result = self.process_configs(current, collect_nodes || chained); + densely_chained &= current_result.densely_chained; + + if chained { + // if the current result is densely chained, we only need to chain the first node + let current_nodes = if current_result.densely_chained { + ¤t_result.nodes[..1] + } else { + ¤t_result.nodes + }; + // if the previous result was densely chained, we only need to chain the last node + let previous_nodes = if previous_result.densely_chained { + &previous_result.nodes[previous_result.nodes.len() - 1..] + } else { + &previous_result.nodes + }; + + for previous_node in previous_nodes { + for current_node in current_nodes { + self.dependency + .graph + .add_edge(*previous_node, *current_node, ()); + + if ignore_deferred { + self.no_sync_edges.insert((*previous_node, *current_node)); } } } - - if collect_nodes { - nodes_in_scope.append(&mut previous_result.nodes); - } - - previous_result = current_result; } - - // ensure the last config's nodes are added if collect_nodes { - nodes_in_scope.append(&mut previous_result.nodes); - } - } else { - for config in config_iter { - let result = self.process_configs(config, collect_nodes); - densely_chained = densely_chained && result.densely_chained; - if collect_nodes { - nodes_in_scope.extend(result.nodes); - } + nodes.append(&mut previous_result.nodes); } - // an "unchained" SystemConfig is only densely chained if it has exactly one densely chained entry - if more_than_one_entry { - densely_chained = false; - } + previous_result = current_result; + } + if collect_nodes { + nodes.append(&mut previous_result.nodes); } ProcessConfigsResult { - nodes: nodes_in_scope, + nodes, densely_chained, } }