Skip to content

Commit

Permalink
Add top-level schedules
Browse files Browse the repository at this point in the history
  • Loading branch information
B-head committed Jul 3, 2023
1 parent eaa137b commit efb328f
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 104 deletions.
4 changes: 2 additions & 2 deletions crates/bevy_app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ pub mod prelude {
pub use crate::{
app::App,
main_schedule::{
First, FixedUpdate, Last, UpdateFlow, PostStartup, PostUpdate, PreStartup, PreUpdate, RenderFlow,
Startup, StateTransition, Update,
Control, First, FixedUpdate, FrameReady, Last, PostStartup, PostUpdate, PreStartup,
PreUpdate, RenderFlow, Startup, StartupFlow, StateTransition, Update, UpdateFlow,
},
DynamicPlugin, Plugin, PluginGroup,
};
Expand Down
100 changes: 57 additions & 43 deletions crates/bevy_app/src/main_schedule.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
use crate::{App, Plugin};
use bevy_ecs::{
schedule::{ExecutorKind, Schedule, ScheduleLabel},
system::{Local, Resource},
system::Resource,
world::{Mut, World},
};

/// The schedule that contains the app logic that is evaluated each tick of event loop.
///
/// By default, it will run the following schedules in the given order:
///
/// On the first run of the schedule (and only on the first run), it will run:
/// * [`PreStartup`]
/// * [`Startup`]
/// * [`PostStartup`]
///
/// Then it will run:
/// * [`First`]
/// * [`PreUpdate`]
/// * [`StateTransition`]
/// * [`RunFixedUpdateLoop`]
/// * This will run [`FixedUpdate`] zero to many times, based on how much time has elapsed.
/// * [`Update`]
/// * [`PostUpdate`]
/// * [`Last`]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct UpdateFlow;
pub struct StartupFlow;

/// The schedule that runs before [`Startup`].
/// This is run by the [`Main`] schedule.
/// This is run by the [`StartupFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PreStartup;

/// The schedule that runs once when the app starts.
/// This is run by the [`Main`] schedule.
/// This is run by the [`StartupFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Startup;

/// The schedule that runs once after [`Startup`].
/// This is run by the [`Main`] schedule.
/// This is run by the [`StartupFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PostStartup;

/// The schedule that contains the app logic that is evaluated each tick of event loop.
///
/// By default, it will run the following schedules in the given order:
/// * [`First`]
/// * [`PreUpdate`]
/// * [`StateTransition`]
/// * [`RunFixedUpdateLoop`]
/// * This will run [`FixedUpdate`] zero to many times, based on how much time has elapsed.
/// * [`Update`]
/// * [`PostUpdate`]
/// * [`Last`]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct UpdateFlow;

/// Runs first in the schedule.
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct First;

Expand All @@ -53,17 +53,17 @@ pub struct First;
/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready".
/// [`PreUpdate`] abstracts out "pre work implementation details".
///
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PreUpdate;

/// Runs [state transitions](bevy_ecs::schedule::States).
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct StateTransition;

/// Runs the [`FixedUpdate`] schedule in a loop according until all relevant elapsed time has been "consumed".
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct RunFixedUpdateLoop;

Expand All @@ -75,7 +75,7 @@ pub struct RunFixedUpdateLoop;
pub struct FixedUpdate;

/// The schedule that contains app logic.
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Update;

Expand All @@ -86,16 +86,26 @@ pub struct Update;
/// [`PostUpdate`] exists to do "engine/plugin response work" to things that happened in [`Update`].
/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`].
///
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PostUpdate;

/// Runs last in the schedule.
/// This is run by the [`Main`] schedule.
/// This is run by the [`UpdateFlow`] schedule.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Last;

/// The main render schedule.
/// Each time an event is received from windows and devices, this schedule is run.
/// This is useful for responding to events regardless of whether tick updates take place.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Control;

/// Each time a frame is ready to be updated, this schedule is run.
/// This is the best place to decide whether to redraw.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct FrameReady;

/// The schedule that builds and sends drawing queries to the GPU.
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
pub struct RenderFlow;

Expand Down Expand Up @@ -135,16 +145,19 @@ impl UpdateFlowOrder {
}
}

impl UpdateFlow {
/// A system that runs the "main schedule"
pub fn run_main(world: &mut World, mut run_at_least_once: Local<bool>) {
if !*run_at_least_once {
let _ = world.try_run_schedule(PreStartup);
let _ = world.try_run_schedule(Startup);
let _ = world.try_run_schedule(PostStartup);
*run_at_least_once = true;
}
/// Initializes the [`StartupFlow`] schedule, [`UpdateFlow`] schedule, sub schedules, and resources for a given [`App`].
pub struct MainSchedulePlugin;

impl MainSchedulePlugin {
/// A system that runs the `StartupFlow` sub schedules
pub fn run_startup(world: &mut World) {
let _ = world.try_run_schedule(PreStartup);
let _ = world.try_run_schedule(Startup);
let _ = world.try_run_schedule(PostStartup);
}

/// A system that runs the `UpdateFlow` sub schedules
pub fn run_update(world: &mut World) {
world.resource_scope(|world, order: Mut<UpdateFlowOrder>| {
for label in &order.labels {
let _ = world.try_run_schedule(&**label);
Expand All @@ -153,20 +166,21 @@ impl UpdateFlow {
}
}

/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
pub struct MainSchedulePlugin;

impl Plugin for MainSchedulePlugin {
fn build(&self, app: &mut App) {
// simple "facilitator" schedules benefit from simpler single threaded scheduling
let mut main_schedule = Schedule::new();
main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
let mut startup_schedule = Schedule::new();
startup_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
let mut update_schedule = Schedule::new();
update_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
let mut fixed_update_loop_schedule = Schedule::new();
fixed_update_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);

app.add_schedule(UpdateFlow, main_schedule)
app.add_schedule(StartupFlow, startup_schedule)
.add_schedule(UpdateFlow, update_schedule)
.add_schedule(RunFixedUpdateLoop, fixed_update_loop_schedule)
.init_resource::<UpdateFlowOrder>()
.add_systems(UpdateFlow, UpdateFlow::run_main);
.add_systems(StartupFlow, Self::run_startup)
.add_systems(UpdateFlow, Self::run_update);
}
}
43 changes: 28 additions & 15 deletions crates/bevy_app/src/schedule_runner.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
app::{App, AppExit},
plugin::Plugin,
UpdateFlow,
StartupFlow, UpdateFlow,
};
use bevy_ecs::{
event::{Events, ManualEventReader},
Expand Down Expand Up @@ -53,18 +53,22 @@ impl Default for RunMode {
pub struct ScheduleRunnerPlugin {
/// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly.
pub run_mode: RunMode,
/// The schedule to run by default.
/// This schedule is run only once at startup.
///
/// This is initially set to [`Main`].
pub main_schedule_label: BoxedScheduleLabel,
/// The default is [`StartupFlow`].
pub startup_schedule_label: BoxedScheduleLabel,
/// This schedule is run for each tick.
///
/// The default is [`UpdateFlow`].
pub update_schedule_label: BoxedScheduleLabel,
}

impl ScheduleRunnerPlugin {
/// See [`RunMode::Once`].
pub fn run_once() -> Self {
ScheduleRunnerPlugin {
run_mode: RunMode::Once,
main_schedule_label: Box::new(UpdateFlow),
..Default::default()
}
}

Expand All @@ -74,7 +78,7 @@ impl ScheduleRunnerPlugin {
run_mode: RunMode::Loop {
wait: Some(wait_duration),
},
main_schedule_label: Box::new(UpdateFlow),
..Default::default()
}
}
}
Expand All @@ -83,27 +87,36 @@ impl Default for ScheduleRunnerPlugin {
fn default() -> Self {
ScheduleRunnerPlugin {
run_mode: RunMode::Loop { wait: None },
main_schedule_label: Box::new(UpdateFlow),
startup_schedule_label: Box::new(StartupFlow),
update_schedule_label: Box::new(UpdateFlow),
}
}
}

impl Plugin for ScheduleRunnerPlugin {
fn build(&self, app: &mut App) {
let run_mode = self.run_mode;
let main_schedule_label = self.main_schedule_label.clone();
let startup_schedule_label = self.startup_schedule_label.clone();
let update_schedule_label = self.update_schedule_label.clone();
app.set_runner(move |mut app: App| {
// Prevent panic when schedules do not exist
app.init_schedule(main_schedule_label.clone());
app.init_schedule(startup_schedule_label.clone());
app.init_schedule(update_schedule_label.clone());

{
#[cfg(feature = "trace")]
let _ = info_span!("run top schedule", name = ?startup_schedule_label).entered();
app.world.run_schedule(startup_schedule_label);
}

let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
match run_mode {
RunMode::Once => {
{
#[cfg(feature = "trace")]
let _main_schedule_span =
info_span!("main schedule", name = ?main_schedule_label).entered();
app.world.run_schedule(main_schedule_label);
let _ =
info_span!("run top schedule", name = ?update_schedule_label).entered();
app.world.run_schedule(update_schedule_label);
}
app.update_sub_apps();
}
Expand All @@ -124,9 +137,9 @@ impl Plugin for ScheduleRunnerPlugin {

{
#[cfg(feature = "trace")]
let _main_schedule_span =
info_span!("main schedule", name = ?main_schedule_label).entered();
app.world.run_schedule(&main_schedule_label);
let _ = info_span!("run top schedule", name = ?update_schedule_label)
.entered();
app.world.run_schedule(&update_schedule_label);
}
app.update_sub_apps();
app.world.clear_trackers();
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,17 @@ impl Plugin for WindowPlugin {

match self.exit_condition {
ExitCondition::OnPrimaryClosed => {
app.add_systems(PostUpdate, exit_on_primary_closed);
app.add_systems(Control, exit_on_primary_closed);
}
ExitCondition::OnAllClosed => {
app.add_systems(PostUpdate, exit_on_all_closed);
app.add_systems(Control, exit_on_all_closed);
}
ExitCondition::DontExit => {}
}

if self.close_when_requested {
// Need to run before `exit_on_*` systems
app.add_systems(Update, close_when_requested);
app.add_systems(Control, close_when_requested);
}

// Register event types
Expand Down
Loading

0 comments on commit efb328f

Please sign in to comment.