use crate::driver::Installer;
use crate::system::{IntoSystem, System};
use crate::world::{Registry, World, WorldBuilder};
#[doc(hidden)]
pub struct StageEnd;
#[doc(hidden)]
pub struct StageNode<Prev, S> {
prev: Prev,
stage: S,
}
#[doc(hidden)]
pub trait RunSchedule: Send {
fn run_schedule(&mut self, world: &mut World) -> (usize, bool);
fn system_count(&self) -> usize;
}
impl RunSchedule for StageEnd {
#[inline(always)]
fn run_schedule(&mut self, _world: &mut World) -> (usize, bool) {
(0, true)
}
fn system_count(&self) -> usize {
0
}
}
impl<Prev: RunSchedule, S: StageRunner> RunSchedule for StageNode<Prev, S> {
#[inline(always)]
fn run_schedule(&mut self, world: &mut World) -> (usize, bool) {
let (prev_ran, prev_fired) = self.prev.run_schedule(world);
if !prev_fired {
return (prev_ran, false);
}
let (stage_ran, stage_fired) = self.stage.run_all(world);
(prev_ran + stage_ran, stage_fired)
}
fn system_count(&self) -> usize {
self.prev.system_count() + self.stage.system_count()
}
}
#[doc(hidden)]
pub trait StageRunner: Send {
fn run_all(&mut self, world: &mut World) -> (usize, bool);
fn system_count(&self) -> usize;
}
macro_rules! impl_stage {
($name:ident, $count:expr, $(($idx:tt, $S:ident)),+) => {
#[doc(hidden)]
pub struct $name<$($S),+>($(pub(crate) $S),+);
impl<$($S: System),+> StageRunner for $name<$($S),+> {
#[inline(always)]
fn run_all(&mut self, world: &mut World) -> (usize, bool) {
let mut fired = false;
$(fired |= self.$idx.run(world);)+
($count, fired)
}
fn system_count(&self) -> usize {
$count
}
}
};
}
impl_stage!(Stage1, 1, (0, S0));
impl_stage!(Stage2, 2, (0, S0), (1, S1));
impl_stage!(Stage3, 3, (0, S0), (1, S1), (2, S2));
impl_stage!(Stage4, 4, (0, S0), (1, S1), (2, S2), (3, S3));
impl_stage!(Stage5, 5, (0, S0), (1, S1), (2, S2), (3, S3), (4, S4));
impl_stage!(
Stage6,
6,
(0, S0),
(1, S1),
(2, S2),
(3, S3),
(4, S4),
(5, S5)
);
impl_stage!(
Stage7,
7,
(0, S0),
(1, S1),
(2, S2),
(3, S3),
(4, S4),
(5, S5),
(6, S6)
);
impl_stage!(
Stage8,
8,
(0, S0),
(1, S1),
(2, S2),
(3, S3),
(4, S4),
(5, S5),
(6, S6),
(7, S7)
);
pub trait IntoStage<Params> {
type Stage: StageRunner + 'static;
fn into_stage(self, registry: &Registry) -> Self::Stage;
}
impl<F, P, M> IntoStage<(P, M)> for F
where
F: IntoSystem<P, M>,
F::System: 'static,
{
type Stage = Stage1<F::System>;
fn into_stage(self, registry: &Registry) -> Self::Stage {
Stage1(self.into_system(registry))
}
}
macro_rules! impl_into_stage {
($stage:ident, $(($F:ident, $P:ident, $M:ident, $idx:tt)),+) => {
impl<$($F, $P, $M),+> IntoStage<($(($P, $M),)+)> for ($($F,)+)
where
$($F: IntoSystem<$P, $M>, $F::System: 'static,)+
{
type Stage = $stage<$($F::System),+>;
fn into_stage(self, registry: &Registry) -> Self::Stage {
$stage($(self.$idx.into_system(registry)),+)
}
}
};
}
impl_into_stage!(Stage2, (F0, P0, M0, 0), (F1, P1, M1, 1));
impl_into_stage!(Stage3, (F0, P0, M0, 0), (F1, P1, M1, 1), (F2, P2, M2, 2));
impl_into_stage!(
Stage4,
(F0, P0, M0, 0),
(F1, P1, M1, 1),
(F2, P2, M2, 2),
(F3, P3, M3, 3)
);
impl_into_stage!(
Stage5,
(F0, P0, M0, 0),
(F1, P1, M1, 1),
(F2, P2, M2, 2),
(F3, P3, M3, 3),
(F4, P4, M4, 4)
);
impl_into_stage!(
Stage6,
(F0, P0, M0, 0),
(F1, P1, M1, 1),
(F2, P2, M2, 2),
(F3, P3, M3, 3),
(F4, P4, M4, 4),
(F5, P5, M5, 5)
);
impl_into_stage!(
Stage7,
(F0, P0, M0, 0),
(F1, P1, M1, 1),
(F2, P2, M2, 2),
(F3, P3, M3, 3),
(F4, P4, M4, 4),
(F5, P5, M5, 5),
(F6, P6, M6, 6)
);
impl_into_stage!(
Stage8,
(F0, P0, M0, 0),
(F1, P1, M1, 1),
(F2, P2, M2, 2),
(F3, P3, M3, 3),
(F4, P4, M4, 4),
(F5, P5, M5, 5),
(F6, P6, M6, 6),
(F7, P7, M7, 7)
);
pub struct SchedulerBuilder;
impl SchedulerBuilder {
pub fn new() -> Self {
Self
}
pub fn root<S, Params>(self, stage: S, registry: &Registry) -> StageNode<StageEnd, S::Stage>
where
S: IntoStage<Params>,
{
StageNode {
prev: StageEnd,
stage: stage.into_stage(registry),
}
}
}
impl Default for SchedulerBuilder {
fn default() -> Self {
Self::new()
}
}
impl<Prev, S> StageNode<Prev, S>
where
Prev: RunSchedule + 'static,
S: StageRunner + 'static,
{
pub fn then<Next, Params>(
self,
stage: Next,
registry: &Registry,
) -> StageNode<Self, Next::Stage>
where
Next: IntoStage<Params>,
{
StageNode {
prev: self,
stage: stage.into_stage(registry),
}
}
}
impl<Prev, S> Installer for StageNode<Prev, S>
where
Self: RunSchedule + 'static,
{
type Poller = SystemScheduler<Self>;
fn install(self, _world: &mut WorldBuilder) -> Self::Poller {
SystemScheduler { chain: self }
}
}
pub struct SystemScheduler<Chain> {
chain: Chain,
}
impl<Chain: RunSchedule> SystemScheduler<Chain> {
pub fn run(&mut self, world: &mut World) -> usize {
let (ran, _) = self.chain.run_schedule(world);
ran
}
pub fn len(&self) -> usize {
self.chain.system_count()
}
pub fn is_empty(&self) -> bool {
self.chain.system_count() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ResMut;
fn increment(mut val: ResMut<u64>) -> bool {
*val += 1;
true
}
fn set_flag(mut flag: ResMut<bool>) -> bool {
*flag = true;
true
}
fn false_source() -> bool {
false
}
fn should_not_run(mut val: ResMut<u64>) -> bool {
*val = 999;
true
}
fn source(mut val: ResMut<u64>) -> bool {
*val += 1;
*val <= 2
}
fn middle(mut val: ResMut<u64>) -> bool {
*val += 10;
true
}
fn leaf(mut val: ResMut<u64>) -> bool {
*val += 100;
true
}
fn double(mut val: ResMut<u64>) -> bool {
*val *= 2;
true
}
#[test]
fn single_root_always_runs() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(SchedulerBuilder::new().root(increment, ®));
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 1);
assert_eq!(*world.resource::<u64>(), 1);
}
#[test]
fn linear_chain_propagation() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(source, ®)
.then(middle, ®)
.then(leaf, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 111);
}
#[test]
fn propagation_stops_on_false() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(false_source, ®)
.then(should_not_run, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 1);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn staged_diamond() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<bool>(false);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(increment, ®)
.then((increment, set_flag), ®)
.then(increment, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 4);
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn multiple_roots() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder
.install_driver(SchedulerBuilder::new().root((increment, increment, increment), ®));
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn scheduler_does_not_bump_sequence() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(SchedulerBuilder::new().root(increment, ®));
let mut world = builder.build();
let before = world.current_sequence();
scheduler.run(&mut world);
assert_eq!(world.current_sequence(), before);
}
#[test]
fn mutations_visible_downstream() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(1);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(double, ®)
.then(double, ®),
);
let mut world = builder.build();
scheduler.run(&mut world);
assert_eq!(*world.resource::<u64>(), 4);
}
#[test]
fn multi_system_stage_all_run() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<bool>(false);
let reg = builder.registry();
let mut scheduler = builder
.install_driver(SchedulerBuilder::new().root((increment, increment, set_flag), ®));
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 2);
assert!(*world.resource::<bool>());
}
#[test]
fn stage_propagation_any_semantics() {
fn false_increment(mut val: ResMut<u64>) -> bool {
*val += 1;
false
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root((false_increment, increment), ®)
.then(increment, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn stage_propagation_all_false_stops() {
fn false_increment(mut val: ResMut<u64>) -> bool {
*val += 1;
false
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root((false_increment, false_increment), ®)
.then(should_not_run, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 2);
assert_eq!(*world.resource::<u64>(), 2);
}
#[test]
fn void_systems_in_stage() {
fn void_increment(mut val: ResMut<u64>) {
*val += 1;
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(void_increment, ®)
.then(increment, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 2);
assert_eq!(*world.resource::<u64>(), 2);
}
#[test]
fn mixed_bool_void_stage() {
fn void_increment(mut val: ResMut<u64>) {
*val += 1;
}
fn false_increment(mut val: ResMut<u64>) -> bool {
*val += 1;
false
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root((false_increment, void_increment), ®)
.then(increment, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn scheduler_chain_is_send() {
fn assert_send<T: Send>(_: &T) {}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<bool>(false);
let reg = builder.registry();
let scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(increment, ®)
.then((increment, set_flag), ®)
.then(double, ®),
);
assert_send(&scheduler);
}
#[test]
fn three_stage_linear() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<bool>(false);
let reg = builder.registry();
let mut scheduler = builder.install_driver(
SchedulerBuilder::new()
.root(increment, ®)
.then((increment, set_flag), ®)
.then(double, ®),
);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 4);
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<u64>(), 4);
}
}