Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn apply_deferred into a ZST System #16642

Merged
merged 8 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1839,7 +1839,7 @@ wasm = false

[package.metadata.example.apply_deferred]
name = "Apply System Buffers"
description = "Show how to use `apply_deferred` system"
description = "Show how to use `ApplyDeferred` system"
category = "ECS (Entity Component System)"
wasm = false

Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub use bevy_ptr as ptr;
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[expect(deprecated)]
#[doc(hidden)]
pub use crate::{
bundle::Bundle,
Expand All @@ -53,8 +54,8 @@ pub mod prelude {
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
removal_detection::RemovedComponents,
schedule::{
apply_deferred, common_conditions::*, Condition, IntoSystemConfigs, IntoSystemSet,
IntoSystemSetConfigs, Schedule, Schedules, SystemSet,
apply_deferred, common_conditions::*, ApplyDeferred, Condition, IntoSystemConfigs,
IntoSystemSet, IntoSystemSetConfigs, Schedule, Schedules, SystemSet,
},
system::{
Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef, IntoSystem, Local,
Expand Down
14 changes: 7 additions & 7 deletions crates/bevy_ecs/src/schedule/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ where
/// Runs before all systems in `set`. If `self` has any systems that produce [`Commands`](crate::system::Commands)
/// or other [`Deferred`](crate::system::Deferred) operations, all systems in `set` will see their effect.
///
/// If automatically inserting [`apply_deferred`](crate::schedule::apply_deferred) like
/// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
/// this isn't desired, use [`before_ignore_deferred`](Self::before_ignore_deferred) instead.
///
/// Calling [`.chain`](Self::chain) is often more convenient and ensures that all systems are added to the schedule.
Expand All @@ -305,7 +305,7 @@ where
/// Run after all systems in `set`. If `set` has any systems that produce [`Commands`](crate::system::Commands)
/// or other [`Deferred`](crate::system::Deferred) operations, all systems in `self` will see their effect.
///
/// If automatically inserting [`apply_deferred`](crate::schedule::apply_deferred) like
/// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
/// this isn't desired, use [`after_ignore_deferred`](Self::after_ignore_deferred) instead.
///
/// Calling [`.chain`](Self::chain) is often more convenient and ensures that all systems are added to the schedule.
Expand Down Expand Up @@ -429,7 +429,7 @@ where
///
/// Ordering constraints will be applied between the successive elements.
///
/// If the preceding node on a edge has deferred parameters, a [`apply_deferred`](crate::schedule::apply_deferred)
/// If the preceding node on a edge has deferred parameters, a [`ApplyDeferred`](crate::schedule::ApplyDeferred)
/// will be inserted on the edge. If this behavior is not desired consider using
/// [`chain_ignore_deferred`](Self::chain_ignore_deferred) instead.
fn chain(self) -> SystemConfigs {
Expand All @@ -440,7 +440,7 @@ where
///
/// Ordering constraints will be applied between the successive elements.
///
/// Unlike [`chain`](Self::chain) this will **not** add [`apply_deferred`](crate::schedule::apply_deferred) on the edges.
/// Unlike [`chain`](Self::chain) this will **not** add [`ApplyDeferred`](crate::schedule::ApplyDeferred) on the edges.
fn chain_ignore_deferred(self) -> SystemConfigs {
self.into_configs().chain_ignore_deferred()
}
Expand Down Expand Up @@ -610,7 +610,7 @@ where
/// Runs before all systems in `set`. If `self` has any systems that produce [`Commands`](crate::system::Commands)
/// or other [`Deferred`](crate::system::Deferred) operations, all systems in `set` will see their effect.
///
/// If automatically inserting [`apply_deferred`](crate::schedule::apply_deferred) like
/// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
/// this isn't desired, use [`before_ignore_deferred`](Self::before_ignore_deferred) instead.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs {
self.into_configs().before(set)
Expand All @@ -619,7 +619,7 @@ where
/// Runs before all systems in `set`. If `set` has any systems that produce [`Commands`](crate::system::Commands)
/// or other [`Deferred`](crate::system::Deferred) operations, all systems in `self` will see their effect.
///
/// If automatically inserting [`apply_deferred`](crate::schedule::apply_deferred) like
/// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
/// this isn't desired, use [`after_ignore_deferred`](Self::after_ignore_deferred) instead.
fn after<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs {
self.into_configs().after(set)
Expand Down Expand Up @@ -672,7 +672,7 @@ where
///
/// Ordering constraints will be applied between the successive elements.
///
/// Unlike [`chain`](Self::chain) this will **not** add [`apply_deferred`](crate::schedule::apply_deferred) on the edges.
/// Unlike [`chain`](Self::chain) this will **not** add [`ApplyDeferred`](crate::schedule::ApplyDeferred) on the edges.
fn chain_ignore_deferred(self) -> SystemSetConfigs {
self.into_configs().chain_ignore_deferred()
}
Expand Down
143 changes: 125 additions & 18 deletions crates/bevy_ecs/src/schedule/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ mod multi_threaded;
mod simple;
mod single_threaded;

use alloc::borrow::Cow;
use core::any::TypeId;

pub use self::{
multi_threaded::{MainThreadExecutor, MultiThreadedExecutor},
simple::SimpleExecutor,
Expand All @@ -11,9 +14,13 @@ pub use self::{
use fixedbitset::FixedBitSet;

use crate::{
schedule::{BoxedCondition, NodeId},
system::BoxedSystem,
world::World,
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
prelude::{IntoSystemSet, SystemSet},
query::Access,
schedule::{BoxedCondition, InternedSystemSet, NodeId, SystemTypeSet},
system::{BoxedSystem, System, SystemIn},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};

/// Types that can run a [`SystemSchedule`] on a [`World`].
Expand Down Expand Up @@ -100,32 +107,132 @@ impl SystemSchedule {
}
}

/// Instructs the executor to call [`System::apply_deferred`](crate::system::System::apply_deferred)
/// on the systems that have run but not applied their [`Deferred`](crate::system::Deferred) system parameters
/// (like [`Commands`](crate::prelude::Commands)) or other system buffers.
/// See [`ApplyDeferred`].
#[deprecated = "Use `ApplyDeferred` instead. This was previously a function but is now a marker struct System."]
#[expect(non_upper_case_globals)]
pub const apply_deferred: ApplyDeferred = ApplyDeferred;

/// A special [`System`] that instructs the executor to call
/// [`System::apply_deferred`] on the systems that have run but not applied
/// their [`Deferred`] system parameters (like [`Commands`]) or other system buffers.
///
/// ## Scheduling
///
/// `apply_deferred` systems are scheduled *by default*
/// `ApplyDeferred` systems are scheduled *by default*
/// - later in the same schedule run (for example, if a system with `Commands` param
/// is scheduled in `Update`, all the changes will be visible in `PostUpdate`)
/// - between systems with dependencies if the dependency
/// [has deferred buffers](crate::system::System::has_deferred)
/// (if system `bar` directly or indirectly depends on `foo`, and `foo` uses `Commands` param,
/// changes to the world in `foo` will be visible in `bar`)
/// - between systems with dependencies if the dependency [has deferred buffers]
/// (if system `bar` directly or indirectly depends on `foo`, and `foo` uses
/// `Commands` param, changes to the world in `foo` will be visible in `bar`)
///
/// ## Notes
/// - This function (currently) does nothing if it's called manually or wrapped inside a [`PipeSystem`](crate::system::PipeSystem).
/// - Modifying a [`Schedule`](super::Schedule) may change the order buffers are applied.
/// - This system (currently) does nothing if it's called manually or wrapped
/// inside a [`PipeSystem`].
/// - Modifying a [`Schedule`] may change the order buffers are applied.
///
/// [`System::apply_deferred`]: crate::system::System::apply_deferred
/// [`Deferred`]: crate::system::Deferred
/// [`Commands`]: crate::prelude::Commands
/// [has deferred buffers]: crate::system::System::has_deferred
/// [`PipeSystem`]: crate::system::PipeSystem
/// [`Schedule`]: super::Schedule
#[doc(alias = "apply_system_buffers")]
#[allow(unused_variables)]
pub fn apply_deferred(world: &mut World) {}
pub struct ApplyDeferred;

/// Returns `true` if the [`System`](crate::system::System) is an instance of [`apply_deferred`].
/// Returns `true` if the [`System`] is an instance of [`ApplyDeferred`].
pub(super) fn is_apply_deferred(system: &BoxedSystem) -> bool {
use crate::system::IntoSystem;
// deref to use `System::type_id` instead of `Any::type_id`
system.as_ref().type_id() == apply_deferred.system_type_id()
system.as_ref().type_id() == TypeId::of::<ApplyDeferred>()
}

impl System for ApplyDeferred {
type In = ();
type Out = ();

fn name(&self) -> Cow<'static, str> {
Cow::Borrowed("bevy_ecs::apply_deferred")
}

fn component_access(&self) -> &Access<ComponentId> {
// This system accesses no components.
const { &Access::new() }
}

fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
// This system accesses no archetype components.
const { &Access::new() }
}

fn is_send(&self) -> bool {
// Although this system itself does nothing on its own, the system
// executor uses it to apply deferred commands. Commands must be allowed
// to access non-send resources, so this system must be non-send for
// scheduling purposes.
false
}

fn is_exclusive(&self) -> bool {
// This system is labeled exclusive because it is used by the system
// executor to find places where deferred commands should be applied,
// and commands can only be applied with exclusive access to the world.
true
}

fn has_deferred(&self) -> bool {
// This system itself doesn't have any commands to apply, but when it
// is pulled from the schedule to be ran, the executor will apply
// deferred commands from other systems.
false
}

unsafe fn run_unsafe(
&mut self,
_input: SystemIn<'_, Self>,
_world: UnsafeWorldCell,
) -> Self::Out {
// This system does nothing on its own. The executor will apply deferred
// commands from other systems instead of running this system.
}

fn run(&mut self, _input: SystemIn<'_, Self>, _world: &mut World) -> Self::Out {
// This system does nothing on its own. The executor will apply deferred
// commands from other systems instead of running this system.
}

fn apply_deferred(&mut self, _world: &mut World) {}

fn queue_deferred(&mut self, _world: DeferredWorld) {}

unsafe fn validate_param_unsafe(&mut self, _world: UnsafeWorldCell) -> bool {
// This system is always valid to run because it doesn't do anything,
// and only used as a marker for the executor.
true
}

fn initialize(&mut self, _world: &mut World) {}

fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}

fn check_change_tick(&mut self, _change_tick: Tick) {}

fn default_system_sets(&self) -> Vec<InternedSystemSet> {
vec![SystemTypeSet::<Self>::new().intern()]
}

fn get_last_run(&self) -> Tick {
// This system is never run, so it has no last run tick.
Tick::MAX
}

fn set_last_run(&mut self, _last_run: Tick) {}
}

impl IntoSystemSet<()> for ApplyDeferred {
type Set = SystemTypeSet<Self>;

fn into_system_set(self) -> Self::Set {
SystemTypeSet::<Self>::new()
}
}

/// These functions hide the bottom of the callstack from `RUST_BACKTRACE=1` (assuming the default panic handler is used).
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/executor/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W
#[cfg(test)]
#[test]
fn skip_automatic_sync_points() {
// Schedules automatically insert apply_deferred systems, but these should
// Schedules automatically insert ApplyDeferred systems, but these should
// not be executed as they only serve as markers and are not initialized
use crate::prelude::*;
let mut sched = Schedule::default();
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/schedule/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ pub(crate) enum DependencyKind {
Before,
/// A node that should be succeeded.
After,
/// A node that should be preceded and will **not** automatically insert an instance of `apply_deferred` on the edge.
/// A node that should be preceded and will **not** automatically insert an instance of `ApplyDeferred` on the edge.
BeforeNoSync,
/// A node that should be succeeded and will **not** automatically insert an instance of `apply_deferred` on the edge.
/// A node that should be succeeded and will **not** automatically insert an instance of `ApplyDeferred` on the edge.
AfterNoSync,
}

Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_ecs/src/schedule/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
#[derive(PartialEq)]
pub enum Chain {
/// Run nodes in order. If there are deferred parameters in preceding systems a
/// [`apply_deferred`] will be added on the edge.
/// [`ApplyDeferred`] will be added on the edge.
Yes,
/// Run nodes in order. This will not add [`apply_deferred`] between nodes.
/// Run nodes in order. This will not add [`ApplyDeferred`] between nodes.
YesIgnoreDeferred,
/// Nodes are allowed to run in any order.
No,
Expand Down Expand Up @@ -367,7 +367,7 @@ impl Schedule {

/// Set whether the schedule applies deferred system buffers on final time or not. This is a catch-all
/// in case a system uses commands but was not explicitly ordered before an instance of
/// [`apply_deferred`]. By default this
/// [`ApplyDeferred`]. By default this
/// setting is true, but may be disabled if needed.
pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
self.executor.set_apply_final_deferred(apply_final_deferred);
Expand Down Expand Up @@ -1191,13 +1191,13 @@ impl ScheduleGraph {
Ok(sync_point_graph)
}

/// add an [`apply_deferred`] system with no config
/// add an [`ApplyDeferred`] system with no config
fn add_auto_sync(&mut self) -> NodeId {
let id = NodeId::System(self.systems.len());

self.systems
.push(SystemNode::new(Box::new(IntoSystem::into_system(
apply_deferred,
ApplyDeferred,
))));
self.system_conditions.push(Vec::new());

Expand Down Expand Up @@ -1998,7 +1998,7 @@ pub struct ScheduleBuildSettings {
///
/// Defaults to [`LogLevel::Warn`].
pub hierarchy_detection: LogLevel,
/// Auto insert [`apply_deferred`] systems into the schedule,
/// Auto insert [`ApplyDeferred`] systems into the schedule,
/// when there are [`Deferred`](crate::prelude::Deferred)
/// in one system and there are ordering dependencies on that system. [`Commands`](crate::system::Commands) is one
/// such deferred buffer.
Expand Down
7 changes: 4 additions & 3 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use parallel_scope::*;
///
/// Since each command requires exclusive access to the `World`,
/// all queued commands are automatically applied in sequence
/// when the `apply_deferred` system runs (see [`apply_deferred`] documentation for more details).
/// when the `ApplyDeferred` system runs (see [`ApplyDeferred`] documentation for more details).
///
/// Each command can be used to modify the [`World`] in arbitrary ways:
/// * spawning or despawning entities
Expand All @@ -43,7 +43,8 @@ pub use parallel_scope::*;
///
/// # Usage
///
/// Add `mut commands: Commands` as a function argument to your system to get a copy of this struct that will be applied the next time a copy of [`apply_deferred`] runs.
/// Add `mut commands: Commands` as a function argument to your system to get a
/// copy of this struct that will be applied the next time a copy of [`ApplyDeferred`] runs.
/// Commands are almost always used as a [`SystemParam`](crate::system::SystemParam).
///
/// ```
Expand Down Expand Up @@ -75,7 +76,7 @@ pub use parallel_scope::*;
/// # }
/// ```
///
/// [`apply_deferred`]: crate::schedule::apply_deferred
/// [`ApplyDeferred`]: crate::schedule::ApplyDeferred
pub struct Commands<'w, 's> {
queue: InternalQueue<'s>,
entities: &'w Entities,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl SystemMeta {
}

/// Marks the system as having deferred buffers like [`Commands`](`super::Commands`)
/// This lets the scheduler insert [`apply_deferred`](`crate::prelude::apply_deferred`) systems automatically.
/// This lets the scheduler insert [`ApplyDeferred`](`crate::prelude::ApplyDeferred`) systems automatically.
#[inline]
pub fn set_has_deferred(&mut self) {
self.has_deferred = true;
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ mod tests {
query::{Added, Changed, Or, With, Without},
removal_detection::RemovedComponents,
schedule::{
apply_deferred, common_conditions::resource_exists, Condition, IntoSystemConfigs,
common_conditions::resource_exists, ApplyDeferred, Condition, IntoSystemConfigs,
Schedule,
},
system::{
Expand Down Expand Up @@ -493,7 +493,7 @@ mod tests {

let mut schedule = Schedule::default();

schedule.add_systems((incr_e_on_flip, apply_deferred, World::clear_trackers).chain());
schedule.add_systems((incr_e_on_flip, ApplyDeferred, World::clear_trackers).chain());

schedule.run(&mut world);
assert_eq!(world.resource::<Added>().0, 1);
Expand Down
Loading
Loading