Skip to content

make it easier to change app to single threaded stages #5966

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

Closed
wants to merge 9 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 58 additions & 23 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,27 @@ struct SubApp {
runner: Box<dyn Fn(&mut World, &mut App)>,
}

impl Default for App {
fn default() -> Self {
let mut app = App::empty();
#[cfg(feature = "bevy_reflect")]
app.init_resource::<AppTypeRegistry>();

app.add_default_stages()
.add_event::<AppExit>()
.add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system());
/// Option for [`App::add_default_stages`] that sets the stages to be single or multi threaded.
pub enum DefaultStagesThreading {
/// run app single threaded
Single,
/// run app multi threaded
Multi,
}

#[cfg(feature = "bevy_ci_testing")]
{
crate::ci_testing::setup_app(&mut app);
impl DefaultStagesThreading {
/// Gets a new instance of [`SystemStage`] for the corresponding value of [`DefaultStagesThreading`]
pub fn get_stage(&self) -> SystemStage {
match self {
DefaultStagesThreading::Multi => SystemStage::parallel(),
DefaultStagesThreading::Single => SystemStage::single_threaded(),
}
}
}

app
impl Default for App {
fn default() -> Self {
App::multi_threaded()
}
}

Expand All @@ -114,6 +119,33 @@ impl App {
}
}

/// Creates a new [`App`] with some default structure with all default stages set to use single threaded system executor.
pub fn single_threaded() -> App {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ideally this is an "app setting", which we use for all app stages (by default). Ex: replace instances of add_stage(Label, stage_reference) with add_default_stage(Label). And with stageless on the horizon, this would need to be a global app setting anyway (ex: your only options are to use a single threaded app executor or a multithreaded app executor).
What are your thoughts on waiting for stageless before defining this api vs building this api (with stageless in mind)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with closing this out until after stageless.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And with stageless on the horizon, this would need to be a global app setting anyway

This could be a per schedule setting in stageless.

App::new_with_threading_option(DefaultStagesThreading::Single)
}

/// Creates a new [`App`] with some default structure with all default stages set to use multithreaded system executor.
pub fn multi_threaded() -> App {
App::new_with_threading_option(DefaultStagesThreading::Multi)
}

fn new_with_threading_option(threading: DefaultStagesThreading) -> App {
let mut app = App::empty();
#[cfg(feature = "bevy_reflect")]
app.init_resource::<AppTypeRegistry>();

app.add_default_stages(threading)
.add_event::<AppExit>()
.add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system());

#[cfg(feature = "bevy_ci_testing")]
{
crate::ci_testing::setup_app(&mut app);
}

app
}

/// Advances the execution of the [`Schedule`] by one cycle.
///
/// This method also updates sub apps.
Expand Down Expand Up @@ -581,6 +613,8 @@ impl App {
/// done by default by calling `App::default`, which is in turn called by
/// [`App::new`].
///
/// Use [`DefaultStagesThreading`] to specify the threading model to be used by the default stages.
///
/// # The stages
///
/// All the added stages, with the exception of the startup stage, run every time the
Expand All @@ -606,23 +640,24 @@ impl App {
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_app::DefaultStagesThreading;
/// #
/// let app = App::empty().add_default_stages();
/// let app = App::empty().add_default_stages(DefaultStagesThreading::Multi);
/// ```
pub fn add_default_stages(&mut self) -> &mut Self {
self.add_stage(CoreStage::First, SystemStage::parallel())
pub fn add_default_stages(&mut self, threading: DefaultStagesThreading) -> &mut Self {
self.add_stage(CoreStage::First, threading.get_stage())
.add_stage(
StartupSchedule,
Schedule::default()
.with_run_criteria(ShouldRun::once)
.with_stage(StartupStage::PreStartup, SystemStage::parallel())
.with_stage(StartupStage::Startup, SystemStage::parallel())
.with_stage(StartupStage::PostStartup, SystemStage::parallel()),
.with_stage(StartupStage::PreStartup, threading.get_stage())
.with_stage(StartupStage::Startup, threading.get_stage())
.with_stage(StartupStage::PostStartup, threading.get_stage()),
)
.add_stage(CoreStage::PreUpdate, SystemStage::parallel())
.add_stage(CoreStage::Update, SystemStage::parallel())
.add_stage(CoreStage::PostUpdate, SystemStage::parallel())
.add_stage(CoreStage::Last, SystemStage::parallel())
.add_stage(CoreStage::PreUpdate, threading.get_stage())
.add_stage(CoreStage::Update, threading.get_stage())
.add_stage(CoreStage::PostUpdate, threading.get_stage())
.add_stage(CoreStage::Last, threading.get_stage())
}

/// Setup the application to manage events of type `T`.
Expand Down