Use SlotMap
s to store systems and system sets in Schedule
s (#19352)
# Objective - First step towards #279 ## Solution Makes the necessary internal data structure changes in order to allow system removal to be added in a future PR: `Vec`s storing systems and system sets in `ScheduleGraph` have been replaced with `SlotMap`s. See the included migration guide for the required changes. ## Testing Internal changes only and no new features *should* mean no new tests are requried.
This commit is contained in:
parent
bbf91a6964
commit
ebf87f56ef
@ -10,7 +10,7 @@ keywords = ["game", "engine", "gamedev", "graphics", "bevy"]
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/bevyengine/bevy"
|
repository = "https://github.com/bevyengine/bevy"
|
||||||
documentation = "https://docs.rs/bevy"
|
documentation = "https://docs.rs/bevy"
|
||||||
rust-version = "1.86.0"
|
rust-version = "1.88.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
@ -119,6 +119,7 @@ tracing = { version = "0.1", default-features = false, optional = true }
|
|||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
bumpalo = "3"
|
bumpalo = "3"
|
||||||
subsecond = { version = "0.7.0-alpha.1", optional = true }
|
subsecond = { version = "0.7.0-alpha.1", optional = true }
|
||||||
|
slotmap = { version = "1.0.7", default-features = false }
|
||||||
|
|
||||||
concurrent-queue = { version = "2.5.0", default-features = false }
|
concurrent-queue = { version = "2.5.0", default-features = false }
|
||||||
[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
|
[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
|
||||||
|
@ -2,8 +2,11 @@ use alloc::{boxed::Box, collections::BTreeSet, vec::Vec};
|
|||||||
|
|
||||||
use bevy_platform::collections::HashMap;
|
use bevy_platform::collections::HashMap;
|
||||||
|
|
||||||
use crate::system::IntoSystem;
|
use crate::{
|
||||||
use crate::world::World;
|
schedule::{SystemKey, SystemSetKey},
|
||||||
|
system::IntoSystem,
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
is_apply_deferred, ApplyDeferred, DiGraph, Direction, NodeId, ReportCycles, ScheduleBuildError,
|
is_apply_deferred, ApplyDeferred, DiGraph, Direction, NodeId, ReportCycles, ScheduleBuildError,
|
||||||
@ -36,29 +39,26 @@ impl AutoInsertApplyDeferredPass {
|
|||||||
self.auto_sync_node_ids
|
self.auto_sync_node_ids
|
||||||
.get(&distance)
|
.get(&distance)
|
||||||
.copied()
|
.copied()
|
||||||
.or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let node_id = self.add_auto_sync(graph);
|
let node_id = NodeId::System(self.add_auto_sync(graph));
|
||||||
self.auto_sync_node_ids.insert(distance, node_id);
|
self.auto_sync_node_ids.insert(distance, node_id);
|
||||||
Some(node_id)
|
node_id
|
||||||
})
|
})
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
/// add an [`ApplyDeferred`] system with no config
|
/// add an [`ApplyDeferred`] system with no config
|
||||||
fn add_auto_sync(&mut self, graph: &mut ScheduleGraph) -> NodeId {
|
fn add_auto_sync(&mut self, graph: &mut ScheduleGraph) -> SystemKey {
|
||||||
let id = NodeId::System(graph.systems.len());
|
let key = graph
|
||||||
|
|
||||||
graph
|
|
||||||
.systems
|
.systems
|
||||||
.push(SystemNode::new(Box::new(IntoSystem::into_system(
|
.insert(SystemNode::new(Box::new(IntoSystem::into_system(
|
||||||
ApplyDeferred,
|
ApplyDeferred,
|
||||||
))));
|
))));
|
||||||
graph.system_conditions.push(Vec::new());
|
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.
|
||||||
graph.ambiguous_with_all.insert(id);
|
graph.ambiguous_with_all.insert(NodeId::System(key));
|
||||||
|
|
||||||
id
|
key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,39 +80,45 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
let mut sync_point_graph = dependency_flattened.clone();
|
let mut sync_point_graph = dependency_flattened.clone();
|
||||||
let topo = graph.topsort_graph(dependency_flattened, ReportCycles::Dependency)?;
|
let topo = graph.topsort_graph(dependency_flattened, ReportCycles::Dependency)?;
|
||||||
|
|
||||||
fn set_has_conditions(graph: &ScheduleGraph, node: NodeId) -> bool {
|
fn set_has_conditions(graph: &ScheduleGraph, set: SystemSetKey) -> bool {
|
||||||
!graph.set_conditions_at(node).is_empty()
|
!graph.set_conditions_at(set).is_empty()
|
||||||
|| graph
|
|| graph
|
||||||
.hierarchy()
|
.hierarchy()
|
||||||
.graph()
|
.graph()
|
||||||
.edges_directed(node, Direction::Incoming)
|
.edges_directed(NodeId::Set(set), Direction::Incoming)
|
||||||
.any(|(parent, _)| set_has_conditions(graph, parent))
|
.any(|(parent, _)| {
|
||||||
|
parent
|
||||||
|
.as_set()
|
||||||
|
.is_some_and(|p| set_has_conditions(graph, p))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn system_has_conditions(graph: &ScheduleGraph, node: NodeId) -> bool {
|
fn system_has_conditions(graph: &ScheduleGraph, key: SystemKey) -> bool {
|
||||||
assert!(node.is_system());
|
!graph.system_conditions[key].is_empty()
|
||||||
!graph.system_conditions[node.index()].is_empty()
|
|
||||||
|| graph
|
|| graph
|
||||||
.hierarchy()
|
.hierarchy()
|
||||||
.graph()
|
.graph()
|
||||||
.edges_directed(node, Direction::Incoming)
|
.edges_directed(NodeId::System(key), Direction::Incoming)
|
||||||
.any(|(parent, _)| set_has_conditions(graph, parent))
|
.any(|(parent, _)| {
|
||||||
|
parent
|
||||||
|
.as_set()
|
||||||
|
.is_some_and(|p| set_has_conditions(graph, p))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut system_has_conditions_cache = HashMap::<usize, bool>::default();
|
let mut system_has_conditions_cache = HashMap::<SystemKey, bool>::default();
|
||||||
let mut is_valid_explicit_sync_point = |system: NodeId| {
|
let mut is_valid_explicit_sync_point = |key: SystemKey| {
|
||||||
let index = system.index();
|
is_apply_deferred(&graph.systems[key].get().unwrap().system)
|
||||||
is_apply_deferred(&graph.systems[index].get().unwrap().system)
|
|
||||||
&& !*system_has_conditions_cache
|
&& !*system_has_conditions_cache
|
||||||
.entry(index)
|
.entry(key)
|
||||||
.or_insert_with(|| system_has_conditions(graph, system))
|
.or_insert_with(|| system_has_conditions(graph, key))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate the distance for each node.
|
// Calculate the distance for each node.
|
||||||
// The "distance" is the number of sync points between a node and the beginning of the graph.
|
// The "distance" is the number of sync points between a node and the beginning of the graph.
|
||||||
// Also store if a preceding edge would have added a sync point but was ignored to add it at
|
// Also store if a preceding edge would have added a sync point but was ignored to add it at
|
||||||
// a later edge that is not ignored.
|
// a later edge that is not ignored.
|
||||||
let mut distances_and_pending_sync: HashMap<usize, (u32, bool)> =
|
let mut distances_and_pending_sync: HashMap<SystemKey, (u32, bool)> =
|
||||||
HashMap::with_capacity_and_hasher(topo.len(), Default::default());
|
HashMap::with_capacity_and_hasher(topo.len(), Default::default());
|
||||||
|
|
||||||
// Keep track of any explicit sync nodes for a specific distance.
|
// Keep track of any explicit sync nodes for a specific distance.
|
||||||
@ -120,17 +126,21 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
|
|
||||||
// Determine the distance for every node and collect the explicit sync points.
|
// Determine the distance for every node and collect the explicit sync points.
|
||||||
for node in &topo {
|
for node in &topo {
|
||||||
|
let &NodeId::System(key) = node else {
|
||||||
|
panic!("Encountered a non-system node in the flattened dependency graph: {node:?}");
|
||||||
|
};
|
||||||
|
|
||||||
let (node_distance, mut node_needs_sync) = distances_and_pending_sync
|
let (node_distance, mut node_needs_sync) = distances_and_pending_sync
|
||||||
.get(&node.index())
|
.get(&key)
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
if is_valid_explicit_sync_point(*node) {
|
if is_valid_explicit_sync_point(key) {
|
||||||
// The distance of this sync point does not change anymore as the iteration order
|
// The distance of this sync point does not change anymore as the iteration order
|
||||||
// makes sure that this node is no unvisited target of another node.
|
// makes sure that this node is no unvisited target of another node.
|
||||||
// Because of this, the sync point can be stored for this distance to be reused as
|
// Because of this, the sync point can be stored for this distance to be reused as
|
||||||
// automatically added sync points later.
|
// automatically added sync points later.
|
||||||
distance_to_explicit_sync_node.insert(node_distance, *node);
|
distance_to_explicit_sync_node.insert(node_distance, NodeId::System(key));
|
||||||
|
|
||||||
// This node just did a sync, so the only reason to do another sync is if one was
|
// This node just did a sync, so the only reason to do another sync is if one was
|
||||||
// explicitly scheduled afterwards.
|
// explicitly scheduled afterwards.
|
||||||
@ -138,26 +148,22 @@ 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[node.index()]
|
node_needs_sync = graph.systems[key].get().unwrap().system.has_deferred();
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.system
|
|
||||||
.has_deferred();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
||||||
let (target_distance, target_pending_sync) = distances_and_pending_sync
|
let NodeId::System(target) = target else {
|
||||||
.entry(target.index())
|
panic!("Encountered a non-system node in the flattened dependency graph: {target:?}");
|
||||||
.or_default();
|
};
|
||||||
|
let (target_distance, target_pending_sync) =
|
||||||
|
distances_and_pending_sync.entry(target).or_default();
|
||||||
|
|
||||||
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.index()]
|
&& !graph.systems[target].get().unwrap().system.is_exclusive()
|
||||||
.get()
|
&& self
|
||||||
.unwrap()
|
.no_sync_edges
|
||||||
.system
|
.contains(&(*node, NodeId::System(target)))
|
||||||
.is_exclusive()
|
|
||||||
&& self.no_sync_edges.contains(&(*node, target))
|
|
||||||
{
|
{
|
||||||
// The node has deferred params to apply, but this edge is ignoring sync points.
|
// The node has deferred params to apply, but this edge is ignoring sync points.
|
||||||
// Mark the target as 'delaying' those commands to a future edge and the current
|
// Mark the target as 'delaying' those commands to a future edge and the current
|
||||||
@ -182,14 +188,20 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
// Find any edges which have a different number of sync points between them and make sure
|
// Find any edges which have a different number of sync points between them and make sure
|
||||||
// there is a sync point between them.
|
// there is a sync point between them.
|
||||||
for node in &topo {
|
for node in &topo {
|
||||||
|
let &NodeId::System(key) = node else {
|
||||||
|
panic!("Encountered a non-system node in the flattened dependency graph: {node:?}");
|
||||||
|
};
|
||||||
let (node_distance, _) = distances_and_pending_sync
|
let (node_distance, _) = distances_and_pending_sync
|
||||||
.get(&node.index())
|
.get(&key)
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
for target in dependency_flattened.neighbors_directed(*node, Direction::Outgoing) {
|
||||||
|
let NodeId::System(target) = target else {
|
||||||
|
panic!("Encountered a non-system node in the flattened dependency graph: {target:?}");
|
||||||
|
};
|
||||||
let (target_distance, _) = distances_and_pending_sync
|
let (target_distance, _) = distances_and_pending_sync
|
||||||
.get(&target.index())
|
.get(&target)
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
@ -198,7 +210,7 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_apply_deferred(&graph.systems[target.index()].get().unwrap().system) {
|
if is_apply_deferred(&graph.systems[target].get().unwrap().system) {
|
||||||
// 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;
|
||||||
@ -210,10 +222,10 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
.unwrap_or_else(|| self.get_sync_point(graph, target_distance));
|
.unwrap_or_else(|| self.get_sync_point(graph, target_distance));
|
||||||
|
|
||||||
sync_point_graph.add_edge(*node, sync_point);
|
sync_point_graph.add_edge(*node, sync_point);
|
||||||
sync_point_graph.add_edge(sync_point, target);
|
sync_point_graph.add_edge(sync_point, NodeId::System(target));
|
||||||
|
|
||||||
// The edge without the sync point is now redundant.
|
// The edge without the sync point is now redundant.
|
||||||
sync_point_graph.remove_edge(*node, target);
|
sync_point_graph.remove_edge(*node, NodeId::System(target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,34 +235,39 @@ impl ScheduleBuildPass for AutoInsertApplyDeferredPass {
|
|||||||
|
|
||||||
fn collapse_set(
|
fn collapse_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
set: NodeId,
|
set: SystemSetKey,
|
||||||
systems: &[NodeId],
|
systems: &[SystemKey],
|
||||||
dependency_flattened: &DiGraph,
|
dependency_flattened: &DiGraph,
|
||||||
) -> impl Iterator<Item = (NodeId, NodeId)> {
|
) -> impl Iterator<Item = (NodeId, NodeId)> {
|
||||||
if systems.is_empty() {
|
if systems.is_empty() {
|
||||||
// collapse dependencies for empty sets
|
// collapse dependencies for empty sets
|
||||||
for a in dependency_flattened.neighbors_directed(set, Direction::Incoming) {
|
for a in dependency_flattened.neighbors_directed(NodeId::Set(set), Direction::Incoming)
|
||||||
for b in dependency_flattened.neighbors_directed(set, Direction::Outgoing) {
|
{
|
||||||
if self.no_sync_edges.contains(&(a, set))
|
for b in
|
||||||
&& self.no_sync_edges.contains(&(set, b))
|
dependency_flattened.neighbors_directed(NodeId::Set(set), Direction::Outgoing)
|
||||||
|
{
|
||||||
|
if self.no_sync_edges.contains(&(a, NodeId::Set(set)))
|
||||||
|
&& self.no_sync_edges.contains(&(NodeId::Set(set), b))
|
||||||
{
|
{
|
||||||
self.no_sync_edges.insert((a, b));
|
self.no_sync_edges.insert((a, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for a in dependency_flattened.neighbors_directed(set, Direction::Incoming) {
|
for a in dependency_flattened.neighbors_directed(NodeId::Set(set), Direction::Incoming)
|
||||||
|
{
|
||||||
for &sys in systems {
|
for &sys in systems {
|
||||||
if self.no_sync_edges.contains(&(a, set)) {
|
if self.no_sync_edges.contains(&(a, NodeId::Set(set))) {
|
||||||
self.no_sync_edges.insert((a, sys));
|
self.no_sync_edges.insert((a, NodeId::System(sys)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for b in dependency_flattened.neighbors_directed(set, Direction::Outgoing) {
|
for b in dependency_flattened.neighbors_directed(NodeId::Set(set), Direction::Outgoing)
|
||||||
|
{
|
||||||
for &sys in systems {
|
for &sys in systems {
|
||||||
if self.no_sync_edges.contains(&(set, b)) {
|
if self.no_sync_edges.contains(&(NodeId::Set(set), b)) {
|
||||||
self.no_sync_edges.insert((sys, b));
|
self.no_sync_edges.insert((NodeId::System(sys), b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,10 @@ use crate::{
|
|||||||
error::{BevyError, ErrorContext, Result},
|
error::{BevyError, ErrorContext, Result},
|
||||||
prelude::{IntoSystemSet, SystemSet},
|
prelude::{IntoSystemSet, SystemSet},
|
||||||
query::FilteredAccessSet,
|
query::FilteredAccessSet,
|
||||||
schedule::{ConditionWithAccess, InternedSystemSet, NodeId, SystemTypeSet, SystemWithAccess},
|
schedule::{
|
||||||
|
ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
|
||||||
|
SystemWithAccess,
|
||||||
|
},
|
||||||
system::{ScheduleSystem, System, SystemIn, SystemParamValidationError, SystemStateFlags},
|
system::{ScheduleSystem, System, SystemIn, SystemParamValidationError, SystemStateFlags},
|
||||||
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
||||||
};
|
};
|
||||||
@ -73,7 +76,7 @@ pub enum ExecutorKind {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SystemSchedule {
|
pub struct SystemSchedule {
|
||||||
/// List of system node ids.
|
/// List of system node ids.
|
||||||
pub(super) system_ids: Vec<NodeId>,
|
pub(super) system_ids: Vec<SystemKey>,
|
||||||
/// Indexed by system node id.
|
/// Indexed by system node id.
|
||||||
pub(super) systems: Vec<SystemWithAccess>,
|
pub(super) systems: Vec<SystemWithAccess>,
|
||||||
/// Indexed by system node id.
|
/// Indexed by system node id.
|
||||||
@ -96,7 +99,7 @@ pub struct SystemSchedule {
|
|||||||
/// List of sets containing the system that have conditions
|
/// List of sets containing the system that have conditions
|
||||||
pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
|
pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
|
||||||
/// List of system set node ids.
|
/// List of system set node ids.
|
||||||
pub(super) set_ids: Vec<NodeId>,
|
pub(super) set_ids: Vec<SystemSetKey>,
|
||||||
/// Indexed by system set node id.
|
/// Indexed by system set node id.
|
||||||
pub(super) set_conditions: Vec<Vec<ConditionWithAccess>>,
|
pub(super) set_conditions: Vec<Vec<ConditionWithAccess>>,
|
||||||
/// Indexed by system set node id.
|
/// Indexed by system set node id.
|
||||||
|
@ -11,6 +11,7 @@ use core::{
|
|||||||
hash::{BuildHasher, Hash},
|
hash::{BuildHasher, Hash},
|
||||||
};
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use slotmap::{Key, KeyData};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use super::NodeId;
|
use super::NodeId;
|
||||||
@ -298,7 +299,7 @@ impl Direction {
|
|||||||
/// Compact storage of a [`NodeId`] and a [`Direction`].
|
/// Compact storage of a [`NodeId`] and a [`Direction`].
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct CompactNodeIdAndDirection {
|
struct CompactNodeIdAndDirection {
|
||||||
index: usize,
|
key: KeyData,
|
||||||
is_system: bool,
|
is_system: bool,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
}
|
}
|
||||||
@ -310,27 +311,30 @@ impl fmt::Debug for CompactNodeIdAndDirection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CompactNodeIdAndDirection {
|
impl CompactNodeIdAndDirection {
|
||||||
const fn store(node: NodeId, direction: Direction) -> Self {
|
fn store(node: NodeId, direction: Direction) -> Self {
|
||||||
let index = node.index();
|
let key = match node {
|
||||||
|
NodeId::System(key) => key.data(),
|
||||||
|
NodeId::Set(key) => key.data(),
|
||||||
|
};
|
||||||
let is_system = node.is_system();
|
let is_system = node.is_system();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
index,
|
key,
|
||||||
is_system,
|
is_system,
|
||||||
direction,
|
direction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn load(self) -> (NodeId, Direction) {
|
fn load(self) -> (NodeId, Direction) {
|
||||||
let Self {
|
let Self {
|
||||||
index,
|
key,
|
||||||
is_system,
|
is_system,
|
||||||
direction,
|
direction,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let node = match is_system {
|
let node = match is_system {
|
||||||
true => NodeId::System(index),
|
true => NodeId::System(key.into()),
|
||||||
false => NodeId::Set(index),
|
false => NodeId::Set(key.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
(node, direction)
|
(node, direction)
|
||||||
@ -340,8 +344,8 @@ impl CompactNodeIdAndDirection {
|
|||||||
/// Compact storage of a [`NodeId`] pair.
|
/// Compact storage of a [`NodeId`] pair.
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
struct CompactNodeIdPair {
|
struct CompactNodeIdPair {
|
||||||
index_a: usize,
|
key_a: KeyData,
|
||||||
index_b: usize,
|
key_b: KeyData,
|
||||||
is_system_a: bool,
|
is_system_a: bool,
|
||||||
is_system_b: bool,
|
is_system_b: bool,
|
||||||
}
|
}
|
||||||
@ -353,37 +357,43 @@ impl fmt::Debug for CompactNodeIdPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CompactNodeIdPair {
|
impl CompactNodeIdPair {
|
||||||
const fn store(a: NodeId, b: NodeId) -> Self {
|
fn store(a: NodeId, b: NodeId) -> Self {
|
||||||
let index_a = a.index();
|
let key_a = match a {
|
||||||
|
NodeId::System(index) => index.data(),
|
||||||
|
NodeId::Set(index) => index.data(),
|
||||||
|
};
|
||||||
let is_system_a = a.is_system();
|
let is_system_a = a.is_system();
|
||||||
|
|
||||||
let index_b = b.index();
|
let key_b = match b {
|
||||||
|
NodeId::System(index) => index.data(),
|
||||||
|
NodeId::Set(index) => index.data(),
|
||||||
|
};
|
||||||
let is_system_b = b.is_system();
|
let is_system_b = b.is_system();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
index_a,
|
key_a,
|
||||||
index_b,
|
key_b,
|
||||||
is_system_a,
|
is_system_a,
|
||||||
is_system_b,
|
is_system_b,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn load(self) -> (NodeId, NodeId) {
|
fn load(self) -> (NodeId, NodeId) {
|
||||||
let Self {
|
let Self {
|
||||||
index_a,
|
key_a,
|
||||||
index_b,
|
key_b,
|
||||||
is_system_a,
|
is_system_a,
|
||||||
is_system_b,
|
is_system_b,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let a = match is_system_a {
|
let a = match is_system_a {
|
||||||
true => NodeId::System(index_a),
|
true => NodeId::System(key_a.into()),
|
||||||
false => NodeId::Set(index_a),
|
false => NodeId::Set(key_a.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = match is_system_b {
|
let b = match is_system_b {
|
||||||
true => NodeId::System(index_b),
|
true => NodeId::System(key_b.into()),
|
||||||
false => NodeId::Set(index_b),
|
false => NodeId::Set(key_b.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
(a, b)
|
(a, b)
|
||||||
@ -392,8 +402,11 @@ impl CompactNodeIdPair {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::schedule::SystemKey;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
|
use slotmap::SlotMap;
|
||||||
|
|
||||||
/// The `Graph` type _must_ preserve the order that nodes are inserted in if
|
/// The `Graph` type _must_ preserve the order that nodes are inserted in if
|
||||||
/// no removals occur. Removals are permitted to swap the latest node into the
|
/// no removals occur. Removals are permitted to swap the latest node into the
|
||||||
@ -402,37 +415,43 @@ mod tests {
|
|||||||
fn node_order_preservation() {
|
fn node_order_preservation() {
|
||||||
use NodeId::System;
|
use NodeId::System;
|
||||||
|
|
||||||
|
let mut slotmap = SlotMap::<SystemKey, ()>::with_key();
|
||||||
let mut graph = <DiGraph>::default();
|
let mut graph = <DiGraph>::default();
|
||||||
|
|
||||||
graph.add_node(System(1));
|
let sys1 = slotmap.insert(());
|
||||||
graph.add_node(System(2));
|
let sys2 = slotmap.insert(());
|
||||||
graph.add_node(System(3));
|
let sys3 = slotmap.insert(());
|
||||||
graph.add_node(System(4));
|
let sys4 = slotmap.insert(());
|
||||||
|
|
||||||
|
graph.add_node(System(sys1));
|
||||||
|
graph.add_node(System(sys2));
|
||||||
|
graph.add_node(System(sys3));
|
||||||
|
graph.add_node(System(sys4));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.nodes().collect::<Vec<_>>(),
|
graph.nodes().collect::<Vec<_>>(),
|
||||||
vec![System(1), System(2), System(3), System(4)]
|
vec![System(sys1), System(sys2), System(sys3), System(sys4)]
|
||||||
);
|
);
|
||||||
|
|
||||||
graph.remove_node(System(1));
|
graph.remove_node(System(sys1));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.nodes().collect::<Vec<_>>(),
|
graph.nodes().collect::<Vec<_>>(),
|
||||||
vec![System(4), System(2), System(3)]
|
vec![System(sys4), System(sys2), System(sys3)]
|
||||||
);
|
);
|
||||||
|
|
||||||
graph.remove_node(System(4));
|
graph.remove_node(System(sys4));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.nodes().collect::<Vec<_>>(),
|
graph.nodes().collect::<Vec<_>>(),
|
||||||
vec![System(3), System(2)]
|
vec![System(sys3), System(sys2)]
|
||||||
);
|
);
|
||||||
|
|
||||||
graph.remove_node(System(2));
|
graph.remove_node(System(sys2));
|
||||||
|
|
||||||
assert_eq!(graph.nodes().collect::<Vec<_>>(), vec![System(3)]);
|
assert_eq!(graph.nodes().collect::<Vec<_>>(), vec![System(sys3)]);
|
||||||
|
|
||||||
graph.remove_node(System(3));
|
graph.remove_node(System(sys3));
|
||||||
|
|
||||||
assert_eq!(graph.nodes().collect::<Vec<_>>(), vec![]);
|
assert_eq!(graph.nodes().collect::<Vec<_>>(), vec![]);
|
||||||
}
|
}
|
||||||
@ -444,18 +463,26 @@ mod tests {
|
|||||||
fn strongly_connected_components() {
|
fn strongly_connected_components() {
|
||||||
use NodeId::System;
|
use NodeId::System;
|
||||||
|
|
||||||
|
let mut slotmap = SlotMap::<SystemKey, ()>::with_key();
|
||||||
let mut graph = <DiGraph>::default();
|
let mut graph = <DiGraph>::default();
|
||||||
|
|
||||||
graph.add_edge(System(1), System(2));
|
let sys1 = slotmap.insert(());
|
||||||
graph.add_edge(System(2), System(1));
|
let sys2 = slotmap.insert(());
|
||||||
|
let sys3 = slotmap.insert(());
|
||||||
|
let sys4 = slotmap.insert(());
|
||||||
|
let sys5 = slotmap.insert(());
|
||||||
|
let sys6 = slotmap.insert(());
|
||||||
|
|
||||||
graph.add_edge(System(2), System(3));
|
graph.add_edge(System(sys1), System(sys2));
|
||||||
graph.add_edge(System(3), System(2));
|
graph.add_edge(System(sys2), System(sys1));
|
||||||
|
|
||||||
graph.add_edge(System(4), System(5));
|
graph.add_edge(System(sys2), System(sys3));
|
||||||
graph.add_edge(System(5), System(4));
|
graph.add_edge(System(sys3), System(sys2));
|
||||||
|
|
||||||
graph.add_edge(System(6), System(2));
|
graph.add_edge(System(sys4), System(sys5));
|
||||||
|
graph.add_edge(System(sys5), System(sys4));
|
||||||
|
|
||||||
|
graph.add_edge(System(sys6), System(sys2));
|
||||||
|
|
||||||
let sccs = graph
|
let sccs = graph
|
||||||
.iter_sccs()
|
.iter_sccs()
|
||||||
@ -465,9 +492,9 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
sccs,
|
sccs,
|
||||||
vec![
|
vec![
|
||||||
vec![System(3), System(2), System(1)],
|
vec![System(sys3), System(sys2), System(sys1)],
|
||||||
vec![System(5), System(4)],
|
vec![System(sys5), System(sys4)],
|
||||||
vec![System(6)]
|
vec![System(sys6)]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::schedule::{SystemKey, SystemSetKey};
|
||||||
|
|
||||||
/// Unique identifier for a system or system set stored in a [`ScheduleGraph`].
|
/// Unique identifier for a system or system set stored in a [`ScheduleGraph`].
|
||||||
///
|
///
|
||||||
/// [`ScheduleGraph`]: crate::schedule::ScheduleGraph
|
/// [`ScheduleGraph`]: crate::schedule::ScheduleGraph
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum NodeId {
|
pub enum NodeId {
|
||||||
/// Identifier for a system.
|
/// Identifier for a system.
|
||||||
System(usize),
|
System(SystemKey),
|
||||||
/// Identifier for a system set.
|
/// Identifier for a system set.
|
||||||
Set(usize),
|
Set(SystemSetKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeId {
|
impl NodeId {
|
||||||
/// Returns the internal integer value.
|
|
||||||
pub const fn index(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
NodeId::System(index) | NodeId::Set(index) => *index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the identified node is a system.
|
/// Returns `true` if the identified node is a system.
|
||||||
pub const fn is_system(&self) -> bool {
|
pub const fn is_system(&self) -> bool {
|
||||||
matches!(self, NodeId::System(_))
|
matches!(self, NodeId::System(_))
|
||||||
@ -29,19 +24,19 @@ impl NodeId {
|
|||||||
matches!(self, NodeId::Set(_))
|
matches!(self, NodeId::Set(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare this [`NodeId`] with another.
|
/// Returns the system key if the node is a system, otherwise `None`.
|
||||||
pub const fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
pub const fn as_system(&self) -> Option<SystemKey> {
|
||||||
use core::cmp::Ordering::{Equal, Greater, Less};
|
match self {
|
||||||
use NodeId::{Set, System};
|
NodeId::System(system) => Some(*system),
|
||||||
|
NodeId::Set(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match (self, other) {
|
/// Returns the system set key if the node is a system set, otherwise `None`.
|
||||||
(System(a), System(b)) | (Set(a), Set(b)) => match a.checked_sub(*b) {
|
pub const fn as_set(&self) -> Option<SystemSetKey> {
|
||||||
None => Less,
|
match self {
|
||||||
Some(0) => Equal,
|
NodeId::System(_) => None,
|
||||||
Some(_) => Greater,
|
NodeId::Set(set) => Some(*set),
|
||||||
},
|
|
||||||
(System(_), Set(_)) => Less,
|
|
||||||
(Set(_), System(_)) => Greater,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ use alloc::{boxed::Box, vec::Vec};
|
|||||||
use core::any::{Any, TypeId};
|
use core::any::{Any, TypeId};
|
||||||
|
|
||||||
use super::{DiGraph, NodeId, ScheduleBuildError, ScheduleGraph};
|
use super::{DiGraph, NodeId, ScheduleBuildError, ScheduleGraph};
|
||||||
use crate::world::World;
|
use crate::{
|
||||||
|
schedule::{SystemKey, SystemSetKey},
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
use bevy_utils::TypeIdMap;
|
use bevy_utils::TypeIdMap;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
@ -19,8 +22,8 @@ pub trait ScheduleBuildPass: Send + Sync + Debug + 'static {
|
|||||||
/// Instead of modifying the graph directly, this method should return an iterator of edges to add to the graph.
|
/// Instead of modifying the graph directly, this method should return an iterator of edges to add to the graph.
|
||||||
fn collapse_set(
|
fn collapse_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
set: NodeId,
|
set: SystemSetKey,
|
||||||
systems: &[NodeId],
|
systems: &[SystemKey],
|
||||||
dependency_flattened: &DiGraph,
|
dependency_flattened: &DiGraph,
|
||||||
) -> impl Iterator<Item = (NodeId, NodeId)>;
|
) -> impl Iterator<Item = (NodeId, NodeId)>;
|
||||||
|
|
||||||
@ -44,8 +47,8 @@ pub(super) trait ScheduleBuildPassObj: Send + Sync + Debug {
|
|||||||
|
|
||||||
fn collapse_set(
|
fn collapse_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
set: NodeId,
|
set: SystemSetKey,
|
||||||
systems: &[NodeId],
|
systems: &[SystemKey],
|
||||||
dependency_flattened: &DiGraph,
|
dependency_flattened: &DiGraph,
|
||||||
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
||||||
);
|
);
|
||||||
@ -63,8 +66,8 @@ impl<T: ScheduleBuildPass> ScheduleBuildPassObj for T {
|
|||||||
}
|
}
|
||||||
fn collapse_set(
|
fn collapse_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
set: NodeId,
|
set: SystemSetKey,
|
||||||
systems: &[NodeId],
|
systems: &[SystemKey],
|
||||||
dependency_flattened: &DiGraph,
|
dependency_flattened: &DiGraph,
|
||||||
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
||||||
) {
|
) {
|
||||||
|
@ -19,6 +19,7 @@ use core::{
|
|||||||
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;
|
||||||
@ -404,7 +405,9 @@ impl Schedule {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.graph.ambiguous_with.add_edge(a_id, b_id);
|
self.graph
|
||||||
|
.ambiguous_with
|
||||||
|
.add_edge(NodeId::Set(a_id), NodeId::Set(b_id));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -599,7 +602,7 @@ impl Schedule {
|
|||||||
/// schedule has never been initialized or run.
|
/// schedule has never been initialized or run.
|
||||||
pub fn systems(
|
pub fn systems(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<impl Iterator<Item = (NodeId, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
|
) -> Result<impl Iterator<Item = (SystemKey, &ScheduleSystem)> + Sized, ScheduleNotInitialized>
|
||||||
{
|
{
|
||||||
if !self.executor_initialized {
|
if !self.executor_initialized {
|
||||||
return Err(ScheduleNotInitialized);
|
return Err(ScheduleNotInitialized);
|
||||||
@ -610,7 +613,7 @@ impl Schedule {
|
|||||||
.system_ids
|
.system_ids
|
||||||
.iter()
|
.iter()
|
||||||
.zip(&self.executable.systems)
|
.zip(&self.executable.systems)
|
||||||
.map(|(node_id, system)| (*node_id, &system.system));
|
.map(|(&node_id, system)| (node_id, &system.system));
|
||||||
|
|
||||||
Ok(iter)
|
Ok(iter)
|
||||||
}
|
}
|
||||||
@ -742,6 +745,21 @@ impl SystemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UninitializedId {
|
||||||
|
System(SystemKey),
|
||||||
|
Set {
|
||||||
|
key: SystemSetKey,
|
||||||
|
first_uninit_condition: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@ -749,18 +767,18 @@ impl SystemNode {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ScheduleGraph {
|
pub struct ScheduleGraph {
|
||||||
/// List of systems in the schedule
|
/// List of systems in the schedule
|
||||||
pub systems: Vec<SystemNode>,
|
pub systems: SlotMap<SystemKey, SystemNode>,
|
||||||
/// List of conditions for each system, in the same order as `systems`
|
/// List of conditions for each system, in the same order as `systems`
|
||||||
pub system_conditions: Vec<Vec<ConditionWithAccess>>,
|
pub system_conditions: SecondaryMap<SystemKey, Vec<ConditionWithAccess>>,
|
||||||
/// List of system sets in the schedule
|
/// List of system sets in the schedule
|
||||||
system_sets: Vec<SystemSetNode>,
|
system_sets: SlotMap<SystemSetKey, SystemSetNode>,
|
||||||
/// List of conditions for each system set, in the same order as `system_sets`
|
/// List of conditions for each system set, in the same order as `system_sets`
|
||||||
system_set_conditions: Vec<Vec<ConditionWithAccess>>,
|
system_set_conditions: SecondaryMap<SystemSetKey, Vec<ConditionWithAccess>>,
|
||||||
/// Map from system set to node id
|
/// Map from system set to node id
|
||||||
system_set_ids: HashMap<InternedSystemSet, NodeId>,
|
system_set_ids: HashMap<InternedSystemSet, SystemSetKey>,
|
||||||
/// Systems that have not been initialized yet; for system sets, we store the index of the first uninitialized condition
|
/// Systems that have not been initialized yet; for system sets, we store the index of the first uninitialized condition
|
||||||
/// (all the conditions after that index still need to be initialized)
|
/// (all the conditions after that index still need to be initialized)
|
||||||
uninit: Vec<(NodeId, usize)>,
|
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)
|
||||||
@ -768,7 +786,7 @@ pub struct ScheduleGraph {
|
|||||||
ambiguous_with: UnGraph,
|
ambiguous_with: UnGraph,
|
||||||
/// Nodes that are allowed to have ambiguous ordering relationship with any other systems.
|
/// Nodes that are allowed to have ambiguous ordering relationship with any other systems.
|
||||||
pub ambiguous_with_all: HashSet<NodeId>,
|
pub ambiguous_with_all: HashSet<NodeId>,
|
||||||
conflicting_systems: Vec<(NodeId, NodeId, Vec<ComponentId>)>,
|
conflicting_systems: Vec<(SystemKey, SystemKey, Vec<ComponentId>)>,
|
||||||
anonymous_sets: usize,
|
anonymous_sets: usize,
|
||||||
changed: bool,
|
changed: bool,
|
||||||
settings: ScheduleBuildSettings,
|
settings: ScheduleBuildSettings,
|
||||||
@ -780,10 +798,10 @@ 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: Vec::new(),
|
systems: SlotMap::with_key(),
|
||||||
system_conditions: Vec::new(),
|
system_conditions: SecondaryMap::new(),
|
||||||
system_sets: Vec::new(),
|
system_sets: SlotMap::with_key(),
|
||||||
system_set_conditions: Vec::new(),
|
system_set_conditions: SecondaryMap::new(),
|
||||||
system_set_ids: HashMap::default(),
|
system_set_ids: HashMap::default(),
|
||||||
uninit: Vec::new(),
|
uninit: Vec::new(),
|
||||||
hierarchy: Dag::new(),
|
hierarchy: Dag::new(),
|
||||||
@ -798,14 +816,11 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the system at the given [`NodeId`], if it exists.
|
/// Returns the system at the given [`SystemKey`], if it exists.
|
||||||
pub fn get_system_at(&self, id: NodeId) -> Option<&ScheduleSystem> {
|
pub fn get_system_at(&self, key: SystemKey) -> Option<&ScheduleSystem> {
|
||||||
if !id.is_system() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.systems
|
self.systems
|
||||||
.get(id.index())
|
.get(key)
|
||||||
.and_then(|system| system.inner.as_ref())
|
.and_then(|system| system.get())
|
||||||
.map(|system| &system.system)
|
.map(|system| &system.system)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,74 +833,59 @@ impl ScheduleGraph {
|
|||||||
///
|
///
|
||||||
/// Panics if it doesn't exist.
|
/// Panics if it doesn't exist.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn system_at(&self, id: NodeId) -> &ScheduleSystem {
|
pub fn system_at(&self, key: SystemKey) -> &ScheduleSystem {
|
||||||
self.get_system_at(id)
|
self.get_system_at(key)
|
||||||
.ok_or_else(|| format!("system with id {id:?} does not exist in this Schedule"))
|
.unwrap_or_else(|| panic!("system with key {key:?} does not exist in this Schedule"))
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set at the given [`NodeId`], if it exists.
|
/// Returns the set at the given [`NodeId`], if it exists.
|
||||||
pub fn get_set_at(&self, id: NodeId) -> Option<&dyn SystemSet> {
|
pub fn get_set_at(&self, key: SystemSetKey) -> Option<&dyn SystemSet> {
|
||||||
if !id.is_set() {
|
self.system_sets.get(key).map(|set| &*set.inner)
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.system_sets.get(id.index()).map(|set| &*set.inner)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set at the given [`NodeId`].
|
/// Returns the set at the given [`NodeId`].
|
||||||
///
|
///
|
||||||
/// Panics if it doesn't exist.
|
/// Panics if it doesn't exist.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn set_at(&self, id: NodeId) -> &dyn SystemSet {
|
pub fn set_at(&self, id: SystemSetKey) -> &dyn SystemSet {
|
||||||
self.get_set_at(id)
|
self.get_set_at(id)
|
||||||
.ok_or_else(|| format!("set with id {id:?} does not exist in this Schedule"))
|
.unwrap_or_else(|| panic!("set with id {id:?} does not exist in this Schedule"))
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the conditions for the set at the given [`NodeId`], if it exists.
|
/// Returns the conditions for the set at the given [`SystemSetKey`], if it exists.
|
||||||
pub fn get_set_conditions_at(&self, id: NodeId) -> Option<&[ConditionWithAccess]> {
|
pub fn get_set_conditions_at(&self, key: SystemSetKey) -> Option<&[ConditionWithAccess]> {
|
||||||
if !id.is_set() {
|
self.system_set_conditions.get(key).map(Vec::as_slice)
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.system_set_conditions
|
|
||||||
.get(id.index())
|
|
||||||
.map(Vec::as_slice)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the conditions for the set at the given [`NodeId`].
|
/// Returns the conditions for the set at the given [`SystemSetKey`].
|
||||||
///
|
///
|
||||||
/// Panics if it doesn't exist.
|
/// Panics if it doesn't exist.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn set_conditions_at(&self, id: NodeId) -> &[ConditionWithAccess] {
|
pub fn set_conditions_at(&self, key: SystemSetKey) -> &[ConditionWithAccess] {
|
||||||
self.get_set_conditions_at(id)
|
self.get_set_conditions_at(key)
|
||||||
.ok_or_else(|| format!("set with id {id:?} does not exist in this Schedule"))
|
.unwrap_or_else(|| panic!("set with key {key:?} does not exist in this Schedule"))
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all systems in this schedule, along with the conditions for each system.
|
/// Returns an iterator over all systems in this schedule, along with the conditions for each system.
|
||||||
pub fn systems(
|
pub fn systems(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (NodeId, &ScheduleSystem, &[ConditionWithAccess])> {
|
) -> impl Iterator<Item = (SystemKey, &ScheduleSystem, &[ConditionWithAccess])> {
|
||||||
self.systems
|
self.systems.iter().filter_map(|(key, system_node)| {
|
||||||
.iter()
|
let system = &system_node.inner.as_ref()?.system;
|
||||||
.zip(self.system_conditions.iter())
|
let conditions = self.system_conditions.get(key)?;
|
||||||
.enumerate()
|
Some((key, system, conditions.as_slice()))
|
||||||
.filter_map(|(i, (system_node, condition))| {
|
})
|
||||||
let system = &system_node.inner.as_ref()?.system;
|
|
||||||
Some((NodeId::System(i), system, condition.as_slice()))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all system sets in this schedule, along with the conditions for each
|
/// Returns an iterator over all system sets in this schedule, along with the conditions for each
|
||||||
/// system set.
|
/// system set.
|
||||||
pub fn system_sets(
|
pub fn system_sets(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (NodeId, &dyn SystemSet, &[ConditionWithAccess])> {
|
) -> impl Iterator<Item = (SystemSetKey, &dyn SystemSet, &[ConditionWithAccess])> {
|
||||||
self.system_set_ids.iter().map(|(_, &node_id)| {
|
self.system_sets.iter().filter_map(|(key, set_node)| {
|
||||||
let set_node = &self.system_sets[node_id.index()];
|
|
||||||
let set = &*set_node.inner;
|
let set = &*set_node.inner;
|
||||||
let conditions = self.system_set_conditions[node_id.index()].as_slice();
|
let conditions = self.system_set_conditions.get(key)?.as_slice();
|
||||||
(node_id, set, conditions)
|
Some((key, set, conditions))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,7 +909,7 @@ impl ScheduleGraph {
|
|||||||
///
|
///
|
||||||
/// If the `Vec<ComponentId>` is empty, the systems conflict on [`World`] access.
|
/// If the `Vec<ComponentId>` is empty, the systems conflict on [`World`] access.
|
||||||
/// Must be called after [`ScheduleGraph::build_schedule`] to be non-empty.
|
/// Must be called after [`ScheduleGraph::build_schedule`] to be non-empty.
|
||||||
pub fn conflicting_systems(&self) -> &[(NodeId, NodeId, Vec<ComponentId>)] {
|
pub fn conflicting_systems(&self) -> &[(SystemKey, SystemKey, Vec<ComponentId>)] {
|
||||||
&self.conflicting_systems
|
&self.conflicting_systems
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,23 +1051,22 @@ impl ScheduleGraph {
|
|||||||
&mut self,
|
&mut self,
|
||||||
config: ScheduleConfig<ScheduleSystem>,
|
config: ScheduleConfig<ScheduleSystem>,
|
||||||
) -> Result<NodeId, ScheduleBuildError> {
|
) -> Result<NodeId, ScheduleBuildError> {
|
||||||
let id = NodeId::System(self.systems.len());
|
let key = self.systems.insert(SystemNode::new(config.node));
|
||||||
|
self.system_conditions.insert(
|
||||||
// graph updates are immediate
|
key,
|
||||||
self.update_graphs(id, config.metadata)?;
|
|
||||||
|
|
||||||
// system init has to be deferred (need `&mut World`)
|
|
||||||
self.uninit.push((id, 0));
|
|
||||||
self.systems.push(SystemNode::new(config.node));
|
|
||||||
self.system_conditions.push(
|
|
||||||
config
|
config
|
||||||
.conditions
|
.conditions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ConditionWithAccess::new)
|
.map(ConditionWithAccess::new)
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
|
// system init has to be deferred (need `&mut World`)
|
||||||
|
self.uninit.push(UninitializedId::System(key));
|
||||||
|
|
||||||
Ok(id)
|
// graph updates are immediate
|
||||||
|
self.update_graphs(NodeId::System(key), config.metadata)?;
|
||||||
|
|
||||||
|
Ok(NodeId::System(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1086,49 +1085,30 @@ impl ScheduleGraph {
|
|||||||
conditions,
|
conditions,
|
||||||
} = set;
|
} = set;
|
||||||
|
|
||||||
let id = match self.system_set_ids.get(&set) {
|
let key = match self.system_set_ids.get(&set) {
|
||||||
Some(&id) => id,
|
Some(&id) => id,
|
||||||
None => self.add_set(set),
|
None => self.add_set(set),
|
||||||
};
|
};
|
||||||
|
|
||||||
// graph updates are immediate
|
// graph updates are immediate
|
||||||
self.update_graphs(id, metadata)?;
|
self.update_graphs(NodeId::Set(key), metadata)?;
|
||||||
|
|
||||||
// system init has to be deferred (need `&mut World`)
|
// system init has to be deferred (need `&mut World`)
|
||||||
let system_set_conditions = &mut self.system_set_conditions[id.index()];
|
let system_set_conditions = self.system_set_conditions.entry(key).unwrap().or_default();
|
||||||
self.uninit.push((id, system_set_conditions.len()));
|
self.uninit.push(UninitializedId::Set {
|
||||||
|
key,
|
||||||
|
first_uninit_condition: system_set_conditions.len(),
|
||||||
|
});
|
||||||
system_set_conditions.extend(conditions.into_iter().map(ConditionWithAccess::new));
|
system_set_conditions.extend(conditions.into_iter().map(ConditionWithAccess::new));
|
||||||
|
|
||||||
Ok(id)
|
Ok(NodeId::Set(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_set(&mut self, set: InternedSystemSet) -> NodeId {
|
fn add_set(&mut self, set: InternedSystemSet) -> SystemSetKey {
|
||||||
let id = NodeId::Set(self.system_sets.len());
|
let key = self.system_sets.insert(SystemSetNode::new(set));
|
||||||
self.system_sets.push(SystemSetNode::new(set));
|
self.system_set_conditions.insert(key, Vec::new());
|
||||||
self.system_set_conditions.push(Vec::new());
|
self.system_set_ids.insert(set, key);
|
||||||
self.system_set_ids.insert(set, id);
|
key
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that a system set isn't included in itself.
|
|
||||||
/// If not present, add the set to the graph.
|
|
||||||
fn check_hierarchy_set(
|
|
||||||
&mut self,
|
|
||||||
id: &NodeId,
|
|
||||||
set: InternedSystemSet,
|
|
||||||
) -> Result<(), ScheduleBuildError> {
|
|
||||||
match self.system_set_ids.get(&set) {
|
|
||||||
Some(set_id) => {
|
|
||||||
if id == set_id {
|
|
||||||
return Err(ScheduleBuildError::HierarchyLoop(self.get_node_name(id)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.add_set(set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_anonymous_set(&mut self) -> AnonymousSet {
|
fn create_anonymous_set(&mut self) -> AnonymousSet {
|
||||||
@ -1141,11 +1121,24 @@ impl ScheduleGraph {
|
|||||||
/// Add all the sets from the [`GraphInfo`]'s hierarchy to the graph.
|
/// Add all the sets from the [`GraphInfo`]'s hierarchy to the graph.
|
||||||
fn check_hierarchy_sets(
|
fn check_hierarchy_sets(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &NodeId,
|
id: NodeId,
|
||||||
graph_info: &GraphInfo,
|
graph_info: &GraphInfo,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
for &set in &graph_info.hierarchy {
|
for &set in &graph_info.hierarchy {
|
||||||
self.check_hierarchy_set(id, set)?;
|
if let Some(&set_id) = self.system_set_ids.get(&set) {
|
||||||
|
if let NodeId::Set(key) = id
|
||||||
|
&& set_id == key
|
||||||
|
{
|
||||||
|
{
|
||||||
|
return Err(ScheduleBuildError::HierarchyLoop(
|
||||||
|
self.get_node_name(&NodeId::Set(key)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the set is not in the graph, we add it
|
||||||
|
self.add_set(set);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1155,22 +1148,29 @@ impl ScheduleGraph {
|
|||||||
/// Add all the sets from the [`GraphInfo`]'s dependencies to the graph.
|
/// Add all the sets from the [`GraphInfo`]'s dependencies to the graph.
|
||||||
fn check_edges(
|
fn check_edges(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &NodeId,
|
id: NodeId,
|
||||||
graph_info: &GraphInfo,
|
graph_info: &GraphInfo,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
for Dependency { set, .. } in &graph_info.dependencies {
|
for Dependency { set, .. } in &graph_info.dependencies {
|
||||||
match self.system_set_ids.get(set) {
|
if let Some(&set_id) = self.system_set_ids.get(set) {
|
||||||
Some(set_id) => {
|
if let NodeId::Set(key) = id
|
||||||
if id == set_id {
|
&& set_id == key
|
||||||
return Err(ScheduleBuildError::DependencyLoop(self.get_node_name(id)));
|
{
|
||||||
}
|
return Err(ScheduleBuildError::DependencyLoop(
|
||||||
}
|
self.get_node_name(&NodeId::Set(key)),
|
||||||
None => {
|
));
|
||||||
self.add_set(*set);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If the set is not in the graph, we add it
|
||||||
|
self.add_set(*set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add all the sets from the [`GraphInfo`]'s ambiguity to the graph.
|
||||||
|
fn add_ambiguities(&mut self, graph_info: &GraphInfo) {
|
||||||
if let Ambiguity::IgnoreWithSet(ambiguous_with) = &graph_info.ambiguous_with {
|
if let Ambiguity::IgnoreWithSet(ambiguous_with) = &graph_info.ambiguous_with {
|
||||||
for set in ambiguous_with {
|
for set in ambiguous_with {
|
||||||
if !self.system_set_ids.contains_key(set) {
|
if !self.system_set_ids.contains_key(set) {
|
||||||
@ -1178,8 +1178,6 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the internal graphs (hierarchy, dependency, ambiguity) by adding a single [`GraphInfo`]
|
/// Update the internal graphs (hierarchy, dependency, ambiguity) by adding a single [`GraphInfo`]
|
||||||
@ -1188,8 +1186,9 @@ impl ScheduleGraph {
|
|||||||
id: NodeId,
|
id: NodeId,
|
||||||
graph_info: GraphInfo,
|
graph_info: GraphInfo,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
self.check_hierarchy_sets(&id, &graph_info)?;
|
self.check_hierarchy_sets(id, &graph_info)?;
|
||||||
self.check_edges(&id, &graph_info)?;
|
self.check_edges(id, &graph_info)?;
|
||||||
|
self.add_ambiguities(&graph_info);
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
|
|
||||||
let GraphInfo {
|
let GraphInfo {
|
||||||
@ -1202,20 +1201,20 @@ impl ScheduleGraph {
|
|||||||
self.hierarchy.graph.add_node(id);
|
self.hierarchy.graph.add_node(id);
|
||||||
self.dependency.graph.add_node(id);
|
self.dependency.graph.add_node(id);
|
||||||
|
|
||||||
for set in sets.into_iter().map(|set| self.system_set_ids[&set]) {
|
for key in sets.into_iter().map(|set| self.system_set_ids[&set]) {
|
||||||
self.hierarchy.graph.add_edge(set, id);
|
self.hierarchy.graph.add_edge(NodeId::Set(key), id);
|
||||||
|
|
||||||
// ensure set also appears in dependency graph
|
// ensure set also appears in dependency graph
|
||||||
self.dependency.graph.add_node(set);
|
self.dependency.graph.add_node(NodeId::Set(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (kind, set, options) in dependencies
|
for (kind, key, options) in dependencies
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|Dependency { kind, set, options }| (kind, self.system_set_ids[&set], options))
|
.map(|Dependency { kind, set, options }| (kind, self.system_set_ids[&set], options))
|
||||||
{
|
{
|
||||||
let (lhs, rhs) = match kind {
|
let (lhs, rhs) = match kind {
|
||||||
DependencyKind::Before => (id, set),
|
DependencyKind::Before => (id, NodeId::Set(key)),
|
||||||
DependencyKind::After => (set, id),
|
DependencyKind::After => (NodeId::Set(key), id),
|
||||||
};
|
};
|
||||||
self.dependency.graph.add_edge(lhs, rhs);
|
self.dependency.graph.add_edge(lhs, rhs);
|
||||||
for pass in self.passes.values_mut() {
|
for pass in self.passes.values_mut() {
|
||||||
@ -1223,17 +1222,17 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensure set also appears in hierarchy graph
|
// ensure set also appears in hierarchy graph
|
||||||
self.hierarchy.graph.add_node(set);
|
self.hierarchy.graph.add_node(NodeId::Set(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
match ambiguous_with {
|
match ambiguous_with {
|
||||||
Ambiguity::Check => (),
|
Ambiguity::Check => (),
|
||||||
Ambiguity::IgnoreWithSet(ambiguous_with) => {
|
Ambiguity::IgnoreWithSet(ambiguous_with) => {
|
||||||
for set in ambiguous_with
|
for key in ambiguous_with
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|set| self.system_set_ids[&set])
|
.map(|set| self.system_set_ids[&set])
|
||||||
{
|
{
|
||||||
self.ambiguous_with.add_edge(id, set);
|
self.ambiguous_with.add_edge(id, NodeId::Set(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ambiguity::IgnoreAll => {
|
Ambiguity::IgnoreAll => {
|
||||||
@ -1246,17 +1245,23 @@ 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, i) in self.uninit.drain(..) {
|
for id in self.uninit.drain(..) {
|
||||||
match id {
|
match id {
|
||||||
NodeId::System(index) => {
|
UninitializedId::System(key) => {
|
||||||
let system = self.systems[index].get_mut().unwrap();
|
let system = self.systems[key].get_mut().unwrap();
|
||||||
system.access = system.system.initialize(world);
|
system.access = system.system.initialize(world);
|
||||||
for condition in &mut self.system_conditions[index] {
|
for condition in &mut self.system_conditions[key] {
|
||||||
condition.access = condition.condition.initialize(world);
|
condition.access = condition.condition.initialize(world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeId::Set(index) => {
|
UninitializedId::Set {
|
||||||
for condition in self.system_set_conditions[index].iter_mut().skip(i) {
|
key,
|
||||||
|
first_uninit_condition,
|
||||||
|
} => {
|
||||||
|
for condition in self.system_set_conditions[key]
|
||||||
|
.iter_mut()
|
||||||
|
.skip(first_uninit_condition)
|
||||||
|
{
|
||||||
condition.access = condition.condition.initialize(world);
|
condition.access = condition.condition.initialize(world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1348,41 +1353,47 @@ impl ScheduleGraph {
|
|||||||
&self,
|
&self,
|
||||||
hierarchy_topsort: &[NodeId],
|
hierarchy_topsort: &[NodeId],
|
||||||
hierarchy_graph: &DiGraph,
|
hierarchy_graph: &DiGraph,
|
||||||
) -> (HashMap<NodeId, Vec<NodeId>>, HashMap<NodeId, FixedBitSet>) {
|
) -> (
|
||||||
let mut set_systems: HashMap<NodeId, Vec<NodeId>> =
|
HashMap<SystemSetKey, Vec<SystemKey>>,
|
||||||
|
HashMap<SystemSetKey, HashSet<SystemKey>>,
|
||||||
|
) {
|
||||||
|
let mut set_systems: HashMap<SystemSetKey, Vec<SystemKey>> =
|
||||||
HashMap::with_capacity_and_hasher(self.system_sets.len(), Default::default());
|
HashMap::with_capacity_and_hasher(self.system_sets.len(), Default::default());
|
||||||
let mut set_system_bitsets =
|
let mut set_system_sets: HashMap<SystemSetKey, HashSet<SystemKey>> =
|
||||||
HashMap::with_capacity_and_hasher(self.system_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() {
|
||||||
if id.is_system() {
|
let NodeId::Set(set_key) = id else {
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
|
|
||||||
let mut systems = Vec::new();
|
let mut systems = Vec::new();
|
||||||
let mut system_bitset = FixedBitSet::with_capacity(self.systems.len());
|
let mut system_set = HashSet::with_capacity(self.systems.len());
|
||||||
|
|
||||||
for child in hierarchy_graph.neighbors_directed(id, Outgoing) {
|
for child in hierarchy_graph.neighbors_directed(id, Outgoing) {
|
||||||
match child {
|
match child {
|
||||||
NodeId::System(_) => {
|
NodeId::System(key) => {
|
||||||
systems.push(child);
|
systems.push(key);
|
||||||
system_bitset.insert(child.index());
|
system_set.insert(key);
|
||||||
}
|
}
|
||||||
NodeId::Set(_) => {
|
NodeId::Set(key) => {
|
||||||
let child_systems = set_systems.get(&child).unwrap();
|
let child_systems = set_systems.get(&key).unwrap();
|
||||||
let child_system_bitset = set_system_bitsets.get(&child).unwrap();
|
let child_system_set = set_system_sets.get(&key).unwrap();
|
||||||
systems.extend_from_slice(child_systems);
|
systems.extend_from_slice(child_systems);
|
||||||
system_bitset.union_with(child_system_bitset);
|
system_set.extend(child_system_set.iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_systems.insert(id, systems);
|
set_systems.insert(set_key, systems);
|
||||||
set_system_bitsets.insert(id, system_bitset);
|
set_system_sets.insert(set_key, system_set);
|
||||||
}
|
}
|
||||||
(set_systems, set_system_bitsets)
|
(set_systems, set_system_sets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dependency_flattened(&mut self, set_systems: &HashMap<NodeId, Vec<NodeId>>) -> DiGraph {
|
fn get_dependency_flattened(
|
||||||
|
&mut self,
|
||||||
|
set_systems: &HashMap<SystemSetKey, Vec<SystemKey>>,
|
||||||
|
) -> DiGraph {
|
||||||
// flatten: combine `in_set` with `before` and `after` information
|
// flatten: combine `in_set` with `before` and `after` information
|
||||||
// have to do it like this to preserve transitivity
|
// have to do it like this to preserve transitivity
|
||||||
let mut dependency_flattened = self.dependency.graph.clone();
|
let mut dependency_flattened = self.dependency.graph.clone();
|
||||||
@ -1393,26 +1404,26 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
if systems.is_empty() {
|
if systems.is_empty() {
|
||||||
// collapse dependencies for empty sets
|
// collapse dependencies for empty sets
|
||||||
for a in dependency_flattened.neighbors_directed(set, Incoming) {
|
for a in dependency_flattened.neighbors_directed(NodeId::Set(set), Incoming) {
|
||||||
for b in dependency_flattened.neighbors_directed(set, Outgoing) {
|
for b in dependency_flattened.neighbors_directed(NodeId::Set(set), Outgoing) {
|
||||||
temp.push((a, b));
|
temp.push((a, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for a in dependency_flattened.neighbors_directed(set, Incoming) {
|
for a in dependency_flattened.neighbors_directed(NodeId::Set(set), Incoming) {
|
||||||
for &sys in systems {
|
for &sys in systems {
|
||||||
temp.push((a, sys));
|
temp.push((a, NodeId::System(sys)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for b in dependency_flattened.neighbors_directed(set, Outgoing) {
|
for b in dependency_flattened.neighbors_directed(NodeId::Set(set), Outgoing) {
|
||||||
for &sys in systems {
|
for &sys in systems {
|
||||||
temp.push((sys, b));
|
temp.push((NodeId::System(sys), b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependency_flattened.remove_node(set);
|
dependency_flattened.remove_node(NodeId::Set(set));
|
||||||
for (a, b) in temp.drain(..) {
|
for (a, b) in temp.drain(..) {
|
||||||
dependency_flattened.add_edge(a, b);
|
dependency_flattened.add_edge(a, b);
|
||||||
}
|
}
|
||||||
@ -1421,27 +1432,31 @@ impl ScheduleGraph {
|
|||||||
dependency_flattened
|
dependency_flattened
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ambiguous_with_flattened(&self, set_systems: &HashMap<NodeId, Vec<NodeId>>) -> UnGraph {
|
fn get_ambiguous_with_flattened(
|
||||||
|
&self,
|
||||||
|
set_systems: &HashMap<SystemSetKey, Vec<SystemKey>>,
|
||||||
|
) -> UnGraph {
|
||||||
let mut ambiguous_with_flattened = UnGraph::default();
|
let mut ambiguous_with_flattened = UnGraph::default();
|
||||||
for (lhs, rhs) in self.ambiguous_with.all_edges() {
|
for (lhs, rhs) in self.ambiguous_with.all_edges() {
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(NodeId::System(_), NodeId::System(_)) => {
|
(NodeId::System(_), NodeId::System(_)) => {
|
||||||
ambiguous_with_flattened.add_edge(lhs, rhs);
|
ambiguous_with_flattened.add_edge(lhs, rhs);
|
||||||
}
|
}
|
||||||
(NodeId::Set(_), NodeId::System(_)) => {
|
(NodeId::Set(lhs), NodeId::System(_)) => {
|
||||||
for &lhs_ in set_systems.get(&lhs).unwrap_or(&Vec::new()) {
|
for &lhs_ in set_systems.get(&lhs).unwrap_or(&Vec::new()) {
|
||||||
ambiguous_with_flattened.add_edge(lhs_, rhs);
|
ambiguous_with_flattened.add_edge(NodeId::System(lhs_), rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(NodeId::System(_), NodeId::Set(_)) => {
|
(NodeId::System(_), NodeId::Set(rhs)) => {
|
||||||
for &rhs_ in set_systems.get(&rhs).unwrap_or(&Vec::new()) {
|
for &rhs_ in set_systems.get(&rhs).unwrap_or(&Vec::new()) {
|
||||||
ambiguous_with_flattened.add_edge(lhs, rhs_);
|
ambiguous_with_flattened.add_edge(lhs, NodeId::System(rhs_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(NodeId::Set(_), NodeId::Set(_)) => {
|
(NodeId::Set(lhs), NodeId::Set(rhs)) => {
|
||||||
for &lhs_ in set_systems.get(&lhs).unwrap_or(&Vec::new()) {
|
for &lhs_ in set_systems.get(&lhs).unwrap_or(&Vec::new()) {
|
||||||
for &rhs_ in set_systems.get(&rhs).unwrap_or(&vec![]) {
|
for &rhs_ in set_systems.get(&rhs).unwrap_or(&vec![]) {
|
||||||
ambiguous_with_flattened.add_edge(lhs_, rhs_);
|
ambiguous_with_flattened
|
||||||
|
.add_edge(NodeId::System(lhs_), NodeId::System(rhs_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1456,7 +1471,7 @@ impl ScheduleGraph {
|
|||||||
flat_results_disconnected: &Vec<(NodeId, NodeId)>,
|
flat_results_disconnected: &Vec<(NodeId, NodeId)>,
|
||||||
ambiguous_with_flattened: &UnGraph,
|
ambiguous_with_flattened: &UnGraph,
|
||||||
ignored_ambiguities: &BTreeSet<ComponentId>,
|
ignored_ambiguities: &BTreeSet<ComponentId>,
|
||||||
) -> Vec<(NodeId, NodeId, Vec<ComponentId>)> {
|
) -> Vec<(SystemKey, SystemKey, Vec<ComponentId>)> {
|
||||||
let mut conflicting_systems = Vec::new();
|
let mut conflicting_systems = Vec::new();
|
||||||
for &(a, b) in flat_results_disconnected {
|
for &(a, b) in flat_results_disconnected {
|
||||||
if ambiguous_with_flattened.contains_edge(a, b)
|
if ambiguous_with_flattened.contains_edge(a, b)
|
||||||
@ -1466,8 +1481,18 @@ impl ScheduleGraph {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let system_a = self.systems[a.index()].get().unwrap();
|
let NodeId::System(a) = a else {
|
||||||
let system_b = self.systems[b.index()].get().unwrap();
|
panic!(
|
||||||
|
"Encountered a non-system node in the flattened disconnected results: {a:?}"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let NodeId::System(b) = b else {
|
||||||
|
panic!(
|
||||||
|
"Encountered a non-system node in the flattened disconnected results: {b:?}"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let system_a = self.systems[a].get().unwrap();
|
||||||
|
let system_b = self.systems[b].get().unwrap();
|
||||||
if system_a.system.is_exclusive() || system_b.system.is_exclusive() {
|
if system_a.system.is_exclusive() || system_b.system.is_exclusive() {
|
||||||
conflicting_systems.push((a, b, Vec::new()));
|
conflicting_systems.push((a, b, Vec::new()));
|
||||||
} else {
|
} else {
|
||||||
@ -1503,7 +1528,11 @@ impl ScheduleGraph {
|
|||||||
dependency_flattened_dag: Dag,
|
dependency_flattened_dag: Dag,
|
||||||
hier_results_reachable: FixedBitSet,
|
hier_results_reachable: FixedBitSet,
|
||||||
) -> SystemSchedule {
|
) -> SystemSchedule {
|
||||||
let dg_system_ids = dependency_flattened_dag.topsort.clone();
|
let dg_system_ids = dependency_flattened_dag
|
||||||
|
.topsort
|
||||||
|
.iter()
|
||||||
|
.filter_map(NodeId::as_system)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let dg_system_idx_map = dg_system_ids
|
let dg_system_idx_map = dg_system_ids
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -1517,7 +1546,7 @@ impl ScheduleGraph {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|&(_i, id)| id.is_system())
|
.filter_map(|(i, id)| Some((i, id.as_system()?)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = self
|
let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = self
|
||||||
@ -1526,10 +1555,11 @@ impl ScheduleGraph {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|&(_i, id)| {
|
.filter_map(|(i, id)| {
|
||||||
// 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)
|
||||||
id.is_set() && !self.system_set_conditions[id.index()].is_empty()
|
let key = id.as_set()?;
|
||||||
|
(!self.system_set_conditions[key].is_empty()).then_some((i, key))
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
@ -1541,16 +1571,19 @@ impl ScheduleGraph {
|
|||||||
// (needed by multi_threaded executor to run systems in the correct order)
|
// (needed by multi_threaded executor to run systems in the correct order)
|
||||||
let mut system_dependencies = Vec::with_capacity(sys_count);
|
let mut system_dependencies = Vec::with_capacity(sys_count);
|
||||||
let mut system_dependents = Vec::with_capacity(sys_count);
|
let mut system_dependents = Vec::with_capacity(sys_count);
|
||||||
for &sys_id in &dg_system_ids {
|
for &sys_key in &dg_system_ids {
|
||||||
let num_dependencies = dependency_flattened_dag
|
let num_dependencies = dependency_flattened_dag
|
||||||
.graph
|
.graph
|
||||||
.neighbors_directed(sys_id, Incoming)
|
.neighbors_directed(NodeId::System(sys_key), Incoming)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
let dependents = dependency_flattened_dag
|
let dependents = dependency_flattened_dag
|
||||||
.graph
|
.graph
|
||||||
.neighbors_directed(sys_id, Outgoing)
|
.neighbors_directed(NodeId::System(sys_key), Outgoing)
|
||||||
.map(|dep_id| dg_system_idx_map[&dep_id])
|
.filter_map(|dep_id| {
|
||||||
|
let dep_key = dep_id.as_system()?;
|
||||||
|
Some(dg_system_idx_map[&dep_key])
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
system_dependencies.push(num_dependencies);
|
system_dependencies.push(num_dependencies);
|
||||||
@ -1563,8 +1596,8 @@ impl ScheduleGraph {
|
|||||||
vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
|
vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
|
||||||
for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
|
for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
|
||||||
let bitset = &mut systems_in_sets_with_conditions[i];
|
let bitset = &mut systems_in_sets_with_conditions[i];
|
||||||
for &(col, sys_id) in &hg_systems {
|
for &(col, sys_key) in &hg_systems {
|
||||||
let idx = dg_system_idx_map[&sys_id];
|
let idx = dg_system_idx_map[&sys_key];
|
||||||
let is_descendant = hier_results_reachable[index(row, col, hg_node_count)];
|
let is_descendant = hier_results_reachable[index(row, col, hg_node_count)];
|
||||||
bitset.set(idx, is_descendant);
|
bitset.set(idx, is_descendant);
|
||||||
}
|
}
|
||||||
@ -1572,8 +1605,8 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
let mut sets_with_conditions_of_systems =
|
let mut sets_with_conditions_of_systems =
|
||||||
vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
|
vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
|
||||||
for &(col, sys_id) in &hg_systems {
|
for &(col, sys_key) in &hg_systems {
|
||||||
let i = dg_system_idx_map[&sys_id];
|
let i = dg_system_idx_map[&sys_key];
|
||||||
let bitset = &mut sets_with_conditions_of_systems[i];
|
let bitset = &mut sets_with_conditions_of_systems[i];
|
||||||
for (idx, &row) in hg_set_with_conditions_idxs
|
for (idx, &row) in hg_set_with_conditions_idxs
|
||||||
.iter()
|
.iter()
|
||||||
@ -1611,36 +1644,36 @@ impl ScheduleGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move systems out of old schedule
|
// move systems out of old schedule
|
||||||
for ((id, system), conditions) in schedule
|
for ((key, system), conditions) in schedule
|
||||||
.system_ids
|
.system_ids
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.zip(schedule.systems.drain(..))
|
.zip(schedule.systems.drain(..))
|
||||||
.zip(schedule.system_conditions.drain(..))
|
.zip(schedule.system_conditions.drain(..))
|
||||||
{
|
{
|
||||||
self.systems[id.index()].inner = Some(system);
|
self.systems[key].inner = Some(system);
|
||||||
self.system_conditions[id.index()] = conditions;
|
self.system_conditions[key] = conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, conditions) in schedule
|
for (key, conditions) in schedule
|
||||||
.set_ids
|
.set_ids
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.zip(schedule.set_conditions.drain(..))
|
.zip(schedule.set_conditions.drain(..))
|
||||||
{
|
{
|
||||||
self.system_set_conditions[id.index()] = conditions;
|
self.system_set_conditions[key] = 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 &id in &schedule.system_ids {
|
for &key in &schedule.system_ids {
|
||||||
let system = self.systems[id.index()].inner.take().unwrap();
|
let system = self.systems[key].inner.take().unwrap();
|
||||||
let conditions = core::mem::take(&mut self.system_conditions[id.index()]);
|
let conditions = core::mem::take(&mut self.system_conditions[key]);
|
||||||
schedule.systems.push(system);
|
schedule.systems.push(system);
|
||||||
schedule.system_conditions.push(conditions);
|
schedule.system_conditions.push(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
for &id in &schedule.set_ids {
|
for &key in &schedule.set_ids {
|
||||||
let conditions = core::mem::take(&mut self.system_set_conditions[id.index()]);
|
let conditions = core::mem::take(&mut self.system_set_conditions[key]);
|
||||||
schedule.set_conditions.push(conditions);
|
schedule.set_conditions.push(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1693,9 +1726,9 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
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(_) => {
|
NodeId::System(key) => {
|
||||||
let name = self.systems[id.index()].get().unwrap().system.name();
|
let name = self.systems[key].get().unwrap().system.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 {
|
||||||
@ -1714,8 +1747,8 @@ impl ScheduleGraph {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeId::Set(_) => {
|
NodeId::Set(key) => {
|
||||||
let set = &self.system_sets[id.index()];
|
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 {
|
||||||
@ -1903,16 +1936,16 @@ impl ScheduleGraph {
|
|||||||
fn check_order_but_intersect(
|
fn check_order_but_intersect(
|
||||||
&self,
|
&self,
|
||||||
dep_results_connected: &HashSet<(NodeId, NodeId)>,
|
dep_results_connected: &HashSet<(NodeId, NodeId)>,
|
||||||
set_system_bitsets: &HashMap<NodeId, FixedBitSet>,
|
set_system_sets: &HashMap<SystemSetKey, HashSet<SystemKey>>,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
// check that there is no ordering between system sets that intersect
|
// check that there is no ordering between system sets that intersect
|
||||||
for (a, b) in dep_results_connected {
|
for (a, b) in dep_results_connected {
|
||||||
if !(a.is_set() && b.is_set()) {
|
let (NodeId::Set(a_key), NodeId::Set(b_key)) = (a, b) else {
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
|
|
||||||
let a_systems = set_system_bitsets.get(a).unwrap();
|
let a_systems = set_system_sets.get(a_key).unwrap();
|
||||||
let b_systems = set_system_bitsets.get(b).unwrap();
|
let b_systems = set_system_sets.get(b_key).unwrap();
|
||||||
|
|
||||||
if !a_systems.is_disjoint(b_systems) {
|
if !a_systems.is_disjoint(b_systems) {
|
||||||
return Err(ScheduleBuildError::SetsHaveOrderButIntersect(
|
return Err(ScheduleBuildError::SetsHaveOrderButIntersect(
|
||||||
@ -1927,19 +1960,25 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
fn check_system_type_set_ambiguity(
|
fn check_system_type_set_ambiguity(
|
||||||
&self,
|
&self,
|
||||||
set_systems: &HashMap<NodeId, Vec<NodeId>>,
|
set_systems: &HashMap<SystemSetKey, Vec<SystemKey>>,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
for (&id, systems) in set_systems {
|
for (&key, systems) in set_systems {
|
||||||
let set = &self.system_sets[id.index()];
|
let set = &self.system_sets[key];
|
||||||
if set.is_system_type() {
|
if set.is_system_type() {
|
||||||
let instances = systems.len();
|
let instances = systems.len();
|
||||||
let ambiguous_with = self.ambiguous_with.edges(id);
|
let ambiguous_with = self.ambiguous_with.edges(NodeId::Set(key));
|
||||||
let before = self.dependency.graph.edges_directed(id, Incoming);
|
let before = self
|
||||||
let after = self.dependency.graph.edges_directed(id, Outgoing);
|
.dependency
|
||||||
|
.graph
|
||||||
|
.edges_directed(NodeId::Set(key), Incoming);
|
||||||
|
let after = self
|
||||||
|
.dependency
|
||||||
|
.graph
|
||||||
|
.edges_directed(NodeId::Set(key), Outgoing);
|
||||||
let relations = before.count() + after.count() + ambiguous_with.count();
|
let relations = before.count() + after.count() + ambiguous_with.count();
|
||||||
if instances > 1 && relations > 0 {
|
if instances > 1 && relations > 0 {
|
||||||
return Err(ScheduleBuildError::SystemTypeSetAmbiguity(
|
return Err(ScheduleBuildError::SystemTypeSetAmbiguity(
|
||||||
self.get_node_name(&id),
|
self.get_node_name(&NodeId::Set(key)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1950,7 +1989,7 @@ impl ScheduleGraph {
|
|||||||
/// if [`ScheduleBuildSettings::ambiguity_detection`] is [`LogLevel::Ignore`], this check is skipped
|
/// if [`ScheduleBuildSettings::ambiguity_detection`] is [`LogLevel::Ignore`], this check is skipped
|
||||||
fn optionally_check_conflicts(
|
fn optionally_check_conflicts(
|
||||||
&self,
|
&self,
|
||||||
conflicts: &[(NodeId, NodeId, Vec<ComponentId>)],
|
conflicts: &[(SystemKey, SystemKey, Vec<ComponentId>)],
|
||||||
components: &Components,
|
components: &Components,
|
||||||
schedule_label: InternedScheduleLabel,
|
schedule_label: InternedScheduleLabel,
|
||||||
) -> Result<(), ScheduleBuildError> {
|
) -> Result<(), ScheduleBuildError> {
|
||||||
@ -1971,7 +2010,7 @@ impl ScheduleGraph {
|
|||||||
|
|
||||||
fn get_conflicts_error_message(
|
fn get_conflicts_error_message(
|
||||||
&self,
|
&self,
|
||||||
ambiguities: &[(NodeId, NodeId, Vec<ComponentId>)],
|
ambiguities: &[(SystemKey, SystemKey, Vec<ComponentId>)],
|
||||||
components: &Components,
|
components: &Components,
|
||||||
) -> String {
|
) -> String {
|
||||||
let n_ambiguities = ambiguities.len();
|
let n_ambiguities = ambiguities.len();
|
||||||
@ -1999,17 +2038,14 @@ impl ScheduleGraph {
|
|||||||
/// convert conflicts to human readable format
|
/// convert conflicts to human readable format
|
||||||
pub fn conflicts_to_string<'a>(
|
pub fn conflicts_to_string<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ambiguities: &'a [(NodeId, NodeId, Vec<ComponentId>)],
|
ambiguities: &'a [(SystemKey, SystemKey, Vec<ComponentId>)],
|
||||||
components: &'a Components,
|
components: &'a Components,
|
||||||
) -> impl Iterator<Item = (String, String, Vec<DebugName>)> + 'a {
|
) -> impl Iterator<Item = (String, String, Vec<DebugName>)> + 'a {
|
||||||
ambiguities
|
ambiguities
|
||||||
.iter()
|
.iter()
|
||||||
.map(move |(system_a, system_b, conflicts)| {
|
.map(move |(system_a, system_b, conflicts)| {
|
||||||
let name_a = self.get_node_name(system_a);
|
let name_a = self.get_node_name(&NodeId::System(*system_a));
|
||||||
let name_b = self.get_node_name(system_b);
|
let name_b = self.get_node_name(&NodeId::System(*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
|
let conflict_names: Vec<_> = conflicts
|
||||||
.iter()
|
.iter()
|
||||||
@ -2020,22 +2056,25 @@ impl ScheduleGraph {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(NodeId) -> bool) {
|
fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(SystemSetKey) -> bool) {
|
||||||
for (set_id, _) in self.hierarchy.graph.edges_directed(id, Incoming) {
|
for (set_id, _) in self.hierarchy.graph.edges_directed(id, Incoming) {
|
||||||
if f(set_id) {
|
let NodeId::Set(set_key) = set_id else {
|
||||||
self.traverse_sets_containing_node(set_id, f);
|
continue;
|
||||||
|
};
|
||||||
|
if f(set_key) {
|
||||||
|
self.traverse_sets_containing_node(NodeId::Set(set_key), f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 |set_id| {
|
self.traverse_sets_containing_node(*id, &mut |key| {
|
||||||
!self.system_sets[set_id.index()].is_system_type() && sets.insert(set_id)
|
!self.system_sets[key].is_system_type() && sets.insert(key)
|
||||||
});
|
});
|
||||||
let mut sets: Vec<_> = sets
|
let mut sets: Vec<_> = sets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|set_id| self.get_node_name(&set_id))
|
.map(|key| self.get_node_name(&NodeId::Set(key)))
|
||||||
.collect();
|
.collect();
|
||||||
sets.sort();
|
sets.sort();
|
||||||
sets
|
sets
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel},
|
schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel, SystemKey},
|
||||||
system::{IntoSystem, ResMut},
|
system::{IntoSystem, ResMut},
|
||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@ -173,7 +173,7 @@ impl Stepping {
|
|||||||
state
|
state
|
||||||
.node_ids
|
.node_ids
|
||||||
.get(self.cursor.system)
|
.get(self.cursor.system)
|
||||||
.map(|node_id| (*label, *node_id))
|
.map(|node_id| (*label, NodeId::System(*node_id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable stepping for the provided schedule
|
/// Enable stepping for the provided schedule
|
||||||
@ -606,7 +606,7 @@ struct ScheduleState {
|
|||||||
/// This is a cached copy of `SystemExecutable::system_ids`. We need it
|
/// This is a cached copy of `SystemExecutable::system_ids`. We need it
|
||||||
/// available here to be accessed by [`Stepping::cursor()`] so we can return
|
/// available here to be accessed by [`Stepping::cursor()`] so we can return
|
||||||
/// [`NodeId`]s to the caller.
|
/// [`NodeId`]s to the caller.
|
||||||
node_ids: Vec<NodeId>,
|
node_ids: Vec<SystemKey>,
|
||||||
|
|
||||||
/// changes to system behavior that should be applied the next time
|
/// changes to system behavior that should be applied the next time
|
||||||
/// [`ScheduleState::skipped_systems()`] is called
|
/// [`ScheduleState::skipped_systems()`] is called
|
||||||
@ -662,15 +662,15 @@ impl ScheduleState {
|
|||||||
// updates for the system TypeId.
|
// updates for the system TypeId.
|
||||||
// PERF: If we add a way to efficiently query schedule systems by their TypeId, we could remove the full
|
// PERF: If we add a way to efficiently query schedule systems by their TypeId, we could remove the full
|
||||||
// system scan here
|
// system scan here
|
||||||
for (node_id, system) in schedule.systems().unwrap() {
|
for (key, system) in schedule.systems().unwrap() {
|
||||||
let behavior = self.behavior_updates.get(&system.type_id());
|
let behavior = self.behavior_updates.get(&system.type_id());
|
||||||
match behavior {
|
match behavior {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
self.behaviors.remove(&node_id);
|
self.behaviors.remove(&NodeId::System(key));
|
||||||
}
|
}
|
||||||
Some(Some(behavior)) => {
|
Some(Some(behavior)) => {
|
||||||
self.behaviors.insert(node_id, *behavior);
|
self.behaviors.insert(NodeId::System(key), *behavior);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -703,8 +703,8 @@ impl ScheduleState {
|
|||||||
|
|
||||||
// if we don't have a first system set, set it now
|
// if we don't have a first system set, set it now
|
||||||
if self.first.is_none() {
|
if self.first.is_none() {
|
||||||
for (i, (node_id, _)) in schedule.systems().unwrap().enumerate() {
|
for (i, (key, _)) in schedule.systems().unwrap().enumerate() {
|
||||||
match self.behaviors.get(&node_id) {
|
match self.behaviors.get(&NodeId::System(key)) {
|
||||||
Some(SystemBehavior::AlwaysRun | SystemBehavior::NeverRun) => continue,
|
Some(SystemBehavior::AlwaysRun | SystemBehavior::NeverRun) => continue,
|
||||||
Some(_) | None => {
|
Some(_) | None => {
|
||||||
self.first = Some(i);
|
self.first = Some(i);
|
||||||
@ -717,10 +717,10 @@ impl ScheduleState {
|
|||||||
let mut skip = FixedBitSet::with_capacity(schedule.systems_len());
|
let mut skip = FixedBitSet::with_capacity(schedule.systems_len());
|
||||||
let mut pos = start;
|
let mut pos = start;
|
||||||
|
|
||||||
for (i, (node_id, _system)) in schedule.systems().unwrap().enumerate() {
|
for (i, (key, _system)) in schedule.systems().unwrap().enumerate() {
|
||||||
let behavior = self
|
let behavior = self
|
||||||
.behaviors
|
.behaviors
|
||||||
.get(&node_id)
|
.get(&NodeId::System(key))
|
||||||
.unwrap_or(&SystemBehavior::Continue);
|
.unwrap_or(&SystemBehavior::Continue);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -825,6 +825,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{prelude::*, schedule::ScheduleLabel};
|
use crate::{prelude::*, schedule::ScheduleLabel};
|
||||||
use alloc::{format, vec};
|
use alloc::{format, vec};
|
||||||
|
use slotmap::SlotMap;
|
||||||
use std::println;
|
use std::println;
|
||||||
|
|
||||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
@ -1467,10 +1468,11 @@ mod tests {
|
|||||||
// helper to build a cursor tuple for the supplied schedule
|
// helper to build a cursor tuple for the supplied schedule
|
||||||
fn cursor(schedule: &Schedule, index: usize) -> (InternedScheduleLabel, NodeId) {
|
fn cursor(schedule: &Schedule, index: usize) -> (InternedScheduleLabel, NodeId) {
|
||||||
let node_id = schedule.executable().system_ids[index];
|
let node_id = schedule.executable().system_ids[index];
|
||||||
(schedule.label(), node_id)
|
(schedule.label(), NodeId::System(node_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
let mut slotmap = SlotMap::<SystemKey, ()>::with_key();
|
||||||
|
|
||||||
// create two schedules with a number of systems in them
|
// create two schedules with a number of systems in them
|
||||||
let mut schedule_a = Schedule::new(TestScheduleA);
|
let mut schedule_a = Schedule::new(TestScheduleA);
|
||||||
@ -1517,6 +1519,11 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let sys0 = slotmap.insert(());
|
||||||
|
let sys1 = slotmap.insert(());
|
||||||
|
let _sys2 = slotmap.insert(());
|
||||||
|
let sys3 = slotmap.insert(());
|
||||||
|
|
||||||
// reset our cursor (disable/enable), and update stepping to test if the
|
// reset our cursor (disable/enable), and update stepping to test if the
|
||||||
// cursor properly skips over AlwaysRun & NeverRun systems. Also set
|
// cursor properly skips over AlwaysRun & NeverRun systems. Also set
|
||||||
// a Break system to ensure that shows properly in the cursor
|
// a Break system to ensure that shows properly in the cursor
|
||||||
@ -1524,9 +1531,9 @@ mod tests {
|
|||||||
// disable/enable to reset cursor
|
// disable/enable to reset cursor
|
||||||
.disable()
|
.disable()
|
||||||
.enable()
|
.enable()
|
||||||
.set_breakpoint_node(TestScheduleA, NodeId::System(1))
|
.set_breakpoint_node(TestScheduleA, NodeId::System(sys1))
|
||||||
.always_run_node(TestScheduleA, NodeId::System(3))
|
.always_run_node(TestScheduleA, NodeId::System(sys3))
|
||||||
.never_run_node(TestScheduleB, NodeId::System(0));
|
.never_run_node(TestScheduleB, NodeId::System(sys0));
|
||||||
|
|
||||||
let mut cursors = Vec::new();
|
let mut cursors = Vec::new();
|
||||||
for _ in 0..9 {
|
for _ in 0..9 {
|
||||||
|
@ -132,18 +132,20 @@ fn build_ui(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (node_id, system) in systems {
|
for (key, system) in systems {
|
||||||
// skip bevy default systems; we don't want to step those
|
// skip bevy default systems; we don't want to step those
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
if system.name().as_string().starts_with("bevy") {
|
if system.name().as_string().starts_with("bevy") {
|
||||||
always_run.push((*label, node_id));
|
always_run.push((*label, NodeId::System(key)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an entry to our systems list so we can find where to draw
|
// Add an entry to our systems list so we can find where to draw
|
||||||
// the cursor when the stepping cursor is at this system
|
// the cursor when the stepping cursor is at this system
|
||||||
// we add plus 1 to account for the empty root span
|
// we add plus 1 to account for the empty root span
|
||||||
state.systems.push((*label, node_id, text_spans.len() + 1));
|
state
|
||||||
|
.systems
|
||||||
|
.push((*label, NodeId::System(key), text_spans.len() + 1));
|
||||||
|
|
||||||
// Add a text section for displaying the cursor for this system
|
// Add a text section for displaying the cursor for this system
|
||||||
text_spans.push((
|
text_spans.push((
|
||||||
|
34
release-content/migration-guides/schedule_slotmaps.md
Normal file
34
release-content/migration-guides/schedule_slotmaps.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
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