Improve node encapsulation in ScheduleGraph (#20119)
# Objective - Part of #20115 We want to encapsulate each part of `ScheduleGraph` into its own specific struct to make parts of it easier to reuse and maintain. ## Solution - Pulled `ScheduleGraph::systems` and `ScheduleGraph::system_conditions` into a `Systems` struct and added a field for this new struct to `ScheduleGraph` - Broke up `ScheduleGraph::uninit` into `Systems::uninit` and `SystemSets::uninit` to eliminate `ScheduleGraph`'s direct field access of these types - Removed node and condition accessors from `ScheduleGraph`; the same operations are now available on `Systems` and `SystemSets` instead (accessible via their `pub` fields on `ScheduleGraph`) - Moved `Systems`, `SystemSets`, `SystemNode`, `SystemWithAccess`, and `ConditionWithAccess` into a separate file. ## Testing Added two new tests covering the API surface of `Systems` and `SystemSets`, respectively. --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
This commit is contained in:
parent
0747b66602
commit
2be3bc5310
@ -4,13 +4,13 @@ use bevy_platform::collections::HashMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
schedule::{SystemKey, SystemSetKey},
|
schedule::{SystemKey, SystemSetKey},
|
||||||
system::IntoSystem,
|
system::{IntoSystem, System},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
is_apply_deferred, ApplyDeferred, DiGraph, Direction, NodeId, ReportCycles, ScheduleBuildError,
|
is_apply_deferred, ApplyDeferred, DiGraph, Direction, NodeId, ReportCycles, ScheduleBuildError,
|
||||||
ScheduleBuildPass, ScheduleGraph, SystemNode,
|
ScheduleBuildPass, ScheduleGraph,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A [`ScheduleBuildPass`] that inserts [`ApplyDeferred`] systems into the schedule graph
|
/// A [`ScheduleBuildPass`] that inserts [`ApplyDeferred`] systems into the schedule graph
|
||||||
@ -49,10 +49,7 @@ impl AutoInsertApplyDeferredPass {
|
|||||||
fn add_auto_sync(&mut self, graph: &mut ScheduleGraph) -> SystemKey {
|
fn add_auto_sync(&mut self, graph: &mut ScheduleGraph) -> SystemKey {
|
||||||
let key = graph
|
let key = graph
|
||||||
.systems
|
.systems
|
||||||
.insert(SystemNode::new(Box::new(IntoSystem::into_system(
|
.insert(Box::new(IntoSystem::into_system(ApplyDeferred)), Vec::new());
|
||||||
ApplyDeferred,
|
|
||||||
))));
|
|
||||||
graph.system_conditions.insert(key, Vec::new());
|
|
||||||
|
|
||||||
// ignore ambiguities with auto sync points
|
// ignore ambiguities with auto sync points
|
||||||
// They aren't under user control, so no one should know or care.
|
// They aren't under user control, so no one should know or care.
|
||||||
@ -81,7 +78,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
let topo = graph.topsort_graph(dependency_flattened, ReportCycles::Dependency)?;
|
let topo = graph.topsort_graph(dependency_flattened, ReportCycles::Dependency)?;
|
||||||
|
|
||||||
fn set_has_conditions(graph: &ScheduleGraph, set: SystemSetKey) -> bool {
|
fn set_has_conditions(graph: &ScheduleGraph, set: SystemSetKey) -> bool {
|
||||||
!graph.set_conditions_at(set).is_empty()
|
graph.system_sets.has_conditions(set)
|
||||||
|| graph
|
|| graph
|
||||||
.hierarchy()
|
.hierarchy()
|
||||||
.graph()
|
.graph()
|
||||||
@ -94,7 +91,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn system_has_conditions(graph: &ScheduleGraph, key: SystemKey) -> bool {
|
fn system_has_conditions(graph: &ScheduleGraph, key: SystemKey) -> bool {
|
||||||
!graph.system_conditions[key].is_empty()
|
graph.systems.has_conditions(key)
|
||||||
|| graph
|
|| graph
|
||||||
.hierarchy()
|
.hierarchy()
|
||||||
.graph()
|
.graph()
|
||||||
@ -108,7 +105,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
|
|
||||||
let mut system_has_conditions_cache = HashMap::<SystemKey, bool>::default();
|
let mut system_has_conditions_cache = HashMap::<SystemKey, bool>::default();
|
||||||
let mut is_valid_explicit_sync_point = |key: SystemKey| {
|
let mut is_valid_explicit_sync_point = |key: SystemKey| {
|
||||||
is_apply_deferred(&graph.systems[key].get().unwrap().system)
|
is_apply_deferred(&graph.systems[key])
|
||||||
&& !*system_has_conditions_cache
|
&& !*system_has_conditions_cache
|
||||||
.entry(key)
|
.entry(key)
|
||||||
.or_insert_with(|| system_has_conditions(graph, key))
|
.or_insert_with(|| system_has_conditions(graph, key))
|
||||||
@ -148,7 +145,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
} else if !node_needs_sync {
|
} else if !node_needs_sync {
|
||||||
// No previous node has postponed sync points to add so check if the system itself
|
// No previous node has postponed sync points to add so check if the system itself
|
||||||
// has deferred params that require a sync point to apply them.
|
// has deferred params that require a sync point to apply them.
|
||||||
node_needs_sync = graph.systems[key].get().unwrap().system.has_deferred();
|
node_needs_sync = graph.systems[key].has_deferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
||||||
@ -160,7 +157,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
|
|
||||||
let mut edge_needs_sync = node_needs_sync;
|
let mut edge_needs_sync = node_needs_sync;
|
||||||
if node_needs_sync
|
if node_needs_sync
|
||||||
&& !graph.systems[target].get().unwrap().system.is_exclusive()
|
&& !graph.systems[target].is_exclusive()
|
||||||
&& self
|
&& self
|
||||||
.no_sync_edges
|
.no_sync_edges
|
||||||
.contains(&(*node, NodeId::System(target)))
|
.contains(&(*node, NodeId::System(target)))
|
||||||
@ -210,7 +207,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_apply_deferred(&graph.systems[target].get().unwrap().system) {
|
if is_apply_deferred(&graph.systems[target]) {
|
||||||
// We don't need to insert a sync point since ApplyDeferred is a sync point
|
// We don't need to insert a sync point since ApplyDeferred is a sync point
|
||||||
// already!
|
// already!
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -24,10 +24,7 @@ use crate::{
|
|||||||
ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
|
ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
|
||||||
SystemWithAccess,
|
SystemWithAccess,
|
||||||
},
|
},
|
||||||
system::{
|
system::{RunSystemError, System, SystemIn, SystemParamValidationError, SystemStateFlags},
|
||||||
RunSystemError, ScheduleSystem, System, SystemIn, SystemParamValidationError,
|
|
||||||
SystemStateFlags,
|
|
||||||
},
|
|
||||||
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,7 +154,7 @@ impl SystemSchedule {
|
|||||||
pub struct ApplyDeferred;
|
pub struct ApplyDeferred;
|
||||||
|
|
||||||
/// Returns `true` if the [`System`] is an instance of [`ApplyDeferred`].
|
/// Returns `true` if the [`System`] is an instance of [`ApplyDeferred`].
|
||||||
pub(super) fn is_apply_deferred(system: &ScheduleSystem) -> bool {
|
pub(super) fn is_apply_deferred(system: &dyn System<In = (), Out = ()>) -> bool {
|
||||||
system.type_id() == TypeId::of::<ApplyDeferred>()
|
system.type_id() == TypeId::of::<ApplyDeferred>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -712,7 +712,7 @@ impl ExecutorState {
|
|||||||
// Move the full context object into the new future.
|
// Move the full context object into the new future.
|
||||||
let context = *context;
|
let context = *context;
|
||||||
|
|
||||||
if is_apply_deferred(system) {
|
if is_apply_deferred(&**system) {
|
||||||
// TODO: avoid allocation
|
// TODO: avoid allocation
|
||||||
let unapplied_systems = self.unapplied_systems.clone();
|
let unapplied_systems = self.unapplied_systems.clone();
|
||||||
self.unapplied_systems.clear();
|
self.unapplied_systems.clear();
|
||||||
|
|||||||
@ -124,7 +124,7 @@ impl SystemExecutor for SimpleExecutor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_apply_deferred(system) {
|
if is_apply_deferred(&**system) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,7 +125,7 @@ impl SystemExecutor for SingleThreadedExecutor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_apply_deferred(system) {
|
if is_apply_deferred(&**system) {
|
||||||
self.apply_deferred(schedule, world);
|
self.apply_deferred(schedule, world);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,14 @@ mod auto_insert_apply_deferred;
|
|||||||
mod condition;
|
mod condition;
|
||||||
mod config;
|
mod config;
|
||||||
mod executor;
|
mod executor;
|
||||||
|
mod node;
|
||||||
mod pass;
|
mod pass;
|
||||||
mod schedule;
|
mod schedule;
|
||||||
mod set;
|
mod set;
|
||||||
mod stepping;
|
mod stepping;
|
||||||
|
|
||||||
use self::graph::*;
|
use self::graph::*;
|
||||||
pub use self::{condition::*, config::*, executor::*, schedule::*, set::*};
|
pub use self::{condition::*, config::*, executor::*, node::*, schedule::*, set::*};
|
||||||
pub use pass::ScheduleBuildPass;
|
pub use pass::ScheduleBuildPass;
|
||||||
|
|
||||||
pub use self::graph::NodeId;
|
pub use self::graph::NodeId;
|
||||||
|
|||||||
615
crates/bevy_ecs/src/schedule/node.rs
Normal file
615
crates/bevy_ecs/src/schedule/node.rs
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
|
use bevy_utils::prelude::DebugName;
|
||||||
|
use core::{
|
||||||
|
any::TypeId,
|
||||||
|
ops::{Index, IndexMut, Range},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy_platform::collections::HashMap;
|
||||||
|
use slotmap::{new_key_type, SecondaryMap, SlotMap};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
component::{CheckChangeTicks, ComponentId, Tick},
|
||||||
|
prelude::{SystemIn, SystemSet},
|
||||||
|
query::FilteredAccessSet,
|
||||||
|
schedule::{BoxedCondition, InternedSystemSet},
|
||||||
|
system::{
|
||||||
|
ReadOnlySystem, RunSystemError, ScheduleSystem, System, SystemParamValidationError,
|
||||||
|
SystemStateFlags,
|
||||||
|
},
|
||||||
|
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A [`SystemWithAccess`] stored in a [`ScheduleGraph`](crate::schedule::ScheduleGraph).
|
||||||
|
pub(crate) struct SystemNode {
|
||||||
|
pub(crate) inner: Option<SystemWithAccess>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`ScheduleSystem`] stored alongside the access returned from [`System::initialize`].
|
||||||
|
pub struct SystemWithAccess {
|
||||||
|
/// The system itself.
|
||||||
|
pub system: ScheduleSystem,
|
||||||
|
/// The access returned by [`System::initialize`].
|
||||||
|
/// This will be empty if the system has not been initialized yet.
|
||||||
|
pub access: FilteredAccessSet<ComponentId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemWithAccess {
|
||||||
|
/// Constructs a new [`SystemWithAccess`] from a [`ScheduleSystem`].
|
||||||
|
/// The `access` will initially be empty.
|
||||||
|
pub fn new(system: ScheduleSystem) -> Self {
|
||||||
|
Self {
|
||||||
|
system,
|
||||||
|
access: FilteredAccessSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System for SystemWithAccess {
|
||||||
|
type In = ();
|
||||||
|
type Out = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> DebugName {
|
||||||
|
self.system.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn type_id(&self) -> TypeId {
|
||||||
|
self.system.type_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flags(&self) -> SystemStateFlags {
|
||||||
|
self.system.flags()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn run_unsafe(
|
||||||
|
&mut self,
|
||||||
|
input: SystemIn<'_, Self>,
|
||||||
|
world: UnsafeWorldCell,
|
||||||
|
) -> Result<Self::Out, RunSystemError> {
|
||||||
|
// SAFETY: Caller ensures the same safety requirements.
|
||||||
|
unsafe { self.system.run_unsafe(input, world) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hotpatching")]
|
||||||
|
#[inline]
|
||||||
|
fn refresh_hotpatch(&mut self) {
|
||||||
|
self.system.refresh_hotpatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn apply_deferred(&mut self, world: &mut World) {
|
||||||
|
self.system.apply_deferred(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn queue_deferred(&mut self, world: DeferredWorld) {
|
||||||
|
self.system.queue_deferred(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn validate_param_unsafe(
|
||||||
|
&mut self,
|
||||||
|
world: UnsafeWorldCell,
|
||||||
|
) -> Result<(), SystemParamValidationError> {
|
||||||
|
// SAFETY: Caller ensures the same safety requirements.
|
||||||
|
unsafe { self.system.validate_param_unsafe(world) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet<ComponentId> {
|
||||||
|
self.system.initialize(world)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn check_change_tick(&mut self, check: CheckChangeTicks) {
|
||||||
|
self.system.check_change_tick(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
|
||||||
|
self.system.default_system_sets()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_last_run(&self) -> Tick {
|
||||||
|
self.system.get_last_run()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_last_run(&mut self, last_run: Tick) {
|
||||||
|
self.system.set_last_run(last_run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`BoxedCondition`] stored alongside the access returned from [`System::initialize`].
|
||||||
|
pub struct ConditionWithAccess {
|
||||||
|
/// The condition itself.
|
||||||
|
pub condition: BoxedCondition,
|
||||||
|
/// The access returned by [`System::initialize`].
|
||||||
|
/// This will be empty if the system has not been initialized yet.
|
||||||
|
pub access: FilteredAccessSet<ComponentId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConditionWithAccess {
|
||||||
|
/// Constructs a new [`ConditionWithAccess`] from a [`BoxedCondition`].
|
||||||
|
/// The `access` will initially be empty.
|
||||||
|
pub const fn new(condition: BoxedCondition) -> Self {
|
||||||
|
Self {
|
||||||
|
condition,
|
||||||
|
access: FilteredAccessSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System for ConditionWithAccess {
|
||||||
|
type In = ();
|
||||||
|
type Out = bool;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> DebugName {
|
||||||
|
self.condition.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn type_id(&self) -> TypeId {
|
||||||
|
self.condition.type_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flags(&self) -> SystemStateFlags {
|
||||||
|
self.condition.flags()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn run_unsafe(
|
||||||
|
&mut self,
|
||||||
|
input: SystemIn<'_, Self>,
|
||||||
|
world: UnsafeWorldCell,
|
||||||
|
) -> Result<Self::Out, RunSystemError> {
|
||||||
|
// SAFETY: Caller ensures the same safety requirements.
|
||||||
|
unsafe { self.condition.run_unsafe(input, world) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hotpatching")]
|
||||||
|
#[inline]
|
||||||
|
fn refresh_hotpatch(&mut self) {
|
||||||
|
self.condition.refresh_hotpatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn apply_deferred(&mut self, world: &mut World) {
|
||||||
|
self.condition.apply_deferred(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn queue_deferred(&mut self, world: DeferredWorld) {
|
||||||
|
self.condition.queue_deferred(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn validate_param_unsafe(
|
||||||
|
&mut self,
|
||||||
|
world: UnsafeWorldCell,
|
||||||
|
) -> Result<(), SystemParamValidationError> {
|
||||||
|
// SAFETY: Caller ensures the same safety requirements.
|
||||||
|
unsafe { self.condition.validate_param_unsafe(world) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet<ComponentId> {
|
||||||
|
self.condition.initialize(world)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn check_change_tick(&mut self, check: CheckChangeTicks) {
|
||||||
|
self.condition.check_change_tick(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
|
||||||
|
self.condition.default_system_sets()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_last_run(&self) -> Tick {
|
||||||
|
self.condition.get_last_run()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_last_run(&mut self, last_run: Tick) {
|
||||||
|
self.condition.set_last_run(last_run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemNode {
|
||||||
|
/// Create a new [`SystemNode`]
|
||||||
|
pub fn new(system: ScheduleSystem) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Some(SystemWithAccess::new(system)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain a reference to the [`SystemWithAccess`] represented by this node.
|
||||||
|
pub fn get(&self) -> Option<&SystemWithAccess> {
|
||||||
|
self.inner.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain a mutable reference to the [`SystemWithAccess`] represented by this node.
|
||||||
|
pub fn get_mut(&mut self) -> Option<&mut SystemWithAccess> {
|
||||||
|
self.inner.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_key_type! {
|
||||||
|
/// A unique identifier for a system in a [`ScheduleGraph`].
|
||||||
|
pub struct SystemKey;
|
||||||
|
/// A unique identifier for a system set in a [`ScheduleGraph`].
|
||||||
|
pub struct SystemSetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Container for systems in a schedule.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Systems {
|
||||||
|
/// List of systems in the schedule.
|
||||||
|
nodes: SlotMap<SystemKey, SystemNode>,
|
||||||
|
/// List of conditions for each system, in the same order as `nodes`.
|
||||||
|
conditions: SecondaryMap<SystemKey, Vec<ConditionWithAccess>>,
|
||||||
|
/// Systems and their conditions that have not been initialized yet.
|
||||||
|
uninit: Vec<SystemKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Systems {
|
||||||
|
/// Returns the number of systems in this container.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.nodes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this container is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.nodes.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the system with the given key, if it exists.
|
||||||
|
pub fn get(&self, key: SystemKey) -> Option<&SystemWithAccess> {
|
||||||
|
self.nodes.get(key).and_then(|node| node.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the system with the given key, if it exists.
|
||||||
|
pub fn get_mut(&mut self, key: SystemKey) -> Option<&mut SystemWithAccess> {
|
||||||
|
self.nodes.get_mut(key).and_then(|node| node.get_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the system with the given key, panicking
|
||||||
|
/// if it does not exist.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the system with the given key does not exist in this container.
|
||||||
|
pub(crate) fn node_mut(&mut self, key: SystemKey) -> &mut SystemNode {
|
||||||
|
&mut self.nodes[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the system with the given key has conditions.
|
||||||
|
pub fn has_conditions(&self, key: SystemKey) -> bool {
|
||||||
|
self.conditions
|
||||||
|
.get(key)
|
||||||
|
.is_some_and(|conditions| !conditions.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the conditions for the system with the given key, if it exists.
|
||||||
|
pub fn get_conditions(&self, key: SystemKey) -> Option<&[ConditionWithAccess]> {
|
||||||
|
self.conditions.get(key).map(Vec::as_slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the conditions for the system with the given key, if it exists.
|
||||||
|
pub fn get_conditions_mut(&mut self, key: SystemKey) -> Option<&mut Vec<ConditionWithAccess>> {
|
||||||
|
self.conditions.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all systems and their conditions in this
|
||||||
|
/// container.
|
||||||
|
pub fn iter(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (SystemKey, &ScheduleSystem, &[ConditionWithAccess])> + '_ {
|
||||||
|
self.nodes.iter().filter_map(|(key, node)| {
|
||||||
|
let system = &node.get()?.system;
|
||||||
|
let conditions = self
|
||||||
|
.conditions
|
||||||
|
.get(key)
|
||||||
|
.map(Vec::as_slice)
|
||||||
|
.unwrap_or_default();
|
||||||
|
Some((key, system, conditions))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new system into the container, along with its conditions,
|
||||||
|
/// and queues it to be initialized later in [`Systems::initialize`].
|
||||||
|
///
|
||||||
|
/// We have to defer initialization of systems in the container until we have
|
||||||
|
/// `&mut World` access, so we store these in a list until
|
||||||
|
/// [`Systems::initialize`] is called. This is usually done upon the first
|
||||||
|
/// run of the schedule.
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
system: ScheduleSystem,
|
||||||
|
conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
|
||||||
|
) -> SystemKey {
|
||||||
|
let key = self.nodes.insert(SystemNode::new(system));
|
||||||
|
self.conditions.insert(
|
||||||
|
key,
|
||||||
|
conditions
|
||||||
|
.into_iter()
|
||||||
|
.map(ConditionWithAccess::new)
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
self.uninit.push(key);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if all systems in this container have been initialized.
|
||||||
|
pub fn is_initialized(&self) -> bool {
|
||||||
|
self.uninit.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes all systems and their conditions that have not been
|
||||||
|
/// initialized yet.
|
||||||
|
pub fn initialize(&mut self, world: &mut World) {
|
||||||
|
for key in self.uninit.drain(..) {
|
||||||
|
let Some(system) = self.nodes.get_mut(key).and_then(|node| node.get_mut()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
system.access = system.system.initialize(world);
|
||||||
|
let Some(conditions) = self.conditions.get_mut(key) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for condition in conditions {
|
||||||
|
condition.access = condition.condition.initialize(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<SystemKey> for Systems {
|
||||||
|
type Output = SystemWithAccess;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn index(&self, key: SystemKey) -> &Self::Output {
|
||||||
|
self.get(key)
|
||||||
|
.unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<SystemKey> for Systems {
|
||||||
|
#[track_caller]
|
||||||
|
fn index_mut(&mut self, key: SystemKey) -> &mut Self::Output {
|
||||||
|
self.get_mut(key)
|
||||||
|
.unwrap_or_else(|| panic!("System with key {:?} does not exist in the schedule", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Container for system sets in a schedule.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SystemSets {
|
||||||
|
/// List of system sets in the schedule.
|
||||||
|
sets: SlotMap<SystemSetKey, InternedSystemSet>,
|
||||||
|
/// List of conditions for each system set, in the same order as `sets`.
|
||||||
|
conditions: SecondaryMap<SystemSetKey, Vec<ConditionWithAccess>>,
|
||||||
|
/// Map from system sets to their keys.
|
||||||
|
ids: HashMap<InternedSystemSet, SystemSetKey>,
|
||||||
|
/// System sets that have not been initialized yet.
|
||||||
|
uninit: Vec<UninitializedSet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A system set's conditions that have not been initialized yet.
|
||||||
|
struct UninitializedSet {
|
||||||
|
key: SystemSetKey,
|
||||||
|
/// The range of indices in [`SystemSets::conditions`] that correspond
|
||||||
|
/// to conditions that have not been initialized yet.
|
||||||
|
///
|
||||||
|
/// [`SystemSets::conditions`] for a given set may be appended to
|
||||||
|
/// multiple times (e.g. when `configure_sets` is called multiple with
|
||||||
|
/// the same set), so we need to track which conditions in that list
|
||||||
|
/// are newly added and not yet initialized.
|
||||||
|
///
|
||||||
|
/// Systems don't need this tracking because each `add_systems` call
|
||||||
|
/// creates separate nodes in the graph with their own conditions,
|
||||||
|
/// so all conditions are initialized together.
|
||||||
|
uninitialized_conditions: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemSets {
|
||||||
|
/// Returns the number of system sets in this container.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.sets.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this container is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.sets.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the given set is present in this container.
|
||||||
|
pub fn contains(&self, set: impl SystemSet) -> bool {
|
||||||
|
self.ids.contains_key(&set.intern())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the system set with the given key, if it exists.
|
||||||
|
pub fn get(&self, key: SystemSetKey) -> Option<&dyn SystemSet> {
|
||||||
|
self.sets.get(key).map(|set| &**set)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the key for the given system set, inserting it into this
|
||||||
|
/// container if it does not already exist.
|
||||||
|
pub fn get_key_or_insert(&mut self, set: InternedSystemSet) -> SystemSetKey {
|
||||||
|
*self.ids.entry(set).or_insert_with(|| {
|
||||||
|
let key = self.sets.insert(set);
|
||||||
|
self.conditions.insert(key, Vec::new());
|
||||||
|
key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the system set with the given key has conditions.
|
||||||
|
pub fn has_conditions(&self, key: SystemSetKey) -> bool {
|
||||||
|
self.conditions
|
||||||
|
.get(key)
|
||||||
|
.is_some_and(|conditions| !conditions.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the conditions for the system set with the given
|
||||||
|
/// key, if it exists.
|
||||||
|
pub fn get_conditions(&self, key: SystemSetKey) -> Option<&[ConditionWithAccess]> {
|
||||||
|
self.conditions.get(key).map(Vec::as_slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the conditions for the system set with
|
||||||
|
/// the given key, if it exists.
|
||||||
|
pub fn get_conditions_mut(
|
||||||
|
&mut self,
|
||||||
|
key: SystemSetKey,
|
||||||
|
) -> Option<&mut Vec<ConditionWithAccess>> {
|
||||||
|
self.conditions.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all system sets in this container, along with
|
||||||
|
/// their conditions.
|
||||||
|
pub fn iter(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (SystemSetKey, &dyn SystemSet, &[ConditionWithAccess])> {
|
||||||
|
self.sets.iter().filter_map(|(key, set)| {
|
||||||
|
let conditions = self.conditions.get(key)?.as_slice();
|
||||||
|
Some((key, &**set, conditions))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts conditions for a system set into the container, and queues the
|
||||||
|
/// newly added conditions to be initialized later in [`SystemSets::initialize`].
|
||||||
|
///
|
||||||
|
/// If the set was not already present in the container, it is added automatically.
|
||||||
|
///
|
||||||
|
/// We have to defer initialization of system set conditions in the container
|
||||||
|
/// until we have `&mut World` access, so we store these in a list until
|
||||||
|
/// [`SystemSets::initialize`] is called. This is usually done upon the
|
||||||
|
/// first run of the schedule.
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
set: InternedSystemSet,
|
||||||
|
new_conditions: Vec<Box<dyn ReadOnlySystem<In = (), Out = bool>>>,
|
||||||
|
) -> SystemSetKey {
|
||||||
|
let key = self.get_key_or_insert(set);
|
||||||
|
if !new_conditions.is_empty() {
|
||||||
|
let current_conditions = &mut self.conditions[key];
|
||||||
|
let start = current_conditions.len();
|
||||||
|
self.uninit.push(UninitializedSet {
|
||||||
|
key,
|
||||||
|
uninitialized_conditions: start..(start + new_conditions.len()),
|
||||||
|
});
|
||||||
|
current_conditions.extend(new_conditions.into_iter().map(ConditionWithAccess::new));
|
||||||
|
}
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if all system sets' conditions in this container have
|
||||||
|
/// been initialized.
|
||||||
|
pub fn is_initialized(&self) -> bool {
|
||||||
|
self.uninit.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes all system sets' conditions that have not been
|
||||||
|
/// initialized yet. Because a system set's conditions may be appended to
|
||||||
|
/// multiple times, we track which conditions were added since the last
|
||||||
|
/// initialization and only initialize those.
|
||||||
|
pub fn initialize(&mut self, world: &mut World) {
|
||||||
|
for uninit in self.uninit.drain(..) {
|
||||||
|
let Some(conditions) = self.conditions.get_mut(uninit.key) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for condition in &mut conditions[uninit.uninitialized_conditions] {
|
||||||
|
condition.access = condition.initialize(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<SystemSetKey> for SystemSets {
|
||||||
|
type Output = dyn SystemSet;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn index(&self, key: SystemSetKey) -> &Self::Output {
|
||||||
|
self.get(key).unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"System set with key {:?} does not exist in the schedule",
|
||||||
|
key
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use alloc::{boxed::Box, vec};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
prelude::SystemSet,
|
||||||
|
schedule::{SystemSets, Systems},
|
||||||
|
system::IntoSystem,
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(SystemSet, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct TestSet;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn systems() {
|
||||||
|
fn empty_system() {}
|
||||||
|
|
||||||
|
let mut systems = Systems::default();
|
||||||
|
assert!(systems.is_empty());
|
||||||
|
assert_eq!(systems.len(), 0);
|
||||||
|
|
||||||
|
let system = Box::new(IntoSystem::into_system(empty_system));
|
||||||
|
let key = systems.insert(system, vec![]);
|
||||||
|
|
||||||
|
assert!(!systems.is_empty());
|
||||||
|
assert_eq!(systems.len(), 1);
|
||||||
|
assert!(systems.get(key).is_some());
|
||||||
|
assert!(systems.get_conditions(key).is_some());
|
||||||
|
assert!(systems.get_conditions(key).unwrap().is_empty());
|
||||||
|
assert!(systems.get_mut(key).is_some());
|
||||||
|
assert!(!systems.is_initialized());
|
||||||
|
assert!(systems.iter().next().is_some());
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
systems.initialize(&mut world);
|
||||||
|
assert!(systems.is_initialized());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn system_sets() {
|
||||||
|
fn always_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sets = SystemSets::default();
|
||||||
|
assert!(sets.is_empty());
|
||||||
|
assert_eq!(sets.len(), 0);
|
||||||
|
|
||||||
|
let condition = Box::new(IntoSystem::into_system(always_true));
|
||||||
|
let key = sets.insert(TestSet.intern(), vec![condition]);
|
||||||
|
|
||||||
|
assert!(!sets.is_empty());
|
||||||
|
assert_eq!(sets.len(), 1);
|
||||||
|
assert!(sets.get(key).is_some());
|
||||||
|
assert!(sets.get_conditions(key).is_some());
|
||||||
|
assert!(!sets.get_conditions(key).unwrap().is_empty());
|
||||||
|
assert!(!sets.is_initialized());
|
||||||
|
assert!(sets.iter().next().is_some());
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
sets.initialize(&mut world);
|
||||||
|
assert!(sets.is_initialized());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,21 +15,18 @@ use bevy_utils::{default, prelude::DebugName, TypeIdMap};
|
|||||||
use core::{
|
use core::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
fmt::{Debug, Write},
|
fmt::{Debug, Write},
|
||||||
ops::Range,
|
|
||||||
};
|
};
|
||||||
use fixedbitset::FixedBitSet;
|
use fixedbitset::FixedBitSet;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use pass::ScheduleBuildPassObj;
|
use pass::ScheduleBuildPassObj;
|
||||||
use slotmap::{new_key_type, SecondaryMap, SlotMap};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
|
|
||||||
use crate::component::CheckChangeTicks;
|
use crate::{component::CheckChangeTicks, system::System};
|
||||||
use crate::{
|
use crate::{
|
||||||
component::{ComponentId, Components},
|
component::{ComponentId, Components},
|
||||||
prelude::Component,
|
prelude::Component,
|
||||||
query::FilteredAccessSet,
|
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
schedule::*,
|
schedule::*,
|
||||||
system::ScheduleSystem,
|
system::ScheduleSystem,
|
||||||
@ -391,20 +388,8 @@ impl Schedule {
|
|||||||
let a = a.into_system_set();
|
let a = a.into_system_set();
|
||||||
let b = b.into_system_set();
|
let b = b.into_system_set();
|
||||||
|
|
||||||
let Some(&a_id) = self.graph.system_sets.ids.get(&a.intern()) else {
|
let a_id = self.graph.system_sets.get_key_or_insert(a.intern());
|
||||||
panic!(
|
let b_id = self.graph.system_sets.get_key_or_insert(b.intern());
|
||||||
"Could not mark system as ambiguous, `{:?}` was not found in the schedule.
|
|
||||||
Did you try to call `ambiguous_with` before adding the system to the world?",
|
|
||||||
a
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let Some(&b_id) = self.graph.system_sets.ids.get(&b.intern()) else {
|
|
||||||
panic!(
|
|
||||||
"Could not mark system as ambiguous, `{:?}` was not found in the schedule.
|
|
||||||
Did you try to call `ambiguous_with` before adding the system to the world?",
|
|
||||||
b
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.graph
|
self.graph
|
||||||
.ambiguous_with
|
.ambiguous_with
|
||||||
@ -564,21 +549,21 @@ impl Schedule {
|
|||||||
/// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
|
/// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
|
||||||
/// This prevents overflow and thus prevents false positives.
|
/// This prevents overflow and thus prevents false positives.
|
||||||
pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
|
pub fn check_change_ticks(&mut self, check: CheckChangeTicks) {
|
||||||
for SystemWithAccess { system, .. } in &mut self.executable.systems {
|
for system in &mut self.executable.systems {
|
||||||
if !is_apply_deferred(system) {
|
if !is_apply_deferred(system) {
|
||||||
system.check_change_tick(check);
|
system.check_change_tick(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for conditions in &mut self.executable.system_conditions {
|
for conditions in &mut self.executable.system_conditions {
|
||||||
for system in conditions {
|
for condition in conditions {
|
||||||
system.condition.check_change_tick(check);
|
condition.check_change_tick(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for conditions in &mut self.executable.set_conditions {
|
for conditions in &mut self.executable.set_conditions {
|
||||||
for system in conditions {
|
for condition in conditions {
|
||||||
system.condition.check_change_tick(check);
|
condition.check_change_tick(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,141 +644,16 @@ impl Dag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`SystemWithAccess`] stored in a [`ScheduleGraph`].
|
|
||||||
pub struct SystemNode {
|
|
||||||
inner: Option<SystemWithAccess>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [`ScheduleSystem`] stored alongside the access returned from [`System::initialize`](crate::system::System::initialize).
|
|
||||||
pub struct SystemWithAccess {
|
|
||||||
/// The system itself.
|
|
||||||
pub system: ScheduleSystem,
|
|
||||||
/// The access returned by [`System::initialize`](crate::system::System::initialize).
|
|
||||||
/// This will be empty if the system has not been initialized yet.
|
|
||||||
pub access: FilteredAccessSet<ComponentId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemWithAccess {
|
|
||||||
/// Constructs a new [`SystemWithAccess`] from a [`ScheduleSystem`].
|
|
||||||
/// The `access` will initially be empty.
|
|
||||||
pub fn new(system: ScheduleSystem) -> Self {
|
|
||||||
Self {
|
|
||||||
system,
|
|
||||||
access: FilteredAccessSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [`BoxedCondition`] stored alongside the access returned from [`System::initialize`](crate::system::System::initialize).
|
|
||||||
pub struct ConditionWithAccess {
|
|
||||||
/// The condition itself.
|
|
||||||
pub condition: BoxedCondition,
|
|
||||||
/// The access returned by [`System::initialize`](crate::system::System::initialize).
|
|
||||||
/// This will be empty if the system has not been initialized yet.
|
|
||||||
pub access: FilteredAccessSet<ComponentId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConditionWithAccess {
|
|
||||||
/// Constructs a new [`ConditionWithAccess`] from a [`BoxedCondition`].
|
|
||||||
/// The `access` will initially be empty.
|
|
||||||
pub const fn new(condition: BoxedCondition) -> Self {
|
|
||||||
Self {
|
|
||||||
condition,
|
|
||||||
access: FilteredAccessSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemNode {
|
|
||||||
/// Create a new [`SystemNode`]
|
|
||||||
pub fn new(system: ScheduleSystem) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Some(SystemWithAccess::new(system)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Obtain a reference to the [`SystemWithAccess`] represented by this node.
|
|
||||||
pub fn get(&self) -> Option<&SystemWithAccess> {
|
|
||||||
self.inner.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Obtain a mutable reference to the [`SystemWithAccess`] represented by this node.
|
|
||||||
pub fn get_mut(&mut self) -> Option<&mut SystemWithAccess> {
|
|
||||||
self.inner.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_key_type! {
|
|
||||||
/// A unique identifier for a system in a [`ScheduleGraph`].
|
|
||||||
pub struct SystemKey;
|
|
||||||
/// A unique identifier for a system set in a [`ScheduleGraph`].
|
|
||||||
pub struct SystemSetKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A node in a [`ScheduleGraph`] with a system or conditions that have not been
|
|
||||||
/// initialized yet.
|
|
||||||
///
|
|
||||||
/// We have to defer initialization of nodes in the graph until we have
|
|
||||||
/// `&mut World` access, so we store these in a list ([`ScheduleGraph::uninit`])
|
|
||||||
/// until then. In most cases, initialization occurs upon the first run of the
|
|
||||||
/// schedule.
|
|
||||||
enum UninitializedId {
|
|
||||||
/// A system and its conditions that have not been initialized yet.
|
|
||||||
System(SystemKey),
|
|
||||||
/// A system set's conditions that have not been initialized yet.
|
|
||||||
Set {
|
|
||||||
key: SystemSetKey,
|
|
||||||
/// The range of indices in [`SystemSets::conditions`] that correspond
|
|
||||||
/// to conditions that have not been initialized yet.
|
|
||||||
///
|
|
||||||
/// [`SystemSets::conditions`] for a given set may be appended to
|
|
||||||
/// multiple times (e.g. when `configure_sets` is called multiple with
|
|
||||||
/// the same set), so we need to track which conditions in that list
|
|
||||||
/// are newly added and not yet initialized.
|
|
||||||
///
|
|
||||||
/// Systems don't need this tracking because each `add_systems` call
|
|
||||||
/// creates separate nodes in the graph with their own conditions,
|
|
||||||
/// so all conditions are initialized together.
|
|
||||||
uninitialized_conditions: Range<usize>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata for system sets in a schedule.
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SystemSets {
|
|
||||||
/// List of system sets in the schedule
|
|
||||||
sets: SlotMap<SystemSetKey, InternedSystemSet>,
|
|
||||||
/// List of conditions for each system set, in the same order as `system_sets`
|
|
||||||
conditions: SecondaryMap<SystemSetKey, Vec<ConditionWithAccess>>,
|
|
||||||
/// Map from system set to node id
|
|
||||||
ids: HashMap<InternedSystemSet, SystemSetKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemSets {
|
|
||||||
fn get_or_add_set(&mut self, set: InternedSystemSet) -> SystemSetKey {
|
|
||||||
*self.ids.entry(set).or_insert_with(|| {
|
|
||||||
let key = self.sets.insert(set);
|
|
||||||
self.conditions.insert(key, Vec::new());
|
|
||||||
key
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata for a [`Schedule`].
|
/// Metadata for a [`Schedule`].
|
||||||
///
|
///
|
||||||
/// The order isn't optimized; calling `ScheduleGraph::build_schedule` will return a
|
/// The order isn't optimized; calling `ScheduleGraph::build_schedule` will return a
|
||||||
/// `SystemSchedule` where the order is optimized for execution.
|
/// `SystemSchedule` where the order is optimized for execution.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ScheduleGraph {
|
pub struct ScheduleGraph {
|
||||||
/// List of systems in the schedule
|
/// Container of systems in the schedule.
|
||||||
pub systems: SlotMap<SystemKey, SystemNode>,
|
pub systems: Systems,
|
||||||
/// List of conditions for each system, in the same order as `systems`
|
/// Container of system sets in the schedule.
|
||||||
pub system_conditions: SecondaryMap<SystemKey, Vec<ConditionWithAccess>>,
|
pub system_sets: SystemSets,
|
||||||
/// Data about system sets in the schedule
|
|
||||||
system_sets: SystemSets,
|
|
||||||
/// Systems, their conditions, and system set conditions that need to be
|
|
||||||
/// initialized before the schedule can be run.
|
|
||||||
uninit: Vec<UninitializedId>,
|
|
||||||
/// Directed acyclic graph of the hierarchy (which systems/sets are children of which sets)
|
/// Directed acyclic graph of the hierarchy (which systems/sets are children of which sets)
|
||||||
hierarchy: Dag,
|
hierarchy: Dag,
|
||||||
/// Directed acyclic graph of the dependency (which systems/sets have to run before which other systems/sets)
|
/// Directed acyclic graph of the dependency (which systems/sets have to run before which other systems/sets)
|
||||||
@ -812,10 +672,8 @@ impl ScheduleGraph {
|
|||||||
/// Creates an empty [`ScheduleGraph`] with default settings.
|
/// Creates an empty [`ScheduleGraph`] with default settings.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
systems: SlotMap::with_key(),
|
systems: Systems::default(),
|
||||||
system_conditions: SecondaryMap::new(),
|
|
||||||
system_sets: SystemSets::default(),
|
system_sets: SystemSets::default(),
|
||||||
uninit: Vec::new(),
|
|
||||||
hierarchy: Dag::new(),
|
hierarchy: Dag::new(),
|
||||||
dependency: Dag::new(),
|
dependency: Dag::new(),
|
||||||
ambiguous_with: UnGraph::default(),
|
ambiguous_with: UnGraph::default(),
|
||||||
@ -828,78 +686,6 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the system at the given [`SystemKey`], if it exists.
|
|
||||||
pub fn get_system_at(&self, key: SystemKey) -> Option<&ScheduleSystem> {
|
|
||||||
self.systems
|
|
||||||
.get(key)
|
|
||||||
.and_then(|system| system.get())
|
|
||||||
.map(|system| &system.system)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the given system set is part of the graph. Otherwise, returns `false`.
|
|
||||||
pub fn contains_set(&self, set: impl SystemSet) -> bool {
|
|
||||||
self.system_sets.ids.contains_key(&set.intern())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the system at the given [`NodeId`].
|
|
||||||
///
|
|
||||||
/// Panics if it doesn't exist.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn system_at(&self, key: SystemKey) -> &ScheduleSystem {
|
|
||||||
self.get_system_at(key)
|
|
||||||
.unwrap_or_else(|| panic!("system with key {key:?} does not exist in this Schedule"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the set at the given [`NodeId`], if it exists.
|
|
||||||
pub fn get_set_at(&self, key: SystemSetKey) -> Option<&dyn SystemSet> {
|
|
||||||
self.system_sets.sets.get(key).map(|set| &**set)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the set at the given [`NodeId`].
|
|
||||||
///
|
|
||||||
/// Panics if it doesn't exist.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn set_at(&self, id: SystemSetKey) -> &dyn SystemSet {
|
|
||||||
self.get_set_at(id)
|
|
||||||
.unwrap_or_else(|| panic!("set with id {id:?} does not exist in this Schedule"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the conditions for the set at the given [`SystemSetKey`], if it exists.
|
|
||||||
pub fn get_set_conditions_at(&self, key: SystemSetKey) -> Option<&[ConditionWithAccess]> {
|
|
||||||
self.system_sets.conditions.get(key).map(Vec::as_slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the conditions for the set at the given [`SystemSetKey`].
|
|
||||||
///
|
|
||||||
/// Panics if it doesn't exist.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn set_conditions_at(&self, key: SystemSetKey) -> &[ConditionWithAccess] {
|
|
||||||
self.get_set_conditions_at(key)
|
|
||||||
.unwrap_or_else(|| panic!("set with key {key:?} does not exist in this Schedule"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all systems in this schedule, along with the conditions for each system.
|
|
||||||
pub fn systems(
|
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = (SystemKey, &ScheduleSystem, &[ConditionWithAccess])> {
|
|
||||||
self.systems.iter().filter_map(|(key, system_node)| {
|
|
||||||
let system = &system_node.inner.as_ref()?.system;
|
|
||||||
let conditions = self.system_conditions.get(key)?;
|
|
||||||
Some((key, system, conditions.as_slice()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all system sets in this schedule, along with the conditions for each
|
|
||||||
/// system set.
|
|
||||||
pub fn system_sets(
|
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = (SystemSetKey, &dyn SystemSet, &[ConditionWithAccess])> {
|
|
||||||
self.system_sets.sets.iter().filter_map(|(key, set)| {
|
|
||||||
let conditions = self.system_sets.conditions.get(key)?.as_slice();
|
|
||||||
Some((key, &**set, conditions))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [`Dag`] of the hierarchy.
|
/// Returns the [`Dag`] of the hierarchy.
|
||||||
///
|
///
|
||||||
/// The hierarchy is a directed acyclic graph of the systems and sets,
|
/// The hierarchy is a directed acyclic graph of the systems and sets,
|
||||||
@ -1059,17 +845,7 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
/// Add a [`ScheduleConfig`] to the graph, including its dependencies and conditions.
|
/// Add a [`ScheduleConfig`] to the graph, including its dependencies and conditions.
|
||||||
fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
|
fn add_system_inner(&mut self, config: ScheduleConfig<ScheduleSystem>) -> SystemKey {
|
||||||
let key = self.systems.insert(SystemNode::new(config.node));
|
let key = self.systems.insert(config.node, config.conditions);
|
||||||
self.system_conditions.insert(
|
|
||||||
key,
|
|
||||||
config
|
|
||||||
.conditions
|
|
||||||
.into_iter()
|
|
||||||
.map(ConditionWithAccess::new)
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
// system init has to be deferred (need `&mut World`)
|
|
||||||
self.uninit.push(UninitializedId::System(key));
|
|
||||||
|
|
||||||
// graph updates are immediate
|
// graph updates are immediate
|
||||||
self.update_graphs(NodeId::System(key), config.metadata);
|
self.update_graphs(NodeId::System(key), config.metadata);
|
||||||
@ -1083,26 +859,11 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a single `ScheduleConfig` to the graph, including its dependencies and conditions.
|
/// Add a single `ScheduleConfig` to the graph, including its dependencies and conditions.
|
||||||
fn configure_set_inner(&mut self, set: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
|
fn configure_set_inner(&mut self, config: ScheduleConfig<InternedSystemSet>) -> SystemSetKey {
|
||||||
let ScheduleConfig {
|
let key = self.system_sets.insert(config.node, config.conditions);
|
||||||
node: set,
|
|
||||||
metadata,
|
|
||||||
conditions,
|
|
||||||
} = set;
|
|
||||||
|
|
||||||
let key = self.system_sets.get_or_add_set(set);
|
|
||||||
|
|
||||||
// graph updates are immediate
|
// graph updates are immediate
|
||||||
self.update_graphs(NodeId::Set(key), metadata);
|
self.update_graphs(NodeId::Set(key), config.metadata);
|
||||||
|
|
||||||
// system init has to be deferred (need `&mut World`)
|
|
||||||
let system_set_conditions = self.system_sets.conditions.entry(key).unwrap().or_default();
|
|
||||||
let start = system_set_conditions.len();
|
|
||||||
self.uninit.push(UninitializedId::Set {
|
|
||||||
key,
|
|
||||||
uninitialized_conditions: start..(start + conditions.len()),
|
|
||||||
});
|
|
||||||
system_set_conditions.extend(conditions.into_iter().map(ConditionWithAccess::new));
|
|
||||||
|
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
@ -1129,7 +890,7 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
for key in sets
|
for key in sets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|set| self.system_sets.get_or_add_set(set))
|
.map(|set| self.system_sets.get_key_or_insert(set))
|
||||||
{
|
{
|
||||||
self.hierarchy.graph.add_edge(NodeId::Set(key), id);
|
self.hierarchy.graph.add_edge(NodeId::Set(key), id);
|
||||||
|
|
||||||
@ -1141,7 +902,7 @@ impl ScheduleGraph {
|
|||||||
dependencies
|
dependencies
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|Dependency { kind, set, options }| {
|
.map(|Dependency { kind, set, options }| {
|
||||||
(kind, self.system_sets.get_or_add_set(set), options)
|
(kind, self.system_sets.get_key_or_insert(set), options)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let (lhs, rhs) = match kind {
|
let (lhs, rhs) = match kind {
|
||||||
@ -1162,7 +923,7 @@ impl ScheduleGraph {
|
|||||||
Ambiguity::IgnoreWithSet(ambiguous_with) => {
|
Ambiguity::IgnoreWithSet(ambiguous_with) => {
|
||||||
for key in ambiguous_with
|
for key in ambiguous_with
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|set| self.system_sets.get_or_add_set(set))
|
.map(|set| self.system_sets.get_key_or_insert(set))
|
||||||
{
|
{
|
||||||
self.ambiguous_with.add_edge(id, NodeId::Set(key));
|
self.ambiguous_with.add_edge(id, NodeId::Set(key));
|
||||||
}
|
}
|
||||||
@ -1173,28 +934,11 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes any newly-added systems and conditions by calling [`System::initialize`](crate::system::System)
|
/// Initializes any newly-added systems and conditions by calling
|
||||||
|
/// [`System::initialize`](crate::system::System).
|
||||||
pub fn initialize(&mut self, world: &mut World) {
|
pub fn initialize(&mut self, world: &mut World) {
|
||||||
for id in self.uninit.drain(..) {
|
self.systems.initialize(world);
|
||||||
match id {
|
self.system_sets.initialize(world);
|
||||||
UninitializedId::System(key) => {
|
|
||||||
let system = self.systems[key].get_mut().unwrap();
|
|
||||||
system.access = system.system.initialize(world);
|
|
||||||
for condition in &mut self.system_conditions[key] {
|
|
||||||
condition.access = condition.condition.initialize(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UninitializedId::Set {
|
|
||||||
key,
|
|
||||||
uninitialized_conditions,
|
|
||||||
} => {
|
|
||||||
for condition in &mut self.system_sets.conditions[key][uninitialized_conditions]
|
|
||||||
{
|
|
||||||
condition.access = condition.condition.initialize(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a [`SystemSchedule`] optimized for scheduler access from the [`ScheduleGraph`].
|
/// Build a [`SystemSchedule`] optimized for scheduler access from the [`ScheduleGraph`].
|
||||||
@ -1286,9 +1030,9 @@ impl ScheduleGraph {
|
|||||||
HashMap<SystemSetKey, HashSet<SystemKey>>,
|
HashMap<SystemSetKey, HashSet<SystemKey>>,
|
||||||
) {
|
) {
|
||||||
let mut set_systems: HashMap<SystemSetKey, Vec<SystemKey>> =
|
let mut set_systems: HashMap<SystemSetKey, Vec<SystemKey>> =
|
||||||
HashMap::with_capacity_and_hasher(self.system_sets.sets.len(), Default::default());
|
HashMap::with_capacity_and_hasher(self.system_sets.len(), Default::default());
|
||||||
let mut set_system_sets: HashMap<SystemSetKey, HashSet<SystemKey>> =
|
let mut set_system_sets: HashMap<SystemSetKey, HashSet<SystemKey>> =
|
||||||
HashMap::with_capacity_and_hasher(self.system_sets.sets.len(), Default::default());
|
HashMap::with_capacity_and_hasher(self.system_sets.len(), Default::default());
|
||||||
for &id in hierarchy_topsort.iter().rev() {
|
for &id in hierarchy_topsort.iter().rev() {
|
||||||
let NodeId::Set(set_key) = id else {
|
let NodeId::Set(set_key) = id else {
|
||||||
continue;
|
continue;
|
||||||
@ -1419,9 +1163,9 @@ impl ScheduleGraph {
|
|||||||
"Encountered a non-system node in the flattened disconnected results: {b:?}"
|
"Encountered a non-system node in the flattened disconnected results: {b:?}"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let system_a = self.systems[a].get().unwrap();
|
let system_a = &self.systems[a];
|
||||||
let system_b = self.systems[b].get().unwrap();
|
let system_b = &self.systems[b];
|
||||||
if system_a.system.is_exclusive() || system_b.system.is_exclusive() {
|
if system_a.is_exclusive() || system_b.is_exclusive() {
|
||||||
conflicting_systems.push((a, b, Vec::new()));
|
conflicting_systems.push((a, b, Vec::new()));
|
||||||
} else {
|
} else {
|
||||||
let access_a = &system_a.access;
|
let access_a = &system_a.access;
|
||||||
@ -1487,7 +1231,7 @@ impl ScheduleGraph {
|
|||||||
// ignore system sets that have no conditions
|
// ignore system sets that have no conditions
|
||||||
// ignore system type sets (already covered, they don't have conditions)
|
// ignore system type sets (already covered, they don't have conditions)
|
||||||
let key = id.as_set()?;
|
let key = id.as_set()?;
|
||||||
(!self.system_sets.conditions[key].is_empty()).then_some((i, key))
|
self.system_sets.has_conditions(key).then_some((i, key))
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
@ -1567,7 +1311,7 @@ impl ScheduleGraph {
|
|||||||
ignored_ambiguities: &BTreeSet<ComponentId>,
|
ignored_ambiguities: &BTreeSet<ComponentId>,
|
||||||
schedule_label: InternedScheduleLabel,
|
schedule_label: InternedScheduleLabel,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
if !self.uninit.is_empty() {
|
if !self.systems.is_initialized() || !self.system_sets.is_initialized() {
|
||||||
return Err(ScheduleBuildError::Uninitialized);
|
return Err(ScheduleBuildError::Uninitialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1578,8 +1322,8 @@ impl ScheduleGraph {
|
|||||||
.zip(schedule.systems.drain(..))
|
.zip(schedule.systems.drain(..))
|
||||||
.zip(schedule.system_conditions.drain(..))
|
.zip(schedule.system_conditions.drain(..))
|
||||||
{
|
{
|
||||||
self.systems[key].inner = Some(system);
|
self.systems.node_mut(key).inner = Some(system);
|
||||||
self.system_conditions[key] = conditions;
|
*self.systems.get_conditions_mut(key).unwrap() = conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (key, conditions) in schedule
|
for (key, conditions) in schedule
|
||||||
@ -1587,21 +1331,21 @@ impl ScheduleGraph {
|
|||||||
.drain(..)
|
.drain(..)
|
||||||
.zip(schedule.set_conditions.drain(..))
|
.zip(schedule.set_conditions.drain(..))
|
||||||
{
|
{
|
||||||
self.system_sets.conditions[key] = conditions;
|
*self.system_sets.get_conditions_mut(key).unwrap() = conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
*schedule = self.build_schedule(world, schedule_label, ignored_ambiguities)?;
|
*schedule = self.build_schedule(world, schedule_label, ignored_ambiguities)?;
|
||||||
|
|
||||||
// move systems into new schedule
|
// move systems into new schedule
|
||||||
for &key in &schedule.system_ids {
|
for &key in &schedule.system_ids {
|
||||||
let system = self.systems[key].inner.take().unwrap();
|
let system = self.systems.node_mut(key).inner.take().unwrap();
|
||||||
let conditions = core::mem::take(&mut self.system_conditions[key]);
|
let conditions = core::mem::take(self.systems.get_conditions_mut(key).unwrap());
|
||||||
schedule.systems.push(system);
|
schedule.systems.push(system);
|
||||||
schedule.system_conditions.push(conditions);
|
schedule.system_conditions.push(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
for &key in &schedule.set_ids {
|
for &key in &schedule.set_ids {
|
||||||
let conditions = core::mem::take(&mut self.system_sets.conditions[key]);
|
let conditions = core::mem::take(self.system_sets.get_conditions_mut(key).unwrap());
|
||||||
schedule.set_conditions.push(conditions);
|
schedule.set_conditions.push(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1656,7 +1400,7 @@ impl ScheduleGraph {
|
|||||||
fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
|
fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
|
||||||
match *id {
|
match *id {
|
||||||
NodeId::System(key) => {
|
NodeId::System(key) => {
|
||||||
let name = self.systems[key].get().unwrap().system.name();
|
let name = self.systems[key].name();
|
||||||
let name = if self.settings.use_shortnames {
|
let name = if self.settings.use_shortnames {
|
||||||
name.shortname().to_string()
|
name.shortname().to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -1676,7 +1420,7 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeId::Set(key) => {
|
NodeId::Set(key) => {
|
||||||
let set = &self.system_sets.sets[key];
|
let set = &self.system_sets[key];
|
||||||
if set.is_anonymous() {
|
if set.is_anonymous() {
|
||||||
self.anonymous_set_name(id)
|
self.anonymous_set_name(id)
|
||||||
} else {
|
} else {
|
||||||
@ -1902,7 +1646,7 @@ impl ScheduleGraph {
|
|||||||
set_systems: &HashMap<SystemSetKey, Vec<SystemKey>>,
|
set_systems: &HashMap<SystemSetKey, Vec<SystemKey>>,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
for (&key, systems) in set_systems {
|
for (&key, systems) in set_systems {
|
||||||
let set = &self.system_sets.sets[key];
|
let set = &self.system_sets[key];
|
||||||
if set.system_type().is_some() {
|
if set.system_type().is_some() {
|
||||||
let instances = systems.len();
|
let instances = systems.len();
|
||||||
let ambiguous_with = self.ambiguous_with.edges(NodeId::Set(key));
|
let ambiguous_with = self.ambiguous_with.edges(NodeId::Set(key));
|
||||||
@ -2009,7 +1753,7 @@ impl ScheduleGraph {
|
|||||||
fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
|
fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec<String> {
|
||||||
let mut sets = <HashSet<_>>::default();
|
let mut sets = <HashSet<_>>::default();
|
||||||
self.traverse_sets_containing_node(*id, &mut |key| {
|
self.traverse_sets_containing_node(*id, &mut |key| {
|
||||||
self.system_sets.sets[key].system_type().is_none() && sets.insert(key)
|
self.system_sets[key].system_type().is_none() && sets.insert(key)
|
||||||
});
|
});
|
||||||
let mut sets: Vec<_> = sets
|
let mut sets: Vec<_> = sets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
37
release-content/migration-guides/schedule_cleanup.md
Normal file
37
release-content/migration-guides/schedule_cleanup.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: Schedule API Cleanup
|
||||||
|
pull_requests: [19352, 20119]
|
||||||
|
---
|
||||||
|
|
||||||
|
In order to support removing systems from schedules, `Vec`s storing `System`s and
|
||||||
|
`SystemSet`s have been replaced with `SlotMap`s which allow safely removing nodes and
|
||||||
|
reusing indices. The maps are respectively keyed by `SystemKey`s and `SystemSetKey`s.
|
||||||
|
|
||||||
|
The following signatures were changed:
|
||||||
|
|
||||||
|
- `NodeId::System`: Now stores a `SystemKey` instead of a plain `usize`
|
||||||
|
- `NodeId::Set`: Now stores a `SystemSetKey` instead of a plain `usize`
|
||||||
|
- `ScheduleBuildPass::collapse_set`: Now takes the type-specific keys. Wrap them back into a `NodeId` if necessary.
|
||||||
|
- The following functions now return the type-specific keys. Wrap them back into a `NodeId` if necessary.
|
||||||
|
- `Schedule::systems`
|
||||||
|
- `ScheduleGraph::conflicting_systems`
|
||||||
|
|
||||||
|
The following functions were replaced. Those that took or returned `NodeId` now
|
||||||
|
take or return `SystemKey` or `SystemSetKey`. Wrap/unwrap them as necessary.
|
||||||
|
|
||||||
|
- `ScheduleGraph::contains_set`: Use `ScheduleGraph::system_sets` and `SystemSets::contains`.
|
||||||
|
- `ScheduleGraph::get_set_at`: Use `ScheduleGraph::system_sets` and `SystemSets::get`.
|
||||||
|
- `ScheduleGraph::set_at`: Use `ScheduleGraph::system_sets` and `SystemSets::index` (`system_sets[key]`).
|
||||||
|
- `ScheduleGraph::get_set_conditions_at`: Use `ScheduleGraph::system_sets` and `SystemSets::get_conditions`.
|
||||||
|
- `ScheduleGraph::system_sets`: Use `ScheduleGraph::system_sets` and `SystemSets::iter`.
|
||||||
|
- `ScheduleGraph::get_system_at`: Use `ScheduleGraph::systems` and `Systems::get`.
|
||||||
|
- `ScheduleGraph::system_at`: Use `ScheduleGraph::systems` and `Systems::index` (`systems[key]`).
|
||||||
|
- `ScheduleGraph::systems`: Use `ScheduleGraph::systems` and `Systems::iter`.
|
||||||
|
|
||||||
|
The following functions were removed:
|
||||||
|
|
||||||
|
- `NodeId::index`: You should match on and use the `SystemKey` and `SystemSetKey` instead.
|
||||||
|
- `NodeId::cmp`: Use the `PartialOrd` and `Ord` traits instead.
|
||||||
|
- `ScheduleGraph::set_conditions_at`: If needing to check presence of conditions,
|
||||||
|
use `ScheduleGraph::system_sets` and `SystemSets::has_conditions`.
|
||||||
|
Otherwise, use `SystemSets::get_conditions`.
|
||||||
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
title: Schedule SlotMaps
|
|
||||||
pull_requests: [19352]
|
|
||||||
---
|
|
||||||
|
|
||||||
In order to support removing systems from schedules, `Vec`s storing `System`s and
|
|
||||||
`SystemSet`s have been replaced with `SlotMap`s which allow safely removing and
|
|
||||||
reusing indices. The maps are respectively keyed by `SystemKey`s and `SystemSetKey`s.
|
|
||||||
|
|
||||||
The following signatures were changed:
|
|
||||||
|
|
||||||
- `NodeId::System`: Now stores a `SystemKey` instead of a plain `usize`
|
|
||||||
- `NodeId::Set`: Now stores a `SystemSetKey` instead of a plain `usize`
|
|
||||||
- `ScheduleBuildPass::collapse_set`: Now takes the type-specific keys. Wrap them back into a `NodeId` if necessary.
|
|
||||||
- The following functions now return the type-specific keys. Wrap them back into a `NodeId` if necessary.
|
|
||||||
- `Schedule::systems`
|
|
||||||
- `ScheduleGraph::systems`
|
|
||||||
- `ScheduleGraph::system_sets`
|
|
||||||
- `ScheduleGraph::conflicting_systems`
|
|
||||||
- Use the appropriate key types to index these structures rather than bare `usize`s:
|
|
||||||
- `ScheduleGraph::systems` field
|
|
||||||
- `ScheduleGraph::system_conditions`
|
|
||||||
- The following functions now take the type-specific keys. Use pattern matching to extract them from `NodeId`s, if necessary:
|
|
||||||
- `ScheduleGraph::get_system_at`
|
|
||||||
- `ScheduleGraph::system_at`
|
|
||||||
- `ScheduleGraph::get_set_at`
|
|
||||||
- `ScheduleGraph::set_at`
|
|
||||||
- `ScheduleGraph::get_set_conditions_at`
|
|
||||||
- `ScheduleGraph::set_conditions_at`
|
|
||||||
|
|
||||||
The following functions were removed:
|
|
||||||
|
|
||||||
- `NodeId::index`: You should match on and use the `SystemKey` and `SystemSetKey` instead.
|
|
||||||
- `NodeId::cmp`: Use the `PartialOrd` and `Ord` traits instead.
|
|
||||||
Loading…
Reference in New Issue
Block a user