Skip to content

Commit 0e82323

Browse files
authored
Try #2507:
2 parents 0aced5f + cfd590c commit 0e82323

File tree

10 files changed

+266
-16
lines changed

10 files changed

+266
-16
lines changed

crates/bevy_core/src/time/fixed_timestep.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bevy_ecs::{
33
archetype::{Archetype, ArchetypeComponentId},
44
component::ComponentId,
55
query::Access,
6-
schedule::ShouldRun,
6+
schedule::{ScheduleCommandQueue, ShouldRun},
77
system::{IntoSystem, Local, Res, ResMut, System, SystemId},
88
world::World,
99
};
@@ -178,6 +178,10 @@ impl System for FixedTimestep {
178178
self.internal_system.apply_buffers(world)
179179
}
180180

181+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue> {
182+
self.internal_system.schedule_commands()
183+
}
184+
181185
fn initialize(&mut self, world: &mut World) {
182186
self.internal_system = Box::new(
183187
Self::prepare_system
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use std::marker::PhantomData;
2+
3+
use super::{IntoSystemDescriptor, Schedule, StageLabel};
4+
5+
#[derive(Default)]
6+
pub struct ScheduleCommandQueue {
7+
items: Vec<Box<dyn ScheduleCommand>>
8+
}
9+
10+
impl ScheduleCommandQueue {
11+
pub fn push<C>(&mut self, command: C)
12+
where
13+
C: ScheduleCommand,
14+
{
15+
self.items.push(Box::new(command));
16+
}
17+
18+
pub fn apply(&mut self, schedule: &mut Schedule) {
19+
for command in self.items.drain(..) {
20+
command.write(schedule);
21+
}
22+
}
23+
24+
pub fn transfer(&mut self, queue: &mut ScheduleCommandQueue) {
25+
queue.items.extend(self.items.drain(..));
26+
}
27+
28+
pub fn is_empty(&self) -> bool {
29+
self.items.is_empty()
30+
}
31+
}
32+
33+
/// A [`Schedule`] mutation.
34+
pub trait ScheduleCommand: Send + Sync + 'static {
35+
fn write(self: Box<Self>, schedule: &mut Schedule);
36+
}
37+
38+
pub struct ScheduleCommands<'a> {
39+
queue: &'a mut ScheduleCommandQueue
40+
}
41+
42+
impl<'a> ScheduleCommands<'a> {
43+
pub fn new(queue: &'a mut ScheduleCommandQueue) -> Self {
44+
Self {
45+
queue
46+
}
47+
}
48+
49+
pub fn insert_system<T: 'static, S, Params: 'static>(&mut self, system: T, stage_label: S)
50+
where
51+
T: IntoSystemDescriptor<Params> + Send + Sync,
52+
S: StageLabel
53+
{
54+
self.queue.push(InsertSystem {
55+
system: system,
56+
stage_label: stage_label,
57+
phantom: Default::default(),
58+
});
59+
}
60+
}
61+
62+
pub struct InsertSystem<T, S, Params>
63+
where
64+
T: IntoSystemDescriptor<Params>,
65+
S: StageLabel
66+
{
67+
pub system: T,
68+
pub stage_label: S,
69+
phantom: PhantomData<fn() -> Params>,
70+
}
71+
72+
impl<T: 'static, S, Params: 'static> ScheduleCommand for InsertSystem<T, S, Params>
73+
where
74+
T: IntoSystemDescriptor<Params> + Send + Sync,
75+
S: StageLabel
76+
{
77+
fn write(self: Box<Self>, schedule: &mut Schedule) {
78+
schedule.add_system_to_stage(self.stage_label, self.system);
79+
}
80+
}
81+
82+
#[cfg(test)]
83+
mod tests {
84+
use crate::{
85+
schedule::{Schedule, ScheduleCommands, ScheduleCommandQueue, SystemStage},
86+
system::Commands,
87+
world::World,
88+
};
89+
90+
#[test]
91+
fn insert_system() {
92+
fn sample_system(mut _commands: Commands) {
93+
94+
}
95+
let mut schedule = Schedule::default();
96+
schedule.add_stage("test", SystemStage::parallel());
97+
let mut queue = ScheduleCommandQueue::default();
98+
let mut schedule_commands = ScheduleCommands::new(&mut queue);
99+
schedule_commands.insert_system(sample_system, "test");
100+
queue.apply(&mut schedule);
101+
102+
let stage = schedule.get_stage::<SystemStage>(&"test").unwrap();
103+
assert_eq!(stage.parallel_systems().len(), 1);
104+
}
105+
106+
#[test]
107+
fn insert_system_from_system() {
108+
fn sample_system(mut schedule_commands: ScheduleCommands) {
109+
schedule_commands.insert_system(|| {}, "test");
110+
}
111+
112+
let mut world = World::default();
113+
let mut schedule = Schedule::default();
114+
schedule.add_stage("test", SystemStage::parallel());
115+
schedule.add_system_to_stage("test", sample_system);
116+
schedule.run_once(&mut world);
117+
118+
let stage = schedule.get_stage::<SystemStage>(&"test").unwrap();
119+
assert_eq!(stage.parallel_systems().len(), 2);
120+
}
121+
}

crates/bevy_ecs/src/schedule/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod state;
88
mod system_container;
99
mod system_descriptor;
1010
mod system_set;
11+
mod commands;
1112

1213
pub use executor::*;
1314
pub use executor_parallel::*;
@@ -19,6 +20,7 @@ pub use state::*;
1920
pub use system_container::*;
2021
pub use system_descriptor::*;
2122
pub use system_set::*;
23+
pub use commands::*;
2224

2325
use std::fmt::Debug;
2426

@@ -198,6 +200,7 @@ impl Schedule {
198200
}
199201

200202
pub fn run_once(&mut self, world: &mut World) {
203+
let mut commands = ScheduleCommandQueue::default();
201204
for label in self.stage_order.iter() {
202205
#[cfg(feature = "trace")]
203206
let stage_span =
@@ -206,7 +209,12 @@ impl Schedule {
206209
let _stage_guard = stage_span.enter();
207210
let stage = self.stages.get_mut(label).unwrap();
208211
stage.run(world);
212+
if let Some(mut stage_commands) = stage.commands() {
213+
stage_commands.transfer(&mut commands);
214+
}
209215
}
216+
217+
commands.apply(self);
210218
}
211219

212220
/// Iterates over all of schedule's stages and their labels, in execution order.
@@ -235,4 +243,8 @@ impl Stage for Schedule {
235243
}
236244
}
237245
}
246+
247+
fn commands(&mut self) -> Option<ScheduleCommandQueue> {
248+
None
249+
}
238250
}

crates/bevy_ecs/src/schedule/run_criteria.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration},
33
component::ComponentId,
44
query::Access,
5-
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel},
5+
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel, ScheduleCommandQueue},
66
system::{BoxedSystem, IntoSystem, System, SystemId},
77
world::World,
88
};
@@ -450,6 +450,7 @@ impl System for RunOnce {
450450
}
451451

452452
fn apply_buffers(&mut self, _world: &mut World) {}
453+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue> { None }
453454

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

crates/bevy_ecs/src/schedule/stage.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ use crate::{
55
BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy,
66
ExclusiveSystemContainer, GraphNode, InsertionPoint, ParallelExecutor,
77
ParallelSystemContainer, ParallelSystemExecutor, RunCriteriaContainer,
8-
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun,
9-
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
8+
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner,
9+
ShouldRun, SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
1010
},
11-
system::System,
11+
system::{System, ExclusiveSystem},
1212
world::{World, WorldId},
1313
};
1414
use bevy_utils::{tracing::info, HashMap, HashSet};
1515
use downcast_rs::{impl_downcast, Downcast};
1616
use fixedbitset::FixedBitSet;
1717
use std::fmt::Debug;
1818

19-
use super::IntoSystemDescriptor;
19+
use super::{IntoSystemDescriptor, ScheduleCommandQueue};
2020

2121
pub trait Stage: Downcast + Send + Sync {
2222
/// Runs the stage; this happens once per update.
2323
/// Implementors must initialize all of their state and systems before running the first time.
2424
fn run(&mut self, world: &mut World);
25+
fn commands(&mut self) -> Option<ScheduleCommandQueue>;
2526
}
2627

2728
impl_downcast!(Stage);
@@ -83,6 +84,8 @@ pub struct SystemStage {
8384
uninitialized_parallel: Vec<usize>,
8485
/// Saves the value of the World change_tick during the last tick check
8586
last_tick_check: u32,
87+
// Contains commands for the schedule
88+
schedule_commands: ScheduleCommandQueue,
8689
}
8790

8891
impl SystemStage {
@@ -104,6 +107,7 @@ impl SystemStage {
104107
uninitialized_before_commands: vec![],
105108
uninitialized_at_end: vec![],
106109
last_tick_check: Default::default(),
110+
schedule_commands: Default::default(),
107111
}
108112
}
109113

@@ -805,10 +809,17 @@ impl Stage for SystemStage {
805809
)
806810
}
807811

812+
fn run_exclusive(world: &mut World, system: &mut Box<dyn ExclusiveSystem>, commands: &mut ScheduleCommandQueue) {
813+
system.run(world);
814+
if let Some(mut c) = system.schedule_commands() {
815+
c.transfer(commands);
816+
}
817+
}
818+
808819
// Run systems that want to be at the start of stage.
809820
for container in &mut self.exclusive_at_start {
810821
if should_run(container, &self.run_criteria, default_should_run) {
811-
container.system_mut().run(world);
822+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
812823
}
813824
}
814825

@@ -823,21 +834,25 @@ impl Stage for SystemStage {
823834
// Run systems that want to be between parallel systems and their command buffers.
824835
for container in &mut self.exclusive_before_commands {
825836
if should_run(container, &self.run_criteria, default_should_run) {
826-
container.system_mut().run(world);
837+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
827838
}
828839
}
829840

830841
// Apply parallel systems' buffers.
831842
for container in &mut self.parallel {
832843
if container.should_run {
833-
container.system_mut().apply_buffers(world);
844+
let system = container.system_mut();
845+
system.apply_buffers(world);
846+
if let Some(mut commands) = system.schedule_commands() {
847+
commands.transfer(&mut self.schedule_commands);
848+
}
834849
}
835850
}
836851

837852
// Run systems that want to be at the end of stage.
838853
for container in &mut self.exclusive_at_end {
839854
if should_run(container, &self.run_criteria, default_should_run) {
840-
container.system_mut().run(world);
855+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
841856
}
842857
}
843858

@@ -885,6 +900,16 @@ impl Stage for SystemStage {
885900
}
886901
}
887902
}
903+
904+
fn commands(&mut self) -> Option<ScheduleCommandQueue> {
905+
if self.schedule_commands.is_empty() {
906+
return None;
907+
}
908+
909+
let mut commands = Default::default();
910+
self.schedule_commands.transfer(&mut commands);
911+
Some(commands)
912+
}
888913
}
889914

890915
#[cfg(test)]

crates/bevy_ecs/src/system/exclusive_system.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
archetype::ArchetypeGeneration,
3+
schedule::ScheduleCommandQueue,
34
system::{check_system_change_tick, BoxedSystem, IntoSystem, SystemId},
45
world::World,
56
};
@@ -12,6 +13,8 @@ pub trait ExclusiveSystem: Send + Sync + 'static {
1213

1314
fn run(&mut self, world: &mut World);
1415

16+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue>;
17+
1518
fn initialize(&mut self, world: &mut World);
1619

1720
fn check_change_tick(&mut self, change_tick: u32);
@@ -48,6 +51,10 @@ impl ExclusiveSystem for ExclusiveSystemFn {
4851
world.last_change_tick = saved_last_tick;
4952
}
5053

54+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue> {
55+
None
56+
}
57+
5158
fn initialize(&mut self, _: &mut World) {}
5259

5360
fn check_change_tick(&mut self, change_tick: u32) {
@@ -101,6 +108,10 @@ impl ExclusiveSystem for ExclusiveSystemCoerced {
101108
self.system.apply_buffers(world);
102109
}
103110

111+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue> {
112+
self.system.schedule_commands()
113+
}
114+
104115
fn initialize(&mut self, world: &mut World) {
105116
self.system.initialize(world);
106117
}

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{
77
SystemParamFetch, SystemParamState,
88
},
99
world::{World, WorldId},
10+
schedule::ScheduleCommandQueue,
1011
};
1112
use bevy_ecs_macros::all_tuples;
1213
use std::{borrow::Cow, marker::PhantomData};
@@ -383,6 +384,11 @@ where
383384
param_state.apply(world);
384385
}
385386

387+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue> {
388+
let param_state = self.param_state.as_mut().unwrap();
389+
param_state.schedule_commands()
390+
}
391+
386392
#[inline]
387393
fn initialize(&mut self, world: &mut World) {
388394
self.param_state = Some(<Param::Fetch as SystemParamState>::init(

crates/bevy_ecs/src/system/system.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
use bevy_utils::tracing::warn;
22

3-
use crate::{
4-
archetype::{Archetype, ArchetypeComponentId},
5-
component::ComponentId,
6-
query::Access,
7-
world::World,
8-
};
3+
use crate::{archetype::{Archetype, ArchetypeComponentId}, component::ComponentId, query::Access, schedule::ScheduleCommandQueue, world::World};
94
use std::borrow::Cow;
105

116
/// A [`System`] identifier.
@@ -66,6 +61,7 @@ pub trait System: Send + Sync + 'static {
6661
unsafe { self.run_unsafe(input, world) }
6762
}
6863
fn apply_buffers(&mut self, world: &mut World);
64+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue>;
6965
/// Initialize the system.
7066
fn initialize(&mut self, _world: &mut World);
7167
fn check_change_tick(&mut self, change_tick: u32);

0 commit comments

Comments
 (0)