mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
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:
parent
92e78a4bc5
commit
dc3f801239
37 changed files with 756 additions and 856 deletions
|
@ -1,10 +1,4 @@
|
|||
use bevy_ecs::{
|
||||
component::Component,
|
||||
prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion},
|
||||
schedule::{RunCriteriaLabel, ShouldRun, Stage, SystemStage},
|
||||
system::Query,
|
||||
world::World,
|
||||
};
|
||||
use bevy_ecs::{prelude::*, schedule::ShouldRun};
|
||||
use criterion::Criterion;
|
||||
|
||||
fn run_stage(stage: &mut SystemStage, world: &mut World) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use bevy_ecs::{
|
|||
entity::Entity,
|
||||
prelude::Component,
|
||||
reflect::ReflectComponent,
|
||||
schedule::ParallelSystemDescriptorCoercion,
|
||||
schedule::IntoSystemDescriptor,
|
||||
system::{Query, Res},
|
||||
};
|
||||
use bevy_hierarchy::Children;
|
||||
|
|
|
@ -3,7 +3,7 @@ pub use bevy_derive::AppLabel;
|
|||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
event::{Event, Events},
|
||||
prelude::{FromWorld, IntoExclusiveSystem},
|
||||
prelude::FromWorld,
|
||||
schedule::{
|
||||
IntoSystemDescriptor, Schedule, ShouldRun, Stage, StageLabel, State, StateData, SystemSet,
|
||||
SystemStage,
|
||||
|
@ -84,7 +84,7 @@ impl Default for App {
|
|||
|
||||
app.add_default_stages()
|
||||
.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")]
|
||||
{
|
||||
|
|
|
@ -79,7 +79,7 @@ type IdCursor = isize;
|
|||
/// }
|
||||
/// #
|
||||
/// # 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.
|
||||
|
|
|
@ -34,14 +34,13 @@ pub mod prelude {
|
|||
event::{EventReader, EventWriter, Events},
|
||||
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
|
||||
schedule::{
|
||||
ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria,
|
||||
RunCriteriaDescriptorCoercion, RunCriteriaLabel, Schedule, Stage, StageLabel, State,
|
||||
SystemLabel, SystemSet, SystemStage,
|
||||
IntoSystemDescriptor, RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel,
|
||||
Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage,
|
||||
},
|
||||
system::{
|
||||
adapter as system_adapter, Commands, In, IntoChainSystem, IntoExclusiveSystem,
|
||||
IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query,
|
||||
RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction,
|
||||
adapter as system_adapter, Commands, In, IntoChainSystem, IntoSystem, Local, NonSend,
|
||||
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut,
|
||||
Resource, System, SystemParamFunction,
|
||||
},
|
||||
world::{FromWorld, Mut, World},
|
||||
};
|
||||
|
|
|
@ -45,7 +45,6 @@ impl SystemOrderAmbiguity {
|
|||
stage: &SystemStage,
|
||||
world: &World,
|
||||
) -> Self {
|
||||
use crate::schedule::graph_utils::GraphNode;
|
||||
use SystemStageSegment::*;
|
||||
|
||||
// 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,
|
||||
/// along with specific components that have triggered the warning.
|
||||
/// 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_dependants = Vec::<FixedBitSet>::with_capacity(systems.len());
|
||||
for (index, container) in systems.iter().enumerate() {
|
||||
|
@ -263,15 +262,17 @@ fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize, Vec<
|
|||
// .take(index_a)
|
||||
{
|
||||
if !processed.contains(index_b) {
|
||||
let a_access = systems[index_a].component_access();
|
||||
let b_access = systems[index_b].component_access();
|
||||
if let (Some(a), Some(b)) = (a_access, b_access) {
|
||||
let conflicts = a.get_conflicts(b);
|
||||
let system_a = &systems[index_a];
|
||||
let system_b = &systems[index_b];
|
||||
if system_a.is_exclusive() || system_b.is_exclusive() {
|
||||
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() {
|
||||
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();
|
||||
test_stage
|
||||
// All 3 of these conflict with each other
|
||||
.add_system(write_world_system.exclusive_system())
|
||||
.add_system(write_world_system.exclusive_system().at_end())
|
||||
.add_system(res_system.exclusive_system())
|
||||
.add_system(write_world_system)
|
||||
.add_system(write_world_system.at_end())
|
||||
.add_system(res_system.at_start())
|
||||
// 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.exclusive_system().before_commands());
|
||||
.add_system(write_world_system.at_start())
|
||||
.add_system(write_world_system.before_commands());
|
||||
|
||||
test_stage.run(&mut world);
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{schedule::ParallelSystemContainer, world::World};
|
||||
use crate::{schedule::SystemContainer, world::World};
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
|
||||
pub trait ParallelSystemExecutor: Downcast + Send + Sync {
|
||||
/// 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);
|
||||
|
@ -14,9 +14,9 @@ impl_downcast!(ParallelSystemExecutor);
|
|||
pub struct 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 {
|
||||
if system.should_run() {
|
||||
#[cfg(feature = "trace")]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
archetype::ArchetypeComponentId,
|
||||
query::Access,
|
||||
schedule::{ParallelSystemContainer, ParallelSystemExecutor},
|
||||
schedule::{ParallelSystemExecutor, SystemContainer},
|
||||
world::World,
|
||||
};
|
||||
use async_channel::{Receiver, Sender};
|
||||
|
@ -77,7 +77,7 @@ impl Default 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.queued.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)]
|
||||
if self.events_sender.is_none() {
|
||||
let (sender, receiver) = async_channel::unbounded::<SchedulingEvent>();
|
||||
|
@ -167,7 +167,7 @@ impl ParallelExecutor {
|
|||
fn prepare_systems<'scope>(
|
||||
&mut self,
|
||||
scope: &mut Scope<'scope, ()>,
|
||||
systems: &'scope mut [ParallelSystemContainer],
|
||||
systems: &'scope mut [SystemContainer],
|
||||
world: &'scope World,
|
||||
) {
|
||||
// These are used as a part of a unit test.
|
||||
|
|
|
@ -5,11 +5,10 @@ use crate::{
|
|||
prelude::IntoSystem,
|
||||
schedule::{
|
||||
graph_utils::{self, DependencyGraphError},
|
||||
BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveSystemContainer, GraphNode,
|
||||
InsertionPoint, ParallelExecutor, ParallelSystemContainer, ParallelSystemExecutor,
|
||||
RunCriteriaContainer, RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel,
|
||||
RunCriteriaInner, RunCriteriaLabelId, ShouldRun, SingleThreadedExecutor, SystemContainer,
|
||||
SystemDescriptor, SystemLabelId, SystemSet,
|
||||
BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveInsertionPoint, GraphNode,
|
||||
ParallelExecutor, ParallelSystemExecutor, RunCriteriaContainer, RunCriteriaDescriptor,
|
||||
RunCriteriaDescriptorOrLabel, RunCriteriaInner, RunCriteriaLabelId, ShouldRun,
|
||||
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemLabelId, SystemSet,
|
||||
},
|
||||
world::{World, WorldId},
|
||||
};
|
||||
|
@ -62,14 +61,14 @@ pub struct SystemStage {
|
|||
/// Topologically sorted run criteria of systems.
|
||||
run_criteria: Vec<RunCriteriaContainer>,
|
||||
/// 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
|
||||
/// 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.
|
||||
pub(super) exclusive_at_end: Vec<ExclusiveSystemContainer>,
|
||||
pub(super) exclusive_at_end: Vec<SystemContainer>,
|
||||
/// 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.
|
||||
pub(super) systems_modified: bool,
|
||||
/// Determines if the stage's executor was changed.
|
||||
|
@ -156,63 +155,63 @@ impl SystemStage {
|
|||
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;
|
||||
match system {
|
||||
SystemDescriptor::Exclusive(mut descriptor) => {
|
||||
let insertion_point = descriptor.insertion_point;
|
||||
let criteria = descriptor.run_criteria.take();
|
||||
let mut container = ExclusiveSystemContainer::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;
|
||||
}
|
||||
if let Some(insertion_point) = descriptor.exclusive_insertion_point {
|
||||
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);
|
||||
}
|
||||
match insertion_point {
|
||||
InsertionPoint::AtStart => {
|
||||
let index = self.exclusive_at_start.len();
|
||||
self.uninitialized_at_start.push(index);
|
||||
self.exclusive_at_start.push(container);
|
||||
}
|
||||
InsertionPoint::BeforeCommands => {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
SystemDescriptor::Parallel(mut descriptor) => {
|
||||
let criteria = descriptor.run_criteria.take();
|
||||
let mut container = ParallelSystemContainer::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;
|
||||
}
|
||||
match insertion_point {
|
||||
ExclusiveInsertionPoint::AtStart => {
|
||||
let index = self.exclusive_at_start.len();
|
||||
self.uninitialized_at_start.push(index);
|
||||
self.exclusive_at_start.push(container);
|
||||
}
|
||||
ExclusiveInsertionPoint::BeforeCommands => {
|
||||
let index = self.exclusive_before_commands.len();
|
||||
self.uninitialized_before_commands.push(index);
|
||||
self.exclusive_before_commands.push(container);
|
||||
}
|
||||
ExclusiveInsertionPoint::AtEnd => {
|
||||
let index = self.exclusive_at_end.len();
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn exclusive_at_start_systems(&self) -> &[impl SystemContainer] {
|
||||
pub fn exclusive_at_start_systems(&self) -> &[SystemContainer] {
|
||||
&self.exclusive_at_start
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn exclusive_at_end_systems(&self) -> &[impl SystemContainer] {
|
||||
pub fn exclusive_at_end_systems(&self) -> &[SystemContainer] {
|
||||
&self.exclusive_at_end
|
||||
}
|
||||
|
||||
|
@ -255,7 +254,7 @@ impl SystemStage {
|
|||
/// before the application of their command buffers.
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
|
||||
|
@ -270,17 +269,12 @@ impl SystemStage {
|
|||
let (run_criteria, mut systems) = system_set.bake();
|
||||
let set_run_criteria_index = run_criteria.and_then(|criteria| {
|
||||
// validate that no systems have criteria
|
||||
for system in &mut systems {
|
||||
if let Some(name) = match system {
|
||||
SystemDescriptor::Exclusive(descriptor) => descriptor
|
||||
.run_criteria
|
||||
.is_some()
|
||||
.then(|| descriptor.system.name()),
|
||||
SystemDescriptor::Parallel(descriptor) => descriptor
|
||||
.run_criteria
|
||||
.is_some()
|
||||
.then(|| descriptor.system.name()),
|
||||
} {
|
||||
for descriptor in &mut systems {
|
||||
if let Some(name) = descriptor
|
||||
.run_criteria
|
||||
.is_some()
|
||||
.then(|| descriptor.system.name())
|
||||
{
|
||||
panic!(
|
||||
"The system {} has a run criteria, but its `SystemSet` also has a run \
|
||||
criteria. This is not supported. Consider moving the system into a \
|
||||
|
@ -295,16 +289,7 @@ impl SystemStage {
|
|||
}
|
||||
RunCriteriaDescriptorOrLabel::Label(label) => {
|
||||
for system in &mut systems {
|
||||
match system {
|
||||
SystemDescriptor::Exclusive(descriptor) => {
|
||||
descriptor.run_criteria =
|
||||
Some(RunCriteriaDescriptorOrLabel::Label(label));
|
||||
}
|
||||
SystemDescriptor::Parallel(descriptor) => {
|
||||
descriptor.run_criteria =
|
||||
Some(RunCriteriaDescriptorOrLabel::Label(label));
|
||||
}
|
||||
}
|
||||
system.run_criteria = Some(RunCriteriaDescriptorOrLabel::Label(label));
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -503,8 +488,7 @@ impl SystemStage {
|
|||
fn check_uses_resource(&self, resource_id: ComponentId, world: &World) {
|
||||
debug_assert!(!self.systems_modified);
|
||||
for system in &self.parallel {
|
||||
let access = system.component_access().unwrap();
|
||||
if !access.has_read(resource_id) {
|
||||
if !system.component_access().has_read(resource_id) {
|
||||
let component_name = world.components().get_info(resource_id).unwrap().name();
|
||||
warn!(
|
||||
"System {} doesn't access resource {component_name}, despite being required to",
|
||||
|
@ -578,8 +562,8 @@ impl SystemStage {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_run_criteria_indices<T: SystemContainer>(
|
||||
systems: &mut [T],
|
||||
fn update_run_criteria_indices(
|
||||
systems: &mut [SystemContainer],
|
||||
order_inverted: &[(usize, &usize)],
|
||||
) {
|
||||
for system in systems {
|
||||
|
@ -605,7 +589,7 @@ impl SystemStage {
|
|||
/// Sorts given system containers topologically, populates their resolved dependencies
|
||||
/// and run criteria.
|
||||
fn process_systems(
|
||||
systems: &mut Vec<impl SystemContainer>,
|
||||
systems: &mut Vec<SystemContainer>,
|
||||
run_criteria_labels: &HashMap<RunCriteriaLabelId, usize>,
|
||||
) -> Result<(), DependencyGraphError<HashSet<SystemLabelId>>> {
|
||||
let mut graph = graph_utils::build_dependency_graph(systems);
|
||||
|
@ -701,7 +685,7 @@ impl Stage for SystemStage {
|
|||
run_system_loop = false;
|
||||
|
||||
fn should_run(
|
||||
container: &impl SystemContainer,
|
||||
container: &SystemContainer,
|
||||
run_criteria: &[RunCriteriaContainer],
|
||||
default: ShouldRun,
|
||||
) -> bool {
|
||||
|
@ -717,13 +701,24 @@ impl Stage for SystemStage {
|
|||
// Run systems that want to be at the start of stage.
|
||||
for container in &mut self.exclusive_at_start {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.entered();
|
||||
container.system_mut().run(world);
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.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.
|
||||
for container in &mut self.exclusive_before_commands {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.entered();
|
||||
container.system_mut().run(world);
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.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.
|
||||
for container in &mut self.exclusive_at_end {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.entered();
|
||||
container.system_mut().run(world);
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _system_span = bevy_utils::tracing::info_span!(
|
||||
"exclusive_system",
|
||||
name = &*container.name()
|
||||
)
|
||||
.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::{
|
||||
schedule::{
|
||||
ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria,
|
||||
RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage, SystemLabel,
|
||||
SystemSet, SystemStage,
|
||||
IntoSystemDescriptor, RunCriteria, RunCriteriaDescriptorCoercion, ShouldRun,
|
||||
SingleThreadedExecutor, Stage, SystemLabel, SystemSet, SystemStage,
|
||||
},
|
||||
system::{In, IntoExclusiveSystem, Local, Query, ResMut},
|
||||
system::{In, Local, Query, ResMut},
|
||||
world::World,
|
||||
};
|
||||
|
||||
|
@ -868,10 +884,10 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
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_exclusive(2).exclusive_system().before_commands())
|
||||
.with_system(make_exclusive(3).exclusive_system().at_end());
|
||||
.with_system(make_exclusive(2).before_commands())
|
||||
.with_system(make_exclusive(3).at_end());
|
||||
stage.run(&mut world);
|
||||
assert_eq!(world.resource_mut::<EntityCount>().0, vec![0, 1, 2, 3]);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -883,10 +899,10 @@ mod tests {
|
|||
|
||||
world.resource_mut::<EntityCount>().0.clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().before_commands())
|
||||
.with_system(make_exclusive(3).exclusive_system().at_end())
|
||||
.with_system(make_exclusive(2).before_commands())
|
||||
.with_system(make_exclusive(3).at_end())
|
||||
.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);
|
||||
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -898,10 +914,10 @@ mod tests {
|
|||
|
||||
world.resource_mut::<EntityCount>().0.clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel(2).exclusive_system().before_commands())
|
||||
.with_system(make_parallel(3).exclusive_system().at_end())
|
||||
.with_system(make_parallel(2).before_commands())
|
||||
.with_system(make_parallel(3).at_end())
|
||||
.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);
|
||||
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -930,9 +946,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(1).exclusive_system().label(L1).after(L0))
|
||||
.with_system(make_exclusive(2).exclusive_system().after(L1))
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0));
|
||||
.with_system(make_exclusive(1).label(L1).after(L0))
|
||||
.with_system(make_exclusive(2).after(L1))
|
||||
.with_system(make_exclusive(0).label(L0));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -944,9 +960,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(1).exclusive_system().label(L1).before(L2))
|
||||
.with_system(make_exclusive(2).exclusive_system().label(L2))
|
||||
.with_system(make_exclusive(0).exclusive_system().before(L1));
|
||||
.with_system(make_exclusive(1).label(L1).before(L2))
|
||||
.with_system(make_exclusive(2).label(L2))
|
||||
.with_system(make_exclusive(0).before(L1));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -958,11 +974,11 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().label(L2))
|
||||
.with_system(make_exclusive(1).exclusive_system().after(L0).before(L2))
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0))
|
||||
.with_system(make_exclusive(4).exclusive_system().label(L4))
|
||||
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4));
|
||||
.with_system(make_exclusive(2).label(L2))
|
||||
.with_system(make_exclusive(1).after(L0).before(L2))
|
||||
.with_system(make_exclusive(0).label(L0))
|
||||
.with_system(make_exclusive(4).label(L4))
|
||||
.with_system(make_exclusive(3).after(L2).before(L4));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -977,9 +993,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(1).exclusive_system().label(First).after(L0))
|
||||
.with_system(make_exclusive(2).exclusive_system().after(First))
|
||||
.with_system(make_exclusive(0).exclusive_system().label(First).label(L0));
|
||||
.with_system(make_exclusive(1).label(First).after(L0))
|
||||
.with_system(make_exclusive(2).after(First))
|
||||
.with_system(make_exclusive(0).label(First).label(L0));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -987,11 +1003,11 @@ mod tests {
|
|||
|
||||
world.resource_mut::<EntityCount>().0.clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().after(L01).label(L2))
|
||||
.with_system(make_exclusive(1).exclusive_system().label(L01).after(L0))
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L01).label(L0))
|
||||
.with_system(make_exclusive(4).exclusive_system().label(L4))
|
||||
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4));
|
||||
.with_system(make_exclusive(2).after(L01).label(L2))
|
||||
.with_system(make_exclusive(1).label(L01).after(L0))
|
||||
.with_system(make_exclusive(0).label(L01).label(L0))
|
||||
.with_system(make_exclusive(4).label(L4))
|
||||
.with_system(make_exclusive(3).after(L2).before(L4));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1002,17 +1018,11 @@ mod tests {
|
|||
|
||||
world.resource_mut::<EntityCount>().0.clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().label(L234).label(L2))
|
||||
.with_system(make_exclusive(1).exclusive_system().before(L234).after(L0))
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0))
|
||||
.with_system(make_exclusive(4).exclusive_system().label(L234).label(L4))
|
||||
.with_system(
|
||||
make_exclusive(3)
|
||||
.exclusive_system()
|
||||
.label(L234)
|
||||
.after(L2)
|
||||
.before(L4),
|
||||
);
|
||||
.with_system(make_exclusive(2).label(L234).label(L2))
|
||||
.with_system(make_exclusive(1).before(L234).after(L0))
|
||||
.with_system(make_exclusive(0).label(L0))
|
||||
.with_system(make_exclusive(4).label(L234).label(L4))
|
||||
.with_system(make_exclusive(3).label(L234).after(L2).before(L4));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1027,31 +1037,11 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
make_exclusive(2)
|
||||
.exclusive_system()
|
||||
.label(L2)
|
||||
.after(L1)
|
||||
.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),
|
||||
);
|
||||
.with_system(make_exclusive(2).label(L2).after(L1).before(L3).before(L3))
|
||||
.with_system(make_exclusive(1).label(L1).after(L0).after(L0).before(L2))
|
||||
.with_system(make_exclusive(0).label(L0).before(L1))
|
||||
.with_system(make_exclusive(4).label(L4).after(L3))
|
||||
.with_system(make_exclusive(3).label(L3).after(L2).before(L4));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1066,14 +1056,14 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().label(L2))
|
||||
.with_system(make_exclusive(2).label(L2))
|
||||
.with_system_set(
|
||||
SystemSet::new()
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0))
|
||||
.with_system(make_exclusive(4).exclusive_system().label(L4))
|
||||
.with_system(make_exclusive(3).exclusive_system().after(L2).before(L4)),
|
||||
.with_system(make_exclusive(0).label(L0))
|
||||
.with_system(make_exclusive(4).label(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.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1088,13 +1078,13 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(0).exclusive_system().before(L1))
|
||||
.with_system(make_exclusive(0).before(L1))
|
||||
.with_system_set(
|
||||
SystemSet::new()
|
||||
.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.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -1111,8 +1101,7 @@ mod tests {
|
|||
fn exclusive_cycle_1() {
|
||||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0).after(L0));
|
||||
let mut stage = SystemStage::parallel().with_system(make_exclusive(0).label(L0).after(L0));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1122,8 +1111,8 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0).after(L1))
|
||||
.with_system(make_exclusive(1).exclusive_system().label(L1).after(L0));
|
||||
.with_system(make_exclusive(0).label(L0).after(L1))
|
||||
.with_system(make_exclusive(1).label(L1).after(L0));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1133,9 +1122,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.init_resource::<EntityCount>();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(0).exclusive_system().label(L0))
|
||||
.with_system(make_exclusive(1).exclusive_system().after(L0).before(L2))
|
||||
.with_system(make_exclusive(2).exclusive_system().label(L2).before(L0));
|
||||
.with_system(make_exclusive(0).label(L0))
|
||||
.with_system(make_exclusive(1).after(L0).before(L2))
|
||||
.with_system(make_exclusive(2).label(L2).before(L0));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,117 +1,26 @@
|
|||
use crate::{
|
||||
component::ComponentId,
|
||||
query::Access,
|
||||
schedule::{
|
||||
ExclusiveSystemDescriptor, GraphNode, ParallelSystemDescriptor, RunCriteriaLabelId,
|
||||
SystemLabelId,
|
||||
},
|
||||
system::{ExclusiveSystem, System},
|
||||
schedule::{GraphNode, RunCriteriaLabelId, SystemDescriptor, SystemLabelId},
|
||||
system::System,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// System metadata like its name, labels, order requirements and component access.
|
||||
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 {
|
||||
pub struct SystemContainer {
|
||||
system: Box<dyn System<In = (), Out = ()>>,
|
||||
pub(crate) run_criteria_index: Option<usize>,
|
||||
pub(crate) run_criteria_label: Option<RunCriteriaLabelId>,
|
||||
pub(crate) should_run: bool,
|
||||
is_exclusive: bool,
|
||||
dependencies: Vec<usize>,
|
||||
labels: Vec<SystemLabelId>,
|
||||
before: Vec<SystemLabelId>,
|
||||
after: Vec<SystemLabelId>,
|
||||
}
|
||||
|
||||
impl ParallelSystemContainer {
|
||||
pub(crate) fn from_descriptor(descriptor: ParallelSystemDescriptor) -> Self {
|
||||
ParallelSystemContainer {
|
||||
impl SystemContainer {
|
||||
pub(crate) fn from_descriptor(descriptor: SystemDescriptor) -> Self {
|
||||
SystemContainer {
|
||||
system: descriptor.system,
|
||||
should_run: false,
|
||||
run_criteria_index: None,
|
||||
|
@ -120,6 +29,7 @@ impl ParallelSystemContainer {
|
|||
labels: descriptor.labels,
|
||||
before: descriptor.before,
|
||||
after: descriptor.after,
|
||||
is_exclusive: descriptor.exclusive_insertion_point.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,9 +52,34 @@ impl ParallelSystemContainer {
|
|||
pub fn dependencies(&self) -> &[usize] {
|
||||
&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;
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
|
@ -163,30 +98,3 @@ impl GraphNode for ParallelSystemContainer {
|
|||
&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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use crate::{
|
||||
schedule::{IntoRunCriteria, RunCriteriaDescriptorOrLabel, SystemLabel, SystemLabelId},
|
||||
system::{
|
||||
AsSystemLabel, BoxedSystem, ExclusiveSystem, ExclusiveSystemCoerced, ExclusiveSystemFn,
|
||||
IntoSystem,
|
||||
},
|
||||
system::{AsSystemLabel, BoxedSystem, IntoSystem},
|
||||
};
|
||||
|
||||
/// Encapsulates a system and information on when it run in a `SystemStage`.
|
||||
|
@ -32,301 +29,188 @@ use crate::{
|
|||
/// SystemStage::parallel()
|
||||
/// .with_system(do_something.label(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 {
|
||||
Parallel(ParallelSystemDescriptor),
|
||||
Exclusive(ExclusiveSystemDescriptor),
|
||||
pub struct SystemDescriptor {
|
||||
pub(crate) system: BoxedSystem<(), ()>,
|
||||
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> {
|
||||
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
|
||||
/// run criteria defined elsewhere.
|
||||
fn with_run_criteria<Marker>(
|
||||
self,
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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>(
|
||||
mut self,
|
||||
run_criteria: impl IntoRunCriteria<Marker>,
|
||||
) -> ParallelSystemDescriptor {
|
||||
) -> SystemDescriptor {
|
||||
self.run_criteria = Some(run_criteria.into());
|
||||
self
|
||||
}
|
||||
|
||||
fn label(mut self, label: impl SystemLabel) -> ParallelSystemDescriptor {
|
||||
fn label(mut self, label: impl SystemLabel) -> SystemDescriptor {
|
||||
self.labels.push(label.as_label());
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
S: IntoSystem<(), (), Params>,
|
||||
{
|
||||
fn with_run_criteria<Marker>(
|
||||
self,
|
||||
run_criteria: impl IntoRunCriteria<Marker>,
|
||||
) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(Box::new(IntoSystem::into_system(self)))
|
||||
) -> SystemDescriptor {
|
||||
SystemDescriptor::new(Box::new(IntoSystem::into_system(self)))
|
||||
.with_run_criteria(run_criteria)
|
||||
}
|
||||
|
||||
fn label(self, label: impl SystemLabel) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).label(label)
|
||||
fn label(self, label: impl SystemLabel) -> SystemDescriptor {
|
||||
SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).label(label)
|
||||
}
|
||||
|
||||
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).before(label)
|
||||
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
|
||||
SystemDescriptor::new(Box::new(IntoSystem::into_system(self))).before(label)
|
||||
}
|
||||
|
||||
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(Box::new(IntoSystem::into_system(self))).after(label)
|
||||
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
|
||||
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>(
|
||||
self,
|
||||
run_criteria: impl IntoRunCriteria<Marker>,
|
||||
) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(self).with_run_criteria(run_criteria)
|
||||
) -> SystemDescriptor {
|
||||
SystemDescriptor::new(self).with_run_criteria(run_criteria)
|
||||
}
|
||||
|
||||
fn label(self, label: impl SystemLabel) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(self).label(label)
|
||||
fn label(self, label: impl SystemLabel) -> SystemDescriptor {
|
||||
SystemDescriptor::new(self).label(label)
|
||||
}
|
||||
|
||||
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(self).before(label)
|
||||
fn before<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
|
||||
SystemDescriptor::new(self).before(label)
|
||||
}
|
||||
|
||||
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
|
||||
new_parallel_descriptor(self).after(label)
|
||||
fn after<Marker>(self, label: impl AsSystemLabel<Marker>) -> SystemDescriptor {
|
||||
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)]
|
||||
pub(crate) enum InsertionPoint {
|
||||
pub(crate) enum ExclusiveInsertionPoint {
|
||||
AtStart,
|
||||
BeforeCommands,
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,18 +107,9 @@ impl SystemSet {
|
|||
after,
|
||||
} = self;
|
||||
for descriptor in &mut systems {
|
||||
match descriptor {
|
||||
SystemDescriptor::Parallel(descriptor) => {
|
||||
descriptor.labels.extend(labels.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());
|
||||
}
|
||||
}
|
||||
descriptor.labels.extend(labels.iter().cloned());
|
||||
descriptor.before.extend(before.iter().cloned());
|
||||
descriptor.after.extend(after.iter().cloned());
|
||||
}
|
||||
(run_criteria, systems)
|
||||
}
|
||||
|
|
203
crates/bevy_ecs/src/system/exclusive_function_system.rs
Normal file
203
crates/bevy_ecs/src/system/exclusive_function_system.rs
Normal 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);
|
|
@ -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]);
|
||||
}
|
||||
}
|
130
crates/bevy_ecs/src/system/exclusive_system_param.rs
Normal file
130
crates/bevy_ecs/src/system/exclusive_system_param.rs
Normal 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);
|
|
@ -27,7 +27,7 @@ pub struct SystemMeta {
|
|||
}
|
||||
|
||||
impl SystemMeta {
|
||||
fn new<T>() -> Self {
|
||||
pub(crate) fn new<T>() -> Self {
|
||||
Self {
|
||||
name: std::any::type_name::<T>().into(),
|
||||
archetype_component_access: Access::default(),
|
||||
|
@ -112,8 +112,8 @@ impl SystemMeta {
|
|||
///
|
||||
/// struct MyEvent;
|
||||
/// #[derive(Resource)]
|
||||
/// struct CachedSystemState<'w, 's>{
|
||||
/// event_state: SystemState<EventReader<'w, 's, MyEvent>>
|
||||
/// struct CachedSystemState {
|
||||
/// event_state: SystemState<EventReader<'static, 'static, MyEvent>>
|
||||
/// }
|
||||
///
|
||||
/// // 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,
|
||||
param_state: <Param as SystemParam>::Fetch,
|
||||
world_id: WorldId,
|
||||
|
@ -387,6 +387,11 @@ where
|
|||
self.system_meta.is_send
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_exclusive(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
|
||||
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`.
|
||||
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> {
|
||||
#[inline]
|
||||
|
|
|
@ -69,7 +69,8 @@
|
|||
//! - [`()` (unit primitive type)](https://doc.rust-lang.org/stable/std/primitive.unit.html)
|
||||
|
||||
mod commands;
|
||||
mod exclusive_system;
|
||||
mod exclusive_function_system;
|
||||
mod exclusive_system_param;
|
||||
mod function_system;
|
||||
mod query;
|
||||
#[allow(clippy::module_inception)]
|
||||
|
@ -78,7 +79,8 @@ mod system_chaining;
|
|||
mod system_param;
|
||||
|
||||
pub use commands::*;
|
||||
pub use exclusive_system::*;
|
||||
pub use exclusive_function_system::*;
|
||||
pub use exclusive_system_param::*;
|
||||
pub use function_system::*;
|
||||
pub use query::*;
|
||||
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)]
|
||||
mod tests {
|
||||
use std::any::TypeId;
|
||||
|
@ -142,8 +116,8 @@ mod tests {
|
|||
query::{Added, Changed, Or, With, Without},
|
||||
schedule::{Schedule, Stage, SystemStage},
|
||||
system::{
|
||||
Commands, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query,
|
||||
RemovedComponents, Res, ResMut, Resource, System, SystemState,
|
||||
Commands, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, RemovedComponents,
|
||||
Res, ResMut, Resource, System, SystemState,
|
||||
},
|
||||
world::{FromWorld, World},
|
||||
};
|
||||
|
@ -322,10 +296,7 @@ mod tests {
|
|||
let mut update = SystemStage::parallel();
|
||||
update.add_system(incr_e_on_flip);
|
||||
schedule.add_stage(UpdateStage, update);
|
||||
schedule.add_stage(
|
||||
ClearTrackers,
|
||||
SystemStage::single(World::clear_trackers.exclusive_system()),
|
||||
);
|
||||
schedule.add_stage(ClearTrackers, SystemStage::single(World::clear_trackers));
|
||||
|
||||
schedule.run(&mut world);
|
||||
assert_eq!(world.resource::<Added>().0, 1);
|
||||
|
|
|
@ -32,6 +32,9 @@ pub trait System: Send + Sync + 'static {
|
|||
/// Returns true if the system is [`Send`].
|
||||
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
|
||||
/// takes a shared reference to [`World`] and may therefore break Rust's aliasing rules, making
|
||||
/// it unsafe to call.
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
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 {
|
||||
let out = self.system_a.run_unsafe(input, world);
|
||||
self.system_b.run_unsafe(out, world)
|
||||
|
|
|
@ -680,7 +680,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
|
|||
/// // .add_system(reset_to_system(my_config))
|
||||
/// # 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
|
||||
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>`].
|
||||
#[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> {
|
||||
type Fetch = LocalState<T>;
|
||||
|
|
|
@ -2,7 +2,7 @@ mod converter;
|
|||
mod gilrs_system;
|
||||
|
||||
use bevy_app::{App, CoreStage, Plugin, StartupStage};
|
||||
use bevy_ecs::schedule::ParallelSystemDescriptorCoercion;
|
||||
use bevy_ecs::schedule::IntoSystemDescriptor;
|
||||
use bevy_input::InputSystem;
|
||||
use bevy_utils::tracing::error;
|
||||
use gilrs::GilrsBuilder;
|
||||
|
|
|
@ -6,7 +6,7 @@ pub mod mouse;
|
|||
pub mod touch;
|
||||
|
||||
pub use axis::*;
|
||||
use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel};
|
||||
use bevy_ecs::schedule::{IntoSystemDescriptor, SystemLabel};
|
||||
pub use input::*;
|
||||
|
||||
pub mod prelude {
|
||||
|
|
|
@ -142,7 +142,7 @@ impl Plugin for PbrPlugin {
|
|||
// NOTE: Clusters need to have been added before update_clusters is run so
|
||||
// add as an exclusive system
|
||||
add_clusters
|
||||
.exclusive_system()
|
||||
.at_start()
|
||||
.label(SimulationLightSystems::AddClusters),
|
||||
)
|
||||
.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)
|
||||
// _before_ the `prepare_views()` system is run. ideally this becomes a normal system when "stageless" features come out
|
||||
render::prepare_lights
|
||||
.exclusive_system()
|
||||
.at_start()
|
||||
.label(RenderLightSystems::PrepareLights),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
|
|
|
@ -10,7 +10,7 @@ use bevy_ecs::{
|
|||
entity::Entity,
|
||||
event::EventReader,
|
||||
prelude::World,
|
||||
schedule::ParallelSystemDescriptorCoercion,
|
||||
schedule::IntoSystemDescriptor,
|
||||
system::{
|
||||
lifetimeless::{Read, SQuery, SRes},
|
||||
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
|
||||
|
|
|
@ -57,7 +57,7 @@ where
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ExtractState<P: SystemParam> {
|
||||
pub struct ExtractState<P: SystemParam + 'static> {
|
||||
state: SystemState<P>,
|
||||
main_world_state: ResState<MainWorld>,
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ impl Plugin for RenderPlugin {
|
|||
RenderStage::Render,
|
||||
SystemStage::parallel()
|
||||
.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())
|
||||
.init_resource::<RenderGraph>()
|
||||
|
|
|
@ -151,7 +151,7 @@ impl<P: PhaseItem> DrawFunctions<P> {
|
|||
pub trait RenderCommand<P: PhaseItem> {
|
||||
/// Specifies all ECS data required by [`RenderCommand::render`].
|
||||
/// All parameters have to be read only.
|
||||
type Param: SystemParam;
|
||||
type Param: SystemParam + 'static;
|
||||
|
||||
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
|
||||
fn render<'w>(
|
||||
|
@ -168,7 +168,7 @@ pub enum RenderCommandResult {
|
|||
}
|
||||
|
||||
pub trait EntityRenderCommand {
|
||||
type Param: SystemParam;
|
||||
type Param: SystemParam + 'static;
|
||||
fn render<'w>(
|
||||
view: Entity,
|
||||
item: Entity,
|
||||
|
@ -231,7 +231,10 @@ pub enum BatchResult {
|
|||
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;
|
||||
|
||||
#[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.
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ pub mod prelude {
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::{schedule::ExclusiveSystemDescriptorCoercion, system::IntoExclusiveSystem};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScenePlugin;
|
||||
|
@ -29,10 +29,7 @@ impl Plugin for ScenePlugin {
|
|||
.add_asset::<Scene>()
|
||||
.init_asset_loader::<SceneLoader>()
|
||||
.init_resource::<SceneSpawner>()
|
||||
.add_system_to_stage(
|
||||
CoreStage::PreUpdate,
|
||||
scene_spawner_system.exclusive_system().at_end(),
|
||||
)
|
||||
.add_system_to_stage(CoreStage::PreUpdate, scene_spawner_system.at_end())
|
||||
// Systems `*_bundle_spawner` must run before `scene_spawner_system`
|
||||
.add_system_to_stage(CoreStage::PreUpdate, scene_spawner);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ pub use texture_atlas_builder::*;
|
|||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{AddAsset, Assets, HandleUntyped};
|
||||
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_render::{
|
||||
render_phase::AddRenderCommand,
|
||||
|
|
|
@ -6,7 +6,7 @@ use bevy_ecs::{
|
|||
entity::Entity,
|
||||
event::EventReader,
|
||||
prelude::{Bundle, World},
|
||||
schedule::ParallelSystemDescriptorCoercion,
|
||||
schedule::IntoSystemDescriptor,
|
||||
system::{
|
||||
lifetimeless::{Read, SQuery, SRes},
|
||||
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
|
||||
|
|
|
@ -28,7 +28,7 @@ pub mod prelude {
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
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_sprite::SpriteSystem;
|
||||
use bevy_window::ModifiesWindows;
|
||||
|
|
|
@ -189,6 +189,10 @@ impl System for FixedTimestep {
|
|||
self.internal_system.is_send()
|
||||
}
|
||||
|
||||
fn is_exclusive(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun {
|
||||
// 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
|
||||
|
|
|
@ -40,10 +40,7 @@ impl Plugin for TimePlugin {
|
|||
.register_type::<Stopwatch>()
|
||||
// time system is added as an "exclusive system" to ensure it runs before other systems
|
||||
// in CoreStage::First
|
||||
.add_system_to_stage(
|
||||
CoreStage::First,
|
||||
time_system.exclusive_system().at_start().label(TimeSystem),
|
||||
);
|
||||
.add_system_to_stage(CoreStage::First, time_system.at_start().label(TimeSystem));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ pub mod prelude {
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::{
|
||||
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
|
||||
schedule::{IntoSystemDescriptor, SystemLabel},
|
||||
system::Resource,
|
||||
};
|
||||
use bevy_input::InputSystem;
|
||||
|
|
|
@ -25,7 +25,7 @@ pub mod prelude {
|
|||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::{
|
||||
event::Events,
|
||||
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
|
||||
schedule::{IntoSystemDescriptor, SystemLabel},
|
||||
system::Resource,
|
||||
};
|
||||
|
||||
|
|
|
@ -316,13 +316,7 @@ fn main() {
|
|||
MyStage::BeforeRound,
|
||||
new_player_system.after(new_round_system),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
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::BeforeRound, exclusive_player_system)
|
||||
.add_system_to_stage(MyStage::AfterRound, score_check_system)
|
||||
.add_system_to_stage(
|
||||
// We can ensure that `game_over_system` runs after `score_check_system` using explicit ordering
|
||||
|
|
|
@ -16,7 +16,7 @@ fn main() {
|
|||
.add_plugins(DefaultPlugins)
|
||||
.register_type::<ComponentA>()
|
||||
.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(infotext_system)
|
||||
.add_system(log_system)
|
||||
|
|
Loading…
Reference in a new issue