Skip to content
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2005,12 +2005,12 @@ category = "Window"
wasm = true

[[example]]
name = "low_power"
path = "examples/window/low_power.rs"
name = "run_control"
path = "examples/window/run_control.rs"

[package.metadata.example.low_power]
name = "Low Power"
description = "Demonstrates settings to reduce power use for bevy applications"
[package.metadata.example.run_control]
name = "Run Control"
description = "Demonstration of controlling the run of an application"
category = "Window"
wasm = true

Expand Down
4 changes: 2 additions & 2 deletions benches/benches/bevy_ecs/scheduling/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn build_schedule(criterion: &mut Criterion) {
for _ in 0..graph_size {
app.add_systems(Update, empty_system);
}
app.update();
app.world.run_schedule(Update);
});
});

Expand All @@ -111,7 +111,7 @@ pub fn build_schedule(criterion: &mut Criterion) {
// This is necessary since dependency resolution does not occur until the game runs.
// FIXME: Running the game clutters up the benchmarks, so ideally we'd be
// able to benchmark the dependency resolution directly.
app.update();
app.world.run_schedule(Update);
});
});
}
Expand Down
78 changes: 38 additions & 40 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{First, Main, MainSchedulePlugin, Plugin, Plugins, Startup, StateTransition, Update};
use crate::{
First, MainSchedulePlugin, Plugin, Plugins, Startup, StateTransition, Update, UpdateFlow,
};
pub use bevy_derive::AppLabel;
use bevy_ecs::{
prelude::*,
Expand Down Expand Up @@ -64,13 +66,12 @@ pub struct App {
/// the application's event loop and advancing the [`Schedule`].
/// Typically, it is not configured manually, but set by one of Bevy's built-in plugins.
/// See `bevy::winit::WinitPlugin` and [`ScheduleRunnerPlugin`](crate::schedule_runner::ScheduleRunnerPlugin).
pub runner: Box<dyn Fn(App) + Send>, // Send bound is required to make App Send
/// The schedule that systems are added to by default.
///
/// The schedule that runs the main loop of schedule execution.
/// # Note
///
/// This is initially set to [`Main`].
pub main_schedule_label: BoxedScheduleLabel,
/// Inside the runner function, `World::clear_trackers()` must be called periodically.
/// If that isn't called on a world, it may lead to memory leaks in `RemovedComponents`.
pub runner: Box<dyn FnOnce(App) + Send + Sync>,
sub_apps: HashMap<AppLabelId, SubApp>,
plugin_registry: Vec<Box<dyn Plugin>>,
plugin_name_added: HashSet<String>,
Expand All @@ -95,7 +96,7 @@ impl Debug for App {
/// # Example
///
/// ```rust
/// # use bevy_app::{App, AppLabel, SubApp, Main};
/// # use bevy_app::{App, AppLabel, SubApp, UpdateFlow};
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::schedule::ScheduleLabel;
///
Expand All @@ -116,7 +117,7 @@ impl Debug for App {
/// sub_app.insert_resource(Val(100));
///
/// // initialize main schedule
/// sub_app.add_systems(Main, |counter: Res<Val>| {
/// sub_app.add_systems(UpdateFlow, |counter: Res<Val>| {
/// // since we assigned the value from the main world in extract
/// // we see that value instead of 100
/// assert_eq!(counter.0, 10);
Expand All @@ -135,28 +136,34 @@ pub struct SubApp {
/// The [`SubApp`]'s instance of [`App`]
pub app: App,

/// The schedule to run by default.
///
/// This is initially set to [`UpdateFlow`].
pub main_schedule_label: BoxedScheduleLabel,

/// A function that allows access to both the main [`App`] [`World`] and the [`SubApp`]. This is
/// useful for moving data between the sub app and the main app.
extract: Box<dyn Fn(&mut World, &mut App) + Send>,
extract: Box<dyn Fn(&mut World, &mut App) + Send + Sync>,
}

impl SubApp {
/// Creates a new [`SubApp`].
///
/// The provided function `extract` is normally called by the [`update`](App::update) method.
/// The provided function `extract` is normally called by the [`run_schedule`](World::run_schedule) method.
/// After extract is called, the [`Schedule`] of the sub app is run. The [`World`]
/// parameter represents the main app world, while the [`App`] parameter is just a mutable
/// reference to the `SubApp` itself.
pub fn new(app: App, extract: impl Fn(&mut World, &mut App) + Send + 'static) -> Self {
pub fn new(app: App, extract: impl Fn(&mut World, &mut App) + Send + Sync + 'static) -> Self {
Self {
app,
main_schedule_label: Box::new(UpdateFlow),
extract: Box::new(extract),
}
}

/// Runs the [`SubApp`]'s default schedule.
pub fn run(&mut self) {
self.app.world.run_schedule(&*self.app.main_schedule_label);
self.app.world.run_schedule(&*self.main_schedule_label);
self.app.world.clear_trackers();
}

Expand Down Expand Up @@ -219,38 +226,19 @@ impl App {
sub_apps: HashMap::default(),
plugin_registry: Vec::default(),
plugin_name_added: Default::default(),
main_schedule_label: Box::new(Main),
building_plugin_depth: 0,
}
}

/// Advances the execution of the [`Schedule`] by one cycle.
///
/// This method also updates sub apps.
/// Update sub apps.
/// See [`insert_sub_app`](Self::insert_sub_app) for more details.
///
/// The schedule run by this method is determined by the [`main_schedule_label`](App) field.
/// By default this is [`Main`].
///
/// # Panics
///
/// The active schedule of the app must be set before this method is called.
pub fn update(&mut self) {
#[cfg(feature = "trace")]
let _bevy_update_span = info_span!("update").entered();
{
#[cfg(feature = "trace")]
let _bevy_main_update_span = info_span!("main app").entered();
self.world.run_schedule(&*self.main_schedule_label);
}
pub fn update_sub_apps(&mut self) {
for (_label, sub_app) in self.sub_apps.iter_mut() {
#[cfg(feature = "trace")]
let _sub_app_span = info_span!("sub app", name = ?_label).entered();
sub_app.extract(&mut self.world);
sub_app.run();
}

self.world.clear_trackers();
}

/// Starts the application by calling the app's [runner function](Self::set_runner).
Expand Down Expand Up @@ -293,7 +281,7 @@ impl App {
}

/// Check that [`Plugin::ready`] of all plugins returns true. This is usually called by the
/// event loop, but can be useful for situations where you want to use [`App::update`]
/// event loop, but can be useful for situations where you want to no use [`App::run`]
pub fn ready(&self) -> bool {
for plugin in &self.plugin_registry {
if !plugin.ready(self) {
Expand All @@ -304,8 +292,8 @@ impl App {
}

/// Run [`Plugin::finish`] for each plugin. This is usually called by the event loop once all
/// plugins are [`App::ready`], but can be useful for situations where you want to use
/// [`App::update`].
/// plugins are [`App::ready`], but can be useful for situations where you want to no use
/// [`App::run`].
pub fn finish(&mut self) {
// temporarily remove the plugin registry to run each plugin's setup function on app.
let plugin_registry = std::mem::take(&mut self.plugin_registry);
Expand All @@ -316,7 +304,7 @@ impl App {
}

/// Run [`Plugin::cleanup`] for each plugin. This is usually called by the event loop after
/// [`App::finish`], but can be useful for situations where you want to use [`App::update`].
/// [`App::finish`], but can be useful for situations where you want to no use [`App::run`].
pub fn cleanup(&mut self) {
// temporarily remove the plugin registry to run each plugin's setup function on app.
let plugin_registry = std::mem::take(&mut self.plugin_registry);
Expand Down Expand Up @@ -636,6 +624,11 @@ impl App {
/// The runner function is usually not set manually, but by Bevy integrated plugins
/// (e.g. `WinitPlugin`).
///
/// # Note
///
/// Inside the runner function, `World::clear_trackers()` must be called periodically.
/// If that isn't called on a world, it may lead to memory leaks in `RemovedComponents`.
///
/// # Examples
///
/// ```
Expand All @@ -644,14 +637,14 @@ impl App {
/// fn my_runner(mut app: App) {
/// loop {
/// println!("In main loop");
/// app.update();
/// app.world.run_schedule(UpdateFlow);
/// }
/// }
///
/// App::new()
/// .set_runner(my_runner);
/// ```
pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static + Send) -> &mut Self {
pub fn set_runner(&mut self, run_fn: impl FnOnce(App) + 'static + Send + Sync) -> &mut Self {
self.runner = Box::new(run_fn);
self
}
Expand Down Expand Up @@ -970,7 +963,12 @@ fn run_once(mut app: App) {
app.finish();
app.cleanup();

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

/// An event that indicates the [`App`] should exit. This will fully exit the app process at the
Expand Down
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, Main, PostStartup, PostUpdate, PreStartup, PreUpdate,
Startup, StateTransition, Update,
Control, First, FixedUpdate, FrameReady, Last, PostStartup, PostUpdate, PreStartup,
PreUpdate, RenderFlow, Startup, StartupFlow, StateTransition, Update, UpdateFlow,
},
DynamicPlugin, Plugin, PluginGroup,
};
Expand Down
Loading