From 3982de518800fa919313133a741dd97467af5c15 Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Mon, 24 Nov 2025 02:13:02 -0600 Subject: [PATCH 1/2] pull `Schedules::ignored_scheduling_ambiguities` into a separate resource --- crates/bevy_ecs/src/schedule/ambiguity.rs | 46 ++++++++++++ crates/bevy_ecs/src/schedule/mod.rs | 16 ++--- crates/bevy_ecs/src/schedule/node.rs | 4 +- crates/bevy_ecs/src/schedule/schedule.rs | 71 ++++--------------- crates/bevy_ecs/src/world/mod.rs | 12 ++-- .../migration-guides/schedule_cleanup.md | 5 ++ 6 files changed, 76 insertions(+), 78 deletions(-) create mode 100644 crates/bevy_ecs/src/schedule/ambiguity.rs diff --git a/crates/bevy_ecs/src/schedule/ambiguity.rs b/crates/bevy_ecs/src/schedule/ambiguity.rs new file mode 100644 index 0000000000000..27041fe7eee9b --- /dev/null +++ b/crates/bevy_ecs/src/schedule/ambiguity.rs @@ -0,0 +1,46 @@ +use core::{ + fmt::Write as _, + ops::{Deref, DerefMut}, +}; + +use alloc::string::{String, ToString}; +use bevy_platform::collections::HashSet; + +use crate::{ + component::{ComponentId, Components}, + resource::Resource, +}; + +/// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts. +#[derive(Resource, Default)] +pub struct IgnoredAmbiguities(pub HashSet); + +impl IgnoredAmbiguities { + /// Returns a string listing all ignored ambiguity component names. + /// + /// May panic or retrieve incorrect names if [`Components`] is not from the + /// same world. + pub fn to_string(&self, components: &Components) -> String { + let mut message = + "System order ambiguities caused by conflicts on the following types are ignored:\n" + .to_string(); + for id in self.iter() { + writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap(); + } + message + } +} + +impl Deref for IgnoredAmbiguities { + type Target = HashSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for IgnoredAmbiguities { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index f3b687dfc5ba7..c88bac8187c4f 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -1,5 +1,6 @@ //! Contains APIs for ordering systems and executing them on a [`World`](crate::world::World) +mod ambiguity; mod auto_insert_apply_deferred; mod condition; mod config; @@ -12,7 +13,9 @@ mod set; mod stepping; pub use self::graph::GraphInfo; -pub use self::{condition::*, config::*, error::*, executor::*, node::*, schedule::*, set::*}; +pub use self::{ + ambiguity::*, condition::*, config::*, error::*, executor::*, node::*, schedule::*, set::*, +}; pub use pass::ScheduleBuildPass; /// An implementation of a graph data structure. @@ -801,9 +804,6 @@ mod tests { } mod system_ambiguity { - #[cfg(feature = "trace")] - use alloc::collections::BTreeSet; - use super::*; use crate::prelude::*; @@ -1145,9 +1145,7 @@ mod tests { )); schedule.graph_mut().initialize(&mut world); - let _ = schedule - .graph_mut() - .build_schedule(&mut world, &BTreeSet::new()); + let _ = schedule.graph_mut().build_schedule(&mut world); let ambiguities: Vec<_> = schedule .graph() @@ -1204,9 +1202,7 @@ mod tests { let mut world = World::new(); schedule.graph_mut().initialize(&mut world); - let _ = schedule - .graph_mut() - .build_schedule(&mut world, &BTreeSet::new()); + let _ = schedule.graph_mut().build_schedule(&mut world); let ambiguities: Vec<_> = schedule .graph() diff --git a/crates/bevy_ecs/src/schedule/node.rs b/crates/bevy_ecs/src/schedule/node.rs index f73d8f9d49c0f..bac822e51a91f 100644 --- a/crates/bevy_ecs/src/schedule/node.rs +++ b/crates/bevy_ecs/src/schedule/node.rs @@ -1,4 +1,4 @@ -use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec}; +use alloc::{boxed::Box, string::String, vec::Vec}; use core::{ any::TypeId, fmt::{self, Debug}, @@ -609,7 +609,7 @@ impl Systems { flat_dependency_analysis: &DagAnalysis, flat_ambiguous_with: &UnGraph, ambiguous_with_all: &HashSet, - ignored_ambiguities: &BTreeSet, + ignored_ambiguities: &HashSet, ) -> ConflictingSystems { let mut conflicting_systems: Vec<(_, _, Box<[_]>)> = Vec::new(); for &(a, b) in flat_dependency_analysis.disconnected() { diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index ddcd844cb19cf..499249e2338c3 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -4,7 +4,7 @@ )] use alloc::{ boxed::Box, - collections::{BTreeMap, BTreeSet}, + collections::BTreeMap, format, string::{String, ToString}, vec, @@ -14,24 +14,17 @@ use bevy_platform::collections::{HashMap, HashSet}; use bevy_utils::{default, TypeIdMap}; use core::{ any::{Any, TypeId}, - fmt::{Debug, Write}, + fmt::Debug, }; use fixedbitset::FixedBitSet; -use log::{info, warn}; +use log::warn; use pass::ScheduleBuildPassObj; use thiserror::Error; #[cfg(feature = "trace")] use tracing::info_span; use crate::{change_detection::CheckChangeTicks, system::System}; -use crate::{ - component::{ComponentId, Components}, - prelude::Component, - resource::Resource, - schedule::*, - system::ScheduleSystem, - world::World, -}; +use crate::{resource::Resource, schedule::*, system::ScheduleSystem, world::World}; pub use stepping::Stepping; use Direction::{Incoming, Outgoing}; @@ -40,8 +33,6 @@ use Direction::{Incoming, Outgoing}; #[derive(Default, Resource)] pub struct Schedules { inner: HashMap, - /// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts - pub ignored_scheduling_ambiguities: BTreeSet, } impl Schedules { @@ -135,38 +126,6 @@ impl Schedules { } } - /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. - pub fn allow_ambiguous_component(&mut self, world: &mut World) { - self.ignored_scheduling_ambiguities - .insert(world.register_component::()); - } - - /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. - pub fn allow_ambiguous_resource(&mut self, world: &mut World) { - self.ignored_scheduling_ambiguities - .insert(world.components_registrator().register_resource::()); - } - - /// Iterate through the [`ComponentId`]'s that will be ignored. - pub fn iter_ignored_ambiguities(&self) -> impl Iterator + '_ { - self.ignored_scheduling_ambiguities.iter() - } - - /// Prints the names of the components and resources with [`info`] - /// - /// May panic or retrieve incorrect names if [`Components`] is not from the same - /// world - pub fn print_ignored_ambiguities(&self, components: &Components) { - let mut message = - "System order ambiguities caused by conflicts on the following types are ignored:\n" - .to_string(); - for id in self.iter_ignored_ambiguities() { - writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap(); - } - - info!("{message}"); - } - /// Adds one or more systems to the [`Schedule`] matching the provided [`ScheduleLabel`]. pub fn add_systems( &mut self, @@ -562,16 +521,9 @@ impl Schedule { pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> { if self.graph.changed { self.graph.initialize(world); - let ignored_ambiguities = world - .get_resource_or_init::() - .ignored_scheduling_ambiguities - .clone(); - self.warnings = self.graph.update_schedule( - world, - &mut self.executable, - &ignored_ambiguities, - self.label, - )?; + self.warnings = self + .graph + .update_schedule(world, &mut self.executable, self.label)?; self.graph.changed = false; self.executor_initialized = false; } @@ -1105,7 +1057,6 @@ impl ScheduleGraph { pub fn build_schedule( &mut self, world: &mut World, - ignored_ambiguities: &BTreeSet, ) -> Result<(SystemSchedule, Vec), ScheduleBuildError> { let mut warnings = Vec::new(); @@ -1189,7 +1140,10 @@ impl ScheduleGraph { &flat_dependency_analysis, &flat_ambiguous_with, &self.ambiguous_with_all, - ignored_ambiguities, + world + .get_resource::() + .map(|ia| &ia.0) + .unwrap_or(&HashSet::new()), ); // If there are any ambiguities, log warnings or return errors as configured. if self.settings.ambiguity_detection != LogLevel::Ignore @@ -1309,7 +1263,6 @@ impl ScheduleGraph { &mut self, world: &mut World, schedule: &mut SystemSchedule, - ignored_ambiguities: &BTreeSet, schedule_label: InternedScheduleLabel, ) -> Result, ScheduleBuildError> { if !self.systems.is_initialized() || !self.system_sets.is_initialized() { @@ -1342,7 +1295,7 @@ impl ScheduleGraph { } } - let (new_schedule, warnings) = self.build_schedule(world, ignored_ambiguities)?; + let (new_schedule, warnings) = self.build_schedule(world)?; *schedule = new_schedule; for warning in &warnings { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index a4f49c7460a35..9cb4649aa9966 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -53,7 +53,7 @@ use crate::{ query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, resource::Resource, - schedule::{Schedule, ScheduleLabel, Schedules}, + schedule::{IgnoredAmbiguities, Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, system::Commands, world::{ @@ -3675,16 +3675,14 @@ impl World { /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. pub fn allow_ambiguous_component(&mut self) { - let mut schedules = self.remove_resource::().unwrap_or_default(); - schedules.allow_ambiguous_component::(self); - self.insert_resource(schedules); + let id = self.register_component::(); + self.get_resource_or_init::().insert(id); } /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. pub fn allow_ambiguous_resource(&mut self) { - let mut schedules = self.remove_resource::().unwrap_or_default(); - schedules.allow_ambiguous_resource::(self); - self.insert_resource(schedules); + let id = self.components_registrator().register_resource::(); + self.get_resource_or_init::().insert(id); } } diff --git a/release-content/migration-guides/schedule_cleanup.md b/release-content/migration-guides/schedule_cleanup.md index 0b1281519d677..44ba7278199ff 100644 --- a/release-content/migration-guides/schedule_cleanup.md +++ b/release-content/migration-guides/schedule_cleanup.md @@ -22,3 +22,8 @@ pull_requests: [21608, 21817] - `ScheduleBuildPass::collapse_set` now takes `&HashSet` instead of a slice, to reduce redundant allocations. - `simple_cycles_in_component` has been changed from a free function into a method on `DiGraph`. - `DiGraph::try_into`/`UnGraph::try_into` was renamed to `DiGraph::try_convert`/`UnGraph::try_convert` to prevent overlap with the `TryInto` trait, and now makes use of `TryInto` instead of `TryFrom` for conversions. +- Removed `Schedules::ignored_scheduling_ambiguities`: access the same info via the new `IgnoredAmbiguities` resource instead. +- Removed `Schedules::allow_ambiguous_component` and `Schedules::allow_ambiguous_resource`: use the equivalent functions on `World` instead. +- Removed `Schedules::iter_ignored_ambiguities`: use `IgnoredAmbiguities::iter` via Deref instead. +- Removed `Schedules::print_ignored_ambiguities`: print the string returned by `IgnoredAmbiguities::to_string` yourself. +- Removed the `ignored_ambiguities` parameter from `Schedule::build_schedule`, it is now fetched internally from the new `IgnoredAmbiguities` resource. From e135f22e216210b753db53bc03ff75ae101f000d Mon Sep 17 00:00:00 2001 From: Christian Hughes Date: Mon, 24 Nov 2025 18:10:04 -0600 Subject: [PATCH 2/2] reintroduce but deprecate some functions --- crates/bevy_ecs/src/schedule/schedule.rs | 20 ++++++++++++++++++- .../migration-guides/schedule_cleanup.md | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 499249e2338c3..fb0684b096944 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -23,7 +23,7 @@ use thiserror::Error; #[cfg(feature = "trace")] use tracing::info_span; -use crate::{change_detection::CheckChangeTicks, system::System}; +use crate::{change_detection::CheckChangeTicks, component::Component, system::System}; use crate::{resource::Resource, schedule::*, system::ScheduleSystem, world::World}; pub use stepping::Stepping; @@ -126,6 +126,24 @@ impl Schedules { } } + /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. + #[deprecated( + since = "0.18.0", + note = "Use `World::allow_ambiguous_component` instead" + )] + pub fn allow_ambiguous_component(&mut self, world: &mut World) { + world.allow_ambiguous_component::(); + } + + /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. + #[deprecated( + since = "0.18.0", + note = "Use `World::allow_ambiguous_resource` instead" + )] + pub fn allow_ambiguous_resource(&mut self, world: &mut World) { + world.allow_ambiguous_resource::(); + } + /// Adds one or more systems to the [`Schedule`] matching the provided [`ScheduleLabel`]. pub fn add_systems( &mut self, diff --git a/release-content/migration-guides/schedule_cleanup.md b/release-content/migration-guides/schedule_cleanup.md index 44ba7278199ff..e0320b91b0126 100644 --- a/release-content/migration-guides/schedule_cleanup.md +++ b/release-content/migration-guides/schedule_cleanup.md @@ -23,7 +23,7 @@ pull_requests: [21608, 21817] - `simple_cycles_in_component` has been changed from a free function into a method on `DiGraph`. - `DiGraph::try_into`/`UnGraph::try_into` was renamed to `DiGraph::try_convert`/`UnGraph::try_convert` to prevent overlap with the `TryInto` trait, and now makes use of `TryInto` instead of `TryFrom` for conversions. - Removed `Schedules::ignored_scheduling_ambiguities`: access the same info via the new `IgnoredAmbiguities` resource instead. -- Removed `Schedules::allow_ambiguous_component` and `Schedules::allow_ambiguous_resource`: use the equivalent functions on `World` instead. +- Deprecated `Schedules::allow_ambiguous_component` and `Schedules::allow_ambiguous_resource`: use the equivalent functions on `World` instead. - Removed `Schedules::iter_ignored_ambiguities`: use `IgnoredAmbiguities::iter` via Deref instead. - Removed `Schedules::print_ignored_ambiguities`: print the string returned by `IgnoredAmbiguities::to_string` yourself. - Removed the `ignored_ambiguities` parameter from `Schedule::build_schedule`, it is now fetched internally from the new `IgnoredAmbiguities` resource.