From d7dbb52dd09988a12368377567a8d816a2bfd979 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 09:47:50 -0700 Subject: [PATCH 01/14] ignore ambiguities --- crates/bevy_ecs/src/schedule/mod.rs | 85 +++++++++++++++++++----- crates/bevy_ecs/src/schedule/schedule.rs | 71 ++++++++++++++++---- 2 files changed, 127 insertions(+), 29 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 1d58a7d59fe72..bad5271eb09a6 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -718,8 +718,10 @@ mod tests { } mod system_ambiguity { + use super::*; // Required to make the derive macro behave use crate as bevy_ecs; + use crate::component::ComponentId; use crate::event::Events; use crate::prelude::*; @@ -981,14 +983,12 @@ mod tests { assert_eq!(schedule.graph().conflicting_systems().len(), 0); } + #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)] + struct TestSchedule; + // Tests that the correct ambiguities were reported in the correct order. #[test] fn correct_ambiguities() { - use super::*; - - #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)] - struct TestSchedule; - fn system_a(_res: ResMut) {} fn system_b(_res: ResMut) {} fn system_c(_res: ResMut) {} @@ -1008,9 +1008,11 @@ mod tests { )); schedule.graph_mut().initialize(&mut world); - let _ = schedule - .graph_mut() - .build_schedule(world.components(), &TestSchedule.dyn_clone()); + let _ = schedule.graph_mut().build_schedule( + world.components(), + &TestSchedule.dyn_clone(), + &Vec::new(), + ); let ambiguities: Vec<_> = schedule .graph() @@ -1050,19 +1052,16 @@ mod tests { // Related issue https://github.com/bevyengine/bevy/issues/9641 #[test] fn anonymous_set_name() { - use super::*; - - #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)] - struct TestSchedule; - let mut schedule = Schedule::new(TestSchedule); schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); let mut world = World::new(); schedule.graph_mut().initialize(&mut world); - let _ = schedule - .graph_mut() - .build_schedule(world.components(), &TestSchedule.dyn_clone()); + let _ = schedule.graph_mut().build_schedule( + world.components(), + &TestSchedule.dyn_clone(), + &Vec::new(), + ); let ambiguities: Vec<_> = schedule .graph() @@ -1078,5 +1077,59 @@ mod tests { ) ); } + + // helper function for testing ambiguities + fn assert_ambiguities_empty( + schedule: &mut Schedule, + world: &mut World, + ignored_ambiguities: Vec, + ) { + schedule.graph_mut().initialize(world); + let _ = schedule.graph_mut().build_schedule( + world.components(), + &TestSchedule.dyn_clone(), + &ignored_ambiguities, + ); + + let ambiguities: Vec<_> = schedule + .graph() + .conflicts_to_string(schedule.graph().conflicting_systems(), world.components()) + .collect(); + + assert!(ambiguities.is_empty()); + } + + #[test] + fn ignore_component_resource_ambiguities() { + fn system_a(_res: ResMut) {} + fn system_b(_res: ResMut) {} + + let mut world = World::new(); + world.insert_resource(R); + + let resource_id = world.components.resource_id::().unwrap(); + + let mut schedule = Schedule::new(TestSchedule); + schedule.add_systems((system_a, system_b)); + + assert_ambiguities_empty(&mut schedule, &mut world, vec![resource_id]); + } + + #[test] + fn ignore_before_add() { + todo!(); + fn system_a(_res: ResMut) {} + fn system_b(_res: ResMut) {} + + let mut world = World::new(); + world.insert_resource(R); + + let resource_id = world.components.resource_id::().unwrap(); + + let mut schedule = Schedule::new(TestSchedule); + schedule.add_systems((system_a, system_b)); + + assert_ambiguities_empty(&mut schedule, &mut world, vec![resource_id]); + } } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 79e4e99916893..008349672352e 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -17,7 +17,9 @@ use fixedbitset::FixedBitSet; use crate::{ self as bevy_ecs, - component::{ComponentId, Components, Tick}, + change_detection::Mut, + component::{ComponentDescriptor, ComponentId, Components, Tick}, + prelude::Component, schedule::*, system::{BoxedSystem, Resource, System}, world::World, @@ -27,6 +29,8 @@ use crate::{ #[derive(Default, Resource)] pub struct Schedules { inner: HashMap, + /// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems + ambiguous_components: Vec, } impl Schedules { @@ -34,6 +38,7 @@ impl Schedules { pub fn new() -> Self { Self { inner: HashMap::new(), + ambiguous_components: Vec::new(), } } @@ -110,6 +115,27 @@ impl Schedules { schedule.set_build_settings(schedule_build_settings.clone()); } } + + /// Ignore ambiguities in component T. + /// + /// [`Components`] should be from the same world that Schedules belongs to + pub fn allow_ambiguous_component(&mut self, world: &mut World) { + self.ambiguous_components.push(world.init_component::()); + } + + /// Ignore ambiguities in resource T. + /// + /// [`Components`] should be from the same world that Schedules belongs to + pub fn allow_ambiguous_resource(&mut self, world: &mut World) { + // TODO: think about moving this to World or Components + let resource_id = world.components.resource_id::().unwrap_or_else(|| { + world.components.init_component_with_descriptor( + &mut world.storages, + ComponentDescriptor::new_resource::(), + ) + }); + self.ambiguous_components.push(resource_id); + } } fn make_executor(kind: ExecutorKind) -> Box { @@ -262,8 +288,14 @@ impl Schedule { pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> { if self.graph.changed { self.graph.initialize(world); - self.graph - .update_schedule(&mut self.executable, world.components(), &self.name)?; + world.resource_scope(|world, schedules: Mut| { + self.graph.update_schedule( + &mut self.executable, + world.components(), + &schedules.ambiguous_components, + &self.name, + ) + })?; self.graph.changed = false; self.executor_initialized = false; } @@ -871,6 +903,7 @@ impl ScheduleGraph { &mut self, components: &Components, schedule_label: &BoxedScheduleLabel, + ignored_ambiguities: &Vec, ) -> Result { // check hierarchy for cycles self.hierarchy.topsort = @@ -919,8 +952,11 @@ impl ScheduleGraph { let ambiguous_with_flattened = self.get_ambiguous_with_flattened(&set_systems); // check for conflicts - let conflicting_systems = - self.get_conflicting_systems(&flat_results.disconnected, &ambiguous_with_flattened); + let conflicting_systems = self.get_conflicting_systems( + &flat_results.disconnected, + &ambiguous_with_flattened, + ignored_ambiguities, + ); self.optionally_check_conflicts(&conflicting_systems, components, schedule_label)?; self.conflicting_systems = conflicting_systems; @@ -1040,6 +1076,7 @@ impl ScheduleGraph { &self, flat_results_disconnected: &Vec<(NodeId, NodeId)>, ambiguous_with_flattened: &GraphMap, + ignored_ambiguities: &Vec, ) -> Vec<(NodeId, NodeId, Vec)> { let mut conflicting_systems = Vec::new(); for &(a, b) in flat_results_disconnected { @@ -1058,8 +1095,15 @@ impl ScheduleGraph { let access_a = system_a.component_access(); let access_b = system_b.component_access(); if !access_a.is_compatible(access_b) { - let conflicts = access_a.get_conflicts(access_b); - conflicting_systems.push((a, b, conflicts)); + let conflicts: Vec<_> = access_a + .get_conflicts(access_b) + .into_iter() + .filter(|id| ignored_ambiguities.contains(id)) + .collect(); + + if !conflicts.is_empty() { + conflicting_systems.push((a, b, conflicts)); + } } } } @@ -1171,6 +1215,7 @@ impl ScheduleGraph { &mut self, schedule: &mut SystemSchedule, components: &Components, + ignored_ambiguities: &Vec, schedule_label: &BoxedScheduleLabel, ) -> Result<(), ScheduleBuildError> { if !self.uninit.is_empty() { @@ -1196,7 +1241,7 @@ impl ScheduleGraph { self.system_set_conditions[id.index()] = conditions; } - *schedule = self.build_schedule(components, schedule_label)?; + *schedule = self.build_schedule(components, schedule_label, ignored_ambiguities)?; // move systems into new schedule for &id in &schedule.system_ids { @@ -1579,17 +1624,17 @@ impl ScheduleGraph { ambiguities .iter() .map(move |(system_a, system_b, conflicts)| { + let conflict_names: Vec<_> = conflicts + .iter() + .map(|id| components.get_name(*id).unwrap()) + .collect(); + let name_a = self.get_node_name(system_a); let name_b = self.get_node_name(system_b); debug_assert!(system_a.is_system(), "{name_a} is not a system."); debug_assert!(system_b.is_system(), "{name_b} is not a system."); - let conflict_names: Vec<_> = conflicts - .iter() - .map(|id| components.get_name(*id).unwrap()) - .collect(); - (name_a, name_b, conflict_names) }) } From 5c56cbc1ad83dfdcf402a0623d4bb00e45f8385e Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 14:00:55 -0700 Subject: [PATCH 02/14] refactor to store ignored to resource on world --- crates/bevy_ecs/src/schedule/mod.rs | 63 ++++++++------------- crates/bevy_ecs/src/schedule/schedule.rs | 72 +++++++++++++----------- crates/bevy_ecs/src/world/mod.rs | 18 +++++- 3 files changed, 79 insertions(+), 74 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index bad5271eb09a6..5ce57911e4d67 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -721,7 +721,6 @@ mod tests { use super::*; // Required to make the derive macro behave use crate as bevy_ecs; - use crate::component::ComponentId; use crate::event::Events; use crate::prelude::*; @@ -1056,6 +1055,7 @@ mod tests { schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); let mut world = World::new(); + schedule.graph_mut().initialize(&mut world); let _ = schedule.graph_mut().build_schedule( world.components(), @@ -1078,58 +1078,43 @@ mod tests { ); } - // helper function for testing ambiguities - fn assert_ambiguities_empty( - schedule: &mut Schedule, - world: &mut World, - ignored_ambiguities: Vec, - ) { - schedule.graph_mut().initialize(world); - let _ = schedule.graph_mut().build_schedule( - world.components(), - &TestSchedule.dyn_clone(), - &ignored_ambiguities, - ); - - let ambiguities: Vec<_> = schedule - .graph() - .conflicts_to_string(schedule.graph().conflicting_systems(), world.components()) - .collect(); - - assert!(ambiguities.is_empty()); - } - #[test] fn ignore_component_resource_ambiguities() { - fn system_a(_res: ResMut) {} - fn system_b(_res: ResMut) {} - let mut world = World::new(); world.insert_resource(R); - - let resource_id = world.components.resource_id::().unwrap(); - + world.allow_ambiguous_resource::(); let mut schedule = Schedule::new(TestSchedule); - schedule.add_systems((system_a, system_b)); - assert_ambiguities_empty(&mut schedule, &mut world, vec![resource_id]); + //check resource + schedule.add_systems((resmut_system, res_system)); + schedule.initialize(&mut world).unwrap(); + assert!(schedule.graph().conflicting_systems().is_empty()); + + // check components + world.allow_ambiguous_component::(); + schedule.add_systems((write_component_system, read_component_system)); + schedule.initialize(&mut world).unwrap(); + assert!(schedule.graph().conflicting_systems().is_empty()); } #[test] fn ignore_before_add() { - todo!(); - fn system_a(_res: ResMut) {} - fn system_b(_res: ResMut) {} - let mut world = World::new(); + world.allow_ambiguous_resource::(); world.insert_resource(R); - - let resource_id = world.components.resource_id::().unwrap(); - let mut schedule = Schedule::new(TestSchedule); - schedule.add_systems((system_a, system_b)); - assert_ambiguities_empty(&mut schedule, &mut world, vec![resource_id]); + //check resource + schedule.add_systems((resmut_system, res_system)); + schedule.initialize(&mut world).unwrap(); + assert!(schedule.graph().conflicting_systems().is_empty()); + + // check components + world.init_component::(); + world.allow_ambiguous_component::(); + schedule.add_systems((write_component_system, read_component_system)); + schedule.initialize(&mut world).unwrap(); + assert!(schedule.graph().conflicting_systems().is_empty()); } } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 008349672352e..97723c71c1cff 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -17,7 +17,6 @@ use fixedbitset::FixedBitSet; use crate::{ self as bevy_ecs, - change_detection::Mut, component::{ComponentDescriptor, ComponentId, Components, Tick}, prelude::Component, schedule::*, @@ -29,8 +28,6 @@ use crate::{ #[derive(Default, Resource)] pub struct Schedules { inner: HashMap, - /// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems - ambiguous_components: Vec, } impl Schedules { @@ -38,7 +35,6 @@ impl Schedules { pub fn new() -> Self { Self { inner: HashMap::new(), - ambiguous_components: Vec::new(), } } @@ -115,27 +111,6 @@ impl Schedules { schedule.set_build_settings(schedule_build_settings.clone()); } } - - /// Ignore ambiguities in component T. - /// - /// [`Components`] should be from the same world that Schedules belongs to - pub fn allow_ambiguous_component(&mut self, world: &mut World) { - self.ambiguous_components.push(world.init_component::()); - } - - /// Ignore ambiguities in resource T. - /// - /// [`Components`] should be from the same world that Schedules belongs to - pub fn allow_ambiguous_resource(&mut self, world: &mut World) { - // TODO: think about moving this to World or Components - let resource_id = world.components.resource_id::().unwrap_or_else(|| { - world.components.init_component_with_descriptor( - &mut world.storages, - ComponentDescriptor::new_resource::(), - ) - }); - self.ambiguous_components.push(resource_id); - } } fn make_executor(kind: ExecutorKind) -> Box { @@ -288,14 +263,16 @@ impl Schedule { pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> { if self.graph.changed { self.graph.initialize(world); - world.resource_scope(|world, schedules: Mut| { - self.graph.update_schedule( - &mut self.executable, - world.components(), - &schedules.ambiguous_components, - &self.name, - ) - })?; + let ignore_ambiguities = world + .get_resource::() + .cloned() + .unwrap_or_default(); + self.graph.update_schedule( + &mut self.executable, + world.components(), + &ignore_ambiguities.0, + &self.name, + )?; self.graph.changed = false; self.executor_initialized = false; } @@ -1098,7 +1075,7 @@ impl ScheduleGraph { let conflicts: Vec<_> = access_a .get_conflicts(access_b) .into_iter() - .filter(|id| ignored_ambiguities.contains(id)) + .filter(|id| !ignored_ambiguities.contains(id)) .collect(); if !conflicts.is_empty() { @@ -1755,6 +1732,33 @@ impl ScheduleBuildSettings { } } +/// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems +#[derive(Resource, Default, Clone)] +pub struct IgnoreSchedulingAmbiguities(Vec); + +impl IgnoreSchedulingAmbiguities { + /// Ignore ambiguities in component T. + /// + /// [`Components`] should be from the same world that Schedules belongs to + pub fn allow_ambiguous_component(&mut self, world: &mut World) { + self.0.push(world.init_component::()); + } + + /// Ignore ambiguities in resource T. + /// + /// [`Components`] should be from the same world that Schedules belongs to + pub fn allow_ambiguous_resource(&mut self, world: &mut World) { + // TODO: think about moving this to World or Components + let resource_id = world.components.resource_id::().unwrap_or_else(|| { + world.components.init_component_with_descriptor( + &mut world.storages, + ComponentDescriptor::new_resource::(), + ) + }); + self.0.push(resource_id); + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index be43fcce3d770..6a9cf1922c2f4 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -20,7 +20,7 @@ use crate::{ event::{Event, Events}, query::{DebugCheckedUnwrap, QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery}, removal_detection::RemovedComponentEvents, - schedule::{Schedule, ScheduleLabel, Schedules}, + schedule::{IgnoreSchedulingAmbiguities, Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, system::Resource, world::error::TryRunScheduleError, @@ -2087,6 +2087,22 @@ impl World { pub fn run_schedule(&mut self, label: impl AsRef) { self.schedule_scope(label, |world, sched| sched.run(world)); } + + pub fn allow_ambiguous_component(&mut self) { + let mut ignore_ambiguities = self + .remove_resource::() + .unwrap_or_default(); + ignore_ambiguities.allow_ambiguous_component::(self); + self.insert_resource(ignore_ambiguities); + } + + pub fn allow_ambiguous_resource(&mut self) { + let mut ignore_ambiguities = self + .remove_resource::() + .unwrap_or_default(); + ignore_ambiguities.allow_ambiguous_resource::(self); + self.insert_resource(ignore_ambiguities); + } } impl fmt::Debug for World { From e8339f6a26b26adcc07dfbb55f66a62f68276b69 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 15:41:02 -0700 Subject: [PATCH 03/14] clean up --- crates/bevy_ecs/src/schedule/mod.rs | 25 ++--------------- crates/bevy_ecs/src/schedule/schedule.rs | 35 ++++++++---------------- crates/bevy_ecs/src/world/mod.rs | 2 ++ 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 5ce57911e4d67..263c7f409d14b 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -1010,7 +1010,7 @@ mod tests { let _ = schedule.graph_mut().build_schedule( world.components(), &TestSchedule.dyn_clone(), - &Vec::new(), + &[], ); let ambiguities: Vec<_> = schedule @@ -1055,12 +1055,11 @@ mod tests { schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); let mut world = World::new(); - schedule.graph_mut().initialize(&mut world); let _ = schedule.graph_mut().build_schedule( world.components(), &TestSchedule.dyn_clone(), - &Vec::new(), + &[], ); let ambiguities: Vec<_> = schedule @@ -1096,25 +1095,5 @@ mod tests { schedule.initialize(&mut world).unwrap(); assert!(schedule.graph().conflicting_systems().is_empty()); } - - #[test] - fn ignore_before_add() { - let mut world = World::new(); - world.allow_ambiguous_resource::(); - world.insert_resource(R); - let mut schedule = Schedule::new(TestSchedule); - - //check resource - schedule.add_systems((resmut_system, res_system)); - schedule.initialize(&mut world).unwrap(); - assert!(schedule.graph().conflicting_systems().is_empty()); - - // check components - world.init_component::(); - world.allow_ambiguous_component::(); - schedule.add_systems((write_component_system, read_component_system)); - schedule.initialize(&mut world).unwrap(); - assert!(schedule.graph().conflicting_systems().is_empty()); - } } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 97723c71c1cff..ad96f35a0b7cf 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -17,7 +17,7 @@ use fixedbitset::FixedBitSet; use crate::{ self as bevy_ecs, - component::{ComponentDescriptor, ComponentId, Components, Tick}, + component::{ComponentId, Components, Tick}, prelude::Component, schedule::*, system::{BoxedSystem, Resource, System}, @@ -880,7 +880,7 @@ impl ScheduleGraph { &mut self, components: &Components, schedule_label: &BoxedScheduleLabel, - ignored_ambiguities: &Vec, + ignored_ambiguities: &[ComponentId], ) -> Result { // check hierarchy for cycles self.hierarchy.topsort = @@ -1053,7 +1053,7 @@ impl ScheduleGraph { &self, flat_results_disconnected: &Vec<(NodeId, NodeId)>, ambiguous_with_flattened: &GraphMap, - ignored_ambiguities: &Vec, + ignored_ambiguities: &[ComponentId], ) -> Vec<(NodeId, NodeId, Vec)> { let mut conflicting_systems = Vec::new(); for &(a, b) in flat_results_disconnected { @@ -1192,7 +1192,7 @@ impl ScheduleGraph { &mut self, schedule: &mut SystemSchedule, components: &Components, - ignored_ambiguities: &Vec, + ignored_ambiguities: &[ComponentId], schedule_label: &BoxedScheduleLabel, ) -> Result<(), ScheduleBuildError> { if !self.uninit.is_empty() { @@ -1601,17 +1601,17 @@ impl ScheduleGraph { ambiguities .iter() .map(move |(system_a, system_b, conflicts)| { - let conflict_names: Vec<_> = conflicts - .iter() - .map(|id| components.get_name(*id).unwrap()) - .collect(); - let name_a = self.get_node_name(system_a); let name_b = self.get_node_name(system_b); debug_assert!(system_a.is_system(), "{name_a} is not a system."); debug_assert!(system_b.is_system(), "{name_b} is not a system."); + let conflict_names: Vec<_> = conflicts + .iter() + .map(|id| components.get_name(*id).unwrap()) + .collect(); + (name_a, name_b, conflict_names) }) } @@ -1737,25 +1737,14 @@ impl ScheduleBuildSettings { pub struct IgnoreSchedulingAmbiguities(Vec); impl IgnoreSchedulingAmbiguities { - /// Ignore ambiguities in component T. - /// - /// [`Components`] should be from the same world that Schedules belongs to + /// Ignore ambiguities between systems in [`Component`] T. pub fn allow_ambiguous_component(&mut self, world: &mut World) { self.0.push(world.init_component::()); } - /// Ignore ambiguities in resource T. - /// - /// [`Components`] should be from the same world that Schedules belongs to + /// Ignore ambiguities between systems in [`Resource`] T. pub fn allow_ambiguous_resource(&mut self, world: &mut World) { - // TODO: think about moving this to World or Components - let resource_id = world.components.resource_id::().unwrap_or_else(|| { - world.components.init_component_with_descriptor( - &mut world.storages, - ComponentDescriptor::new_resource::(), - ) - }); - self.0.push(resource_id); + self.0.push(world.components.init_resource::()); } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 6a9cf1922c2f4..569fe641b7b42 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2088,6 +2088,7 @@ impl World { self.schedule_scope(label, |world, sched| sched.run(world)); } + /// Ignore ambiguities between systems in [`Component`] T. pub fn allow_ambiguous_component(&mut self) { let mut ignore_ambiguities = self .remove_resource::() @@ -2096,6 +2097,7 @@ impl World { self.insert_resource(ignore_ambiguities); } + /// Ignore ambiguities between systems in [`Resource`] T. pub fn allow_ambiguous_resource(&mut self) { let mut ignore_ambiguities = self .remove_resource::() From 8354ee9641cdf58e7240e71d1ef4cf5462d894ef Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 17:03:23 -0700 Subject: [PATCH 04/14] add methods on app --- crates/bevy_app/src/app.rs | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 566de2686c9ca..daf3c8b6d9de3 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -854,6 +854,83 @@ impl App { .configure_schedules(schedule_build_settings); self } + + /// When doing [ambiguity checking](bevy_ecs::scheduling::ScheduleBuildSettings) this + /// ignores systems that are ambiguious on [`Component`] T. + /// + /// This settings only applies to the main world. To apply this to other worlds call the + /// [corresponding method](World::allow_ambiguous_component) on World + /// + /// ## Example + /// + /// ```rust + /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; + /// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings}; + /// # use bevy_utils::default; + /// + /// #[derive(Component)] + /// struct A; + /// + /// // these systems are ambiguous on A + /// fn system_1(_: Query<&mut A>) {} + /// fn system_2(_: Query<&A>) {} + /// + /// let mut app = App::new(); + /// app.configure_schedules(ScheduleBuildSettings { + /// ambiguity_detection: LogLevel::Error, + /// ..default() + /// }); + /// + /// app.add_systems(Update, ( system_1, system_2 )); + /// app.allow_ambiguous_component::(); + /// + /// // running the app does not error. + /// app.update(); + /// ``` + pub fn allow_ambiguous_component(&mut self) -> &mut Self { + self.world.allow_ambiguous_component::(); + self + } + + /// When doing [ambiguity checking](bevy_ecs::scheduling::ScheduleBuildSettings) this + /// ignores systems that are ambiguious on [`Resource`] T. + /// + /// This settings only applies to the main world. To apply this to other worlds call the + /// [corresponding method](World::allow_ambiguous_resource) on World + /// + /// ## Example + /// + /// ```rust + /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; + /// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings}; + /// # use bevy_utils::default; + /// + /// #[derive(Resource)] + /// struct R; + /// + /// // these systems are ambiguous on A + /// fn system_1(_: ResMut) {} + /// fn system_2(_: Res) {} + /// + /// let mut app = App::new(); + /// app.configure_schedules(ScheduleBuildSettings { + /// ambiguity_detection: LogLevel::Error, + /// ..default() + /// }); + /// app.insert_resource(R); + /// + /// app.add_systems(Update, ( system_1, system_2 )); + /// app.allow_ambiguous_resource::(); + /// + /// // running the app does not error. + /// app.update(); + /// ``` + pub fn allow_ambiguous_resource(&mut self) -> &mut Self { + self.world.allow_ambiguous_resource::(); + self + } } fn run_once(mut app: App) { From e0af2188b5e4d282a8f84d15473b11983d17a080 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 17:14:55 -0700 Subject: [PATCH 05/14] fix docs --- crates/bevy_app/src/app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index daf3c8b6d9de3..369d7959b260f 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -855,7 +855,7 @@ impl App { self } - /// When doing [ambiguity checking](bevy_ecs::scheduling::ScheduleBuildSettings) this + /// When doing [ambiguity checking](bevy_ecs::schedule::ScheduleBuildSettings) this /// ignores systems that are ambiguious on [`Component`] T. /// /// This settings only applies to the main world. To apply this to other worlds call the @@ -893,7 +893,7 @@ impl App { self } - /// When doing [ambiguity checking](bevy_ecs::scheduling::ScheduleBuildSettings) this + /// When doing [ambiguity checking](bevy_ecs::schedule::ScheduleBuildSettings) this /// ignores systems that are ambiguious on [`Resource`] T. /// /// This settings only applies to the main world. To apply this to other worlds call the From d5f0864ed3e805d8a86d4c3f46820c2b92de6de7 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 21 Sep 2023 22:10:02 -0700 Subject: [PATCH 06/14] ignore ambiguities on Assets --- crates/bevy_asset/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index fcf4b09f19ba3..a65e6fff9f17f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -261,6 +261,9 @@ pub trait AssetApp { /// * Registering the [`Asset`] in the [`AssetServer`] /// * Initializing the [`AssetEvent`] resource for the [`Asset`] /// * Adding other relevant systems and resources for the [`Asset`] + /// * Ignoring schedule ambiguities in [`Assets`] resource. Any time a system takes + /// mutable access to this resource this causes a conflict, but they rarely actually + /// modify the same underlying asset. fn init_asset(&mut self) -> &mut Self; /// Registers the asset type `T` using `[App::register]`, /// and adds [`ReflectAsset`] type data to `T` and [`ReflectHandle`] type data to [`Handle`] in the type registry. @@ -301,6 +304,7 @@ impl AssetApp for App { )); } self.insert_resource(assets) + .allow_ambiguous_resource::>() .add_event::>() .register_type::>() .register_type::>() From 5b3669901e66fc9245144e9ea6d5a1bd4b532f83 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Fri, 22 Sep 2023 13:14:13 -0700 Subject: [PATCH 07/14] ignore -> ignored --- crates/bevy_ecs/src/schedule/schedule.rs | 12 ++++++------ crates/bevy_ecs/src/world/mod.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index ad96f35a0b7cf..8b951d2830a3a 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -263,14 +263,14 @@ impl Schedule { pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> { if self.graph.changed { self.graph.initialize(world); - let ignore_ambiguities = world - .get_resource::() + let ignored_ambiguities = world + .get_resource::() .cloned() .unwrap_or_default(); self.graph.update_schedule( &mut self.executable, world.components(), - &ignore_ambiguities.0, + &ignored_ambiguities.0, &self.name, )?; self.graph.changed = false; @@ -1733,10 +1733,10 @@ impl ScheduleBuildSettings { } /// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems -#[derive(Resource, Default, Clone)] -pub struct IgnoreSchedulingAmbiguities(Vec); +#[derive(Resource, Default, Clone, Debug)] +pub struct IgnoredSchedulingAmbiguities(Vec); -impl IgnoreSchedulingAmbiguities { +impl IgnoredSchedulingAmbiguities { /// Ignore ambiguities between systems in [`Component`] T. pub fn allow_ambiguous_component(&mut self, world: &mut World) { self.0.push(world.init_component::()); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 569fe641b7b42..fa970f237a269 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -20,7 +20,7 @@ use crate::{ event::{Event, Events}, query::{DebugCheckedUnwrap, QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery}, removal_detection::RemovedComponentEvents, - schedule::{IgnoreSchedulingAmbiguities, Schedule, ScheduleLabel, Schedules}, + schedule::{IgnoredSchedulingAmbiguities, Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, system::Resource, world::error::TryRunScheduleError, @@ -2090,20 +2090,20 @@ impl World { /// Ignore ambiguities between systems in [`Component`] T. pub fn allow_ambiguous_component(&mut self) { - let mut ignore_ambiguities = self - .remove_resource::() + let mut ignored_ambiguities = self + .remove_resource::() .unwrap_or_default(); - ignore_ambiguities.allow_ambiguous_component::(self); - self.insert_resource(ignore_ambiguities); + ignored_ambiguities.allow_ambiguous_component::(self); + self.insert_resource(ignored_ambiguities); } /// Ignore ambiguities between systems in [`Resource`] T. pub fn allow_ambiguous_resource(&mut self) { - let mut ignore_ambiguities = self - .remove_resource::() + let mut ignored_ambiguities = self + .remove_resource::() .unwrap_or_default(); - ignore_ambiguities.allow_ambiguous_resource::(self); - self.insert_resource(ignore_ambiguities); + ignored_ambiguities.allow_ambiguous_resource::(self); + self.insert_resource(ignored_ambiguities); } } From fc34034254c8cd6f5817a5e7f297378f0ac0dfe7 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Fri, 22 Sep 2023 14:38:54 -0700 Subject: [PATCH 08/14] use BTreeSet instead of Vec --- crates/bevy_ecs/src/schedule/mod.rs | 6 ++++-- crates/bevy_ecs/src/schedule/schedule.rs | 13 +++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 263c7f409d14b..4ed863944acfb 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -718,6 +718,8 @@ mod tests { } mod system_ambiguity { + use std::collections::BTreeSet; + use super::*; // Required to make the derive macro behave use crate as bevy_ecs; @@ -1010,7 +1012,7 @@ mod tests { let _ = schedule.graph_mut().build_schedule( world.components(), &TestSchedule.dyn_clone(), - &[], + &BTreeSet::new(), ); let ambiguities: Vec<_> = schedule @@ -1059,7 +1061,7 @@ mod tests { let _ = schedule.graph_mut().build_schedule( world.components(), &TestSchedule.dyn_clone(), - &[], + &BTreeSet::new(), ); let ambiguities: Vec<_> = schedule diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 8b951d2830a3a..dd93036681539 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1,4 +1,5 @@ use std::{ + collections::BTreeSet, fmt::{Debug, Write}, result::Result, }; @@ -880,7 +881,7 @@ impl ScheduleGraph { &mut self, components: &Components, schedule_label: &BoxedScheduleLabel, - ignored_ambiguities: &[ComponentId], + ignored_ambiguities: &BTreeSet, ) -> Result { // check hierarchy for cycles self.hierarchy.topsort = @@ -1053,7 +1054,7 @@ impl ScheduleGraph { &self, flat_results_disconnected: &Vec<(NodeId, NodeId)>, ambiguous_with_flattened: &GraphMap, - ignored_ambiguities: &[ComponentId], + ignored_ambiguities: &BTreeSet, ) -> Vec<(NodeId, NodeId, Vec)> { let mut conflicting_systems = Vec::new(); for &(a, b) in flat_results_disconnected { @@ -1192,7 +1193,7 @@ impl ScheduleGraph { &mut self, schedule: &mut SystemSchedule, components: &Components, - ignored_ambiguities: &[ComponentId], + ignored_ambiguities: &BTreeSet, schedule_label: &BoxedScheduleLabel, ) -> Result<(), ScheduleBuildError> { if !self.uninit.is_empty() { @@ -1734,17 +1735,17 @@ impl ScheduleBuildSettings { /// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems #[derive(Resource, Default, Clone, Debug)] -pub struct IgnoredSchedulingAmbiguities(Vec); +pub struct IgnoredSchedulingAmbiguities(BTreeSet); impl IgnoredSchedulingAmbiguities { /// Ignore ambiguities between systems in [`Component`] T. pub fn allow_ambiguous_component(&mut self, world: &mut World) { - self.0.push(world.init_component::()); + self.0.insert(world.init_component::()); } /// Ignore ambiguities between systems in [`Resource`] T. pub fn allow_ambiguous_resource(&mut self, world: &mut World) { - self.0.push(world.components.init_resource::()); + self.0.insert(world.components.init_resource::()); } } From 3330c4a2e1efa4f34bd6cfca9318dbfa9ca2fc9d Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Fri, 22 Sep 2023 16:27:53 -0700 Subject: [PATCH 09/14] add pretty printer for ignored ambiguities --- crates/bevy_ecs/src/schedule/schedule.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index dd93036681539..c285c794b806e 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1,12 +1,12 @@ use std::{ - collections::BTreeSet, + collections::{btree_set::Iter, BTreeSet}, fmt::{Debug, Write}, result::Result, }; -use bevy_utils::default; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; +use bevy_utils::{default, tracing::info}; use bevy_utils::{ petgraph::{algo::TarjanScc, prelude::*}, thiserror::Error, @@ -1747,6 +1747,24 @@ impl IgnoredSchedulingAmbiguities { pub fn allow_ambiguous_resource(&mut self, world: &mut World) { self.0.insert(world.components.init_resource::()); } + + /// Iterate through the [`ComponentId`]'s that will be ignored. + pub fn iter(&self) -> Iter<'_, ComponentId> { + self.0.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_names(&self, components: &Components) { + let mut message = "Ambiguities of the following types are ignored:\n".to_string(); + for id in self.iter() { + writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap(); + } + + info!("{}", message); + } } #[cfg(test)] From e93b97a60dab6b9d183dff7e4ed0908aefc1faef Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 22 Sep 2023 16:33:22 -0700 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Ryan Johnson --- crates/bevy_app/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 369d7959b260f..f3c6d6ecb370a 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -910,7 +910,7 @@ impl App { /// #[derive(Resource)] /// struct R; /// - /// // these systems are ambiguous on A + /// // these systems are ambiguous on R /// fn system_1(_: ResMut) {} /// fn system_2(_: Res) {} /// From a2db34053522af962fb2687a26ffeaf450915740 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Fri, 22 Sep 2023 16:58:09 -0700 Subject: [PATCH 11/14] reword docs --- crates/bevy_ecs/src/schedule/schedule.rs | 10 ++++++---- crates/bevy_ecs/src/world/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index c285c794b806e..752a3ec20388a 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1733,17 +1733,17 @@ impl ScheduleBuildSettings { } } -/// list of [`ComponentId`]'s to ignore when reporting ambiguity conflicts between systems +/// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts #[derive(Resource, Default, Clone, Debug)] pub struct IgnoredSchedulingAmbiguities(BTreeSet); impl IgnoredSchedulingAmbiguities { - /// Ignore ambiguities between systems in [`Component`] T. + /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. pub fn allow_ambiguous_component(&mut self, world: &mut World) { self.0.insert(world.init_component::()); } - /// Ignore ambiguities between systems in [`Resource`] T. + /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. pub fn allow_ambiguous_resource(&mut self, world: &mut World) { self.0.insert(world.components.init_resource::()); } @@ -1758,7 +1758,9 @@ impl IgnoredSchedulingAmbiguities { /// May panic or retrieve incorrect names if [`Components`] is not from the same /// world pub fn print_names(&self, components: &Components) { - let mut message = "Ambiguities of the following types are ignored:\n".to_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(); } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index fa970f237a269..819981845c626 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2088,7 +2088,7 @@ impl World { self.schedule_scope(label, |world, sched| sched.run(world)); } - /// Ignore ambiguities between systems in [`Component`] T. + /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. pub fn allow_ambiguous_component(&mut self) { let mut ignored_ambiguities = self .remove_resource::() @@ -2097,7 +2097,7 @@ impl World { self.insert_resource(ignored_ambiguities); } - /// Ignore ambiguities between systems in [`Resource`] T. + /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. pub fn allow_ambiguous_resource(&mut self) { let mut ignored_ambiguities = self .remove_resource::() From 3d407c5530012c127b6ddb663f05872a1c433ee4 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Mon, 25 Sep 2023 11:25:50 -0700 Subject: [PATCH 12/14] return impl Iterator --- crates/bevy_ecs/src/schedule/schedule.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 752a3ec20388a..85253a2ee77c5 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1,5 +1,5 @@ use std::{ - collections::{btree_set::Iter, BTreeSet}, + collections::BTreeSet, fmt::{Debug, Write}, result::Result, }; @@ -1749,7 +1749,7 @@ impl IgnoredSchedulingAmbiguities { } /// Iterate through the [`ComponentId`]'s that will be ignored. - pub fn iter(&self) -> Iter<'_, ComponentId> { + pub fn iter(&self) -> impl Iterator + '_ { self.0.iter() } From 4e988c1f735fbf3fade97d4300fa19be7d5edfc8 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Thu, 28 Sep 2023 21:53:58 -0700 Subject: [PATCH 13/14] add test for ignoring assets ambiguities --- crates/bevy_asset/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index a65e6fff9f17f..751b56d8b3260 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -431,8 +431,11 @@ mod tests { }; use bevy_app::{App, Update}; use bevy_core::TaskPoolPlugin; - use bevy_ecs::event::ManualEventReader; use bevy_ecs::prelude::*; + use bevy_ecs::{ + event::ManualEventReader, + schedule::{LogLevel, ScheduleBuildSettings}, + }; use bevy_log::LogPlugin; use bevy_reflect::TypePath; use bevy_utils::BoxedFuture; @@ -1170,4 +1173,23 @@ mod tests { None }); } + + #[test] + fn ignore_system_ambiguities_on_assets() { + let mut app = App::new(); + app.add_plugins(AssetPlugin::default()) + .init_asset::(); + + fn uses_assets(_asset: ResMut>) {} + app.add_systems(Update, (uses_assets, uses_assets)); + app.edit_schedule(Update, |s| { + s.set_build_settings(ScheduleBuildSettings { + ambiguity_detection: LogLevel::Error, + ..Default::default() + }); + }); + + // running schedule does not error on ambiguity between the 2 uses_assets systems + app.world.run_schedule(Update); + } } From 84da2af32a42dfcdb0eb020e717c0439aaf097af Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 3 Oct 2023 09:38:40 -0700 Subject: [PATCH 14/14] move ignore ambiguities into schedules --- crates/bevy_ecs/src/schedule/schedule.rs | 79 ++++++++++++------------ crates/bevy_ecs/src/world/mod.rs | 18 +++--- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 85253a2ee77c5..434454e83482c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -29,6 +29,8 @@ use crate::{ #[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 { @@ -36,6 +38,7 @@ impl Schedules { pub fn new() -> Self { Self { inner: HashMap::new(), + ignored_scheduling_ambiguities: BTreeSet::new(), } } @@ -112,6 +115,38 @@ impl Schedules { schedule.set_build_settings(schedule_build_settings.clone()); } } + + /// 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.init_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.init_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); + } } fn make_executor(kind: ExecutorKind) -> Box { @@ -265,13 +300,13 @@ impl Schedule { if self.graph.changed { self.graph.initialize(world); let ignored_ambiguities = world - .get_resource::() - .cloned() - .unwrap_or_default(); + .get_resource_or_insert_with::(Schedules::default) + .ignored_scheduling_ambiguities + .clone(); self.graph.update_schedule( &mut self.executable, world.components(), - &ignored_ambiguities.0, + &ignored_ambiguities, &self.name, )?; self.graph.changed = false; @@ -1733,42 +1768,6 @@ impl ScheduleBuildSettings { } } -/// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts -#[derive(Resource, Default, Clone, Debug)] -pub struct IgnoredSchedulingAmbiguities(BTreeSet); - -impl IgnoredSchedulingAmbiguities { - /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. - pub fn allow_ambiguous_component(&mut self, world: &mut World) { - self.0.insert(world.init_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.0.insert(world.components.init_resource::()); - } - - /// Iterate through the [`ComponentId`]'s that will be ignored. - pub fn iter(&self) -> impl Iterator + '_ { - self.0.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_names(&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() { - writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap(); - } - - info!("{}", message); - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 819981845c626..f4ca74b7185af 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -20,7 +20,7 @@ use crate::{ event::{Event, Events}, query::{DebugCheckedUnwrap, QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery}, removal_detection::RemovedComponentEvents, - schedule::{IgnoredSchedulingAmbiguities, Schedule, ScheduleLabel, Schedules}, + schedule::{Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, system::Resource, world::error::TryRunScheduleError, @@ -2090,20 +2090,16 @@ impl World { /// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`. pub fn allow_ambiguous_component(&mut self) { - let mut ignored_ambiguities = self - .remove_resource::() - .unwrap_or_default(); - ignored_ambiguities.allow_ambiguous_component::(self); - self.insert_resource(ignored_ambiguities); + let mut schedules = self.remove_resource::().unwrap_or_default(); + schedules.allow_ambiguous_component::(self); + self.insert_resource(schedules); } /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. pub fn allow_ambiguous_resource(&mut self) { - let mut ignored_ambiguities = self - .remove_resource::() - .unwrap_or_default(); - ignored_ambiguities.allow_ambiguous_resource::(self); - self.insert_resource(ignored_ambiguities); + let mut schedules = self.remove_resource::().unwrap_or_default(); + schedules.allow_ambiguous_resource::(self); + self.insert_resource(schedules); } }