Replace IntoSystemSetConfig with IntoSystemSetConfigs (#9247)

# Objective

- Fixes #9244.

## Solution


- Changed the `(Into)SystemSetConfigs` traits and structs be more like
the `(Into)SystemConfigs` traits and structs.
- Replaced uses of `IntoSystemSetConfig` with `IntoSystemSetConfigs`
- Added generic `ItemConfig` and `ItemConfigs` types.
- Changed `SystemConfig(s)` and `SystemSetConfig(s)` to be type aliases
to `ItemConfig(s)`.
- Added generic `process_configs` to `ScheduleGraph`.
- Changed `configure_sets_inner` and `add_systems_inner` to reuse
`process_configs`.

---

## Changelog

- Added `run_if` to `IntoSystemSetConfigs`
- Deprecated `Schedule::configure_set` and `App::configure_set`
- Removed `IntoSystemSetConfig`

## Migration Guide

- Use `App::configure_sets` instead of `App::configure_set`
- Use `Schedule::configure_sets` instead of `Schedule::configure_set`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Edgar Geier 2023-09-05 19:15:27 +02:00 committed by GitHub
parent 04885eb49c
commit 118509e4aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 197 additions and 286 deletions

View file

@ -393,21 +393,14 @@ impl App {
}
/// Configures a system set in the default schedule, adding the set if it does not exist.
#[deprecated(since = "0.12.0", note = "Please use `configure_sets` instead.")]
#[track_caller]
pub fn configure_set(
&mut self,
schedule: impl ScheduleLabel,
set: impl IntoSystemSetConfig,
set: impl IntoSystemSetConfigs,
) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(&schedule) {
schedule.configure_set(set);
} else {
let mut new_schedule = Schedule::new(schedule);
new_schedule.configure_set(set);
schedules.insert(new_schedule);
}
self
self.configure_sets(schedule, set)
}
/// Configures a collection of system sets in the default schedule, adding any sets that do not exist.

View file

@ -71,7 +71,7 @@ pub struct AudioPlugin {
impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(self.global_volume)
.configure_set(PostUpdate, AudioPlaySet.run_if(audio_output_available))
.configure_sets(PostUpdate, AudioPlaySet.run_if(audio_output_available))
.init_resource::<AudioOutput>();
#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]

View file

@ -45,8 +45,8 @@ pub mod prelude {
removal_detection::RemovedComponents,
schedule::{
apply_deferred, apply_state_transition, common_conditions::*, Condition,
IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, IntoSystemSetConfigs, NextState,
OnEnter, OnExit, OnTransition, Schedule, Schedules, State, States, SystemSet,
IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfigs, NextState, OnEnter, OnExit,
OnTransition, Schedule, Schedules, State, States, SystemSet,
},
system::{
Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,

View file

@ -47,21 +47,24 @@ impl IntoSystemConfigs<()> for BoxedSystem<(), ()> {
}
}
/// Stores configuration for a single system.
pub struct SystemConfig {
pub(crate) system: BoxedSystem,
/// Stores configuration for a single generic node.
pub struct NodeConfig<T> {
pub(crate) node: T,
pub(crate) graph_info: GraphInfo,
pub(crate) conditions: Vec<BoxedCondition>,
}
/// A collection of [`SystemConfig`].
pub enum SystemConfigs {
/// Configuration for a single system.
SystemConfig(SystemConfig),
/// Configuration for a tuple of nested `SystemConfigs` instances.
/// Stores configuration for a single system.
pub type SystemConfig = NodeConfig<BoxedSystem>;
/// A collections of generic [`NodeConfig`]s.
pub enum NodeConfigs<T> {
/// Configuratin for a single node.
NodeConfig(NodeConfig<T>),
/// Configuration for a tuple of nested `Configs` instances.
Configs {
/// Configuration for each element of the tuple.
configs: Vec<SystemConfigs>,
configs: Vec<NodeConfigs<T>>,
/// Run conditions applied to everything in the tuple.
collective_conditions: Vec<BoxedCondition>,
/// If `true`, adds `before -> after` ordering constraints between the successive elements.
@ -69,12 +72,15 @@ pub enum SystemConfigs {
},
}
/// A collection of [`SystemConfig`].
pub type SystemConfigs = NodeConfigs<BoxedSystem>;
impl SystemConfigs {
fn new_system(system: BoxedSystem) -> Self {
// include system in its default sets
let sets = system.default_system_sets().into_iter().collect();
Self::SystemConfig(SystemConfig {
system,
Self::NodeConfig(SystemConfig {
node: system,
graph_info: GraphInfo {
sets,
..Default::default()
@ -82,14 +88,16 @@ impl SystemConfigs {
conditions: Vec::new(),
})
}
}
impl<T> NodeConfigs<T> {
/// Adds a new boxed system set to the systems.
pub fn in_set_dyn(&mut self, set: BoxedSystemSet) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config.graph_info.sets.push(set);
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.in_set_dyn(set.dyn_clone());
}
@ -99,13 +107,13 @@ impl SystemConfigs {
fn before_inner(&mut self, set: BoxedSystemSet) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config
.graph_info
.dependencies
.push(Dependency::new(DependencyKind::Before, set));
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.before_inner(set.dyn_clone());
}
@ -115,13 +123,13 @@ impl SystemConfigs {
fn after_inner(&mut self, set: BoxedSystemSet) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config
.graph_info
.dependencies
.push(Dependency::new(DependencyKind::After, set));
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.after_inner(set.dyn_clone());
}
@ -131,10 +139,10 @@ impl SystemConfigs {
fn distributive_run_if_inner<M>(&mut self, condition: impl Condition<M> + Clone) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config.conditions.push(new_condition(condition));
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.distributive_run_if_inner(condition.clone());
}
@ -144,10 +152,10 @@ impl SystemConfigs {
fn ambiguous_with_inner(&mut self, set: BoxedSystemSet) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
ambiguous_with(&mut config.graph_info, set);
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.ambiguous_with_inner(set.dyn_clone());
}
@ -157,10 +165,10 @@ impl SystemConfigs {
fn ambiguous_with_all_inner(&mut self) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
}
SystemConfigs::Configs { configs, .. } => {
Self::Configs { configs, .. } => {
for config in configs {
config.ambiguous_with_all_inner();
}
@ -174,10 +182,10 @@ impl SystemConfigs {
/// Prefer `run_if` for run conditions whose type is known at compile time.
pub fn run_if_dyn(&mut self, condition: BoxedCondition) {
match self {
SystemConfigs::SystemConfig(config) => {
Self::NodeConfig(config) => {
config.conditions.push(condition);
}
SystemConfigs::Configs {
Self::Configs {
collective_conditions,
..
} => {
@ -185,6 +193,16 @@ impl SystemConfigs {
}
}
}
fn chain_inner(mut self) -> Self {
match &mut self {
Self::NodeConfig(_) => { /* no op */ }
Self::Configs { chained, .. } => {
*chained = true;
}
}
self
}
}
/// Types that can convert into a [`SystemConfigs`].
@ -239,7 +257,7 @@ where
/// that all evaluations in a single schedule run will yield the same result. If another
/// system is run inbetween two evaluations it could cause the result of the condition to change.
///
/// Use [`run_if`](IntoSystemSetConfig::run_if) on a [`SystemSet`] if you want to make sure
/// Use [`run_if`](IntoSystemSetConfigs::run_if) on a [`SystemSet`] if you want to make sure
/// that either all or none of the systems are run, or you don't want to evaluate the run
/// condition for each contained system separately.
fn distributive_run_if<M>(self, condition: impl Condition<M> + Clone) -> SystemConfigs {
@ -265,7 +283,7 @@ where
/// # #[derive(SystemSet, Debug, Eq, PartialEq, Hash, Clone, Copy)]
/// # struct C;
/// schedule.add_systems((a, b).run_if(condition));
/// schedule.add_systems((a, b).in_set(C)).configure_set(C.run_if(condition));
/// schedule.add_systems((a, b).in_set(C)).configure_sets(C.run_if(condition));
/// ```
///
/// # Note
@ -350,14 +368,8 @@ impl IntoSystemConfigs<()> for SystemConfigs {
self
}
fn chain(mut self) -> Self {
match &mut self {
SystemConfigs::SystemConfig(_) => { /* no op */ }
SystemConfigs::Configs { chained, .. } => {
*chained = true;
}
}
self
fn chain(self) -> Self {
self.chain_inner()
}
}
@ -386,15 +398,11 @@ macro_rules! impl_system_collection {
all_tuples!(impl_system_collection, 1, 20, P, S);
/// A [`SystemSet`] with scheduling metadata.
pub struct SystemSetConfig {
pub(super) set: BoxedSystemSet,
pub(super) graph_info: GraphInfo,
pub(super) conditions: Vec<BoxedCondition>,
}
pub type SystemSetConfig = NodeConfig<BoxedSystemSet>;
impl SystemSetConfig {
#[track_caller]
fn new(set: BoxedSystemSet) -> Self {
pub(super) fn new(set: BoxedSystemSet) -> Self {
// system type sets are automatically populated
// to avoid unintentionally broad changes, they cannot be configured
assert!(
@ -403,118 +411,15 @@ impl SystemSetConfig {
);
Self {
set,
node: set,
graph_info: GraphInfo::default(),
conditions: Vec::new(),
}
}
}
/// Types that can be converted into a [`SystemSetConfig`].
///
/// This has been implemented for all types that implement [`SystemSet`] and boxed trait objects.
pub trait IntoSystemSetConfig: Sized {
/// Convert into a [`SystemSetConfig`].
#[doc(hidden)]
fn into_config(self) -> SystemSetConfig;
/// Add to the provided `set`.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_set(set)
}
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().before(set)
}
/// Run after all systems in `set`.
fn after<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().after(set)
}
/// Run the systems in this set only if the [`Condition`] is `true`.
///
/// The `Condition` will be evaluated at most once (per schedule run),
/// the first time a system in this set prepares to run.
fn run_if<M>(self, condition: impl Condition<M>) -> SystemSetConfig {
self.into_config().run_if(condition)
}
/// Suppress warnings and errors that would result from systems in this set having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().ambiguous_with(set)
}
/// Suppress warnings and errors that would result from systems in this set having ambiguities
/// (conflicting access but indeterminate order) with any other system.
fn ambiguous_with_all(self) -> SystemSetConfig {
self.into_config().ambiguous_with_all()
}
}
impl<S: SystemSet> IntoSystemSetConfig for S {
#[track_caller]
fn into_config(self) -> SystemSetConfig {
SystemSetConfig::new(Box::new(self))
}
}
impl IntoSystemSetConfig for BoxedSystemSet {
fn into_config(self) -> SystemSetConfig {
SystemSetConfig::new(self)
}
}
impl IntoSystemSetConfig for SystemSetConfig {
fn into_config(self) -> Self {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
set.system_type().is_none(),
"adding arbitrary systems to a system type set is not allowed"
);
self.graph_info.sets.push(Box::new(set));
self
}
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
self.graph_info.dependencies.push(Dependency::new(
DependencyKind::Before,
Box::new(set.into_system_set()),
));
self
}
fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
self.graph_info.dependencies.push(Dependency::new(
DependencyKind::After,
Box::new(set.into_system_set()),
));
self
}
fn run_if<M>(mut self, condition: impl Condition<M>) -> Self {
self.conditions.push(new_condition(condition));
self
}
fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
ambiguous_with(&mut self.graph_info, Box::new(set.into_system_set()));
self
}
fn ambiguous_with_all(mut self) -> Self {
self.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
self
}
}
/// A collection of [`SystemSetConfig`].
pub struct SystemSetConfigs {
pub(super) sets: Vec<SystemSetConfig>,
/// If `true`, adds `before -> after` ordering constraints between the successive elements.
pub(super) chained: bool,
}
pub type SystemSetConfigs = NodeConfigs<BoxedSystemSet>;
/// Types that can convert into a [`SystemSetConfigs`].
pub trait IntoSystemSetConfigs
@ -541,6 +446,14 @@ where
self.into_configs().after(set)
}
/// Run the systems in this set(s) only if the [`Condition`] is `true`.
///
/// The `Condition` will be evaluated at most once (per schedule run),
/// the first time a system in this set(s) prepares to run.
fn run_if<M>(self, condition: impl Condition<M>) -> SystemSetConfigs {
self.into_configs().run_if(condition)
}
/// Suppress warnings and errors that would result from systems in these sets having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs {
@ -572,69 +485,77 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
set.system_type().is_none(),
"adding arbitrary systems to a system type set is not allowed"
);
for config in &mut self.sets {
config.graph_info.sets.push(set.dyn_clone());
}
self.in_set_dyn(set.dyn_clone());
self
}
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.sets {
config
.graph_info
.dependencies
.push(Dependency::new(DependencyKind::Before, set.dyn_clone()));
}
self.before_inner(set.dyn_clone());
self
}
fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.sets {
config
.graph_info
.dependencies
.push(Dependency::new(DependencyKind::After, set.dyn_clone()));
}
self.after_inner(set.dyn_clone());
self
}
fn run_if<M>(mut self, condition: impl Condition<M>) -> SystemSetConfigs {
self.run_if_dyn(new_condition(condition));
self
}
fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.sets {
ambiguous_with(&mut config.graph_info, set.dyn_clone());
}
self.ambiguous_with_inner(set.dyn_clone());
self
}
fn ambiguous_with_all(mut self) -> Self {
for config in &mut self.sets {
config.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
}
self.ambiguous_with_all_inner();
self
}
fn chain(mut self) -> Self {
self.chained = true;
self
fn chain(self) -> Self {
self.chain_inner()
}
}
impl<S: SystemSet> IntoSystemSetConfigs for S {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(SystemSetConfig::new(Box::new(self)))
}
}
impl IntoSystemSetConfigs for BoxedSystemSet {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(SystemSetConfig::new(self))
}
}
impl IntoSystemSetConfigs for SystemSetConfig {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(self)
}
}
macro_rules! impl_system_set_collection {
($($set: ident),*) => {
impl<$($set: IntoSystemSetConfig),*> IntoSystemSetConfigs for ($($set,)*)
impl<$($set: IntoSystemSetConfigs),*> IntoSystemSetConfigs for ($($set,)*)
{
#[allow(non_snake_case)]
fn into_configs(self) -> SystemSetConfigs {
let ($($set,)*) = self;
SystemSetConfigs {
sets: vec![$($set.into_config(),)*],
SystemSetConfigs::Configs {
configs: vec![$($set.into_configs(),)*],
collective_conditions: Vec::new(),
chained: false,
}
}
@ -642,4 +563,4 @@ macro_rules! impl_system_set_collection {
}
}
all_tuples!(impl_system_set_collection, 0, 15, S);
all_tuples!(impl_system_set_collection, 1, 20, S);

View file

@ -25,7 +25,7 @@ mod tests {
use std::sync::atomic::{AtomicU32, Ordering};
pub use crate as bevy_ecs;
pub use crate::schedule::{IntoSystemSetConfig, Schedule, SystemSet};
pub use crate::schedule::{IntoSystemSetConfigs, Schedule, SystemSet};
pub use crate::system::{Res, ResMut};
pub use crate::{prelude::World, system::Resource};
@ -145,7 +145,7 @@ mod tests {
assert_eq!(world.resource::<SystemOrder>().0, vec![]);
// modify the schedule after it's been initialized and test ordering with sets
schedule.configure_set(TestSet::A.after(named_system));
schedule.configure_sets(TestSet::A.after(named_system));
schedule.add_systems((
make_function_system(3)
.before(TestSet::A)
@ -339,13 +339,13 @@ mod tests {
world.init_resource::<Counter>();
schedule.configure_set(TestSet::A.run_if(|| false).run_if(|| false));
schedule.configure_sets(TestSet::A.run_if(|| false).run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::A));
schedule.configure_set(TestSet::B.run_if(|| true).run_if(|| false));
schedule.configure_sets(TestSet::B.run_if(|| true).run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::B));
schedule.configure_set(TestSet::C.run_if(|| false).run_if(|| true));
schedule.configure_sets(TestSet::C.run_if(|| false).run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::C));
schedule.configure_set(TestSet::D.run_if(|| true).run_if(|| true));
schedule.configure_sets(TestSet::D.run_if(|| true).run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::D));
schedule.run(&mut world);
@ -359,13 +359,13 @@ mod tests {
world.init_resource::<Counter>();
schedule.configure_set(TestSet::A.run_if(|| false));
schedule.configure_sets(TestSet::A.run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::A).run_if(|| false));
schedule.configure_set(TestSet::B.run_if(|| true));
schedule.configure_sets(TestSet::B.run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::B).run_if(|| false));
schedule.configure_set(TestSet::C.run_if(|| false));
schedule.configure_sets(TestSet::C.run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::C).run_if(|| true));
schedule.configure_set(TestSet::D.run_if(|| true));
schedule.configure_sets(TestSet::D.run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::D).run_if(|| true));
schedule.run(&mut world);
@ -431,7 +431,7 @@ mod tests {
world.init_resource::<Bool2>();
let mut schedule = Schedule::default();
schedule.configure_set(
schedule.configure_sets(
TestSet::A
.run_if(|res1: Res<RunConditionBool>| res1.is_changed())
.run_if(|res2: Res<Bool2>| res2.is_changed()),
@ -482,7 +482,7 @@ mod tests {
let mut schedule = Schedule::default();
schedule
.configure_set(TestSet::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()));
.configure_sets(TestSet::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()));
schedule.add_systems(
counting_system
@ -529,7 +529,7 @@ mod tests {
#[should_panic]
fn dependency_loop() {
let mut schedule = Schedule::default();
schedule.configure_set(TestSet::X.after(TestSet::X));
schedule.configure_sets(TestSet::X.after(TestSet::X));
}
#[test]
@ -537,8 +537,8 @@ mod tests {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.configure_set(TestSet::A.after(TestSet::B));
schedule.configure_set(TestSet::B.after(TestSet::A));
schedule.configure_sets(TestSet::A.after(TestSet::B));
schedule.configure_sets(TestSet::B.after(TestSet::A));
let result = schedule.initialize(&mut world);
assert!(matches!(
@ -564,7 +564,7 @@ mod tests {
#[should_panic]
fn hierarchy_loop() {
let mut schedule = Schedule::default();
schedule.configure_set(TestSet::X.in_set(TestSet::X));
schedule.configure_sets(TestSet::X.in_set(TestSet::X));
}
#[test]
@ -572,8 +572,8 @@ mod tests {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.configure_set(TestSet::A.in_set(TestSet::B));
schedule.configure_set(TestSet::B.in_set(TestSet::A));
schedule.configure_sets(TestSet::A.in_set(TestSet::B));
schedule.configure_sets(TestSet::B.in_set(TestSet::A));
let result = schedule.initialize(&mut world);
assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle(_))));
@ -625,7 +625,7 @@ mod tests {
fn configure_system_type_set() {
fn foo() {}
let mut schedule = Schedule::default();
schedule.configure_set(foo.into_system_set());
schedule.configure_sets(foo.into_system_set());
}
#[test]
@ -639,13 +639,13 @@ mod tests {
});
// Add `A`.
schedule.configure_set(TestSet::A);
schedule.configure_sets(TestSet::A);
// Add `B` as child of `A`.
schedule.configure_set(TestSet::B.in_set(TestSet::A));
schedule.configure_sets(TestSet::B.in_set(TestSet::A));
// Add `X` as child of both `A` and `B`.
schedule.configure_set(TestSet::X.in_set(TestSet::A).in_set(TestSet::B));
schedule.configure_sets(TestSet::X.in_set(TestSet::A).in_set(TestSet::B));
// `X` cannot be the `A`'s child and grandchild at the same time.
let result = schedule.initialize(&mut world);
@ -661,8 +661,8 @@ mod tests {
let mut schedule = Schedule::default();
// Add `B` and give it both kinds of relationships with `A`.
schedule.configure_set(TestSet::B.in_set(TestSet::A));
schedule.configure_set(TestSet::B.after(TestSet::A));
schedule.configure_sets(TestSet::B.in_set(TestSet::A));
schedule.configure_sets(TestSet::B.after(TestSet::A));
let result = schedule.initialize(&mut world);
assert!(matches!(
result,

View file

@ -192,15 +192,15 @@ impl Schedule {
/// Add a collection of systems to the schedule.
pub fn add_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
self.graph.add_systems_inner(systems.into_configs(), false);
self.graph.process_configs(systems.into_configs(), false);
self
}
/// Configures a system set in this schedule, adding it if it does not exist.
#[deprecated(since = "0.12.0", note = "Please use `configure_sets` instead.")]
#[track_caller]
pub fn configure_set(&mut self, set: impl IntoSystemSetConfig) -> &mut Self {
self.graph.configure_set(set);
self
pub fn configure_set(&mut self, set: impl IntoSystemSetConfigs) -> &mut Self {
self.configure_sets(set)
}
/// Configures a collection of system sets in this schedule, adding them if they does not exist.
@ -522,30 +522,36 @@ impl ScheduleGraph {
&self.conflicting_systems
}
/// Adds the systems to the graph. Returns a vector of all node ids contained the nested `SystemConfigs`
/// if `ancestor_chained` is true. Also returns true if "densely chained", meaning that all nested items
/// are linearly chained in the order they are defined
fn add_systems_inner(
/// 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`].
/// `process_config` is the function which processes each individual config node and returns a corresponding `NodeId`.
///
/// The fields on the returned [`ProcessConfigsResult`] are:
/// - `nodes`: a vector of all node ids contained in the nested `NodeConfigs`
/// - `densely_chained`: a boolean that is true if all nested nodes are linearly chained (with successive `after` orderings) in the order they are defined
#[track_caller]
fn process_configs<T: ProcessNodeConfig>(
&mut self,
configs: SystemConfigs,
ancestor_chained: bool,
) -> AddSystemsInnerResult {
configs: NodeConfigs<T>,
collect_nodes: bool,
) -> ProcessConfigsResult {
match configs {
SystemConfigs::SystemConfig(config) => {
let node_id = self.add_system_inner(config).unwrap();
if ancestor_chained {
AddSystemsInnerResult {
NodeConfigs::NodeConfig(config) => {
let node_id = T::process_config(self, config);
if collect_nodes {
ProcessConfigsResult {
densely_chained: true,
nodes: vec![node_id],
}
} else {
AddSystemsInnerResult {
ProcessConfigsResult {
densely_chained: true,
nodes: Vec::new(),
}
}
}
SystemConfigs::Configs {
NodeConfigs::Configs {
mut configs,
collective_conditions,
chained,
@ -557,9 +563,9 @@ impl ScheduleGraph {
for config in &mut configs {
config.in_set_dyn(set.dyn_clone());
}
let mut set_config = set.into_config();
let mut set_config = SystemSetConfig::new(set.dyn_clone());
set_config.conditions.extend(collective_conditions);
self.configure_set(set_config);
self.configure_set_inner(set_config).unwrap();
} else {
for condition in collective_conditions {
configs[0].run_if_dyn(condition);
@ -571,15 +577,15 @@ impl ScheduleGraph {
let mut densely_chained = true;
if chained {
let Some(prev) = config_iter.next() else {
return AddSystemsInnerResult {
return ProcessConfigsResult {
nodes: Vec::new(),
densely_chained: true,
};
};
let mut previous_result = self.add_systems_inner(prev, true);
let mut previous_result = self.process_configs(prev, true);
densely_chained = previous_result.densely_chained;
for current in config_iter {
let current_result = self.add_systems_inner(current, true);
let current_result = self.process_configs(current, true);
densely_chained = densely_chained && current_result.densely_chained;
match (
previous_result.densely_chained,
@ -635,7 +641,7 @@ impl ScheduleGraph {
}
}
if ancestor_chained {
if collect_nodes {
nodes_in_scope.append(&mut previous_result.nodes);
}
@ -643,14 +649,14 @@ impl ScheduleGraph {
}
// ensure the last config's nodes are added
if ancestor_chained {
if collect_nodes {
nodes_in_scope.append(&mut previous_result.nodes);
}
} else {
for config in config_iter {
let result = self.add_systems_inner(config, ancestor_chained);
let result = self.process_configs(config, collect_nodes);
densely_chained = densely_chained && result.densely_chained;
if ancestor_chained {
if collect_nodes {
nodes_in_scope.extend(result.nodes);
}
}
@ -661,7 +667,7 @@ impl ScheduleGraph {
}
}
AddSystemsInnerResult {
ProcessConfigsResult {
nodes: nodes_in_scope,
densely_chained,
}
@ -677,7 +683,7 @@ impl ScheduleGraph {
// system init has to be deferred (need `&mut World`)
self.uninit.push((id, 0));
self.systems.push(SystemNode::new(config.system));
self.systems.push(SystemNode::new(config.node));
self.system_conditions.push(config.conditions);
Ok(id)
@ -685,46 +691,15 @@ impl ScheduleGraph {
#[track_caller]
fn configure_sets(&mut self, sets: impl IntoSystemSetConfigs) {
let SystemSetConfigs { sets, chained } = sets.into_configs();
let mut set_iter = sets.into_iter();
if chained {
let Some(prev) = set_iter.next() else { return };
let mut prev_id = self.configure_set_inner(prev).unwrap();
for next in set_iter {
let next_id = self.configure_set_inner(next).unwrap();
self.dependency.graph.add_edge(prev_id, next_id, ());
prev_id = next_id;
}
} else {
for set in set_iter {
if let Err(e) = self.configure_set_inner(set) {
// using `unwrap_or_else(panic!)` led to the error being reported
// from this line instead of in the user code
panic!("{e}");
};
}
}
self.process_configs(sets.into_configs(), false);
}
#[track_caller]
fn configure_set(&mut self, set: impl IntoSystemSetConfig) {
if let Err(e) = self.configure_set_inner(set) {
// using `unwrap_or_else(panic!)` led to the error being reported
// from this line instead of in the user code
panic!("{e}");
};
}
#[track_caller]
fn configure_set_inner(
&mut self,
set: impl IntoSystemSetConfig,
) -> Result<NodeId, ScheduleBuildError> {
fn configure_set_inner(&mut self, set: SystemSetConfig) -> Result<NodeId, ScheduleBuildError> {
let SystemSetConfig {
set,
node: set,
graph_info,
mut conditions,
} = set.into_config();
} = set;
let id = match self.system_set_ids.get(&set) {
Some(&id) => id,
@ -1240,14 +1215,35 @@ impl ScheduleGraph {
}
}
/// Values returned by `ScheduleGraph::add_systems_inner`
struct AddSystemsInnerResult {
/// All nodes contained inside this add_systems_inner call's SystemConfigs hierarchy
/// Values returned by [`ScheduleGraph::process_configs`]
struct ProcessConfigsResult {
/// All nodes contained inside this process_configs call's [`NodeConfigs`] hierarchy,
/// if `ancestor_chained` is true
nodes: Vec<NodeId>,
/// True if and only if all nodes are "densely chained"
/// True if and only if all nodes are "densely chained", meaning that all nested nodes
/// are linearly chained (as if `after` system ordering had been applied between each node)
/// in the order they are defined
densely_chained: bool,
}
/// Trait used by [`ScheduleGraph::process_configs`] to process a single [`NodeConfig`].
trait ProcessNodeConfig: Sized {
/// Process a single [`NodeConfig`].
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId;
}
impl ProcessNodeConfig for BoxedSystem {
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId {
schedule_graph.add_system_inner(config).unwrap()
}
}
impl ProcessNodeConfig for BoxedSystemSet {
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId {
schedule_graph.configure_set_inner(config).unwrap()
}
}
/// Used to select the appropriate reporting function.
enum ReportCycles {
Hierarchy,
@ -1718,7 +1714,7 @@ impl ScheduleBuildSettings {
mod tests {
use crate::{
self as bevy_ecs,
schedule::{IntoSystemConfigs, IntoSystemSetConfig, Schedule, SystemSet},
schedule::{IntoSystemConfigs, IntoSystemSetConfigs, Schedule, SystemSet},
world::World,
};
@ -1731,7 +1727,7 @@ mod tests {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.configure_set(Set.run_if(|| false));
schedule.configure_sets(Set.run_if(|| false));
schedule.add_systems(
(|| panic!("This system must not run"))
.ambiguous_with(|| ())

View file

@ -110,7 +110,8 @@ impl<T> SystemSet for SystemTypeSet<T> {
}
/// A [`SystemSet`] implicitly created when using
/// [`Schedule::add_systems`](super::Schedule::add_systems).
/// [`Schedule::add_systems`](super::Schedule::add_systems) or
/// [`Schedule::configure_sets`](super::Schedule::configure_sets).
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct AnonymousSet(usize);

View file

@ -46,7 +46,7 @@
//! You can **explicitly order** systems:
//!
//! - by calling the `.before(this_system)` or `.after(that_system)` methods when adding them to your schedule
//! - by adding them to a [`SystemSet`], and then using `.configure_set(ThisSet.before(ThatSet))` syntax to configure many systems at once
//! - by adding them to a [`SystemSet`], and then using `.configure_sets(ThisSet.before(ThatSet))` syntax to configure many systems at once
//! - through the use of `.add_systems((system_a, system_b, system_c).chain())`
//!
//! [`SystemSet`]: crate::schedule::SystemSet

View file

@ -161,7 +161,7 @@ impl Render {
);
schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
schedule.configure_set(
schedule.configure_sets(
QueueMeshes
.in_set(RenderSet::Queue)
.after(prepare_assets::<Mesh>),

View file

@ -216,7 +216,7 @@ impl Plugin for VisibilityPlugin {
app
// We add an AABB component in CalculateBounds, which must be ready on the same frame.
.add_systems(PostUpdate, apply_deferred.in_set(CalculateBoundsFlush))
.configure_set(PostUpdate, CalculateBoundsFlush.after(CalculateBounds))
.configure_sets(PostUpdate, CalculateBoundsFlush.after(CalculateBounds))
.add_systems(
PostUpdate,
(

View file

@ -102,7 +102,7 @@ impl Plugin for TransformPlugin {
app.register_type::<Transform>()
.register_type::<GlobalTransform>()
.add_plugins(ValidParentCheckPlugin::<GlobalTransform>::default())
.configure_set(
.configure_sets(
PostStartup,
PropagateTransformsSet.in_set(TransformSystem::TransformPropagate),
)
@ -119,7 +119,7 @@ impl Plugin for TransformPlugin {
propagate_transforms.in_set(PropagateTransformsSet),
),
)
.configure_set(
.configure_sets(
PostUpdate,
PropagateTransformsSet.in_set(TransformSystem::TransformPropagate),
)