Skip to content

Commit ed449db

Browse files
committed
that's a lot of files
1 parent 7c70fc1 commit ed449db

34 files changed

+2528
-2609
lines changed

crates/bevy_app/src/app.rs

Lines changed: 342 additions & 602 deletions
Large diffs are not rendered by default.

crates/bevy_app/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod main_schedule;
88
mod plugin;
99
mod plugin_group;
1010
mod schedule_runner;
11+
mod sub_app;
1112

1213
#[cfg(feature = "bevy_ci_testing")]
1314
pub mod ci_testing;
@@ -18,6 +19,7 @@ pub use main_schedule::*;
1819
pub use plugin::*;
1920
pub use plugin_group::*;
2021
pub use schedule_runner::*;
22+
pub use sub_app::*;
2123

2224
#[allow(missing_docs)]
2325
pub mod prelude {
@@ -28,6 +30,7 @@ pub mod prelude {
2830
First, FixedUpdate, Last, Main, PostStartup, PostUpdate, PreStartup, PreUpdate,
2931
Startup, StateTransition, Update,
3032
},
33+
sub_app::SubApp,
3134
DynamicPlugin, Plugin, PluginGroup,
3235
};
3336
}

crates/bevy_app/src/plugin.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub trait Plugin: Downcast + Any + Send + Sync {
2525
/// Configures the [`App`] to which this plugin is added.
2626
fn build(&self, app: &mut App);
2727

28-
/// Has the plugin finished it's setup? This can be useful for plugins that needs something
28+
/// Has the plugin finished its setup? This can be useful for plugins that needs something
2929
/// asynchronous to happen before they can finish their setup, like renderer initialization.
3030
/// Once the plugin is ready, [`finish`](Plugin::finish) should be called.
3131
fn ready(&self, _app: &App) -> bool {
@@ -60,6 +60,13 @@ pub trait Plugin: Downcast + Any + Send + Sync {
6060

6161
impl_downcast!(Plugin);
6262

63+
/// A dummy plugin that's to temporarily occupy an entry in an app's plugin registry.
64+
pub(crate) struct PlaceholderPlugin;
65+
66+
impl Plugin for PlaceholderPlugin {
67+
fn build(&self, _app: &mut App) {}
68+
}
69+
6370
/// A type representing an unsafe function that returns a mutable pointer to a [`Plugin`].
6471
/// It is used for dynamically loading plugins.
6572
///

crates/bevy_app/src/schedule_runner.rs

Lines changed: 88 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,65 @@
11
use crate::{
2-
app::{App, AppExit},
2+
app::{App, AppExit, AppThreadEvent, SubApps},
33
plugin::Plugin,
44
};
55
use bevy_ecs::event::{Events, ManualEventReader};
6+
use bevy_ecs::storage::ThreadLocalAccessor;
67
use bevy_utils::{Duration, Instant};
78

89
#[cfg(target_arch = "wasm32")]
910
use std::{cell::RefCell, rc::Rc};
1011
#[cfg(target_arch = "wasm32")]
1112
use wasm_bindgen::{prelude::*, JsCast};
1213

13-
/// Determines the method used to run an [`App`]'s [`Schedule`](bevy_ecs::schedule::Schedule).
14-
///
15-
/// It is used in the [`ScheduleRunnerPlugin`].
14+
/// Determines how frequently the [`App`] should be updated by the [`ScheduleRunnerPlugin`].
1615
#[derive(Copy, Clone, Debug)]
1716
pub enum RunMode {
18-
/// Indicates that the [`App`]'s schedule should run repeatedly.
17+
/// The [`App`] will update once.
18+
Once,
19+
/// The [`App`] will update over and over, until an [`AppExit`] event appears.
1920
Loop {
20-
/// The minimum [`Duration`] to wait after a [`Schedule`](bevy_ecs::schedule::Schedule)
21-
/// has completed before repeating. A value of [`None`] will not wait.
22-
wait: Option<Duration>,
21+
/// The minimum time from the start of one update to the next.
22+
///
23+
/// **Note:** This has no upper limit, but the [`App`] will hang if you set this too high.
24+
wait: Duration,
2325
},
24-
/// Indicates that the [`App`]'s schedule should run only once.
25-
Once,
2626
}
2727

2828
impl Default for RunMode {
2929
fn default() -> Self {
30-
RunMode::Loop { wait: None }
30+
RunMode::Loop {
31+
wait: Duration::ZERO,
32+
}
3133
}
3234
}
3335

34-
/// Configures an [`App`] to run its [`Schedule`](bevy_ecs::schedule::Schedule) according to a given
35-
/// [`RunMode`].
36+
/// Runs an [`App`] according to the selected [`RunMode`].
3637
///
37-
/// [`ScheduleRunnerPlugin`] is included in the
38-
/// [`MinimalPlugins`](https://docs.rs/bevy/latest/bevy/struct.MinimalPlugins.html) plugin group.
38+
/// This plugin is included in the [`MinimalPlugins`] group, but **not** included in the
39+
/// [`DefaultPlugins`] group. [`DefaultPlugins`] assumes the [`App`] will render to a window,
40+
/// so it comes with the [`WinitPlugin`] instead.
3941
///
40-
/// [`ScheduleRunnerPlugin`] is *not* included in the
41-
/// [`DefaultPlugins`](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html) plugin group
42-
/// which assumes that the [`Schedule`](bevy_ecs::schedule::Schedule) will be executed by other means:
43-
/// typically, the `winit` event loop
44-
/// (see [`WinitPlugin`](https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html))
45-
/// executes the schedule making [`ScheduleRunnerPlugin`] unnecessary.
42+
/// [`DefaultPlugins`]: https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html
43+
/// [`MinimalPlugins`]: https://docs.rs/bevy/latest/bevy/struct.MinimalPlugins.html
44+
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
4645
#[derive(Default)]
4746
pub struct ScheduleRunnerPlugin {
48-
/// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly.
47+
/// Determines how frequently the [`App`] should update.
4948
pub run_mode: RunMode,
5049
}
5150

5251
impl ScheduleRunnerPlugin {
5352
/// See [`RunMode::Once`].
5453
pub fn run_once() -> Self {
55-
ScheduleRunnerPlugin {
54+
Self {
5655
run_mode: RunMode::Once,
5756
}
5857
}
5958

6059
/// See [`RunMode::Loop`].
61-
pub fn run_loop(wait_duration: Duration) -> Self {
62-
ScheduleRunnerPlugin {
63-
run_mode: RunMode::Loop {
64-
wait: Some(wait_duration),
65-
},
60+
pub fn run_loop(wait: Duration) -> Self {
61+
Self {
62+
run_mode: RunMode::Loop { wait },
6663
}
6764
}
6865
}
@@ -71,61 +68,77 @@ impl Plugin for ScheduleRunnerPlugin {
7168
fn build(&self, app: &mut App) {
7269
let run_mode = self.run_mode;
7370
app.set_runner(move |mut app: App| {
74-
while !app.ready() {
71+
while !app.is_ready() {
7572
#[cfg(not(target_arch = "wasm32"))]
7673
bevy_tasks::tick_global_task_pools_on_main_thread();
7774
}
7875
app.finish();
7976
app.cleanup();
8077

81-
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
78+
let mut exit_event_reader = ManualEventReader::<AppExit>::default();
8279
match run_mode {
8380
RunMode::Once => {
8481
app.update();
8582
}
8683
RunMode::Loop { wait } => {
87-
let mut tick = move |app: &mut App,
88-
wait: Option<Duration>|
89-
-> Result<Option<Duration>, AppExit> {
84+
let mut update = move |sub_apps: &mut SubApps| -> Result<Duration, AppExit> {
9085
let start_time = Instant::now();
86+
sub_apps.update();
87+
let end_time = Instant::now();
9188

92-
if let Some(app_exit_events) =
93-
app.world.get_resource_mut::<Events<AppExit>>()
89+
if let Some(exit_events) =
90+
sub_apps.main.world.get_resource_mut::<Events<AppExit>>()
9491
{
95-
if let Some(exit) = app_exit_event_reader.iter(&app_exit_events).last()
96-
{
92+
if let Some(exit) = exit_event_reader.iter(&exit_events).last() {
9793
return Err(exit.clone());
9894
}
9995
}
10096

101-
app.update();
102-
103-
if let Some(app_exit_events) =
104-
app.world.get_resource_mut::<Events<AppExit>>()
105-
{
106-
if let Some(exit) = app_exit_event_reader.iter(&app_exit_events).last()
107-
{
108-
return Err(exit.clone());
109-
}
97+
let elapsed = end_time - start_time;
98+
if elapsed < wait {
99+
return Ok(wait - elapsed);
110100
}
111101

112-
let end_time = Instant::now();
102+
Ok(Duration::ZERO)
103+
};
113104

114-
if let Some(wait) = wait {
115-
let exe_time = end_time - start_time;
116-
if exe_time < wait {
117-
return Ok(Some(wait - exe_time));
118-
}
119-
}
105+
let (mut sub_apps, mut tls, _) = app.into_parts();
120106

121-
Ok(None)
122-
};
107+
// create event loop channel
108+
let (send, recv) = std::sync::mpsc::channel();
109+
110+
// insert TLS accessor
111+
sub_apps.for_each(|sub_app| {
112+
// SAFETY: `tls` is not moved or dropped until `access` has been dropped.
113+
let access = unsafe {
114+
ThreadLocalAccessor::new(std::ptr::addr_of_mut!(tls), send.clone())
115+
};
116+
sub_app.world.insert_resource(access);
117+
});
123118

124119
#[cfg(not(target_arch = "wasm32"))]
125120
{
126-
while let Ok(delay) = tick(&mut app, wait) {
127-
if let Some(delay) = delay {
128-
std::thread::sleep(delay);
121+
// Move sub-apps to another thread and run an event loop in this thread.
122+
let handle = std::thread::spawn(move || {
123+
while let Ok(sleep) = update(&mut sub_apps) {
124+
if !sleep.is_zero() {
125+
std::thread::sleep(sleep);
126+
}
127+
}
128+
129+
send.send(AppThreadEvent::Exit(sub_apps));
130+
});
131+
132+
loop {
133+
let event = recv.recv().unwrap();
134+
match event {
135+
AppThreadEvent::RunTask(f) => {
136+
f(&mut tls);
137+
}
138+
AppThreadEvent::Exit(sub_apps) => {
139+
handle.join();
140+
break;
141+
}
129142
}
130143
}
131144
}
@@ -141,25 +154,32 @@ impl Plugin for ScheduleRunnerPlugin {
141154
)
142155
.expect("Should register `setTimeout`.");
143156
}
144-
let asap = Duration::from_millis(1);
145157

146-
let mut rc = Rc::new(app);
158+
let min_sleep = Duration::from_millis(1);
159+
160+
let mut rc = Rc::new(sub_apps);
147161
let f = Rc::new(RefCell::new(None));
148162
let g = f.clone();
149163

150-
let c = move || {
151-
let mut app = Rc::get_mut(&mut rc).unwrap();
152-
let delay = tick(&mut app, wait);
153-
match delay {
154-
Ok(delay) => {
155-
set_timeout(f.borrow().as_ref().unwrap(), delay.unwrap_or(asap))
164+
let closure = move || {
165+
let mut sub_apps = Rc::get_mut(&mut rc).unwrap();
166+
match update(&mut sub_apps) {
167+
Ok(sleep) => {
168+
set_timeout(f.borrow().as_ref().unwrap(), sleep.max(min_sleep))
156169
}
157170
Err(_) => {}
158171
}
159172
};
160-
*g.borrow_mut() = Some(Closure::wrap(Box::new(c) as Box<dyn FnMut()>));
161-
set_timeout(g.borrow().as_ref().unwrap(), asap);
173+
174+
*g.borrow_mut() =
175+
Some(Closure::wrap(Box::new(closure) as Box<dyn FnMut()>));
176+
177+
set_timeout(g.borrow().as_ref().unwrap(), min_sleep);
162178
};
179+
180+
// remove TLS accessor
181+
sub_apps
182+
.for_each(|sub_app| sub_app.world.remove_resource::<ThreadLocalAccessor>());
163183
}
164184
}
165185
});

0 commit comments

Comments
 (0)