bevy/crates/bevy_ecs/src/schedule/node.rs
2025-07-17 20:37:30 -05:00

800 lines
24 KiB
Rust

use alloc::{boxed::Box, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::{
any::TypeId,
fmt::{self, Debug},
ops::{Index, IndexMut, Range},
};
use bevy_platform::collections::HashMap;
use slotmap::{new_key_type, Key, KeyData, SecondaryMap, SlotMap};
use crate::{
component::{CheckChangeTicks, ComponentId, Tick},
prelude::{SystemIn, SystemSet},
query::FilteredAccessSet,
schedule::{
graph::{Direction, GraphNodeId},
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;
}
impl GraphNodeId for SystemKey {
type Adjacent = (SystemKey, Direction);
type Edge = (SystemKey, SystemKey);
}
impl TryFrom<NodeId> for SystemKey {
type Error = SystemSetKey;
fn try_from(value: NodeId) -> Result<Self, Self::Error> {
match value {
NodeId::System(key) => Ok(key),
NodeId::Set(key) => Err(key),
}
}
}
impl TryFrom<NodeId> for SystemSetKey {
type Error = SystemKey;
fn try_from(value: NodeId) -> Result<Self, Self::Error> {
match value {
NodeId::System(key) => Err(key),
NodeId::Set(key) => Ok(key),
}
}
}
/// Unique identifier for a system or system set stored in a [`ScheduleGraph`].
///
/// [`ScheduleGraph`]: crate::schedule::ScheduleGraph
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NodeId {
/// Identifier for a system.
System(SystemKey),
/// Identifier for a system set.
Set(SystemSetKey),
}
impl NodeId {
/// Returns `true` if the identified node is a system.
pub const fn is_system(&self) -> bool {
matches!(self, NodeId::System(_))
}
/// Returns `true` if the identified node is a system set.
pub const fn is_set(&self) -> bool {
matches!(self, NodeId::Set(_))
}
/// Returns the system key if the node is a system, otherwise `None`.
pub const fn as_system(&self) -> Option<SystemKey> {
match self {
NodeId::System(system) => Some(*system),
NodeId::Set(_) => None,
}
}
/// Returns the system set key if the node is a system set, otherwise `None`.
pub const fn as_set(&self) -> Option<SystemSetKey> {
match self {
NodeId::System(_) => None,
NodeId::Set(set) => Some(*set),
}
}
}
impl GraphNodeId for NodeId {
type Adjacent = CompactNodeIdAndDirection;
type Edge = CompactNodeIdPair;
}
impl From<SystemKey> for NodeId {
fn from(system: SystemKey) -> Self {
NodeId::System(system)
}
}
impl From<SystemSetKey> for NodeId {
fn from(set: SystemSetKey) -> Self {
NodeId::Set(set)
}
}
/// Compact storage of a [`NodeId`] and a [`Direction`].
#[derive(Clone, Copy)]
pub struct CompactNodeIdAndDirection {
key: KeyData,
is_system: bool,
direction: Direction,
}
impl Debug for CompactNodeIdAndDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tuple: (_, _) = (*self).into();
tuple.fmt(f)
}
}
impl From<(NodeId, Direction)> for CompactNodeIdAndDirection {
fn from((id, direction): (NodeId, Direction)) -> Self {
let key = match id {
NodeId::System(key) => key.data(),
NodeId::Set(key) => key.data(),
};
let is_system = id.is_system();
Self {
key,
is_system,
direction,
}
}
}
impl From<CompactNodeIdAndDirection> for (NodeId, Direction) {
fn from(value: CompactNodeIdAndDirection) -> Self {
let node = match value.is_system {
true => NodeId::System(value.key.into()),
false => NodeId::Set(value.key.into()),
};
(node, value.direction)
}
}
/// Compact storage of a [`NodeId`] pair.
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct CompactNodeIdPair {
key_a: KeyData,
key_b: KeyData,
is_system_a: bool,
is_system_b: bool,
}
impl Debug for CompactNodeIdPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tuple: (_, _) = (*self).into();
tuple.fmt(f)
}
}
impl From<(NodeId, NodeId)> for CompactNodeIdPair {
fn from((a, b): (NodeId, NodeId)) -> Self {
let key_a = match a {
NodeId::System(index) => index.data(),
NodeId::Set(index) => index.data(),
};
let is_system_a = a.is_system();
let key_b = match b {
NodeId::System(index) => index.data(),
NodeId::Set(index) => index.data(),
};
let is_system_b = b.is_system();
Self {
key_a,
key_b,
is_system_a,
is_system_b,
}
}
}
impl From<CompactNodeIdPair> for (NodeId, NodeId) {
fn from(value: CompactNodeIdPair) -> Self {
let a = match value.is_system_a {
true => NodeId::System(value.key_a.into()),
false => NodeId::Set(value.key_a.into()),
};
let b = match value.is_system_b {
true => NodeId::System(value.key_b.into()),
false => NodeId::Set(value.key_b.into()),
};
(a, b)
}
}
/// 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());
}
}