Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework winit runner #9034

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
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