Skip to content

Commit 50e871e

Browse files
authored
Try #2507:
2 parents 0aced5f + e5ffab9 commit 50e871e

File tree

10 files changed

+268
-8
lines changed

10 files changed

+268
-8
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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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 { queue }
45+
}
46+
47+
pub fn insert_system<T: 'static, S, Params: 'static>(&mut self, system: T, stage_label: S)
48+
where
49+
T: IntoSystemDescriptor<Params> + Send + Sync,
50+
S: StageLabel,
51+
{
52+
self.queue.push(InsertSystem {
53+
system,
54+
stage_label,
55+
phantom: Default::default(),
56+
});
57+
}
58+
}
59+
60+
pub struct InsertSystem<T, S, Params>
61+
where
62+
T: IntoSystemDescriptor<Params>,
63+
S: StageLabel,
64+
{
65+
pub system: T,
66+
pub stage_label: S,
67+
phantom: PhantomData<fn() -> Params>,
68+
}
69+
70+
impl<T: 'static, S, Params: 'static> ScheduleCommand for InsertSystem<T, S, Params>
71+
where
72+
T: IntoSystemDescriptor<Params> + Send + Sync,
73+
S: StageLabel,
74+
{
75+
fn write(self: Box<Self>, schedule: &mut Schedule) {
76+
schedule.add_system_to_stage(self.stage_label, self.system);
77+
}
78+
}
79+
80+
#[cfg(test)]
81+
mod tests {
82+
use crate::{
83+
schedule::{Schedule, ScheduleCommandQueue, ScheduleCommands, SystemStage},
84+
system::Commands,
85+
world::World,
86+
};
87+
88+
#[test]
89+
fn insert_system() {
90+
fn sample_system(mut _commands: Commands) {}
91+
let mut schedule = Schedule::default();
92+
schedule.add_stage("test", SystemStage::parallel());
93+
let mut queue = ScheduleCommandQueue::default();
94+
let mut schedule_commands = ScheduleCommands::new(&mut queue);
95+
schedule_commands.insert_system(sample_system, "test");
96+
queue.apply(&mut schedule);
97+
98+
let stage = schedule.get_stage::<SystemStage>(&"test").unwrap();
99+
assert_eq!(stage.parallel_systems().len(), 1);
100+
}
101+
102+
#[test]
103+
fn insert_system_from_system() {
104+
fn sample_system(mut schedule_commands: ScheduleCommands) {
105+
schedule_commands.insert_system(|| {}, "test");
106+
}
107+
108+
let mut world = World::default();
109+
let mut schedule = Schedule::default();
110+
schedule.add_stage("test", SystemStage::parallel());
111+
schedule.add_system_to_stage("test", sample_system);
112+
schedule.run_once(&mut world);
113+
114+
let stage = schedule.get_stage::<SystemStage>(&"test").unwrap();
115+
assert_eq!(stage.parallel_systems().len(), 2);
116+
}
117+
}

crates/bevy_ecs/src/schedule/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod commands;
12
mod executor;
23
mod executor_parallel;
34
pub mod graph_utils;
@@ -9,6 +10,7 @@ mod system_container;
910
mod system_descriptor;
1011
mod system_set;
1112

13+
pub use commands::*;
1214
pub use executor::*;
1315
pub use executor_parallel::*;
1416
pub use graph_utils::GraphNode;
@@ -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: 4 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,9 @@ impl System for RunOnce {
450450
}
451451

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

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

crates/bevy_ecs/src/schedule/stage.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ use crate::{
88
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun,
99
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
1010
},
11-
system::System,
11+
system::{ExclusiveSystem, System},
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,21 @@ impl Stage for SystemStage {
805809
)
806810
}
807811

812+
fn run_exclusive(
813+
world: &mut World,
814+
system: &mut Box<dyn ExclusiveSystem>,
815+
commands: &mut ScheduleCommandQueue,
816+
) {
817+
system.run(world);
818+
if let Some(mut c) = system.schedule_commands() {
819+
c.transfer(commands);
820+
}
821+
}
822+
808823
// Run systems that want to be at the start of stage.
809824
for container in &mut self.exclusive_at_start {
810825
if should_run(container, &self.run_criteria, default_should_run) {
811-
container.system_mut().run(world);
826+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
812827
}
813828
}
814829

@@ -823,21 +838,25 @@ impl Stage for SystemStage {
823838
// Run systems that want to be between parallel systems and their command buffers.
824839
for container in &mut self.exclusive_before_commands {
825840
if should_run(container, &self.run_criteria, default_should_run) {
826-
container.system_mut().run(world);
841+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
827842
}
828843
}
829844

830845
// Apply parallel systems' buffers.
831846
for container in &mut self.parallel {
832847
if container.should_run {
833-
container.system_mut().apply_buffers(world);
848+
let system = container.system_mut();
849+
system.apply_buffers(world);
850+
if let Some(mut commands) = system.schedule_commands() {
851+
commands.transfer(&mut self.schedule_commands);
852+
}
834853
}
835854
}
836855

837856
// Run systems that want to be at the end of stage.
838857
for container in &mut self.exclusive_at_end {
839858
if should_run(container, &self.run_criteria, default_should_run) {
840-
container.system_mut().run(world);
859+
run_exclusive(world, container.system_mut(), &mut self.schedule_commands);
841860
}
842861
}
843862

@@ -885,6 +904,16 @@ impl Stage for SystemStage {
885904
}
886905
}
887906
}
907+
908+
fn commands(&mut self) -> Option<ScheduleCommandQueue> {
909+
if self.schedule_commands.is_empty() {
910+
return None;
911+
}
912+
913+
let mut commands = Default::default();
914+
self.schedule_commands.transfer(&mut commands);
915+
Some(commands)
916+
}
888917
}
889918

890919
#[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
@@ -2,6 +2,7 @@ use crate::{
22
archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
33
component::ComponentId,
44
query::{Access, FilteredAccessSet},
5+
schedule::ScheduleCommandQueue,
56
system::{
67
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemId, SystemParam,
78
SystemParamFetch, SystemParamState,
@@ -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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
archetype::{Archetype, ArchetypeComponentId},
55
component::ComponentId,
66
query::Access,
7+
schedule::ScheduleCommandQueue,
78
world::World,
89
};
910
use std::borrow::Cow;
@@ -66,6 +67,7 @@ pub trait System: Send + Sync + 'static {
6667
unsafe { self.run_unsafe(input, world) }
6768
}
6869
fn apply_buffers(&mut self, world: &mut World);
70+
fn schedule_commands(&mut self) -> Option<ScheduleCommandQueue>;
6971
/// Initialize the system.
7072
fn initialize(&mut self, _world: &mut World);
7173
fn check_change_tick(&mut self, change_tick: u32);

0 commit comments

Comments
 (0)