Exclusive Systems Now Implement System. Flexible Exclusive System Params (#6083)

# Objective

The [Stageless RFC](https://github.com/bevyengine/rfcs/pull/45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move.

This is an alternative to #4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

## Solution

This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). 

This means you can remove all cases of `exclusive_system()`:

```rust
// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);
```

I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems:

```rust
fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}
```

Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system.

I added some targeted SystemParam `static` constraints, which removed the need for this:
``` rust
fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}
```

## Related

- #2923
- #3001
- #3946

## Changelog

- `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait.
- `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems
- `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam`
- Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api.

## Migration Guide

Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems:

```rust
// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);
```

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

```rust
// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());
```

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:
```rust
// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}
```
This commit is contained in:
Carter Anderson 2022-09-26 23:57:07 +00:00
parent 92e78a4bc5
commit dc3f801239
37 changed files with 756 additions and 856 deletions

View File

@ -1,10 +1,4 @@
use bevy_ecs::{ use bevy_ecs::{prelude::*, schedule::ShouldRun};
component::Component,
prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion},
schedule::{RunCriteriaLabel, ShouldRun, Stage, SystemStage},
system::Query,
world::World,
};
use criterion::Criterion; use criterion::Criterion;
fn run_stage(stage: &mut SystemStage, world: &mut World) { fn run_stage(stage: &mut SystemStage, world: &mut World) {

View File

@ -12,7 +12,7 @@ use bevy_ecs::{
entity::Entity, entity::Entity,
prelude::Component, prelude::Component,
reflect::ReflectComponent, reflect::ReflectComponent,
schedule::ParallelSystemDescriptorCoercion, schedule::IntoSystemDescriptor,
system::{Query, Res}, system::{Query, Res},
}; };
use bevy_hierarchy::Children; use bevy_hierarchy::Children;

View File

@ -3,7 +3,7 @@ pub use bevy_derive::AppLabel;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
event::{Event, Events}, event::{Event, Events},
prelude::{FromWorld, IntoExclusiveSystem}, prelude::FromWorld,
schedule::{ schedule::{
IntoSystemDescriptor, Schedule, ShouldRun, Stage, StageLabel, State, StateData, SystemSet, IntoSystemDescriptor, Schedule, ShouldRun, Stage, StageLabel, State, StateData, SystemSet,
SystemStage, SystemStage,
@ -84,7 +84,7 @@ impl Default for App {
app.add_default_stages() app.add_default_stages()
.add_event::<AppExit>() .add_event::<AppExit>()
.add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system()); .add_system_to_stage(CoreStage::Last, World::clear_trackers);
#[cfg(feature = "bevy_ci_testing")] #[cfg(feature = "bevy_ci_testing")]
{ {

View File

@ -79,7 +79,7 @@ type IdCursor = isize;
/// } /// }
/// # /// #
/// # bevy_ecs::system::assert_is_system(setup); /// # bevy_ecs::system::assert_is_system(setup);
/// # bevy_ecs::system::IntoExclusiveSystem::exclusive_system(exclusive_system); /// # bevy_ecs::system::assert_is_system(exclusive_system);
/// ``` /// ```
/// ///
/// It can be used to refer to a specific entity to apply [`EntityCommands`], or to call [`Query::get`] (or similar methods) to access its components. /// It can be used to refer to a specific entity to apply [`EntityCommands`], or to call [`Query::get`] (or similar methods) to access its components.

View File

@ -34,14 +34,13 @@ pub mod prelude {
event::{EventReader, EventWriter, Events}, event::{EventReader, EventWriter, Events},
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without}, query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
schedule::{ schedule::{
ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria, IntoSystemDescriptor, RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel,
RunCriteriaDescriptorCoercion, RunCriteriaLabel, Schedule, Stage, StageLabel, State, Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage,
SystemLabel, SystemSet, SystemStage,
}, },
system::{ system::{
adapter as system_adapter, Commands, In, IntoChainSystem, IntoExclusiveSystem, adapter as system_adapter, Commands, In, IntoChainSystem, IntoSystem, Local, NonSend,
IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query, NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut,
RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction, Resource, System, SystemParamFunction,
}, },
world::{FromWorld, Mut, World}, world::{FromWorld, Mut, World},
}; };

View File

@ -45,7 +45,6 @@ impl SystemOrderAmbiguity {
stage: &SystemStage, stage: &SystemStage,
world: &World, world: &World,
) -> Self { ) -> Self {
use crate::schedule::graph_utils::GraphNode;
use SystemStageSegment::*; use SystemStageSegment::*;
// TODO: blocked on https://github.com/bevyengine/bevy/pull/4166 // TODO: blocked on https://github.com/bevyengine/bevy/pull/4166
@ -220,7 +219,7 @@ impl SystemStage {
/// Returns vector containing all pairs of indices of systems with ambiguous execution order, /// Returns vector containing all pairs of indices of systems with ambiguous execution order,
/// along with specific components that have triggered the warning. /// along with specific components that have triggered the warning.
/// Systems must be topologically sorted beforehand. /// Systems must be topologically sorted beforehand.
fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec<ComponentId>)> { fn find_ambiguities(systems: &[SystemContainer]) -> Vec<(usize, usize, Vec<ComponentId>)> {
let mut all_dependencies = Vec::<FixedBitSet>::with_capacity(systems.len()); let mut all_dependencies = Vec::<FixedBitSet>::with_capacity(systems.len());
let mut all_dependants = Vec::<FixedBitSet>::with_capacity(systems.len()); let mut all_dependants = Vec::<FixedBitSet>::with_capacity(systems.len());
for (index, container) in systems.iter().enumerate() { for (index, container) in systems.iter().enumerate() {
@ -263,15 +262,17 @@ fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec<
// .take(index_a) // .take(index_a)
{ {
if !processed.contains(index_b) { if !processed.contains(index_b) {
let a_access = systems[index_a].component_access(); let system_a = &systems[index_a];
let b_access = systems[index_b].component_access(); let system_b = &systems[index_b];
if let (Some(a), Some(b)) = (a_access, b_access) { if system_a.is_exclusive() || system_b.is_exclusive() {
let conflicts = a.get_conflicts(b); ambiguities.push((index_a, index_b, Vec::new()));
} else {
let a_access = systems[index_a].component_access();
let b_access = systems[index_b].component_access();
let conflicts = a_access.get_conflicts(b_access);
if !conflicts.is_empty() { if !conflicts.is_empty() {
ambiguities.push((index_a, index_b, conflicts)); ambiguities.push((index_a, index_b, conflicts));
} }
} else {
ambiguities.push((index_a, index_b, Vec::new()));
} }
} }
} }
@ -467,12 +468,12 @@ mod tests {
let mut test_stage = SystemStage::parallel(); let mut test_stage = SystemStage::parallel();
test_stage test_stage
// All 3 of these conflict with each other // All 3 of these conflict with each other
.add_system(write_world_system.exclusive_system()) .add_system(write_world_system)
.add_system(write_world_system.exclusive_system().at_end()) .add_system(write_world_system.at_end())
.add_system(res_system.exclusive_system()) .add_system(res_system.at_start())
// These do not, as they're in different segments of the stage // These do not, as they're in different segments of the stage
.add_system(write_world_system.exclusive_system().at_start()) .add_system(write_world_system.at_start())
.add_system(write_world_system.exclusive_system().before_commands()); .add_system(write_world_system.before_commands());
test_stage.run(&mut world); test_stage.run(&mut world);

View File

@ -1,11 +1,11 @@
use crate::{schedule::ParallelSystemContainer, world::World}; use crate::{schedule::SystemContainer, world::World};
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
pub trait ParallelSystemExecutor: Downcast + Send + Sync { pub trait ParallelSystemExecutor: Downcast + Send + Sync {
/// Called by `SystemStage` whenever `systems` have been changed. /// Called by `SystemStage` whenever `systems` have been changed.
fn rebuild_cached_data(&mut self, systems: &[ParallelSystemContainer]); fn rebuild_cached_data(&mut self, systems: &[SystemContainer]);
fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World); fn run_systems(&mut self, systems: &mut [SystemContainer], world: &mut World);
} }
impl_downcast!(ParallelSystemExecutor); impl_downcast!(ParallelSystemExecutor);
@ -14,9 +14,9 @@ impl_downcast!(ParallelSystemExecutor);
pub struct SingleThreadedExecutor; pub struct SingleThreadedExecutor;
impl ParallelSystemExecutor for SingleThreadedExecutor { impl ParallelSystemExecutor for SingleThreadedExecutor {
fn rebuild_cached_data(&mut self, _: &[ParallelSystemContainer]) {} fn rebuild_cached_data(&mut self, _: &[SystemContainer]) {}
fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World) { fn run_systems(&mut self, systems: &mut [SystemContainer], world: &mut World) {
for system in systems { for system in systems {
if system.should_run() { if system.should_run() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
archetype::ArchetypeComponentId, archetype::ArchetypeComponentId,
query::Access, query::Access,
schedule::{ParallelSystemContainer, ParallelSystemExecutor}, schedule::{ParallelSystemExecutor, SystemContainer},
world::World, world::World,
}; };
use async_channel::{Receiver, Sender}; use async_channel::{Receiver, Sender};
@ -77,7 +77,7 @@ impl Default for ParallelExecutor {
} }
impl ParallelSystemExecutor for ParallelExecutor { impl ParallelSystemExecutor for ParallelExecutor {
fn rebuild_cached_data(&mut self, systems: &[ParallelSystemContainer]) { fn rebuild_cached_data(&mut self, systems: &[SystemContainer]) {
self.system_metadata.clear(); self.system_metadata.clear();
self.queued.grow(systems.len()); self.queued.grow(systems.len());
self.running.grow(systems.len()); self.running.grow(systems.len());
@ -104,7 +104,7 @@ impl ParallelSystemExecutor for ParallelExecutor {
} }
} }
fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World) { fn run_systems(&mut self, systems: &mut [SystemContainer], world: &mut World) {
#[cfg(test)] #[cfg(test)]
if self.events_sender.is_none() { if self.events_sender.is_none() {
let (sender, receiver) = async_channel::unbounded::<SchedulingEvent>(); let (sender, receiver) = async_channel::unbounded::<SchedulingEvent>();
@ -167,7 +167,7 @@ impl ParallelExecutor {
fn prepare_systems<'scope>( fn prepare_systems<'scope>(
&mut self, &mut self,
scope: &mut Scope<'scope, ()>, scope: &mut Scope<'scope, ()>,
systems: &'scope mut [ParallelSystemContainer], systems: &'scope mut [SystemContainer],
world: &'scope World, world: &'scope World,
) { ) {
// These are used as a part of a unit test. // These are used as a part of a unit test.

View File

@ -5,11 +5,10 @@ use crate::{
prelude::IntoSystem, prelude::IntoSystem,
schedule::{ schedule::{
graph_utils::{self, DependencyGraphError}, graph_utils::{self, DependencyGraphError},
BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveSystemContainer, GraphNode, BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveInsertionPoint, GraphNode,
InsertionPoint, ParallelExecutor, ParallelSystemContainer, ParallelSystemExecutor, ParallelExecutor, ParallelSystemExecutor, RunCriteriaContainer, RunCriteriaDescriptor,
RunCriteriaContainer, RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaDescriptorOrLabel, RunCriteriaInner, RunCriteriaLabelId, ShouldRun,
RunCriteriaInner, RunCriteriaLabelId, ShouldRun, SingleThreadedExecutor, SystemContainer, SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemLabelId, SystemSet,
SystemDescriptor, SystemLabelId, SystemSet,
}, },
world::{World, WorldId}, world::{World, WorldId},
}; };
@ -62,14 +61,14 @@ pub struct SystemStage {
/// Topologically sorted run criteria of systems. /// Topologically sorted run criteria of systems.
run_criteria: Vec<RunCriteriaContainer>, run_criteria: Vec<RunCriteriaContainer>,
/// Topologically sorted exclusive systems that want to be run at the start of the stage. /// Topologically sorted exclusive systems that want to be run at the start of the stage.
pub(super) exclusive_at_start: Vec<ExclusiveSystemContainer>, pub(super) exclusive_at_start: Vec<SystemContainer>,
/// Topologically sorted exclusive systems that want to be run after parallel systems but /// Topologically sorted exclusive systems that want to be run after parallel systems but
/// before the application of their command buffers. /// before the application of their command buffers.
pub(super) exclusive_before_commands: Vec<ExclusiveSystemContainer>, pub(super) exclusive_before_commands: Vec<SystemContainer>,
/// Topologically sorted exclusive systems that want to be run at the end of the stage. /// Topologically sorted exclusive systems that want to be run at the end of the stage.
pub(super) exclusive_at_end: Vec<ExclusiveSystemContainer>, pub(super) exclusive_at_end: Vec<SystemContainer>,
/// Topologically sorted parallel systems. /// Topologically sorted parallel systems.
pub(super) parallel: Vec<ParallelSystemContainer>, pub(super) parallel: Vec<SystemContainer>,
/// Determines if the stage was modified and needs to rebuild its graphs and orders. /// Determines if the stage was modified and needs to rebuild its graphs and orders.
pub(super) systems_modified: bool, pub(super) systems_modified: bool,
/// Determines if the stage's executor was changed. /// Determines if the stage's executor was changed.
@ -156,63 +155,63 @@ impl SystemStage {
self self
} }
fn add_system_inner(&mut self, system: SystemDescriptor, default_run_criteria: Option<usize>) { fn add_system_inner(
&mut self,
mut descriptor: SystemDescriptor,
default_run_criteria: Option<usize>,
) {
self.systems_modified = true; self.systems_modified = true;
match system { if let Some(insertion_point) = descriptor.exclusive_insertion_point {
SystemDescriptor::Exclusive(mut descriptor) => { let criteria = descriptor.run_criteria.take();
let insertion_point = descriptor.insertion_point; let mut container = SystemContainer::from_descriptor(descriptor);
let criteria = descriptor.run_criteria.take(); match criteria {
let mut container = ExclusiveSystemContainer::from_descriptor(descriptor); Some(RunCriteriaDescriptorOrLabel::Label(label)) => {
match criteria { container.run_criteria_label = Some(label);
Some(RunCriteriaDescriptorOrLabel::Label(label)) => {
container.run_criteria_label = Some(label);
}
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor));
}
None => {
container.run_criteria_index = default_run_criteria;
}
} }
match insertion_point { Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
InsertionPoint::AtStart => { container.run_criteria_label = criteria_descriptor.label;
let index = self.exclusive_at_start.len(); container.run_criteria_index =
self.uninitialized_at_start.push(index); Some(self.add_run_criteria_internal(criteria_descriptor));
self.exclusive_at_start.push(container); }
} None => {
InsertionPoint::BeforeCommands => { container.run_criteria_index = default_run_criteria;
let index = self.exclusive_before_commands.len();
self.uninitialized_before_commands.push(index);
self.exclusive_before_commands.push(container);
}
InsertionPoint::AtEnd => {
let index = self.exclusive_at_end.len();
self.uninitialized_at_end.push(index);
self.exclusive_at_end.push(container);
}
} }
} }
SystemDescriptor::Parallel(mut descriptor) => { match insertion_point {
let criteria = descriptor.run_criteria.take(); ExclusiveInsertionPoint::AtStart => {
let mut container = ParallelSystemContainer::from_descriptor(descriptor); let index = self.exclusive_at_start.len();
match criteria { self.uninitialized_at_start.push(index);
Some(RunCriteriaDescriptorOrLabel::Label(label)) => { self.exclusive_at_start.push(container);
container.run_criteria_label = Some(label); }
} ExclusiveInsertionPoint::BeforeCommands => {
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => { let index = self.exclusive_before_commands.len();
container.run_criteria_label = criteria_descriptor.label; self.uninitialized_before_commands.push(index);
container.run_criteria_index = self.exclusive_before_commands.push(container);
Some(self.add_run_criteria_internal(criteria_descriptor)); }
} ExclusiveInsertionPoint::AtEnd => {
None => { let index = self.exclusive_at_end.len();
container.run_criteria_index = default_run_criteria; self.uninitialized_at_end.push(index);
} self.exclusive_at_end.push(container);
} }
self.uninitialized_parallel.push(self.parallel.len());
self.parallel.push(container);
} }
} else {
let criteria = descriptor.run_criteria.take();
let mut container = SystemContainer::from_descriptor(descriptor);
match criteria {
Some(RunCriteriaDescriptorOrLabel::Label(label)) => {
container.run_criteria_label = Some(label);
}
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor));
}
None => {
container.run_criteria_index = default_run_criteria;
}
}
self.uninitialized_parallel.push(self.parallel.len());
self.parallel.push(container);
} }
} }
@ -233,21 +232,21 @@ impl SystemStage {
/// Topologically sorted parallel systems. /// Topologically sorted parallel systems.
/// ///
/// Note that systems won't be fully-formed until the stage has been run at least once. /// Note that systems won't be fully-formed until the stage has been run at least once.
pub fn parallel_systems(&self) -> &[impl SystemContainer] { pub fn parallel_systems(&self) -> &[SystemContainer] {
&self.parallel &self.parallel
} }
/// Topologically sorted exclusive systems that want to be run at the start of the stage. /// Topologically sorted exclusive systems that want to be run at the start of the stage.
/// ///
/// Note that systems won't be fully-formed until the stage has been run at least once. /// Note that systems won't be fully-formed until the stage has been run at least once.
pub fn exclusive_at_start_systems(&self) -> &[impl SystemContainer] { pub fn exclusive_at_start_systems(&self) -> &[SystemContainer] {
&self.exclusive_at_start &self.exclusive_at_start
} }
/// Topologically sorted exclusive systems that want to be run at the end of the stage. /// Topologically sorted exclusive systems that want to be run at the end of the stage.
/// ///
/// Note that systems won't be fully-formed until the stage has been run at least once. /// Note that systems won't be fully-formed until the stage has been run at least once.
pub fn exclusive_at_end_systems(&self) -> &[impl SystemContainer] { pub fn exclusive_at_end_systems(&self) -> &[SystemContainer] {
&self.exclusive_at_end &self.exclusive_at_end
} }
@ -255,7 +254,7 @@ impl SystemStage {
/// before the application of their command buffers. /// before the application of their command buffers.
/// ///
/// Note that systems won't be fully-formed until the stage has been run at least once. /// Note that systems won't be fully-formed until the stage has been run at least once.
pub fn exclusive_before_commands_systems(&self) -> &[impl SystemContainer] { pub fn exclusive_before_commands_systems(&self) -> &[SystemContainer] {
&self.exclusive_before_commands &self.exclusive_before_commands
} }
@ -270,17 +269,12 @@ impl SystemStage {
let (run_criteria, mut systems) = system_set.bake(); let (run_criteria, mut systems) = system_set.bake();
let set_run_criteria_index = run_criteria.and_then(|criteria| { let set_run_criteria_index = run_criteria.and_then(|criteria| {
// validate that no systems have criteria // validate that no systems have criteria
for system in &mut systems { for descriptor in &mut systems {
if let Some(name) = match system { if let Some(name) = descriptor
SystemDescriptor::Exclusive(descriptor) => descriptor .run_criteria
.run_criteria .is_some()
.is_some() .then(|| descriptor.system.name())
.then(|| descriptor.system.name()), {
SystemDescriptor::Parallel(descriptor) => descriptor
.run_criteria
.is_some()
.then(|| descriptor.system.name()),
} {
panic!( panic!(
"The system {} has a run criteria, but its `SystemSet` also has a run \ "The system {} has a run criteria, but its `SystemSet` also has a run \
criteria. This is not supported. Consider moving the system into a \ criteria. This is not supported. Consider moving the system into a \
@ -295,16 +289,7 @@ impl SystemStage {
} }
RunCriteriaDescriptorOrLabel::Label(label) => { RunCriteriaDescriptorOrLabel::Label(label) => {
for system in &mut systems { for system in &mut systems {
match system { system.run_criteria = Some(RunCriteriaDescriptorOrLabel::Label(label));
SystemDescriptor::Exclusive(descriptor) => {
descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label));
}
SystemDescriptor::Parallel(descriptor) => {
descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label));
}
}
} }
None None
@ -503,8 +488,7 @@ impl SystemStage {
fn check_uses_resource(&self, resource_id: ComponentId, world: &World) { fn check_uses_resource(&self, resource_id: ComponentId, world: &World) {
debug_assert!(!self.systems_modified); debug_assert!(!self.systems_modified);
for system in &self.parallel { for system in &self.parallel {
let access = system.component_access().unwrap(); if !system.component_access().has_read(resource_id) {
if !access.has_read(resource_id) {
let component_name = world.components().get_info(resource_id).unwrap().name(); let component_name = world.components().get_info(resource_id).unwrap().name();
warn!( warn!(
"System {} doesn't access resource {component_name}, despite being required to", "System {} doesn't access resource {component_name}, despite being required to",
@ -578,8 +562,8 @@ impl SystemStage {
} }
} }
fn update_run_criteria_indices<T: SystemContainer>( fn update_run_criteria_indices(
systems: &mut [T], systems: &mut [SystemContainer],
order_inverted: &[(usize, &usize)], order_inverted: &[(usize, &usize)],
) { ) {
for system in systems { for system in systems {
@ -605,7 +589,7 @@ impl SystemStage {
/// Sorts given system containers topologically, populates their resolved dependencies /// Sorts given system containers topologically, populates their resolved dependencies
/// and run criteria. /// and run criteria.
fn process_systems( fn process_systems(
systems: &mut Vec<impl SystemContainer>, systems: &mut Vec<SystemContainer>,
run_criteria_labels: &HashMap<RunCriteriaLabelId, usize>, run_criteria_labels: &HashMap<RunCriteriaLabelId, usize>,
) -> Result<(), DependencyGraphError<HashSet<SystemLabelId>>> { ) -> Result<(), DependencyGraphError<HashSet<SystemLabelId>>> {
let mut graph = graph_utils::build_dependency_graph(systems); let mut graph = graph_utils::build_dependency_graph(systems);
@ -701,7 +685,7 @@ impl Stage for SystemStage {
run_system_loop = false; run_system_loop = false;
fn should_run( fn should_run(
container: &impl SystemContainer, container: &SystemContainer,
run_criteria: &[RunCriteriaContainer], run_criteria: &[RunCriteriaContainer],
default: ShouldRun, default: ShouldRun,
) -> bool { ) -> bool {
@ -717,13 +701,24 @@ impl Stage for SystemStage {
// Run systems that want to be at the start of stage. // Run systems that want to be at the start of stage.
for container in &mut self.exclusive_at_start { for container in &mut self.exclusive_at_start {
if should_run(container, &self.run_criteria, default_should_run) { if should_run(container, &self.run_criteria, default_should_run) {
#[cfg(feature = "trace")] {
let _system_span = bevy_utils::tracing::info_span!( #[cfg(feature = "trace")]
"exclusive_system", let _system_span = bevy_utils::tracing::info_span!(
name = &*container.name() "exclusive_system",
) name = &*container.name()
.entered(); )
container.system_mut().run(world); .entered();
container.system_mut().run((), world);
}
{
#[cfg(feature = "trace")]
let _system_span = bevy_utils::tracing::info_span!(
"system_commands",
name = &*container.name()
)
.entered();
container.system_mut().apply_buffers(world);
}
} }
} }
@ -738,13 +733,24 @@ impl Stage for SystemStage {
// Run systems that want to be between parallel systems and their command buffers. // Run systems that want to be between parallel systems and their command buffers.
for container in &mut self.exclusive_before_commands { for container in &mut self.exclusive_before_commands {
if should_run(container, &self.run_criteria, default_should_run) { if should_run(container, &self.run_criteria, default_should_run) {
#[cfg(feature = "trace")] {
let _system_span = bevy_utils::tracing::info_span!( #[cfg(feature = "trace")]
"exclusive_system", let _system_span = bevy_utils::tracing::info_span!(
name = &*container.name() "exclusive_system",
) name = &*container.name()
.entered(); )
container.system_mut().run(world); .entered();
container.system_mut().run((), world);
}
{
#[cfg(feature = "trace")]
let _system_span = bevy_utils::tracing::info_span!(
"system_commands",
name = &*container.name()
)
.entered();
container.system_mut().apply_buffers(world);
}
} }
} }
@ -766,13 +772,24 @@ impl Stage for SystemStage {
// Run systems that want to be at the end of stage. // Run systems that want to be at the end of stage.
for container in &mut self.exclusive_at_end { for container in &mut self.exclusive_at_end {
if should_run(container, &self.run_criteria, default_should_run) { if should_run(container, &self.run_criteria, default_should_run) {
#[cfg(feature = "trace")] {
let _system_span = bevy_utils::tracing::info_span!( #[cfg(feature = "trace")]
"exclusive_system", let _system_span = bevy_utils::tracing::info_span!(
name = &*container.name() "exclusive_system",
) name = &*container.name()
.entered(); )
container.system_mut().run(world); .entered();
container.system_mut().run((), world);
}
{
#[cfg(feature = "trace")]
let _system_span = bevy_utils::tracing::info_span!(
"system_commands",
name = &*container.name()
)
.entered();
container.system_mut().apply_buffers(world);
}
} }
} }
@ -826,11 +843,10 @@ mod tests {
use crate::{ use crate::{
schedule::{ schedule::{
ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria, IntoSystemDescriptor, RunCriteria, RunCriteriaDescriptorCoercion, ShouldRun,
RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage, SystemLabel, SingleThreadedExecutor, Stage, SystemLabel, SystemSet, SystemStage,
SystemSet, SystemStage,
}, },
system::{In, IntoExclusiveSystem, Local, Query, ResMut}, system::{In, Local, Query, ResMut},
world::World, world::World,
}; };
@ -868,10 +884,10 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().at_start()) .with_system(make_exclusive(0).at_start())
.with_system(make_parallel(1)) .with_system(make_parallel(1))
.with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(2).before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end()); .with_system(make_exclusive(3).at_end());
stage.run(&mut world); stage.run(&mut world);
assert_eq!(world.resource_mut::<EntityCount>().0, vec![0, 1, 2, 3]); assert_eq!(world.resource_mut::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -883,10 +899,10 @@ mod tests {
world.resource_mut::<EntityCount>().0.clear(); world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(2).before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end()) .with_system(make_exclusive(3).at_end())
.with_system(make_parallel(1)) .with_system(make_parallel(1))
.with_system(make_exclusive(0).exclusive_system().at_start()); .with_system(make_exclusive(0).at_start());
stage.run(&mut world); stage.run(&mut world);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]); assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -898,10 +914,10 @@ mod tests {
world.resource_mut::<EntityCount>().0.clear(); world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).exclusive_system().before_commands()) .with_system(make_parallel(2).before_commands())
.with_system(make_parallel(3).exclusive_system().at_end()) .with_system(make_parallel(3).at_end())
.with_system(make_parallel(1)) .with_system(make_parallel(1))
.with_system(make_parallel(0).exclusive_system().at_start()); .with_system(make_parallel(0).at_start());
stage.run(&mut world); stage.run(&mut world);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]); assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -930,9 +946,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(1).exclusive_system().label(L1).after(L0)) .with_system(make_exclusive(1).label(L1).after(L0))
.with_system(make_exclusive(2).exclusive_system().after(L1)) .with_system(make_exclusive(2).after(L1))
.with_system(make_exclusive(0).exclusive_system().label(L0)); .with_system(make_exclusive(0).label(L0));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -944,9 +960,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(1).exclusive_system().label(L1).before(L2)) .with_system(make_exclusive(1).label(L1).before(L2))
.with_system(make_exclusive(2).exclusive_system().label(L2)) .with_system(make_exclusive(2).label(L2))
.with_system(make_exclusive(0).exclusive_system().before(L1)); .with_system(make_exclusive(0).before(L1));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -958,11 +974,11 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label(L2)) .with_system(make_exclusive(2).label(L2))
.with_system(make_exclusive(1).exclusive_system().after(L0).before(L2)) .with_system(make_exclusive(1).after(L0).before(L2))
.with_system(make_exclusive(0).exclusive_system().label(L0)) .with_system(make_exclusive(0).label(L0))
.with_system(make_exclusive(4).exclusive_system().label(L4)) .with_system(make_exclusive(4).label(L4))
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4)); .with_system(make_exclusive(3).after(L2).before(L4));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -977,9 +993,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(1).exclusive_system().label(First).after(L0)) .with_system(make_exclusive(1).label(First).after(L0))
.with_system(make_exclusive(2).exclusive_system().after(First)) .with_system(make_exclusive(2).after(First))
.with_system(make_exclusive(0).exclusive_system().label(First).label(L0)); .with_system(make_exclusive(0).label(First).label(L0));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -987,11 +1003,11 @@ mod tests {
world.resource_mut::<EntityCount>().0.clear(); world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().after(L01).label(L2)) .with_system(make_exclusive(2).after(L01).label(L2))
.with_system(make_exclusive(1).exclusive_system().label(L01).after(L0)) .with_system(make_exclusive(1).label(L01).after(L0))
.with_system(make_exclusive(0).exclusive_system().label(L01).label(L0)) .with_system(make_exclusive(0).label(L01).label(L0))
.with_system(make_exclusive(4).exclusive_system().label(L4)) .with_system(make_exclusive(4).label(L4))
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4)); .with_system(make_exclusive(3).after(L2).before(L4));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1002,17 +1018,11 @@ mod tests {
world.resource_mut::<EntityCount>().0.clear(); world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label(L234).label(L2)) .with_system(make_exclusive(2).label(L234).label(L2))
.with_system(make_exclusive(1).exclusive_system().before(L234).after(L0)) .with_system(make_exclusive(1).before(L234).after(L0))
.with_system(make_exclusive(0).exclusive_system().label(L0)) .with_system(make_exclusive(0).label(L0))
.with_system(make_exclusive(4).exclusive_system().label(L234).label(L4)) .with_system(make_exclusive(4).label(L234).label(L4))
.with_system( .with_system(make_exclusive(3).label(L234).after(L2).before(L4));
make_exclusive(3)
.exclusive_system()
.label(L234)
.after(L2)
.before(L4),
);
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1027,31 +1037,11 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(make_exclusive(2).label(L2).after(L1).before(L3).before(L3))
make_exclusive(2) .with_system(make_exclusive(1).label(L1).after(L0).after(L0).before(L2))
.exclusive_system() .with_system(make_exclusive(0).label(L0).before(L1))
.label(L2) .with_system(make_exclusive(4).label(L4).after(L3))
.after(L1) .with_system(make_exclusive(3).label(L3).after(L2).before(L4));
.before(L3)
.before(L3),
)
.with_system(
make_exclusive(1)
.exclusive_system()
.label(L1)
.after(L0)
.after(L0)
.before(L2),
)
.with_system(make_exclusive(0).exclusive_system().label(L0).before(L1))
.with_system(make_exclusive(4).exclusive_system().label(L4).after(L3))
.with_system(
make_exclusive(3)
.exclusive_system()
.label(L3)
.after(L2)
.before(L4),
);
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1066,14 +1056,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label(L2)) .with_system(make_exclusive(2).label(L2))
.with_system_set( .with_system_set(
SystemSet::new() SystemSet::new()
.with_system(make_exclusive(0).exclusive_system().label(L0)) .with_system(make_exclusive(0).label(L0))
.with_system(make_exclusive(4).exclusive_system().label(L4)) .with_system(make_exclusive(4).label(L4))
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4)), .with_system(make_exclusive(3).after(L2).before(L4)),
) )
.with_system(make_exclusive(1).exclusive_system().after(L0).before(L2)); .with_system(make_exclusive(1).after(L0).before(L2));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1088,13 +1078,13 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().before(L1)) .with_system(make_exclusive(0).before(L1))
.with_system_set( .with_system_set(
SystemSet::new() SystemSet::new()
.with_run_criteria(every_other_time) .with_run_criteria(every_other_time)
.with_system(make_exclusive(1).exclusive_system().label(L1)), .with_system(make_exclusive(1).label(L1)),
) )
.with_system(make_exclusive(2).exclusive_system().after(L1)); .with_system(make_exclusive(2).after(L1));
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -1111,8 +1101,7 @@ mod tests {
fn exclusive_cycle_1() { fn exclusive_cycle_1() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel().with_system(make_exclusive(0).label(L0).after(L0));
.with_system(make_exclusive(0).exclusive_system().label(L0).after(L0));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1122,8 +1111,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().label(L0).after(L1)) .with_system(make_exclusive(0).label(L0).after(L1))
.with_system(make_exclusive(1).exclusive_system().label(L1).after(L0)); .with_system(make_exclusive(1).label(L1).after(L0));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1133,9 +1122,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<EntityCount>(); world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().label(L0)) .with_system(make_exclusive(0).label(L0))
.with_system(make_exclusive(1).exclusive_system().after(L0).before(L2)) .with_system(make_exclusive(1).after(L0).before(L2))
.with_system(make_exclusive(2).exclusive_system().label(L2).before(L0)); .with_system(make_exclusive(2).label(L2).before(L0));
stage.run(&mut world); stage.run(&mut world);
} }

View File

@ -1,117 +1,26 @@
use crate::{ use crate::{
component::ComponentId, component::ComponentId,
query::Access, query::Access,
schedule::{ schedule::{GraphNode, RunCriteriaLabelId, SystemDescriptor, SystemLabelId},
ExclusiveSystemDescriptor, GraphNode, ParallelSystemDescriptor, RunCriteriaLabelId, system::System,
SystemLabelId,
},
system::{ExclusiveSystem, System},
}; };
use std::borrow::Cow; use std::borrow::Cow;
/// System metadata like its name, labels, order requirements and component access. pub struct SystemContainer {
pub trait SystemContainer: GraphNode<Label = SystemLabelId> {
#[doc(hidden)]
fn dependencies(&self) -> &[usize];
#[doc(hidden)]
fn set_dependencies(&mut self, dependencies: impl IntoIterator<Item = usize>);
#[doc(hidden)]
fn run_criteria(&self) -> Option<usize>;
#[doc(hidden)]
fn set_run_criteria(&mut self, index: usize);
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId>;
fn component_access(&self) -> Option<&Access<ComponentId>>;
}
pub(super) struct ExclusiveSystemContainer {
system: Box<dyn ExclusiveSystem>,
pub(super) run_criteria_index: Option<usize>,
pub(super) run_criteria_label: Option<RunCriteriaLabelId>,
dependencies: Vec<usize>,
labels: Vec<SystemLabelId>,
before: Vec<SystemLabelId>,
after: Vec<SystemLabelId>,
}
impl ExclusiveSystemContainer {
pub(super) fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self {
ExclusiveSystemContainer {
system: descriptor.system,
run_criteria_index: None,
run_criteria_label: None,
dependencies: Vec::new(),
labels: descriptor.labels,
before: descriptor.before,
after: descriptor.after,
}
}
pub(super) fn system_mut(&mut self) -> &mut Box<dyn ExclusiveSystem> {
&mut self.system
}
}
impl GraphNode for ExclusiveSystemContainer {
type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> {
self.system.name()
}
fn labels(&self) -> &[SystemLabelId] {
&self.labels
}
fn before(&self) -> &[SystemLabelId] {
&self.before
}
fn after(&self) -> &[SystemLabelId] {
&self.after
}
}
impl SystemContainer for ExclusiveSystemContainer {
fn dependencies(&self) -> &[usize] {
&self.dependencies
}
fn set_dependencies(&mut self, dependencies: impl IntoIterator<Item = usize>) {
self.dependencies.clear();
self.dependencies.extend(dependencies);
}
fn run_criteria(&self) -> Option<usize> {
self.run_criteria_index
}
fn set_run_criteria(&mut self, index: usize) {
self.run_criteria_index = Some(index);
}
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref()
}
fn component_access(&self) -> Option<&Access<ComponentId>> {
None
}
}
pub struct ParallelSystemContainer {
system: Box<dyn System<In = (), Out = ()>>, system: Box<dyn System<In = (), Out = ()>>,
pub(crate) run_criteria_index: Option<usize>, pub(crate) run_criteria_index: Option<usize>,
pub(crate) run_criteria_label: Option<RunCriteriaLabelId>, pub(crate) run_criteria_label: Option<RunCriteriaLabelId>,
pub(crate) should_run: bool, pub(crate) should_run: bool,
is_exclusive: bool,
dependencies: Vec<usize>, dependencies: Vec<usize>,
labels: Vec<SystemLabelId>, labels: Vec<SystemLabelId>,
before: Vec<SystemLabelId>, before: Vec<SystemLabelId>,
after: Vec<SystemLabelId>, after: Vec<SystemLabelId>,
} }
impl ParallelSystemContainer { impl SystemContainer {
pub(crate) fn from_descriptor(descriptor: ParallelSystemDescriptor) -> Self { pub(crate) fn from_descriptor(descriptor: SystemDescriptor) -> Self {
ParallelSystemContainer { SystemContainer {
system: descriptor.system, system: descriptor.system,
should_run: false, should_run: false,
run_criteria_index: None, run_criteria_index: None,
@ -120,6 +29,7 @@ impl ParallelSystemContainer {
labels: descriptor.labels, labels: descriptor.labels,
before: descriptor.before, before: descriptor.before,
after: descriptor.after, after: descriptor.after,
is_exclusive: descriptor.exclusive_insertion_point.is_some(),
} }
} }
@ -142,9 +52,34 @@ impl ParallelSystemContainer {
pub fn dependencies(&self) -> &[usize] { pub fn dependencies(&self) -> &[usize] {
&self.dependencies &self.dependencies
} }
pub fn set_dependencies(&mut self, dependencies: impl IntoIterator<Item = usize>) {
self.dependencies.clear();
self.dependencies.extend(dependencies);
}
pub fn run_criteria(&self) -> Option<usize> {
self.run_criteria_index
}
pub fn set_run_criteria(&mut self, index: usize) {
self.run_criteria_index = Some(index);
}
pub fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref()
}
pub fn component_access(&self) -> &Access<ComponentId> {
self.system().component_access()
}
pub fn is_exclusive(&self) -> bool {
self.is_exclusive
}
} }
impl GraphNode for ParallelSystemContainer { impl GraphNode for SystemContainer {
type Label = SystemLabelId; type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> Cow<'static, str> {
@ -163,30 +98,3 @@ impl GraphNode for ParallelSystemContainer {
&self.after &self.after
} }
} }
impl SystemContainer for ParallelSystemContainer {
fn dependencies(&self) -> &[usize] {
&self.dependencies
}
fn set_dependencies(&mut self, dependencies: impl IntoIterator<Item = usize>) {
self.dependencies.clear();
self.dependencies.extend(dependencies);
}
fn run_criteria(&self) -> Option<usize> {
self.run_criteria_index
}
fn set_run_criteria(&mut self, index: usize) {
self.run_criteria_index = Some(index);
}
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref()
}
fn component_access(&self) -> Option<&Access<ComponentId>> {
Some(self.system().component_access())
}
}

View File

@ -1,9 +1,6 @@
use crate::{ use crate::{
schedule::{IntoRunCriteria, RunCriteriaDescriptorOrLabel, SystemLabel, SystemLabelId}, schedule::{IntoRunCriteria, RunCriteriaDescriptorOrLabel, SystemLabel, SystemLabelId},
system::{ system::{AsSystemLabel, BoxedSystem, IntoSystem},
AsSystemLabel, BoxedSystem, ExclusiveSystem, ExclusiveSystemCoerced, ExclusiveSystemFn,
IntoSystem,
},
}; };
/// Encapsulates a system and information on when it run in a `SystemStage`. /// Encapsulates a system and information on when it run in a `SystemStage`.
@ -32,301 +29,188 @@ use crate::{
/// SystemStage::parallel() /// SystemStage::parallel()
/// .with_system(do_something.label(Something)) /// .with_system(do_something.label(Something))
/// .with_system(do_the_other_thing.after(Something)) /// .with_system(do_the_other_thing.after(Something))
/// .with_system(do_something_else.exclusive_system().at_end()); /// .with_system(do_something_else.at_end());
/// ``` /// ```
pub enum SystemDescriptor { pub struct SystemDescriptor {
Parallel(ParallelSystemDescriptor), pub(crate) system: BoxedSystem<(), ()>,
Exclusive(ExclusiveSystemDescriptor), pub(crate) exclusive_insertion_point: Option<ExclusiveInsertionPoint>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
}
impl SystemDescriptor {
fn new(system: BoxedSystem<(), ()>) -> SystemDescriptor {
SystemDescriptor {
labels: system.default_labels(),
exclusive_insertion_point: if system.is_exclusive() {
Some(ExclusiveInsertionPoint::AtStart)
} else {
None
},
system,
run_criteria: None,
before: Vec::new(),
after: Vec::new(),
}
}
} }
pub trait IntoSystemDescriptor<Params> { pub trait IntoSystemDescriptor<Params> {
fn into_descriptor(self) -> SystemDescriptor; fn into_descriptor(self) -> SystemDescriptor;
}
impl IntoSystemDescriptor<()> for ParallelSystemDescriptor {
fn into_descriptor(self) -> SystemDescriptor {
SystemDescriptor::Parallel(self)
}
}
impl<Params, S> IntoSystemDescriptor<Params> for S
where
S: IntoSystem<(), (), Params>,
{
fn into_descriptor(self) -> SystemDescriptor {
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).into_descriptor()
}
}
impl IntoSystemDescriptor<()> for SystemDescriptor {
fn into_descriptor(self) -> SystemDescriptor {
self
}
}
impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> {
fn into_descriptor(self) -> SystemDescriptor {
new_parallel_descriptor(self).into_descriptor()
}
}
impl IntoSystemDescriptor<()> for ExclusiveSystemDescriptor {
fn into_descriptor(self) -> SystemDescriptor {
SystemDescriptor::Exclusive(self)
}
}
impl<F> IntoSystemDescriptor<()> for ExclusiveSystemFn<F>
where
F: FnMut(&mut crate::prelude::World) + Send + Sync + 'static,
{
fn into_descriptor(self) -> SystemDescriptor {
new_exclusive_descriptor(Box::new(self)).into_descriptor()
}
}
impl IntoSystemDescriptor<()> for ExclusiveSystemCoerced {
fn into_descriptor(self) -> SystemDescriptor {
new_exclusive_descriptor(Box::new(self)).into_descriptor()
}
}
/// Encapsulates a parallel system and information on when it runs in a `SystemStage`.
pub struct ParallelSystemDescriptor {
pub(crate) system: BoxedSystem<(), ()>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
}
fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescriptor {
ParallelSystemDescriptor {
labels: system.default_labels(),
system,
run_criteria: None,
before: Vec::new(),
after: Vec::new(),
}
}
pub trait ParallelSystemDescriptorCoercion<Params> {
/// Assigns a run criteria to the system. Can be a new descriptor or a label of a /// Assigns a run criteria to the system. Can be a new descriptor or a label of a
/// run criteria defined elsewhere. /// run criteria defined elsewhere.
fn with_run_criteria<Marker>( fn with_run_criteria<Marker>(
self, self,
run_criteria: impl IntoRunCriteria<Marker>, run_criteria: impl IntoRunCriteria<Marker>,
) -> ParallelSystemDescriptor; ) -> SystemDescriptor;
/// Assigns a label to the system; there can be more than one, and it doesn't have to be unique. /// Assigns a label to the system; there can be more than one, and it doesn't have to be unique.
fn label(self, label: impl SystemLabel) -> ParallelSystemDescriptor; fn label(self, label: impl SystemLabel) -> SystemDescriptor;
/// Specifies that the system should run before systems with the given label. /// Specifies that the system should run before systems with the given label.
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor; fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor;
/// Specifies that the system should run after systems with the given label. /// Specifies that the system should run after systems with the given label.
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor; fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor;
/// Specifies that the system should run with other exclusive systems at the start of stage.
fn at_start(self) -> SystemDescriptor;
/// Specifies that the system should run with other exclusive systems after the parallel
/// systems and before command buffer application.
fn before_commands(self) -> SystemDescriptor;
/// Specifies that the system should run with other exclusive systems at the end of stage.
fn at_end(self) -> SystemDescriptor;
} }
impl ParallelSystemDescriptorCoercion<()> for ParallelSystemDescriptor { impl IntoSystemDescriptor<()> for SystemDescriptor {
fn with_run_criteria<Marker>( fn with_run_criteria<Marker>(
mut self, mut self,
run_criteria: impl IntoRunCriteria<Marker>, run_criteria: impl IntoRunCriteria<Marker>,
) -> ParallelSystemDescriptor { ) -> SystemDescriptor {
self.run_criteria = Some(run_criteria.into()); self.run_criteria = Some(run_criteria.into());
self self
} }
fn label(mut self, label: impl SystemLabel) -> ParallelSystemDescriptor { fn label(mut self, label: impl SystemLabel) -> SystemDescriptor {
self.labels.push(label.as_label()); self.labels.push(label.as_label());
self self
} }
fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
self.before.push(label.as_system_label().as_label()); self.before.push(label.as_system_label().as_label());
self self
} }
fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
self.after.push(label.as_system_label().as_label()); self.after.push(label.as_system_label().as_label());
self self
} }
fn at_start(mut self) -> SystemDescriptor {
self.exclusive_insertion_point = Some(ExclusiveInsertionPoint::AtStart);
self
}
fn before_commands(mut self) -> SystemDescriptor {
self.exclusive_insertion_point = Some(ExclusiveInsertionPoint::BeforeCommands);
self
}
fn at_end(mut self) -> SystemDescriptor {
self.exclusive_insertion_point = Some(ExclusiveInsertionPoint::AtEnd);
self
}
fn into_descriptor(self) -> SystemDescriptor {
self
}
} }
impl<S, Params> ParallelSystemDescriptorCoercion<Params> for S impl<S, Params> IntoSystemDescriptor<Params> for S
where where
S: IntoSystem<(), (), Params>, S: IntoSystem<(), (), Params>,
{ {
fn with_run_criteria<Marker>( fn with_run_criteria<Marker>(
self, self,
run_criteria: impl IntoRunCriteria<Marker>, run_criteria: impl IntoRunCriteria<Marker>,
) -> ParallelSystemDescriptor { ) -> SystemDescriptor {
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))) SystemDescriptor::new(Box::new(IntoSystem::into_system(self)))
.with_run_criteria(run_criteria) .with_run_criteria(run_criteria)
} }
fn label(self, label: impl SystemLabel) -> ParallelSystemDescriptor { fn label(self, label: impl SystemLabel) -> SystemDescriptor {
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).label(label) SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).label(label)
} }
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).before(label) SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).before(label)
} }
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).after(label) SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).after(label)
}
fn at_start(self) -> SystemDescriptor {
SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).at_start()
}
fn before_commands(self) -> SystemDescriptor {
SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).before_commands()
}
fn at_end(self) -> SystemDescriptor {
SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).at_end()
}
fn into_descriptor(self) -> SystemDescriptor {
SystemDescriptor::new(Box::new(IntoSystem::into_system(self)))
} }
} }
impl ParallelSystemDescriptorCoercion<()> for BoxedSystem<(), ()> { impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> {
fn with_run_criteria<Marker>( fn with_run_criteria<Marker>(
self, self,
run_criteria: impl IntoRunCriteria<Marker>, run_criteria: impl IntoRunCriteria<Marker>,
) -> ParallelSystemDescriptor { ) -> SystemDescriptor {
new_parallel_descriptor(self).with_run_criteria(run_criteria) SystemDescriptor::new(self).with_run_criteria(run_criteria)
} }
fn label(self, label: impl SystemLabel) -> ParallelSystemDescriptor { fn label(self, label: impl SystemLabel) -> SystemDescriptor {
new_parallel_descriptor(self).label(label) SystemDescriptor::new(self).label(label)
} }
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
new_parallel_descriptor(self).before(label) SystemDescriptor::new(self).before(label)
} }
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor { fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
new_parallel_descriptor(self).after(label) SystemDescriptor::new(self).after(label)
}
fn at_start(self) -> SystemDescriptor {
SystemDescriptor::new(self).at_start()
}
fn before_commands(self) -> SystemDescriptor {
SystemDescriptor::new(self).before_commands()
}
fn at_end(self) -> SystemDescriptor {
SystemDescriptor::new(self).at_end()
}
fn into_descriptor(self) -> SystemDescriptor {
SystemDescriptor::new(self)
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) enum InsertionPoint { pub(crate) enum ExclusiveInsertionPoint {
AtStart, AtStart,
BeforeCommands, BeforeCommands,
AtEnd, AtEnd,
} }
/// Encapsulates an exclusive system and information on when it runs in a `SystemStage`.
pub struct ExclusiveSystemDescriptor {
pub(crate) system: Box<dyn ExclusiveSystem>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
pub(crate) insertion_point: InsertionPoint,
}
fn new_exclusive_descriptor(system: Box<dyn ExclusiveSystem>) -> ExclusiveSystemDescriptor {
ExclusiveSystemDescriptor {
system,
run_criteria: None,
labels: Vec::new(),
before: Vec::new(),
after: Vec::new(),
insertion_point: InsertionPoint::AtStart,
}
}
pub trait ExclusiveSystemDescriptorCoercion {
/// Assigns a run criteria to the system. Can be a new descriptor or a label of a
/// run criteria defined elsewhere.
fn with_run_criteria<Marker>(
self,
run_criteria: impl IntoRunCriteria<Marker>,
) -> ExclusiveSystemDescriptor;
/// Assigns a label to the system; there can be more than one, and it doesn't have to be unique.
fn label(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor;
/// Specifies that the system should run before systems with the given label.
fn before(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor;
/// Specifies that the system should run after systems with the given label.
fn after(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor;
/// Specifies that the system should run with other exclusive systems at the start of stage.
fn at_start(self) -> ExclusiveSystemDescriptor;
/// Specifies that the system should run with other exclusive systems after the parallel
/// systems and before command buffer application.
fn before_commands(self) -> ExclusiveSystemDescriptor;
/// Specifies that the system should run with other exclusive systems at the end of stage.
fn at_end(self) -> ExclusiveSystemDescriptor;
}
impl ExclusiveSystemDescriptorCoercion for ExclusiveSystemDescriptor {
fn with_run_criteria<Marker>(
mut self,
run_criteria: impl IntoRunCriteria<Marker>,
) -> ExclusiveSystemDescriptor {
self.run_criteria = Some(run_criteria.into());
self
}
fn label(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.labels.push(label.as_label());
self
}
fn before(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.before.push(label.as_label());
self
}
fn after(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.after.push(label.as_label());
self
}
fn at_start(mut self) -> ExclusiveSystemDescriptor {
self.insertion_point = InsertionPoint::AtStart;
self
}
fn before_commands(mut self) -> ExclusiveSystemDescriptor {
self.insertion_point = InsertionPoint::BeforeCommands;
self
}
fn at_end(mut self) -> ExclusiveSystemDescriptor {
self.insertion_point = InsertionPoint::AtEnd;
self
}
}
impl<T> ExclusiveSystemDescriptorCoercion for T
where
T: ExclusiveSystem + 'static,
{
fn with_run_criteria<Marker>(
self,
run_criteria: impl IntoRunCriteria<Marker>,
) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).with_run_criteria(run_criteria)
}
fn label(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).label(label)
}
fn before(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).before(label)
}
fn after(self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).after(label)
}
fn at_start(self) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).at_start()
}
fn before_commands(self) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).before_commands()
}
fn at_end(self) -> ExclusiveSystemDescriptor {
new_exclusive_descriptor(Box::new(self)).at_end()
}
}

View File

@ -107,18 +107,9 @@ impl SystemSet {
after, after,
} = self; } = self;
for descriptor in &mut systems { for descriptor in &mut systems {
match descriptor { descriptor.labels.extend(labels.iter().cloned());
SystemDescriptor::Parallel(descriptor) => { descriptor.before.extend(before.iter().cloned());
descriptor.labels.extend(labels.iter().cloned()); descriptor.after.extend(after.iter().cloned());
descriptor.before.extend(before.iter().cloned());
descriptor.after.extend(after.iter().cloned());
}
SystemDescriptor::Exclusive(descriptor) => {
descriptor.labels.extend(labels.iter().cloned());
descriptor.before.extend(before.iter().cloned());
descriptor.after.extend(after.iter().cloned());
}
}
} }
(run_criteria, systems) (run_criteria, systems)
} }

View File

@ -0,0 +1,203 @@
use crate::{
archetype::ArchetypeComponentId,
change_detection::MAX_CHANGE_AGE,
component::ComponentId,
query::Access,
schedule::{SystemLabel, SystemLabelId},
system::{
check_system_change_tick, AsSystemLabel, ExclusiveSystemParam, ExclusiveSystemParamFetch,
ExclusiveSystemParamItem, ExclusiveSystemParamState, IntoSystem, System, SystemMeta,
SystemTypeIdLabel,
},
world::{World, WorldId},
};
use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, marker::PhantomData};
/// A function system that runs with exclusive [`World`] access.
///
/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
/// [`ExclusiveSystemParam`]s.
///
/// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
pub struct ExclusiveFunctionSystem<Param, Marker, F>
where
Param: ExclusiveSystemParam,
{
func: F,
param_state: Option<Param::Fetch>,
system_meta: SystemMeta,
world_id: Option<WorldId>,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
marker: PhantomData<fn() -> Marker>,
}
pub struct IsExclusiveFunctionSystem;
impl<Param, Marker, F> IntoSystem<(), (), (IsExclusiveFunctionSystem, Param, Marker)> for F
where
Param: ExclusiveSystemParam + 'static,
Marker: 'static,
F: ExclusiveSystemParamFunction<Param, Marker> + Send + Sync + 'static,
{
type System = ExclusiveFunctionSystem<Param, Marker, F>;
fn into_system(func: Self) -> Self::System {
ExclusiveFunctionSystem {
func,
param_state: None,
system_meta: SystemMeta::new::<F>(),
world_id: None,
marker: PhantomData,
}
}
}
const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
impl<Param, Marker, F> System for ExclusiveFunctionSystem<Param, Marker, F>
where
Param: ExclusiveSystemParam + 'static,
Marker: 'static,
F: ExclusiveSystemParamFunction<Param, Marker> + Send + Sync + 'static,
{
type In = ();
type Out = ();
#[inline]
fn name(&self) -> Cow<'static, str> {
self.system_meta.name.clone()
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
self.system_meta.component_access_set.combined_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_meta.archetype_component_access
}
#[inline]
fn is_send(&self) -> bool {
// exclusive systems should have access to non-send resources
// the executor runs exclusive systems on the main thread, so this
// field reflects that constraint
false
}
#[inline]
unsafe fn run_unsafe(&mut self, _input: Self::In, _world: &World) -> Self::Out {
panic!("Cannot run exclusive systems with a shared World reference");
}
fn run(&mut self, _input: Self::In, world: &mut World) -> Self::Out {
let saved_last_tick = world.last_change_tick;
world.last_change_tick = self.system_meta.last_change_tick;
let params = <Param as ExclusiveSystemParam>::Fetch::get_param(
self.param_state.as_mut().expect(PARAM_MESSAGE),
&self.system_meta,
);
self.func.run(world, params);
let change_tick = world.change_tick.get_mut();
self.system_meta.last_change_tick = *change_tick;
*change_tick += 1;
world.last_change_tick = saved_last_tick;
}
#[inline]
fn is_exclusive(&self) -> bool {
true
}
fn get_last_change_tick(&self) -> u32 {
self.system_meta.last_change_tick
}
fn set_last_change_tick(&mut self, last_change_tick: u32) {
self.system_meta.last_change_tick = last_change_tick;
}
#[inline]
fn apply_buffers(&mut self, world: &mut World) {
let param_state = self.param_state.as_mut().expect(PARAM_MESSAGE);
param_state.apply(world);
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.world_id = Some(world.id());
self.system_meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
self.param_state = Some(<Param::Fetch as ExclusiveSystemParamState>::init(
world,
&mut self.system_meta,
));
}
fn update_archetype_component_access(&mut self, _world: &World) {}
#[inline]
fn check_change_tick(&mut self, change_tick: u32) {
check_system_change_tick(
&mut self.system_meta.last_change_tick,
change_tick,
self.system_meta.name.as_ref(),
);
}
fn default_labels(&self) -> Vec<SystemLabelId> {
vec![self.func.as_system_label().as_label()]
}
}
impl<Param: ExclusiveSystemParam, Marker, T: ExclusiveSystemParamFunction<Param, Marker>>
AsSystemLabel<(Param, Marker, IsExclusiveFunctionSystem)> for T
{
#[inline]
fn as_system_label(&self) -> SystemLabelId {
SystemTypeIdLabel::<T>(PhantomData).as_label()
}
}
/// A trait implemented for all exclusive system functions that can be used as [`System`]s.
///
/// This trait can be useful for making your own systems which accept other systems,
/// sometimes called higher order systems.
pub trait ExclusiveSystemParamFunction<Param: ExclusiveSystemParam, Marker>:
Send + Sync + 'static
{
fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem<Param>);
}
macro_rules! impl_exclusive_system_function {
($($param: ident),*) => {
#[allow(non_snake_case)]
impl<Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<($($param,)*), ()> for Func
where
for <'a> &'a mut Func:
FnMut(&mut World, $($param),*) +
FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*)
{
#[inline]
fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem< ($($param,)*)>) {
// Yes, this is strange, but `rustc` fails to compile this impl
// without using this function. It fails to recognise that `func`
// is a function, potentially because of the multiple impls of `FnMut`
#[allow(clippy::too_many_arguments)]
fn call_inner<$($param,)*>(
mut f: impl FnMut(&mut World, $($param,)*),
world: &mut World,
$($param: $param,)*
) {
f(world, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, world, $($param),*)
}
}
};
}
// Note that we rely on the highest impl to be <= the highest order of the tuple impls
// of `SystemParam` created.
all_tuples!(impl_exclusive_system_function, 0, 16, F);

View File

@ -1,177 +0,0 @@
use crate::{
change_detection::MAX_CHANGE_AGE,
system::{check_system_change_tick, BoxedSystem, IntoSystem},
world::World,
};
use std::borrow::Cow;
pub trait ExclusiveSystem: Send + Sync + 'static {
fn name(&self) -> Cow<'static, str>;
fn run(&mut self, world: &mut World);
fn initialize(&mut self, world: &mut World);
fn check_change_tick(&mut self, change_tick: u32);
}
pub struct ExclusiveSystemFn<F> {
func: F,
name: Cow<'static, str>,
last_change_tick: u32,
}
impl<F> ExclusiveSystem for ExclusiveSystemFn<F>
where
F: FnMut(&mut World) + Send + Sync + 'static,
{
fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
fn run(&mut self, world: &mut World) {
// The previous value is saved in case this exclusive system is run by another exclusive
// system
let saved_last_tick = world.last_change_tick;
world.last_change_tick = self.last_change_tick;
(self.func)(world);
let change_tick = world.change_tick.get_mut();
self.last_change_tick = *change_tick;
*change_tick += 1;
world.last_change_tick = saved_last_tick;
}
fn initialize(&mut self, world: &mut World) {
self.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
}
fn check_change_tick(&mut self, change_tick: u32) {
check_system_change_tick(&mut self.last_change_tick, change_tick, self.name.as_ref());
}
}
pub trait IntoExclusiveSystem<Params, SystemType> {
fn exclusive_system(self) -> SystemType;
}
impl<F> IntoExclusiveSystem<&mut World, ExclusiveSystemFn<F>> for F
where
F: FnMut(&mut World) + Send + Sync + 'static,
{
fn exclusive_system(self) -> ExclusiveSystemFn<F> {
ExclusiveSystemFn {
func: self,
name: core::any::type_name::<F>().into(),
last_change_tick: 0,
}
}
}
pub struct ExclusiveSystemCoerced {
system: BoxedSystem<(), ()>,
}
impl ExclusiveSystem for ExclusiveSystemCoerced {
fn name(&self) -> Cow<'static, str> {
self.system.name()
}
fn run(&mut self, world: &mut World) {
self.system.run((), world);
self.system.apply_buffers(world);
}
fn initialize(&mut self, world: &mut World) {
self.system.initialize(world);
}
fn check_change_tick(&mut self, change_tick: u32) {
self.system.check_change_tick(change_tick);
}
}
impl<S, Params> IntoExclusiveSystem<Params, ExclusiveSystemCoerced> for S
where
S: IntoSystem<(), (), Params>,
{
fn exclusive_system(self) -> ExclusiveSystemCoerced {
ExclusiveSystemCoerced {
system: Box::new(IntoSystem::into_system(self)),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
self as bevy_ecs,
component::Component,
entity::Entity,
query::With,
schedule::{Stage, SystemStage},
system::{Commands, IntoExclusiveSystem, Query, ResMut, Resource},
world::World,
};
#[derive(Component)]
struct Foo(f32);
#[test]
fn parallel_with_commands_as_exclusive() {
let mut world = World::new();
#[derive(Resource)]
struct Counter(usize);
fn removal(
mut commands: Commands,
query: Query<Entity, With<Foo>>,
mut counter: ResMut<Counter>,
) {
for entity in &query {
counter.0 += 1;
commands.entity(entity).remove::<Foo>();
}
}
let mut stage = SystemStage::parallel().with_system(removal);
world.spawn(Foo(0.0f32));
world.insert_resource(Counter(0));
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 1);
let mut stage = SystemStage::parallel().with_system(removal.exclusive_system());
world.spawn(Foo(0.0f32));
world.insert_resource(Counter(0));
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 1);
}
#[test]
fn update_archetype_for_exclusive_system_coerced() {
#[derive(Resource, Default)]
struct CountEntities(Vec<usize>);
fn spawn_entity(mut commands: crate::prelude::Commands) {
commands.spawn(Foo(0.0));
}
fn count_entities(query: Query<&Foo>, mut res: ResMut<CountEntities>) {
res.0.push(query.iter().len());
}
let mut world = World::new();
world.init_resource::<CountEntities>();
let mut stage = SystemStage::parallel()
.with_system(spawn_entity)
.with_system(count_entities.exclusive_system());
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(world.resource::<CountEntities>().0, vec![0, 1]);
}
}

View File

@ -0,0 +1,130 @@
use crate::{
prelude::{FromWorld, QueryState},
query::{ReadOnlyWorldQuery, WorldQuery},
system::{Local, LocalState, SystemMeta, SystemParam, SystemState},
world::World,
};
use bevy_ecs_macros::all_tuples;
use bevy_utils::synccell::SyncCell;
pub trait ExclusiveSystemParam: Sized {
type Fetch: for<'s> ExclusiveSystemParamFetch<'s>;
}
pub type ExclusiveSystemParamItem<'s, P> =
<<P as ExclusiveSystemParam>::Fetch as ExclusiveSystemParamFetch<'s>>::Item;
/// The state of a [`SystemParam`].
pub trait ExclusiveSystemParamState: Send + Sync {
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self;
#[inline]
fn apply(&mut self, _world: &mut World) {}
}
pub trait ExclusiveSystemParamFetch<'state>: ExclusiveSystemParamState {
type Item: ExclusiveSystemParam<Fetch = Self>;
fn get_param(state: &'state mut Self, system_meta: &SystemMeta) -> Self::Item;
}
impl<'a, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> ExclusiveSystemParam
for &'a mut QueryState<Q, F>
{
type Fetch = QueryState<Q, F>;
}
impl<'s, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> ExclusiveSystemParamFetch<'s>
for QueryState<Q, F>
{
type Item = &'s mut QueryState<Q, F>;
fn get_param(state: &'s mut Self, _system_meta: &SystemMeta) -> Self::Item {
state
}
}
impl<Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> ExclusiveSystemParamState
for QueryState<Q, F>
{
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
QueryState::new(world)
}
}
impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState<P> {
type Fetch = SystemState<P>;
}
impl<'s, P: SystemParam + 'static> ExclusiveSystemParamFetch<'s> for SystemState<P> {
type Item = &'s mut SystemState<P>;
fn get_param(state: &'s mut Self, _system_meta: &SystemMeta) -> Self::Item {
state
}
}
impl<P: SystemParam> ExclusiveSystemParamState for SystemState<P> {
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
SystemState::new(world)
}
}
impl<'s, T: FromWorld + Send + Sync + 'static> ExclusiveSystemParam for Local<'s, T> {
type Fetch = LocalState<T>;
}
impl<'s, T: FromWorld + Send + Sync + 'static> ExclusiveSystemParamFetch<'s> for LocalState<T> {
type Item = Local<'s, T>;
fn get_param(state: &'s mut Self, _system_meta: &SystemMeta) -> Self::Item {
Local(state.0.get())
}
}
impl<T: FromWorld + Send + Sync> ExclusiveSystemParamState for LocalState<T> {
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
Self(SyncCell::new(T::from_world(world)))
}
}
macro_rules! impl_exclusive_system_param_tuple {
($($param: ident),*) => {
impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
type Fetch = ($($param::Fetch,)*);
}
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'s, $($param: ExclusiveSystemParamFetch<'s>),*> ExclusiveSystemParamFetch<'s> for ($($param,)*) {
type Item = ($($param::Item,)*);
#[inline]
#[allow(clippy::unused_unit)]
fn get_param(
state: &'s mut Self,
system_meta: &SystemMeta,
) -> Self::Item {
let ($($param,)*) = state;
($($param::get_param($param, system_meta),)*)
}
}
// SAFETY: implementors of each `ExclusiveSystemParamState` in the tuple have validated their impls
#[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy
#[allow(non_snake_case)]
impl<$($param: ExclusiveSystemParamState),*> ExclusiveSystemParamState for ($($param,)*) {
#[inline]
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
(($($param::init(_world, _system_meta),)*))
}
#[inline]
fn apply(&mut self, _world: &mut World) {
let ($($param,)*) = self;
$($param.apply(_world);)*
}
}
};
}
all_tuples!(impl_exclusive_system_param_tuple, 0, 16, P);

View File

@ -27,7 +27,7 @@ pub struct SystemMeta {
} }
impl SystemMeta { impl SystemMeta {
fn new<T>() -> Self { pub(crate) fn new<T>() -> Self {
Self { Self {
name: std::any::type_name::<T>().into(), name: std::any::type_name::<T>().into(),
archetype_component_access: Access::default(), archetype_component_access: Access::default(),
@ -112,8 +112,8 @@ impl SystemMeta {
/// ///
/// struct MyEvent; /// struct MyEvent;
/// #[derive(Resource)] /// #[derive(Resource)]
/// struct CachedSystemState<'w, 's>{ /// struct CachedSystemState {
/// event_state: SystemState<EventReader<'w, 's, MyEvent>> /// event_state: SystemState<EventReader<'static, 'static, MyEvent>>
/// } /// }
/// ///
/// // Create and store a system state once /// // Create and store a system state once
@ -133,7 +133,7 @@ impl SystemMeta {
/// }; /// };
/// }); /// });
/// ``` /// ```
pub struct SystemState<Param: SystemParam> { pub struct SystemState<Param: SystemParam + 'static> {
meta: SystemMeta, meta: SystemMeta,
param_state: <Param as SystemParam>::Fetch, param_state: <Param as SystemParam>::Fetch,
world_id: WorldId, world_id: WorldId,
@ -387,6 +387,11 @@ where
self.system_meta.is_send self.system_meta.is_send
} }
#[inline]
fn is_exclusive(&self) -> bool {
false
}
#[inline] #[inline]
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
let change_tick = world.increment_change_tick(); let change_tick = world.increment_change_tick();
@ -459,7 +464,7 @@ where
} }
/// A [`SystemLabel`] that was automatically generated for a system on the basis of its `TypeId`. /// A [`SystemLabel`] that was automatically generated for a system on the basis of its `TypeId`.
pub struct SystemTypeIdLabel<T: 'static>(PhantomData<fn() -> T>); pub struct SystemTypeIdLabel<T: 'static>(pub(crate) PhantomData<fn() -> T>);
impl<T: 'static> SystemLabel for SystemTypeIdLabel<T> { impl<T: 'static> SystemLabel for SystemTypeIdLabel<T> {
#[inline] #[inline]

View File

@ -69,7 +69,8 @@
//! - [`()` (unit primitive type)](https://doc.rust-lang.org/stable/std/primitive.unit.html) //! - [`()` (unit primitive type)](https://doc.rust-lang.org/stable/std/primitive.unit.html)
mod commands; mod commands;
mod exclusive_system; mod exclusive_function_system;
mod exclusive_system_param;
mod function_system; mod function_system;
mod query; mod query;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
@ -78,7 +79,8 @@ mod system_chaining;
mod system_param; mod system_param;
pub use commands::*; pub use commands::*;
pub use exclusive_system::*; pub use exclusive_function_system::*;
pub use exclusive_system_param::*;
pub use function_system::*; pub use function_system::*;
pub use query::*; pub use query::*;
pub use system::*; pub use system::*;
@ -98,34 +100,6 @@ pub fn assert_is_system<In, Out, Params, S: IntoSystem<In, Out, Params>>(sys: S)
} }
} }
/// Ensure that a given function is an exclusive system
///
/// This should be used when writing doc examples,
/// to confirm that systems used in an example are
/// valid exclusive systems
///
/// Passing assert
/// ```
/// # use bevy_ecs::prelude::World;
/// # use bevy_ecs::system::assert_is_exclusive_system;
/// fn an_exclusive_system(_world: &mut World) {}
///
/// assert_is_exclusive_system(an_exclusive_system);
/// ```
///
/// Failing assert
/// ```compile_fail
/// # use bevy_ecs::prelude::World;
/// # use bevy_ecs::system::assert_is_exclusive_system;
/// fn not_an_exclusive_system(_world: &mut World, number: f32) {}
///
/// assert_is_exclusive_system(not_an_exclusive_system);
/// ```
pub fn assert_is_exclusive_system<Params, SystemType>(
_sys: impl IntoExclusiveSystem<Params, SystemType>,
) {
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::any::TypeId; use std::any::TypeId;
@ -142,8 +116,8 @@ mod tests {
query::{Added, Changed, Or, With, Without}, query::{Added, Changed, Or, With, Without},
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
system::{ system::{
Commands, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, Commands, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, RemovedComponents,
RemovedComponents, Res, ResMut, Resource, System, SystemState, Res, ResMut, Resource, System, SystemState,
}, },
world::{FromWorld, World}, world::{FromWorld, World},
}; };
@ -322,10 +296,7 @@ mod tests {
let mut update = SystemStage::parallel(); let mut update = SystemStage::parallel();
update.add_system(incr_e_on_flip); update.add_system(incr_e_on_flip);
schedule.add_stage(UpdateStage, update); schedule.add_stage(UpdateStage, update);
schedule.add_stage( schedule.add_stage(ClearTrackers, SystemStage::single(World::clear_trackers));
ClearTrackers,
SystemStage::single(World::clear_trackers.exclusive_system()),
);
schedule.run(&mut world); schedule.run(&mut world);
assert_eq!(world.resource::<Added>().0, 1); assert_eq!(world.resource::<Added>().0, 1);

View File

@ -32,6 +32,9 @@ pub trait System: Send + Sync + 'static {
/// Returns true if the system is [`Send`]. /// Returns true if the system is [`Send`].
fn is_send(&self) -> bool; fn is_send(&self) -> bool;
/// Returns true if the system must be run exclusively.
fn is_exclusive(&self) -> bool;
/// Runs the system with the given input in the world. Unlike [`System::run`], this function /// Runs the system with the given input in the world. Unlike [`System::run`], this function
/// takes a shared reference to [`World`] and may therefore break Rust's aliasing rules, making /// takes a shared reference to [`World`] and may therefore break Rust's aliasing rules, making
/// it unsafe to call. /// it unsafe to call.

View File

@ -73,6 +73,10 @@ impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem
self.system_a.is_send() && self.system_b.is_send() self.system_a.is_send() && self.system_b.is_send()
} }
fn is_exclusive(&self) -> bool {
self.system_a.is_exclusive() || self.system_b.is_exclusive()
}
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
let out = self.system_a.run_unsafe(input, world); let out = self.system_a.run_unsafe(input, world);
self.system_b.run_unsafe(out, world) self.system_b.run_unsafe(out, world)

View File

@ -680,7 +680,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
/// // .add_system(reset_to_system(my_config)) /// // .add_system(reset_to_system(my_config))
/// # assert_is_system(reset_to_system(Config(10))); /// # assert_is_system(reset_to_system(Config(10)));
/// ``` /// ```
pub struct Local<'a, T: FromWorld + Send + 'static>(&'a mut T); pub struct Local<'a, T: FromWorld + Send + 'static>(pub(crate) &'a mut T);
// SAFETY: Local only accesses internal state // SAFETY: Local only accesses internal state
unsafe impl<T: Send + 'static> ReadOnlySystemParamFetch for LocalState<T> {} unsafe impl<T: Send + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
@ -712,7 +712,7 @@ impl<'a, T: FromWorld + Send + Sync + 'static> DerefMut for Local<'a, T> {
/// The [`SystemParamState`] of [`Local<T>`]. /// The [`SystemParamState`] of [`Local<T>`].
#[doc(hidden)] #[doc(hidden)]
pub struct LocalState<T: Send + 'static>(SyncCell<T>); pub struct LocalState<T: Send + 'static>(pub(crate) SyncCell<T>);
impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> {
type Fetch = LocalState<T>; type Fetch = LocalState<T>;

View File

@ -2,7 +2,7 @@ mod converter;
mod gilrs_system; mod gilrs_system;
use bevy_app::{App, CoreStage, Plugin, StartupStage}; use bevy_app::{App, CoreStage, Plugin, StartupStage};
use bevy_ecs::schedule::ParallelSystemDescriptorCoercion; use bevy_ecs::schedule::IntoSystemDescriptor;
use bevy_input::InputSystem; use bevy_input::InputSystem;
use bevy_utils::tracing::error; use bevy_utils::tracing::error;
use gilrs::GilrsBuilder; use gilrs::GilrsBuilder;

View File

@ -6,7 +6,7 @@ pub mod mouse;
pub mod touch; pub mod touch;
pub use axis::*; pub use axis::*;
use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel}; use bevy_ecs::schedule::{IntoSystemDescriptor, SystemLabel};
pub use input::*; pub use input::*;
pub mod prelude { pub mod prelude {

View File

@ -142,7 +142,7 @@ impl Plugin for PbrPlugin {
// NOTE: Clusters need to have been added before update_clusters is run so // NOTE: Clusters need to have been added before update_clusters is run so
// add as an exclusive system // add as an exclusive system
add_clusters add_clusters
.exclusive_system() .at_start()
.label(SimulationLightSystems::AddClusters), .label(SimulationLightSystems::AddClusters),
) )
.add_system_to_stage( .add_system_to_stage(
@ -219,7 +219,7 @@ impl Plugin for PbrPlugin {
// this is added as an exclusive system because it contributes new views. it must run (and have Commands applied) // this is added as an exclusive system because it contributes new views. it must run (and have Commands applied)
// _before_ the `prepare_views()` system is run. ideally this becomes a normal system when "stageless" features come out // _before_ the `prepare_views()` system is run. ideally this becomes a normal system when "stageless" features come out
render::prepare_lights render::prepare_lights
.exclusive_system() .at_start()
.label(RenderLightSystems::PrepareLights), .label(RenderLightSystems::PrepareLights),
) )
.add_system_to_stage( .add_system_to_stage(

View File

@ -10,7 +10,7 @@ use bevy_ecs::{
entity::Entity, entity::Entity,
event::EventReader, event::EventReader,
prelude::World, prelude::World,
schedule::ParallelSystemDescriptorCoercion, schedule::IntoSystemDescriptor,
system::{ system::{
lifetimeless::{Read, SQuery, SRes}, lifetimeless::{Read, SQuery, SRes},
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem, Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,

View File

@ -57,7 +57,7 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
pub struct ExtractState<P: SystemParam> { pub struct ExtractState<P: SystemParam + 'static> {
state: SystemState<P>, state: SystemState<P>,
main_world_state: ResState<MainWorld>, main_world_state: ResState<MainWorld>,
} }

View File

@ -196,7 +196,7 @@ impl Plugin for RenderPlugin {
RenderStage::Render, RenderStage::Render,
SystemStage::parallel() SystemStage::parallel()
.with_system(PipelineCache::process_pipeline_queue_system) .with_system(PipelineCache::process_pipeline_queue_system)
.with_system(render_system.exclusive_system().at_end()), .with_system(render_system.at_end()),
) )
.add_stage(RenderStage::Cleanup, SystemStage::parallel()) .add_stage(RenderStage::Cleanup, SystemStage::parallel())
.init_resource::<RenderGraph>() .init_resource::<RenderGraph>()

View File

@ -151,7 +151,7 @@ impl<P: PhaseItem> DrawFunctions<P> {
pub trait RenderCommand<P: PhaseItem> { pub trait RenderCommand<P: PhaseItem> {
/// Specifies all ECS data required by [`RenderCommand::render`]. /// Specifies all ECS data required by [`RenderCommand::render`].
/// All parameters have to be read only. /// All parameters have to be read only.
type Param: SystemParam; type Param: SystemParam + 'static;
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`]. /// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
fn render<'w>( fn render<'w>(
@ -168,7 +168,7 @@ pub enum RenderCommandResult {
} }
pub trait EntityRenderCommand { pub trait EntityRenderCommand {
type Param: SystemParam; type Param: SystemParam + 'static;
fn render<'w>( fn render<'w>(
view: Entity, view: Entity,
item: Entity, item: Entity,
@ -231,7 +231,10 @@ pub enum BatchResult {
IncompatibleItems, IncompatibleItems,
} }
impl<P: EntityPhaseItem, E: EntityRenderCommand> RenderCommand<P> for E { impl<P: EntityPhaseItem, E: EntityRenderCommand> RenderCommand<P> for E
where
E::Param: 'static,
{
type Param = E::Param; type Param = E::Param;
#[inline] #[inline]
@ -292,7 +295,7 @@ all_tuples!(render_command_tuple_impl, 0, 15, C);
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function. /// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
/// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command. /// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command.
pub struct RenderCommandState<P: PhaseItem, C: RenderCommand<P>> { pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
state: SystemState<C::Param>, state: SystemState<C::Param>,
} }

View File

@ -18,7 +18,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::AddAsset; use bevy_asset::AddAsset;
use bevy_ecs::{schedule::ExclusiveSystemDescriptorCoercion, system::IntoExclusiveSystem}; use bevy_ecs::prelude::*;
#[derive(Default)] #[derive(Default)]
pub struct ScenePlugin; pub struct ScenePlugin;
@ -29,10 +29,7 @@ impl Plugin for ScenePlugin {
.add_asset::<Scene>() .add_asset::<Scene>()
.init_asset_loader::<SceneLoader>() .init_asset_loader::<SceneLoader>()
.init_resource::<SceneSpawner>() .init_resource::<SceneSpawner>()
.add_system_to_stage( .add_system_to_stage(CoreStage::PreUpdate, scene_spawner_system.at_end())
CoreStage::PreUpdate,
scene_spawner_system.exclusive_system().at_end(),
)
// Systems `*_bundle_spawner` must run before `scene_spawner_system` // Systems `*_bundle_spawner` must run before `scene_spawner_system`
.add_system_to_stage(CoreStage::PreUpdate, scene_spawner); .add_system_to_stage(CoreStage::PreUpdate, scene_spawner);
} }

View File

@ -29,7 +29,7 @@ pub use texture_atlas_builder::*;
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{AddAsset, Assets, HandleUntyped}; use bevy_asset::{AddAsset, Assets, HandleUntyped};
use bevy_core_pipeline::core_2d::Transparent2d; use bevy_core_pipeline::core_2d::Transparent2d;
use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel}; use bevy_ecs::schedule::{IntoSystemDescriptor, SystemLabel};
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render::{ use bevy_render::{
render_phase::AddRenderCommand, render_phase::AddRenderCommand,

View File

@ -6,7 +6,7 @@ use bevy_ecs::{
entity::Entity, entity::Entity,
event::EventReader, event::EventReader,
prelude::{Bundle, World}, prelude::{Bundle, World},
schedule::ParallelSystemDescriptorCoercion, schedule::IntoSystemDescriptor,
system::{ system::{
lifetimeless::{Read, SQuery, SRes}, lifetimeless::{Read, SQuery, SRes},
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem, Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,

View File

@ -28,7 +28,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::AddAsset; use bevy_asset::AddAsset;
use bevy_ecs::{schedule::ParallelSystemDescriptorCoercion, system::Resource}; use bevy_ecs::{schedule::IntoSystemDescriptor, system::Resource};
use bevy_render::{RenderApp, RenderStage}; use bevy_render::{RenderApp, RenderStage};
use bevy_sprite::SpriteSystem; use bevy_sprite::SpriteSystem;
use bevy_window::ModifiesWindows; use bevy_window::ModifiesWindows;

View File

@ -189,6 +189,10 @@ impl System for FixedTimestep {
self.internal_system.is_send() self.internal_system.is_send()
} }
fn is_exclusive(&self) -> bool {
false
}
unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun { unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun {
// SAFETY: this system inherits the internal system's component access and archetype component // SAFETY: this system inherits the internal system's component access and archetype component
// access, which means the caller has ensured running the internal system is safe // access, which means the caller has ensured running the internal system is safe

View File

@ -40,10 +40,7 @@ impl Plugin for TimePlugin {
.register_type::<Stopwatch>() .register_type::<Stopwatch>()
// time system is added as an "exclusive system" to ensure it runs before other systems // time system is added as an "exclusive system" to ensure it runs before other systems
// in CoreStage::First // in CoreStage::First
.add_system_to_stage( .add_system_to_stage(CoreStage::First, time_system.at_start().label(TimeSystem));
CoreStage::First,
time_system.exclusive_system().at_start().label(TimeSystem),
);
} }
} }

View File

@ -27,7 +27,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_ecs::{ use bevy_ecs::{
schedule::{ParallelSystemDescriptorCoercion, SystemLabel}, schedule::{IntoSystemDescriptor, SystemLabel},
system::Resource, system::Resource,
}; };
use bevy_input::InputSystem; use bevy_input::InputSystem;

View File

@ -25,7 +25,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_ecs::{ use bevy_ecs::{
event::Events, event::Events,
schedule::{ParallelSystemDescriptorCoercion, SystemLabel}, schedule::{IntoSystemDescriptor, SystemLabel},
system::Resource, system::Resource,
}; };

View File

@ -316,13 +316,7 @@ fn main() {
MyStage::BeforeRound, MyStage::BeforeRound,
new_player_system.after(new_round_system), new_player_system.after(new_round_system),
) )
.add_system_to_stage( .add_system_to_stage(MyStage::BeforeRound, exclusive_player_system)
MyStage::BeforeRound,
// Systems which take `&mut World` as an argument must call `.exclusive_system()`.
// The following will not compile.
//.add_system_to_stage(MyStage::BeforeRound, exclusive_player_system)
exclusive_player_system.exclusive_system(),
)
.add_system_to_stage(MyStage::AfterRound, score_check_system) .add_system_to_stage(MyStage::AfterRound, score_check_system)
.add_system_to_stage( .add_system_to_stage(
// We can ensure that `game_over_system` runs after `score_check_system` using explicit ordering // We can ensure that `game_over_system` runs after `score_check_system` using explicit ordering

View File

@ -16,7 +16,7 @@ fn main() {
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.register_type::<ComponentA>() .register_type::<ComponentA>()
.register_type::<ComponentB>() .register_type::<ComponentB>()
.add_startup_system(save_scene_system.exclusive_system()) .add_startup_system(save_scene_system)
.add_startup_system(load_scene_system) .add_startup_system(load_scene_system)
.add_startup_system(infotext_system) .add_startup_system(infotext_system)
.add_system(log_system) .add_system(log_system)