Skip to content

Commit

Permalink
Replace App::update() to World::run_schedule()
Browse files Browse the repository at this point in the history
Moved the responsibility for running top-level schedules
from `App` struct to the runner function.
  • Loading branch information
B-head committed Jun 27, 2023
1 parent efad083 commit 5aab12f
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 61 deletions.
54 changes: 25 additions & 29 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,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 FnOnce(App) + Send + Sync>,
/// 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 Down Expand Up @@ -135,6 +134,11 @@ pub struct SubApp {
/// The [`SubApp`]'s instance of [`App`]
pub app: App,

/// The schedule to run by default.
///
/// This is initially set to [`Main`].
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 + Sync>,
Expand All @@ -150,13 +154,14 @@ impl SubApp {
pub fn new(app: App, extract: impl Fn(&mut World, &mut App) + Send + Sync + 'static) -> Self {
Self {
app,
main_schedule_label: Box::new(Main),
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 +224,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 @@ -636,6 +622,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,7 +635,7 @@ impl App {
/// fn my_runner(mut app: App) {
/// loop {
/// println!("In main loop");
/// app.update();
/// app.world.run_schedule(Main);
/// }
/// }
///
Expand Down Expand Up @@ -970,7 +961,12 @@ fn run_once(mut app: App) {
app.finish();
app.cleanup();

app.update();
{
#[cfg(feature = "trace")]
let _main_schedule_span = info_span!("main schedule", name = ?Main).entered();
app.world.run_schedule(Main);
}
app.update_sub_apps();
}

/// An event that indicates the [`App`] should exit. This will fully exit the app process at the
Expand Down
46 changes: 42 additions & 4 deletions crates/bevy_app/src/schedule_runner.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use crate::{
app::{App, AppExit},
plugin::Plugin,
Main,
};
use bevy_ecs::{
event::{Events, ManualEventReader},
schedule::BoxedScheduleLabel,
};
use bevy_ecs::event::{Events, ManualEventReader};
use bevy_utils::{Duration, Instant};

#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

#[cfg(target_arch = "wasm32")]
use std::{cell::RefCell, rc::Rc};
#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -43,17 +50,21 @@ impl Default for RunMode {
/// typically, the `winit` event loop
/// (see [`WinitPlugin`](https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html))
/// executes the schedule making [`ScheduleRunnerPlugin`] unnecessary.
#[derive(Default)]
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 is initially set to [`Main`].
pub main_schedule_label: BoxedScheduleLabel,
}

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

Expand All @@ -63,18 +74,38 @@ impl ScheduleRunnerPlugin {
run_mode: RunMode::Loop {
wait: Some(wait_duration),
},
main_schedule_label: Box::new(Main),
}
}
}

impl Default for ScheduleRunnerPlugin {
fn default() -> Self {
ScheduleRunnerPlugin {
run_mode: RunMode::Loop { wait: None },
main_schedule_label: Box::new(Main),
}
}
}

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();
app.set_runner(move |mut app: App| {
// Prevent panic when schedules do not exist
app.init_schedule(main_schedule_label.clone());

let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
match run_mode {
RunMode::Once => {
app.update();
{
#[cfg(feature = "trace")]
let _main_schedule_span =
info_span!("main schedule", name = ?main_schedule_label).entered();
app.world.run_schedule(main_schedule_label);
}
app.update_sub_apps();
}
RunMode::Loop { wait } => {
let mut tick = move |app: &mut App,
Expand All @@ -91,7 +122,14 @@ impl Plugin for ScheduleRunnerPlugin {
}
}

app.update();
{
#[cfg(feature = "trace")]
let _main_schedule_span =
info_span!("main schedule", name = ?main_schedule_label).entered();
app.world.run_schedule(&main_schedule_label);
}
app.update_sub_apps();
app.world.clear_trackers();

if let Some(app_exit_events) =
app.world.get_resource_mut::<Events<AppExit>>()
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_asset/src/asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ pub fn free_unused_assets_system(asset_server: Res<AssetServer>) {
mod test {
use super::*;
use crate::{loader::LoadedAsset, update_asset_storage_system};
use bevy_app::{App, Update};
use bevy_app::{App, Main, Update};
use bevy_ecs::prelude::*;
use bevy_reflect::{TypePath, TypeUuid};
use bevy_utils::BoxedFuture;
Expand Down Expand Up @@ -896,19 +896,19 @@ mod test {
// asset is loading
assert_eq!(LoadState::Loading, get_load_state(&handle, &app.world));

app.update();
app.world.run_schedule(Main);
// asset should exist and be loaded at this point
assert_eq!(LoadState::Loaded, get_load_state(&handle, &app.world));
assert!(get_asset(&handle, &app.world).is_some());

// after dropping the handle, next call to `tick` will prepare the assets for removal.
drop(handle);
app.update();
app.world.run_schedule(Main);
assert_eq!(LoadState::Loaded, get_load_state(&weak_handle, &app.world));
assert!(get_asset(&weak_handle, &app.world).is_some());

// second call to tick will actually remove the asset.
app.update();
app.world.run_schedule(Main);
assert_eq!(
LoadState::Unloaded,
get_load_state(&weak_handle, &app.world)
Expand All @@ -918,7 +918,7 @@ mod test {
// finally, reload the asset
let handle = load_asset(path.clone(), &app.world).typed();
assert_eq!(LoadState::Loading, get_load_state(&handle, &app.world));
app.update();
app.world.run_schedule(Main);
assert_eq!(LoadState::Loaded, get_load_state(&handle, &app.world));
assert!(get_asset(&handle, &app.world).is_some());
}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_asset/src/debug_asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Internal assets (e.g. shaders) are bundled directly into an application and can't be hot
//! reloaded using the conventional API.
use bevy_app::{App, Plugin, Update};
use bevy_app::{App, Main, Plugin, Update};
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
use bevy_utils::{Duration, HashMap};
Expand Down Expand Up @@ -81,7 +81,7 @@ impl Plugin for DebugAssetServerPlugin {
}

fn run_debug_asset_app(mut debug_asset_app: NonSendMut<DebugAssetApp>) {
debug_asset_app.0.update();
debug_asset_app.0.world.run_schedule(Main);
}

pub(crate) fn sync_debug_assets<T: Asset + Clone>(
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ mod tests {
TypeRegistrationPlugin::default(),
FrameCountPlugin::default(),
));
app.update();
app.world.run_schedule(Main);

let frame_count = app.world.resource::<FrameCount>();
assert_eq!(1, frame_count.0);
Expand Down
13 changes: 7 additions & 6 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ impl Plugin for RenderPlugin {
app.init_resource::<ScratchMainWorld>();

let mut render_app = App::empty();
render_app.main_schedule_label = Box::new(Render);

let mut extract_schedule = Schedule::new();
extract_schedule.set_apply_final_deferred(false);
Expand Down Expand Up @@ -296,14 +295,14 @@ impl Plugin for RenderPlugin {
app.insert_resource(receiver);
render_app.insert_resource(sender);

app.insert_sub_app(RenderApp, SubApp::new(render_app, move |main_world, render_app| {
let mut sub_app = SubApp::new(render_app, move |main_world, render_app| {
#[cfg(feature = "trace")]
let _render_span = bevy_utils::tracing::info_span!("extract main app to render subapp").entered();
let _render_span =
bevy_utils::tracing::info_span!("extract main app to render subapp").entered();
{
#[cfg(feature = "trace")]
let _stage_span =
bevy_utils::tracing::info_span!("reserve_and_flush")
.entered();
bevy_utils::tracing::info_span!("reserve_and_flush").entered();

// reserve all existing main world entities for use in render_app
// they can only be spawned using `get_or_spawn()`
Expand All @@ -326,7 +325,9 @@ impl Plugin for RenderPlugin {

// run extract schedule
extract(main_world, render_app);
}));
});
sub_app.main_schedule_label = Box::new(Render);
app.insert_sub_app(RenderApp, sub_app);
}

app.add_plugins((
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_render/src/view/visibility/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ mod test {
.entity_mut(root2_child2)
.push_children(&[root2_child2_grandchild1]);

app.update();
app.world.run_schedule(Main);

let is_visible = |e: Entity| {
app.world
Expand Down Expand Up @@ -613,7 +613,7 @@ mod test {
.entity_mut(root1_child2)
.push_children(&[root1_child2_grandchild1]);

app.update();
app.world.run_schedule(Main);

let is_visible = |e: Entity| {
app.world
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_transform/src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,13 +424,13 @@ mod test {
})
.id();

app.update();
app.world.run_schedule(Main);

// check the `Children` structure is spawned
assert_eq!(&**app.world.get::<Children>(parent).unwrap(), &[child]);
assert_eq!(&**app.world.get::<Children>(child).unwrap(), &[grandchild]);
// Note that at this point, the `GlobalTransform`s will not have updated yet, due to `Commands` delay
app.update();
app.world.run_schedule(Main);

let mut state = app.world.query::<&GlobalTransform>();
for global in state.iter(&app.world) {
Expand Down Expand Up @@ -474,6 +474,6 @@ mod test {
&mut *temp.get_mut::<Parent>(grandchild).unwrap(),
);

app.update();
app.world.run_schedule(Main);
}
}
Loading

0 comments on commit 5aab12f

Please sign in to comment.