# Objective

NOTE: This depends on #7267 and should not be merged until #7267 is merged. If you are reviewing this before that is merged, I highly recommend viewing the Base Sets commit instead of trying to find my changes amongst those from #7267.

"Default sets" as described by the [Stageless RFC](https://github.com/bevyengine/rfcs/pull/45) have some [unfortunate consequences](https://github.com/bevyengine/bevy/discussions/7365).

## Solution

This adds "base sets" as a variant of `SystemSet`:

A set is a "base set" if `SystemSet::is_base` returns `true`. Typically this will be opted-in to using the `SystemSet` derive:

```rust
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
#[system_set(base)]
enum MyBaseSet {
  A,
  B,
}
``` 

**Base sets are exclusive**: a system can belong to at most one "base set". Adding a system to more than one will result in an error. When possible we fail immediately during system-config-time with a nice file + line number. For the more nested graph-ey cases, this will fail at the final schedule build. 

**Base sets cannot belong to other sets**: this is where the word "base" comes from

Systems and Sets can only be added to base sets using `in_base_set`. Calling `in_set` with a base set will fail. As will calling `in_base_set` with a normal set.

```rust
app.add_system(foo.in_base_set(MyBaseSet::A))
       // X must be a normal set ... base sets cannot be added to base sets
       .configure_set(X.in_base_set(MyBaseSet::A))
```

Base sets can still be configured like normal sets:

```rust
app.add_system(MyBaseSet::B.after(MyBaseSet::Ap))
``` 

The primary use case for base sets is enabling a "default base set":

```rust
schedule.set_default_base_set(CoreSet::Update)
  // this will belong to CoreSet::Update by default
  .add_system(foo)
  // this will override the default base set with PostUpdate
  .add_system(bar.in_base_set(CoreSet::PostUpdate))
```

This allows us to build apis that work by default in the standard Bevy style. This is a rough analog to the "default stage" model, but it use the new "stageless sets" model instead, with all of the ordering flexibility (including exclusive systems) that it provides.

---

## Changelog

- Added "base sets" and ported CoreSet to use them.

## Migration Guide

TODO
This commit is contained in:
Carter Anderson 2023-02-06 03:10:08 +00:00
parent 206c7ce219
commit dcc03724a5
84 changed files with 916 additions and 274 deletions

View File

@ -552,7 +552,7 @@ impl Plugin for AnimationPlugin {
.register_type::<AnimationPlayer>()
.add_system(
animation_player
.in_set(CoreSet::PostUpdate)
.in_base_set(CoreSet::PostUpdate)
.before(TransformSystem::TransformPropagate),
);
}

View File

@ -328,14 +328,14 @@ impl App {
apply_state_transition::<S>,
)
.chain()
.in_set(CoreSet::StateTransitions),
.in_base_set(CoreSet::StateTransitions),
);
let main_schedule = self.get_schedule_mut(CoreSchedule::Main).unwrap();
for variant in S::variants() {
main_schedule.configure_set(
OnUpdate(variant.clone())
.in_set(CoreSet::StateTransitions)
.in_base_set(CoreSet::StateTransitions)
.run_if(state_equals(variant))
.after(apply_state_transition::<S>),
);
@ -580,7 +580,7 @@ impl App {
{
if !self.world.contains_resource::<Events<T>>() {
self.init_resource::<Events<T>>()
.add_system(Events::<T>::update_system.in_set(CoreSet::First));
.add_system(Events::<T>::update_system.in_base_set(CoreSet::First));
}
self
}

View File

@ -29,8 +29,8 @@ pub mod prelude {
use bevy_ecs::{
schedule_v3::{
apply_system_buffers, IntoSystemConfig, IntoSystemSetConfig, Schedule, ScheduleLabel,
SystemSet,
apply_system_buffers, IntoSystemConfig, IntoSystemSetConfig, IntoSystemSetConfigs,
Schedule, ScheduleLabel, SystemSet,
},
system::Local,
world::World,
@ -90,6 +90,7 @@ impl CoreSchedule {
/// that runs immediately after the matching system set.
/// These can be useful for ordering, but you almost never want to add your systems to these sets.
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
#[system_set(base)]
pub enum CoreSet {
/// Runs before all other members of this set.
First,
@ -129,20 +130,30 @@ impl CoreSet {
let mut schedule = Schedule::new();
// Create "stage-like" structure using buffer flushes + ordering
schedule.add_system(apply_system_buffers.in_set(FirstFlush));
schedule.add_system(apply_system_buffers.in_set(PreUpdateFlush));
schedule.add_system(apply_system_buffers.in_set(UpdateFlush));
schedule.add_system(apply_system_buffers.in_set(PostUpdateFlush));
schedule.add_system(apply_system_buffers.in_set(LastFlush));
schedule.configure_set(First.before(FirstFlush));
schedule.configure_set(PreUpdate.after(FirstFlush).before(PreUpdateFlush));
schedule.configure_set(StateTransitions.after(PreUpdateFlush).before(FixedUpdate));
schedule.configure_set(FixedUpdate.after(StateTransitions).before(Update));
schedule.configure_set(Update.after(FixedUpdate).before(UpdateFlush));
schedule.configure_set(PostUpdate.after(UpdateFlush).before(PostUpdateFlush));
schedule.configure_set(Last.after(PostUpdateFlush).before(LastFlush));
schedule
.set_default_base_set(Update)
.add_system(apply_system_buffers.in_base_set(FirstFlush))
.add_system(apply_system_buffers.in_base_set(PreUpdateFlush))
.add_system(apply_system_buffers.in_base_set(UpdateFlush))
.add_system(apply_system_buffers.in_base_set(PostUpdateFlush))
.add_system(apply_system_buffers.in_base_set(LastFlush))
.configure_sets(
(
First,
FirstFlush,
PreUpdate,
PreUpdateFlush,
StateTransitions,
FixedUpdate,
Update,
UpdateFlush,
PostUpdate,
PostUpdateFlush,
Last,
LastFlush,
)
.chain(),
);
schedule
}
}

View File

@ -644,7 +644,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, CoreSet};
use bevy_app::App;
use bevy_ecs::prelude::*;
use bevy_reflect::TypeUuid;
use bevy_utils::BoxedFuture;
@ -852,16 +852,8 @@ mod test {
let mut app = App::new();
app.insert_resource(assets);
app.insert_resource(asset_server);
app.add_system(
free_unused_assets_system
.in_set(FreeUnusedAssets)
.in_set(CoreSet::Update),
);
app.add_system(
update_asset_storage_system::<PngAsset>
.after(FreeUnusedAssets)
.in_set(CoreSet::Update),
);
app.add_system(free_unused_assets_system.in_set(FreeUnusedAssets));
app.add_system(update_asset_storage_system::<PngAsset>.after(FreeUnusedAssets));
fn load_asset(path: AssetPath, world: &World) -> HandleUntyped {
let asset_server = world.resource::<AssetServer>();

View File

@ -331,8 +331,8 @@ impl AddAsset for App {
};
self.insert_resource(assets)
.add_system(Assets::<T>::asset_event_system.in_set(AssetSet::AssetEvents))
.add_system(update_asset_storage_system::<T>.in_set(AssetSet::LoadAssets))
.add_system(Assets::<T>::asset_event_system.in_base_set(AssetSet::AssetEvents))
.add_system(update_asset_storage_system::<T>.in_base_set(AssetSet::LoadAssets))
.register_type::<Handle<T>>()
.add_event::<AssetEvent<T>>()
}

View File

@ -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, CoreSet, Plugin};
use bevy_app::{App, Plugin};
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
use bevy_utils::HashMap;
@ -75,7 +75,7 @@ impl Plugin for DebugAssetServerPlugin {
watch_for_changes: true,
});
app.insert_non_send_resource(DebugAssetApp(debug_asset_app));
app.add_system(run_debug_asset_app.in_set(CoreSet::Update));
app.add_system(run_debug_asset_app);
}
}

View File

@ -19,7 +19,7 @@ impl<T: Asset> Default for AssetCountDiagnosticsPlugin<T> {
impl<T: Asset> Plugin for AssetCountDiagnosticsPlugin<T> {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.in_set(StartupSet::Startup))
.add_system(Self::diagnostic_system.in_set(CoreSet::Update));
.add_system(Self::diagnostic_system);
}
}

View File

@ -51,6 +51,7 @@ use bevy_ecs::prelude::*;
/// [`SystemSet`]s for asset loading in an [`App`] schedule.
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
#[system_set(base)]
pub enum AssetSet {
/// Asset storages are updated.
LoadAssets,
@ -109,22 +110,20 @@ impl Plugin for AssetPlugin {
app.configure_set(
AssetSet::LoadAssets
.no_default_set()
.before(CoreSet::PreUpdate)
.after(CoreSet::First),
)
.configure_set(
AssetSet::AssetEvents
.no_default_set()
.after(CoreSet::PostUpdate)
.before(CoreSet::Last),
)
.add_system(asset_server::free_unused_assets_system.in_set(CoreSet::PreUpdate));
.add_system(asset_server::free_unused_assets_system.in_base_set(CoreSet::PreUpdate));
#[cfg(all(
feature = "filesystem_watcher",
all(not(target_arch = "wasm32"), not(target_os = "android"))
))]
app.add_system(io::filesystem_watcher_system.in_set(AssetSet::LoadAssets));
app.add_system(io::filesystem_watcher_system.in_base_set(AssetSet::LoadAssets));
}
}

View File

@ -56,7 +56,7 @@ impl Plugin for AudioPlugin {
.add_asset::<AudioSource>()
.add_asset::<AudioSink>()
.init_resource::<Audio<AudioSource>>()
.add_system(play_queued_audio_system::<AudioSource>.in_set(CoreSet::PostUpdate));
.add_system(play_queued_audio_system::<AudioSource>.in_base_set(CoreSet::PostUpdate));
#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]
app.init_asset_loader::<AudioLoader>();
@ -71,6 +71,6 @@ impl AddAudioSource for App {
self.add_asset::<T>()
.init_resource::<Audio<T>>()
.init_resource::<AudioOutput<T>>()
.add_system(play_queued_audio_system::<T>.in_set(CoreSet::PostUpdate))
.add_system(play_queued_audio_system::<T>.in_base_set(CoreSet::PostUpdate))
}
}

View File

@ -107,7 +107,7 @@ impl Plugin for TaskPoolPlugin {
self.task_pool_options.create_default_pools();
#[cfg(not(target_arch = "wasm32"))]
app.add_system(tick_global_task_pools.in_set(bevy_app::CoreSet::Last));
app.add_system(tick_global_task_pools.in_base_set(bevy_app::CoreSet::Last));
}
}
/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread.
@ -142,7 +142,7 @@ pub struct FrameCountPlugin;
impl Plugin for FrameCountPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<FrameCount>();
app.add_system(update_frame_count.in_set(CoreSet::Last));
app.add_system(update_frame_count.in_base_set(CoreSet::Last));
}
}

View File

@ -10,7 +10,7 @@ pub struct EntityCountDiagnosticsPlugin;
impl Plugin for EntityCountDiagnosticsPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.in_set(StartupSet::Startup))
.add_system(Self::diagnostic_system.in_set(CoreSet::Update));
.add_system(Self::diagnostic_system);
}
}

View File

@ -11,7 +11,7 @@ pub struct FrameTimeDiagnosticsPlugin;
impl Plugin for FrameTimeDiagnosticsPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_startup_system(Self::setup_system.in_set(StartupSet::Startup))
.add_system(Self::diagnostic_system.in_set(CoreSet::Update));
.add_system(Self::diagnostic_system);
}
}

View File

@ -37,9 +37,9 @@ impl Plugin for LogDiagnosticsPlugin {
});
if self.debug {
app.add_system(Self::log_diagnostics_debug_system.in_set(CoreSet::PostUpdate));
app.add_system(Self::log_diagnostics_debug_system.in_base_set(CoreSet::PostUpdate));
} else {
app.add_system(Self::log_diagnostics_system.in_set(CoreSet::PostUpdate));
app.add_system(Self::log_diagnostics_system.in_base_set(CoreSet::PostUpdate));
}
}
}

View File

@ -16,7 +16,7 @@ pub struct SystemInformationDiagnosticsPlugin;
impl Plugin for SystemInformationDiagnosticsPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system(internal::setup_system.in_set(StartupSet::Startup))
.add_system(internal::diagnostic_system.in_set(CoreSet::Update));
.add_system(internal::diagnostic_system);
}
}

View File

@ -2,9 +2,10 @@ extern crate proc_macro;
mod component;
mod fetch;
mod set;
use crate::fetch::derive_world_query_impl;
use bevy_macro_utils::{derive_boxed_label, derive_set, get_named_struct_fields, BevyManifest};
use crate::{fetch::derive_world_query_impl, set::derive_set};
use bevy_macro_utils::{derive_boxed_label, get_named_struct_fields, BevyManifest};
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote};
@ -537,7 +538,7 @@ pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
}
/// Derive macro generating an impl of the trait `SystemSet`.
#[proc_macro_derive(SystemSet)]
#[proc_macro_derive(SystemSet, attributes(system_set))]
pub fn derive_system_set(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();

View File

@ -0,0 +1,84 @@
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
pub static SYSTEM_SET_ATTRIBUTE_NAME: &str = "system_set";
pub static BASE_ATTRIBUTE_NAME: &str = "base";
/// Derive a label trait
///
/// # Args
///
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
/// - `trait_path`: The path [`syn::Path`] to the label trait
pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
let ident = input.ident;
let mut is_base = false;
for attr in &input.attrs {
if !attr
.path
.get_ident()
.map_or(false, |ident| ident == SYSTEM_SET_ATTRIBUTE_NAME)
{
continue;
}
attr.parse_args_with(|input: ParseStream| {
let meta = input.parse_terminated::<syn::Meta, syn::token::Comma>(syn::Meta::parse)?;
for meta in meta {
let ident = meta.path().get_ident().unwrap_or_else(|| {
panic!(
"Unrecognized attribute: `{}`",
meta.path().to_token_stream()
)
});
if ident == BASE_ATTRIBUTE_NAME {
if let syn::Meta::Path(_) = meta {
is_base = true;
} else {
panic!(
"The `{BASE_ATTRIBUTE_NAME}` attribute is expected to have no value or arguments",
);
}
} else {
panic!(
"Unrecognized attribute: `{}`",
meta.path().to_token_stream()
);
}
}
Ok(())
})
.unwrap_or_else(|_| panic!("Invalid `{SYSTEM_SET_ATTRIBUTE_NAME}` attribute format"));
}
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(
syn::parse2(quote! {
Self: 'static + Send + Sync + Clone + Eq + ::std::fmt::Debug + ::std::hash::Hash
})
.unwrap(),
);
(quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn is_system_type(&self) -> bool {
false
}
fn is_base(&self) -> bool {
#is_base
}
fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
std::boxed::Box::new(std::clone::Clone::clone(self))
}
}
})
.into()
}

View File

@ -28,7 +28,7 @@ impl SystemSetConfig {
Self {
set,
graph_info: GraphInfo::default(),
graph_info: GraphInfo::system_set(),
conditions: Vec::new(),
}
}
@ -45,12 +45,11 @@ impl SystemConfig {
fn new(system: BoxedSystem) -> Self {
// include system in its default sets
let sets = system.default_system_sets().into_iter().collect();
let mut graph_info = GraphInfo::system();
graph_info.sets = sets;
Self {
system,
graph_info: GraphInfo {
sets,
..Default::default()
},
graph_info,
conditions: Vec::new(),
}
}
@ -87,9 +86,13 @@ pub trait IntoSystemSetConfig: sealed::IntoSystemSetConfig {
#[doc(hidden)]
fn into_config(self) -> SystemSetConfig;
/// Add to the provided `set`.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfig;
/// Don't add this set to the schedules's default set.
fn no_default_set(self) -> SystemSetConfig;
/// Add to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig;
/// Add this set to the schedules's default base set.
fn in_default_base_set(self) -> SystemSetConfig;
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig;
/// Run after all systems in `set`.
@ -117,12 +120,18 @@ where
SystemSetConfig::new(Box::new(self))
}
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_set(set)
}
fn no_default_set(self) -> SystemSetConfig {
self.into_config().no_default_set()
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_base_set(set)
}
fn in_default_base_set(self) -> SystemSetConfig {
self.into_config().in_default_base_set()
}
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
@ -155,12 +164,18 @@ impl IntoSystemSetConfig for BoxedSystemSet {
SystemSetConfig::new(self)
}
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_set(set)
}
fn no_default_set(self) -> SystemSetConfig {
self.into_config().no_default_set()
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_base_set(set)
}
fn in_default_base_set(self) -> SystemSetConfig {
self.into_config().in_default_base_set()
}
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
@ -193,17 +208,44 @@ impl IntoSystemSetConfig for SystemSetConfig {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"adding arbitrary systems to a system type set is not allowed"
);
assert!(
!set.is_base(),
"Sets cannot be added to 'base' system sets using 'in_set'. Use 'in_base_set' instead."
);
assert!(
!self.set.is_base(),
"Base system sets cannot be added to other sets."
);
self.graph_info.sets.push(Box::new(set));
self
}
fn no_default_set(mut self) -> SystemSetConfig {
self.graph_info.add_default_set = false;
#[track_caller]
fn in_base_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"System type sets cannot be base sets."
);
assert!(
set.is_base(),
"Sets cannot be added to normal sets using 'in_base_set'. Use 'in_set' instead."
);
assert!(
!self.set.is_base(),
"Base system sets cannot be added to other sets."
);
self.graph_info.set_base_set(Box::new(set));
self
}
fn in_default_base_set(mut self) -> SystemSetConfig {
self.graph_info.add_default_base_set = true;
self
}
@ -252,9 +294,13 @@ pub trait IntoSystemConfig<Params>: sealed::IntoSystemConfig<Params> {
#[doc(hidden)]
fn into_config(self) -> SystemConfig;
/// Add to `set` membership.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemConfig;
/// Add to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemConfig;
/// Don't add this system to the schedules's default set.
fn no_default_set(self) -> SystemConfig;
fn no_default_base_set(self) -> SystemConfig;
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig;
/// Run after all systems in `set`.
@ -282,12 +328,18 @@ where
SystemConfig::new(Box::new(IntoSystem::into_system(self)))
}
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemConfig {
self.into_config().in_set(set)
}
fn no_default_set(self) -> SystemConfig {
self.into_config().no_default_set()
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemConfig {
self.into_config().in_base_set(set)
}
fn no_default_base_set(self) -> SystemConfig {
self.into_config().no_default_base_set()
}
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig {
@ -320,12 +372,18 @@ impl IntoSystemConfig<()> for BoxedSystem<(), ()> {
SystemConfig::new(self)
}
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemConfig {
self.into_config().in_set(set)
}
fn no_default_set(self) -> SystemConfig {
self.into_config().no_default_set()
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemConfig {
self.into_config().in_base_set(set)
}
fn no_default_base_set(self) -> SystemConfig {
self.into_config().no_default_base_set()
}
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig {
@ -358,17 +416,36 @@ impl IntoSystemConfig<()> for SystemConfig {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"adding arbitrary systems to a system type set is not allowed"
);
assert!(
!set.is_base(),
"Systems cannot be added to 'base' system sets using 'in_set'. Use 'in_base_set' instead."
);
self.graph_info.sets.push(Box::new(set));
self
}
fn no_default_set(mut self) -> SystemConfig {
self.graph_info.add_default_set = false;
#[track_caller]
fn in_base_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"System type sets cannot be base sets."
);
assert!(
set.is_base(),
"Systems cannot be added to normal sets using 'in_base_set'. Use 'in_set' instead."
);
self.graph_info.set_base_set(Box::new(set));
self
}
fn no_default_base_set(mut self) -> SystemConfig {
self.graph_info.add_default_base_set = false;
self
}
@ -451,10 +528,17 @@ where
fn into_configs(self) -> SystemConfigs;
/// Add these systems to the provided `set`.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemConfigs {
self.into_configs().in_set(set)
}
/// Add these systems to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemConfigs {
self.into_configs().in_base_set(set)
}
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemConfigs {
self.into_configs().before(set)
@ -495,11 +579,16 @@ impl IntoSystemConfigs<()> for SystemConfigs {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"adding arbitrary systems to a system type set is not allowed"
);
assert!(
!set.is_base(),
"Systems cannot be added to 'base' system sets using 'in_set'. Use 'in_base_set' instead."
);
for config in &mut self.systems {
config.graph_info.sets.push(set.dyn_clone());
}
@ -507,6 +596,23 @@ impl IntoSystemConfigs<()> for SystemConfigs {
self
}
#[track_caller]
fn in_base_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"System type sets cannot be base sets."
);
assert!(
set.is_base(),
"Systems cannot be added to normal sets using 'in_base_set'. Use 'in_set' instead."
);
for config in &mut self.systems {
config.graph_info.set_base_set(set.dyn_clone());
}
self
}
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.systems {
@ -575,10 +681,17 @@ where
fn into_configs(self) -> SystemSetConfigs;
/// Add these system sets to the provided `set`.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfigs {
self.into_configs().in_set(set)
}
/// Add these system sets to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfigs {
self.into_configs().in_base_set(set)
}
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs {
self.into_configs().before(set)
@ -614,18 +727,48 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"adding arbitrary systems to a system type set is not allowed"
);
assert!(
!set.is_base(),
"Sets cannot be added to 'base' system sets using 'in_set'. Use 'in_base_set' instead."
);
for config in &mut self.sets {
assert!(
!config.set.is_base(),
"Base system sets cannot be added to other sets."
);
config.graph_info.sets.push(set.dyn_clone());
}
self
}
#[track_caller]
fn in_base_set(mut self, set: impl SystemSet) -> Self {
assert!(
!set.is_system_type(),
"System type sets cannot be base sets."
);
assert!(
set.is_base(),
"Sets cannot be added to normal sets using 'in_base_set'. Use 'in_set' instead."
);
for config in &mut self.sets {
assert!(
!config.set.is_base(),
"Base system sets cannot be added to other sets."
);
config.graph_info.set_base_set(set.dyn_clone());
}
self
}
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.sets {

View File

@ -54,8 +54,8 @@ pub(super) struct SystemSchedule {
pub(super) set_ids: Vec<NodeId>,
pub(super) system_dependencies: Vec<usize>,
pub(super) system_dependents: Vec<Vec<usize>>,
pub(super) sets_of_systems: Vec<FixedBitSet>,
pub(super) systems_in_sets: Vec<FixedBitSet>,
pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
}
impl SystemSchedule {
@ -68,8 +68,8 @@ impl SystemSchedule {
set_ids: Vec::new(),
system_dependencies: Vec::new(),
system_dependents: Vec::new(),
sets_of_systems: Vec::new(),
systems_in_sets: Vec::new(),
sets_with_conditions_of_systems: Vec::new(),
systems_in_sets_with_conditions: Vec::new(),
}
}
}

View File

@ -32,8 +32,8 @@ struct SyncUnsafeSchedule<'a> {
struct Conditions<'a> {
system_conditions: &'a mut [Vec<BoxedCondition>],
set_conditions: &'a mut [Vec<BoxedCondition>],
sets_of_systems: &'a [FixedBitSet],
systems_in_sets: &'a [FixedBitSet],
sets_with_conditions_of_systems: &'a [FixedBitSet],
systems_in_sets_with_conditions: &'a [FixedBitSet],
}
impl SyncUnsafeSchedule<'_> {
@ -43,8 +43,8 @@ impl SyncUnsafeSchedule<'_> {
conditions: Conditions {
system_conditions: &mut schedule.system_conditions,
set_conditions: &mut schedule.set_conditions,
sets_of_systems: &schedule.sets_of_systems,
systems_in_sets: &schedule.systems_in_sets,
sets_with_conditions_of_systems: &schedule.sets_with_conditions_of_systems,
systems_in_sets_with_conditions: &schedule.systems_in_sets_with_conditions,
},
}
}
@ -333,7 +333,9 @@ impl MultiThreadedExecutor {
}
// TODO: an earlier out if world's archetypes did not change
for set_idx in conditions.sets_of_systems[system_index].difference(&self.evaluated_sets) {
for set_idx in conditions.sets_with_conditions_of_systems[system_index]
.difference(&self.evaluated_sets)
{
for condition in &mut conditions.set_conditions[set_idx] {
condition.update_archetype_component_access(world);
if !condition
@ -382,7 +384,7 @@ impl MultiThreadedExecutor {
world: &World,
) -> bool {
let mut should_run = !self.skipped_systems.contains(system_index);
for set_idx in conditions.sets_of_systems[system_index].ones() {
for set_idx in conditions.sets_with_conditions_of_systems[system_index].ones() {
if self.evaluated_sets.contains(set_idx) {
continue;
}
@ -393,7 +395,7 @@ impl MultiThreadedExecutor {
if !set_conditions_met {
self.skipped_systems
.union_with(&conditions.systems_in_sets[set_idx]);
.union_with(&conditions.systems_in_sets_with_conditions[set_idx]);
}
should_run &= set_conditions_met;

View File

@ -41,7 +41,7 @@ impl SystemExecutor for SimpleExecutor {
let should_run_span = info_span!("check_conditions", name = &*name).entered();
let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_of_systems[system_index].ones() {
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
if self.evaluated_sets.contains(set_idx) {
continue;
}
@ -52,7 +52,7 @@ impl SystemExecutor for SimpleExecutor {
if !set_conditions_met {
self.completed_systems
.union_with(&schedule.systems_in_sets[set_idx]);
.union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
}
should_run &= set_conditions_met;

View File

@ -51,7 +51,7 @@ impl SystemExecutor for SingleThreadedExecutor {
let should_run_span = info_span!("check_conditions", name = &*name).entered();
let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_of_systems[system_index].ones() {
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
if self.evaluated_sets.contains(set_idx) {
continue;
}
@ -62,7 +62,7 @@ impl SystemExecutor for SingleThreadedExecutor {
if !set_conditions_met {
self.completed_systems
.union_with(&schedule.systems_in_sets[set_idx]);
.union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
}
should_run &= set_conditions_met;

View File

@ -72,16 +72,47 @@ pub(crate) struct GraphInfo {
pub(crate) sets: Vec<BoxedSystemSet>,
pub(crate) dependencies: Vec<Dependency>,
pub(crate) ambiguous_with: Ambiguity,
pub(crate) add_default_set: bool,
pub(crate) add_default_base_set: bool,
pub(crate) base_set: Option<BoxedSystemSet>,
}
impl Default for GraphInfo {
fn default() -> Self {
GraphInfo {
sets: Vec::new(),
base_set: None,
dependencies: Vec::new(),
ambiguous_with: Ambiguity::default(),
add_default_set: true,
add_default_base_set: true,
}
}
}
impl GraphInfo {
pub(crate) fn system() -> GraphInfo {
GraphInfo {
// systems get the default base set automatically
add_default_base_set: true,
..Default::default()
}
}
pub(crate) fn system_set() -> GraphInfo {
GraphInfo {
// sets do not get the default base set automatically
add_default_base_set: false,
..Default::default()
}
}
#[track_caller]
pub(crate) fn set_base_set(&mut self, set: BoxedSystemSet) {
if let Some(current) = &self.base_set {
panic!(
"Cannot set the base set because base set {current:?} has already been configured."
);
} else {
self.base_set = Some(set);
}
}
}

View File

@ -644,4 +644,173 @@ mod tests {
assert!(matches!(result, Err(ScheduleBuildError::Ambiguity)));
}
}
mod base_sets {
use super::*;
#[derive(SystemSet, Hash, Debug, Eq, PartialEq, Clone)]
#[system_set(base)]
enum Base {
A,
B,
}
#[derive(SystemSet, Hash, Debug, Eq, PartialEq, Clone)]
enum Normal {
X,
Y,
Z,
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_system_with_in_set() {
let mut schedule = Schedule::new();
schedule.add_system(named_system.in_set(Base::A));
}
#[test]
#[should_panic]
fn disallow_adding_sets_to_system_with_in_base_set() {
let mut schedule = Schedule::new();
schedule.add_system(named_system.in_base_set(Normal::X));
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_systems_with_in_set() {
let mut schedule = Schedule::new();
schedule.add_systems((named_system, named_system).in_set(Base::A));
}
#[test]
#[should_panic]
fn disallow_adding_sets_to_systems_with_in_base_set() {
let mut schedule = Schedule::new();
schedule.add_systems((named_system, named_system).in_base_set(Normal::X));
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_set_with_in_set() {
let mut schedule = Schedule::new();
schedule.configure_set(Normal::Y.in_set(Base::A));
}
#[test]
#[should_panic]
fn disallow_adding_sets_to_set_with_in_base_set() {
let mut schedule = Schedule::new();
schedule.configure_set(Normal::Y.in_base_set(Normal::X));
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_sets_with_in_set() {
let mut schedule = Schedule::new();
schedule.configure_sets((Normal::X, Normal::Y).in_set(Base::A));
}
#[test]
#[should_panic]
fn disallow_adding_sets_to_sets_with_in_base_set() {
let mut schedule = Schedule::new();
schedule.configure_sets((Normal::X, Normal::Y).in_base_set(Normal::Z));
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_sets() {
let mut schedule = Schedule::new();
schedule.configure_set(Base::A.in_set(Normal::X));
}
#[test]
#[should_panic]
fn disallow_adding_base_sets_to_base_sets() {
let mut schedule = Schedule::new();
schedule.configure_set(Base::A.in_base_set(Base::B));
}
#[test]
#[should_panic]
fn disallow_adding_set_to_multiple_base_sets() {
let mut schedule = Schedule::new();
schedule.configure_set(Normal::X.in_base_set(Base::A).in_base_set(Base::B));
}
#[test]
#[should_panic]
fn disallow_adding_sets_to_multiple_base_sets() {
let mut schedule = Schedule::new();
schedule.configure_sets(
(Normal::X, Normal::Y)
.in_base_set(Base::A)
.in_base_set(Base::B),
);
}
#[test]
#[should_panic]
fn disallow_adding_system_to_multiple_base_sets() {
let mut schedule = Schedule::new();
schedule.add_system(named_system.in_base_set(Base::A).in_base_set(Base::B));
}
#[test]
#[should_panic]
fn disallow_adding_systems_to_multiple_base_sets() {
let mut schedule = Schedule::new();
schedule.add_systems(
(make_function_system(0), make_function_system(1))
.in_base_set(Base::A)
.in_base_set(Base::B),
);
}
#[test]
fn disallow_multiple_base_sets() {
let mut world = World::new();
let mut schedule = Schedule::new();
schedule
.configure_set(Normal::X.in_base_set(Base::A))
.configure_set(Normal::Y.in_base_set(Base::B))
.add_system(named_system.in_set(Normal::X).in_set(Normal::Y));
let result = schedule.initialize(&mut world);
assert!(matches!(
result,
Err(ScheduleBuildError::SystemInMultipleBaseSets { .. })
));
let mut schedule = Schedule::new();
schedule
.configure_set(Normal::X.in_base_set(Base::A))
.configure_set(Normal::Y.in_base_set(Base::B).in_set(Normal::X));
let result = schedule.initialize(&mut world);
assert!(matches!(
result,
Err(ScheduleBuildError::SetInMultipleBaseSets { .. })
));
}
#[test]
fn default_base_set_ordering() {
let mut world = World::default();
let mut schedule = Schedule::default();
world.init_resource::<SystemOrder>();
schedule
.set_default_base_set(Base::A)
.configure_set(Base::A.before(Base::B))
.add_system(make_function_system(0).in_base_set(Base::B))
.add_system(make_function_system(1));
schedule.run(&mut world);
assert_eq!(world.resource::<SystemOrder>().0, vec![1, 0]);
}
}
}

View File

@ -127,6 +127,12 @@ impl Schedule {
}
}
pub fn set_default_base_set(&mut self, default_base_set: impl SystemSet) -> &mut Self {
self.graph
.set_default_base_set(Some(Box::new(default_base_set)));
self
}
/// Add a system to the schedule.
pub fn add_system<P>(&mut self, system: impl IntoSystemConfig<P>) -> &mut Self {
self.graph.add_system(system);
@ -264,14 +270,25 @@ impl Dag {
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum BaseSetMembership {
Uncalculated,
None,
Some(NodeId),
}
/// A [`SystemSet`] with metadata, stored in a [`ScheduleGraph`].
struct SystemSetNode {
inner: BoxedSystemSet,
base_set_membership: BaseSetMembership,
}
impl SystemSetNode {
pub fn new(set: BoxedSystemSet) -> Self {
Self { inner: set }
Self {
inner: set,
base_set_membership: BaseSetMembership::Uncalculated,
}
}
pub fn name(&self) -> String {
@ -283,15 +300,43 @@ impl SystemSetNode {
}
}
/// A [`BoxedSystem`] with metadata, stored in a [`ScheduleGraph`].
struct SystemNode {
inner: Option<BoxedSystem>,
base_set_membership: BaseSetMembership,
}
impl SystemNode {
pub fn new(system: BoxedSystem) -> Self {
Self {
inner: Some(system),
base_set_membership: BaseSetMembership::Uncalculated,
}
}
pub fn get(&self) -> Option<&BoxedSystem> {
self.inner.as_ref()
}
pub fn get_mut(&mut self) -> Option<&mut BoxedSystem> {
self.inner.as_mut()
}
pub fn name(&self) -> String {
format!("{:?}", &self.inner)
}
}
/// Metadata for a [`Schedule`].
#[derive(Default)]
struct ScheduleGraph {
systems: Vec<Option<BoxedSystem>>,
systems: Vec<SystemNode>,
system_conditions: Vec<Option<Vec<BoxedCondition>>>,
system_sets: Vec<SystemSetNode>,
system_set_conditions: Vec<Option<Vec<BoxedCondition>>>,
system_set_ids: HashMap<BoxedSystemSet, NodeId>,
uninit: Vec<(NodeId, usize)>,
maybe_default_base_set: Vec<NodeId>,
hierarchy: Dag,
dependency: Dag,
dependency_flattened: Dag,
@ -300,6 +345,7 @@ struct ScheduleGraph {
ambiguous_with_all: HashSet<NodeId>,
changed: bool,
settings: ScheduleBuildSettings,
default_base_set: Option<BoxedSystemSet>,
}
impl ScheduleGraph {
@ -310,6 +356,7 @@ impl ScheduleGraph {
system_sets: Vec::new(),
system_set_conditions: Vec::new(),
system_set_ids: HashMap::new(),
maybe_default_base_set: Vec::new(),
uninit: Vec::new(),
hierarchy: Dag::new(),
dependency: Dag::new(),
@ -319,6 +366,7 @@ impl ScheduleGraph {
ambiguous_with_all: HashSet::new(),
changed: false,
settings: default(),
default_base_set: None,
}
}
@ -357,11 +405,11 @@ impl ScheduleGraph {
let id = NodeId::System(self.systems.len());
// graph updates are immediate
self.update_graphs(id, graph_info)?;
self.update_graphs(id, graph_info, false)?;
// system init has to be deferred (need `&mut World`)
self.uninit.push((id, 0));
self.systems.push(Some(system));
self.systems.push(SystemNode::new(system));
self.system_conditions.push(Some(conditions));
Ok(id)
@ -405,7 +453,7 @@ impl ScheduleGraph {
};
// graph updates are immediate
self.update_graphs(id, graph_info)?;
self.update_graphs(id, graph_info, set.is_base())?;
// system init has to be deferred (need `&mut World`)
let system_set_conditions =
@ -424,23 +472,33 @@ impl ScheduleGraph {
id
}
fn check_set(&mut self, id: &NodeId, set: &dyn SystemSet) -> Result<(), ScheduleBuildError> {
match self.system_set_ids.get(set) {
Some(set_id) => {
if id == set_id {
let string = format!("{:?}", &set);
return Err(ScheduleBuildError::HierarchyLoop(string));
}
}
None => {
self.add_set(set.dyn_clone());
}
}
Ok(())
}
fn check_sets(
&mut self,
id: &NodeId,
graph_info: &GraphInfo,
) -> Result<(), ScheduleBuildError> {
for set in &graph_info.sets {
match self.system_set_ids.get(set) {
Some(set_id) => {
if id == set_id {
let string = format!("{:?}", &set);
return Err(ScheduleBuildError::HierarchyLoop(string));
}
}
None => {
self.add_set(set.dyn_clone());
}
}
self.check_set(id, &**set)?;
}
if let Some(base_set) = &graph_info.base_set {
self.check_set(id, &**base_set)?;
}
Ok(())
@ -480,6 +538,7 @@ impl ScheduleGraph {
&mut self,
id: NodeId,
graph_info: GraphInfo,
is_base_set: bool,
) -> Result<(), ScheduleBuildError> {
self.check_sets(&id, &graph_info)?;
self.check_edges(&id, &graph_info)?;
@ -489,6 +548,8 @@ impl ScheduleGraph {
sets,
dependencies,
ambiguous_with,
base_set,
add_default_base_set,
..
} = graph_info;
@ -502,6 +563,34 @@ impl ScheduleGraph {
self.dependency.graph.add_node(set);
}
// If the current node is not a base set, set the base set if it was configured
if !is_base_set {
if let Some(base_set) = base_set {
let set_id = self.system_set_ids[&base_set];
self.hierarchy.graph.add_edge(set_id, id, ());
} else if let Some(default_base_set) = &self.default_base_set {
if add_default_base_set {
match id {
NodeId::System(_) => {
// Queue the default base set. We queue systems instead of adding directly to allow
// sets to define base sets, which will override the default inheritance behavior
self.maybe_default_base_set.push(id);
}
NodeId::Set(_) => {
// Sets should be added automatically because developers explicitly called
// in_default_base_set()
let set_id = self.system_set_ids[default_base_set];
self.hierarchy.graph.add_edge(set_id, id, ());
}
}
}
}
}
if !self.dependency.graph.contains_node(id) {
self.dependency.graph.add_node(id);
}
for (kind, set) in dependencies
.into_iter()
.map(|Dependency { kind, set }| (kind, self.system_set_ids[&set]))
@ -538,7 +627,7 @@ impl ScheduleGraph {
for (id, i) in self.uninit.drain(..) {
match id {
NodeId::System(index) => {
self.systems[index].as_mut().unwrap().initialize(world);
self.systems[index].get_mut().unwrap().initialize(world);
if let Some(v) = self.system_conditions[index].as_mut() {
for condition in v.iter_mut() {
condition.initialize(world);
@ -556,12 +645,141 @@ impl ScheduleGraph {
}
}
/// Calculates the base set for each node and caches the results on the node
fn calculate_base_sets_and_detect_cycles(&mut self) -> Result<(), ScheduleBuildError> {
let set_ids = (0..self.system_sets.len()).map(NodeId::Set);
let system_ids = (0..self.systems.len()).map(NodeId::System);
let mut visited_sets = vec![false; self.system_sets.len()];
// reset base set membership, as this can change when the schedule updates
for system in &mut self.systems {
system.base_set_membership = BaseSetMembership::Uncalculated;
}
for system_set in &mut self.system_sets {
system_set.base_set_membership = BaseSetMembership::Uncalculated;
}
for node_id in set_ids.chain(system_ids) {
Self::calculate_base_set(
&self.hierarchy,
&mut self.system_sets,
&mut self.systems,
&mut visited_sets,
node_id,
)?;
}
Ok(())
}
fn calculate_base_set(
hierarchy: &Dag,
system_sets: &mut [SystemSetNode],
systems: &mut [SystemNode],
visited_sets: &mut [bool],
node_id: NodeId,
) -> Result<Option<NodeId>, ScheduleBuildError> {
let base_set_membership = match node_id {
// systems only have
NodeId::System(_) => BaseSetMembership::Uncalculated,
NodeId::Set(index) => {
let set_node = &mut system_sets[index];
if set_node.inner.is_base() {
set_node.base_set_membership = BaseSetMembership::Some(node_id);
}
set_node.base_set_membership
}
};
let base_set = match base_set_membership {
BaseSetMembership::None => None,
BaseSetMembership::Some(node_id) => Some(node_id),
BaseSetMembership::Uncalculated => {
let mut base_set: Option<NodeId> = None;
if let NodeId::Set(index) = node_id {
if visited_sets[index] {
return Err(ScheduleBuildError::HierarchyCycle);
}
visited_sets[index] = true;
}
for neighbor in hierarchy
.graph
.neighbors_directed(node_id, Direction::Incoming)
{
if let Some(calculated_base_set) = Self::calculate_base_set(
hierarchy,
system_sets,
systems,
visited_sets,
neighbor,
)? {
if let Some(first_set) = base_set {
return Err(match node_id {
NodeId::System(index) => {
ScheduleBuildError::SystemInMultipleBaseSets {
system: systems[index].name(),
first_set: system_sets[first_set.index()].name(),
second_set: system_sets[calculated_base_set.index()].name(),
}
}
NodeId::Set(index) => ScheduleBuildError::SetInMultipleBaseSets {
set: system_sets[index].name(),
first_set: system_sets[first_set.index()].name(),
second_set: system_sets[calculated_base_set.index()].name(),
},
});
}
base_set = Some(calculated_base_set);
}
}
match node_id {
NodeId::System(index) => {
systems[index].base_set_membership = if let Some(base_set) = base_set {
BaseSetMembership::Some(base_set)
} else {
BaseSetMembership::None
};
}
NodeId::Set(index) => {
system_sets[index].base_set_membership = if let Some(base_set) = base_set {
BaseSetMembership::Some(base_set)
} else {
BaseSetMembership::None
};
}
}
base_set
}
};
Ok(base_set)
}
fn build_schedule(
&mut self,
components: &Components,
) -> Result<SystemSchedule, ScheduleBuildError> {
self.calculate_base_sets_and_detect_cycles()?;
// Add missing base set membership to systems that defaulted to using the
// default base set and weren't added to a set that belongs to a base set.
if let Some(default_base_set) = &self.default_base_set {
let default_set_id = self.system_set_ids[default_base_set];
for system_id in std::mem::take(&mut self.maybe_default_base_set) {
let system_node = &mut self.systems[system_id.index()];
if system_node.base_set_membership == BaseSetMembership::None {
self.hierarchy.graph.add_edge(default_set_id, system_id, ());
system_node.base_set_membership = BaseSetMembership::Some(default_set_id);
}
debug_assert_ne!(
system_node.base_set_membership,
BaseSetMembership::Uncalculated,
"base set membership should have been calculated"
);
}
}
// check hierarchy for cycles
let hier_scc = tarjan_scc(&self.hierarchy.graph);
// PERF: in theory we can skip this contains_cycles because we've already detected cycles
// using calculate_base_sets_and_detect_cycles
if self.contains_cycles(&hier_scc) {
self.report_cycles(&hier_scc);
return Err(ScheduleBuildError::HierarchyCycle);
@ -767,8 +985,8 @@ impl ScheduleGraph {
continue;
}
let system_a = self.systems[a.index()].as_ref().unwrap();
let system_b = self.systems[b.index()].as_ref().unwrap();
let system_a = self.systems[a.index()].get().unwrap();
let system_b = self.systems[b.index()].get().unwrap();
if system_a.is_exclusive() || system_b.is_exclusive() {
conflicting_systems.push((a, b, Vec::new()));
} else {
@ -808,7 +1026,7 @@ impl ScheduleGraph {
.filter(|&(_i, id)| id.is_system())
.collect::<Vec<_>>();
let (hg_set_idxs, hg_set_ids): (Vec<_>, Vec<_>) = self
let (hg_set_with_conditions_idxs, hg_set_ids): (Vec<_>, Vec<_>) = self
.hierarchy
.topsort
.iter()
@ -826,7 +1044,7 @@ impl ScheduleGraph {
.unzip();
let sys_count = self.systems.len();
let set_count = hg_set_ids.len();
let set_with_conditions_count = hg_set_ids.len();
let node_count = self.systems.len() + self.system_sets.len();
// get the number of dependencies and the immediate dependents of each system
@ -853,9 +1071,10 @@ impl ScheduleGraph {
// get the rows and columns of the hierarchy graph's reachability matrix
// (needed to we can evaluate conditions in the correct order)
let mut systems_in_sets = vec![FixedBitSet::with_capacity(sys_count); set_count];
for (i, &row) in hg_set_idxs.iter().enumerate() {
let bitset = &mut systems_in_sets[i];
let mut systems_in_sets_with_conditions =
vec![FixedBitSet::with_capacity(sys_count); set_with_conditions_count];
for (i, &row) in hg_set_with_conditions_idxs.iter().enumerate() {
let bitset = &mut systems_in_sets_with_conditions[i];
for &(col, sys_id) in &hg_systems {
let idx = dg_system_idx_map[&sys_id];
let is_descendant = hier_results.reachable[index(row, col, node_count)];
@ -863,11 +1082,12 @@ impl ScheduleGraph {
}
}
let mut sets_of_systems = vec![FixedBitSet::with_capacity(set_count); sys_count];
let mut sets_with_conditions_of_systems =
vec![FixedBitSet::with_capacity(set_with_conditions_count); sys_count];
for &(col, sys_id) in &hg_systems {
let i = dg_system_idx_map[&sys_id];
let bitset = &mut sets_of_systems[i];
for (idx, &row) in hg_set_idxs
let bitset = &mut sets_with_conditions_of_systems[i];
for (idx, &row) in hg_set_with_conditions_idxs
.iter()
.enumerate()
.take_while(|&(_idx, &row)| row < col)
@ -880,13 +1100,13 @@ impl ScheduleGraph {
Ok(SystemSchedule {
systems: Vec::with_capacity(sys_count),
system_conditions: Vec::with_capacity(sys_count),
set_conditions: Vec::with_capacity(set_count),
set_conditions: Vec::with_capacity(set_with_conditions_count),
system_ids: dg_system_ids,
set_ids: hg_set_ids,
system_dependencies,
system_dependents,
sets_of_systems,
systems_in_sets,
sets_with_conditions_of_systems,
systems_in_sets_with_conditions,
})
}
@ -906,7 +1126,7 @@ impl ScheduleGraph {
.zip(schedule.systems.drain(..))
.zip(schedule.system_conditions.drain(..))
{
self.systems[id.index()] = Some(system);
self.systems[id.index()].inner = Some(system);
self.system_conditions[id.index()] = Some(conditions);
}
@ -922,7 +1142,7 @@ impl ScheduleGraph {
// move systems into new schedule
for &id in &schedule.system_ids {
let system = self.systems[id.index()].take().unwrap();
let system = self.systems[id.index()].inner.take().unwrap();
let conditions = self.system_conditions[id.index()].take().unwrap();
schedule.systems.push(system);
schedule.system_conditions.push(conditions);
@ -935,17 +1155,24 @@ impl ScheduleGraph {
Ok(())
}
fn set_default_base_set(&mut self, set: Option<BoxedSystemSet>) {
if let Some(set) = set {
self.default_base_set = Some(set.dyn_clone());
if self.system_set_ids.get(&set).is_none() {
self.add_set(set);
}
} else {
self.default_base_set = None;
}
}
}
// methods for reporting errors
impl ScheduleGraph {
fn get_node_name(&self, id: &NodeId) -> String {
match id {
NodeId::System(_) => self.systems[id.index()]
.as_ref()
.unwrap()
.name()
.to_string(),
NodeId::System(_) => self.systems[id.index()].get().unwrap().name().to_string(),
NodeId::Set(_) => self.system_sets[id.index()].name(),
}
}
@ -1100,6 +1327,20 @@ pub enum ScheduleBuildError {
/// Tried to run a schedule before all of its systems have been initialized.
#[error("Systems in schedule have not been initialized.")]
Uninitialized,
/// Tried to add a system to multiple base sets.
#[error("System `{system:?}` is in the base sets {first_set:?} and {second_set:?}, but systems can only belong to one base set.")]
SystemInMultipleBaseSets {
system: String,
first_set: String,
second_set: String,
},
/// Tried to add a set to multiple base sets.
#[error("Set `{set:?}` is in the base sets {first_set:?} and {second_set:?}, but sets can only belong to one base set.")]
SetInMultipleBaseSets {
set: String,
first_set: String,
second_set: String,
},
}
/// Specifies how schedule construction should respond to detecting a certain kind of issue.

View File

@ -23,6 +23,16 @@ pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
false
}
/// Returns `true` if this set is a "base system set". Systems
/// can only belong to one base set at a time. Systems and Sets
/// can only be added to base sets using specialized `in_base_set`
/// APIs. This enables "mutually exclusive" behaviors. It also
/// enables schedules to have a "default base set", which can be used
/// to apply default configuration to systems.
fn is_base(&self) -> bool {
false
}
/// Creates a boxed clone of the label corresponding to this system set.
fn dyn_clone(&self) -> Box<dyn SystemSet>;
}

View File

@ -24,7 +24,7 @@ impl Plugin for GilrsPlugin {
.add_system(
gilrs_event_system
.before(InputSystem)
.in_set(CoreSet::PreUpdate),
.in_base_set(CoreSet::PreUpdate),
);
}
Err(err) => error!("Failed to start Gilrs. {}", err),

View File

@ -99,7 +99,7 @@ impl<T: Component> Plugin for ValidParentCheckPlugin<T> {
app.init_resource::<ReportHierarchyIssue<T>>().add_system(
check_hierarchy_component_has_valid_parent::<T>
.run_if(resource_equals(ReportHierarchyIssue::<T>::new(true)))
.in_set(CoreSet::Last),
.in_base_set(CoreSet::Last),
);
}
}

View File

@ -51,7 +51,7 @@ pub struct InputSystem;
impl Plugin for InputPlugin {
fn build(&self, app: &mut App) {
app.configure_set(InputSystem.in_set(CoreSet::PreUpdate))
app.configure_set(InputSystem.in_base_set(CoreSet::PreUpdate))
// keyboard
.add_event::<KeyboardInput>()
.init_resource::<Input<KeyCode>>()

View File

@ -138,40 +138,6 @@ pub fn derive_boxed_label(input: syn::DeriveInput, trait_path: &syn::Path) -> To
.into()
}
/// Derive a label trait
///
/// # Args
///
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
/// - `trait_path`: The path [`syn::Path`] to the label trait
pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
let ident = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(
syn::parse2(quote! {
Self: 'static + Send + Sync + Clone + Eq + ::std::fmt::Debug + ::std::hash::Hash
})
.unwrap(),
);
(quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn is_system_type(&self) -> bool {
false
}
fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
std::boxed::Box::new(std::clone::Clone::clone(self))
}
}
})
.into()
}
/// Derive a label trait
///
/// # Args

View File

@ -181,14 +181,10 @@ impl Plugin for PbrPlugin {
SimulationLightSystems::UpdateDirectionalLightCascades,
SimulationLightSystems::UpdateLightFrusta,
)
.in_set(CoreSet::PostUpdate),
.in_base_set(CoreSet::PostUpdate),
)
.add_plugin(FogPlugin)
.add_system(
// NOTE: Clusters need to have been added before update_clusters is run so
// add as an exclusive system
add_clusters.in_set(SimulationLightSystems::AddClusters),
)
.add_system(add_clusters.in_set(SimulationLightSystems::AddClusters))
.add_system(apply_system_buffers.in_set(SimulationLightSystems::AddClustersFlush))
.add_system(
assign_lights_to_clusters

View File

@ -31,7 +31,7 @@ impl<T: CameraProjection + Component + GetTypeRegistration> Plugin for CameraPro
.edit_schedule(CoreSchedule::Startup, |schedule| {
schedule.configure_set(CameraUpdateSystem.in_set(StartupSet::PostStartup));
})
.configure_set(CameraUpdateSystem.in_set(CoreSet::PostUpdate))
.configure_set(CameraUpdateSystem.in_base_set(CoreSet::PostUpdate))
.add_startup_system(
crate::camera::camera_system::<T>
.in_set(CameraUpdateSystem)

View File

@ -210,19 +210,19 @@ impl Plugin for VisibilityPlugin {
fn build(&self, app: &mut bevy_app::App) {
use VisibilitySystems::*;
app.configure_set(CalculateBounds.in_set(CoreSet::PostUpdate))
app.configure_set(CalculateBounds.in_base_set(CoreSet::PostUpdate))
// We add an AABB component in CaclulateBounds, which must be ready on the same frame.
.add_system(apply_system_buffers.in_set(CalculateBoundsFlush))
.configure_set(
CalculateBoundsFlush
.after(CalculateBounds)
.in_set(CoreSet::PostUpdate),
.in_base_set(CoreSet::PostUpdate),
)
.configure_set(UpdateOrthographicFrusta.in_set(CoreSet::PostUpdate))
.configure_set(UpdatePerspectiveFrusta.in_set(CoreSet::PostUpdate))
.configure_set(UpdateProjectionFrusta.in_set(CoreSet::PostUpdate))
.configure_set(CheckVisibility.in_set(CoreSet::PostUpdate))
.configure_set(VisibilityPropagate.in_set(CoreSet::PostUpdate))
.configure_set(UpdateOrthographicFrusta.in_base_set(CoreSet::PostUpdate))
.configure_set(UpdatePerspectiveFrusta.in_base_set(CoreSet::PostUpdate))
.configure_set(UpdateProjectionFrusta.in_base_set(CoreSet::PostUpdate))
.configure_set(CheckVisibility.in_base_set(CoreSet::PostUpdate))
.configure_set(VisibilityPropagate.in_base_set(CoreSet::PostUpdate))
.add_system(calculate_bounds.in_set(CalculateBounds))
.add_system(
update_frusta::<OrthographicProjection>
@ -247,7 +247,7 @@ impl Plugin for VisibilityPlugin {
)
.add_system(
update_frusta::<Projection>
.in_set(CoreSet::PostUpdate)
.in_base_set(CoreSet::PostUpdate)
.after(camera_system::<Projection>)
.after(TransformSystem::TransformPropagate),
)

View File

@ -36,9 +36,9 @@ impl Plugin for ScenePlugin {
.add_asset::<Scene>()
.init_asset_loader::<SceneLoader>()
.init_resource::<SceneSpawner>()
.add_system(scene_spawner_system.in_set(CoreSet::Update))
.add_system(scene_spawner_system)
// Systems `*_bundle_spawner` must run before `scene_spawner_system`
.add_system(scene_spawner.in_set(CoreSet::PreUpdate));
.add_system(scene_spawner.in_base_set(CoreSet::PreUpdate));
}
}

View File

@ -82,7 +82,7 @@ impl Plugin for TextPlugin {
.insert_resource(TextPipeline::default())
.add_system(
update_text2d_layout
.in_set(CoreSet::PostUpdate)
.in_base_set(CoreSet::PostUpdate)
.after(ModifiesWindows)
// Potential conflict: `Assets<Image>`
// In practice, they run independently since `bevy_render::camera_update_system`

View File

@ -39,9 +39,9 @@ impl Plugin for TimePlugin {
.register_type::<Time>()
.register_type::<Stopwatch>()
.init_resource::<FixedTime>()
.configure_set(TimeSystem.in_set(CoreSet::First))
.configure_set(TimeSystem.in_base_set(CoreSet::First))
.add_system(time_system.in_set(TimeSystem))
.add_system(run_fixed_update_schedule.in_set(CoreSet::FixedUpdate));
.add_system(run_fixed_update_schedule.in_base_set(CoreSet::FixedUpdate));
}
}

View File

@ -94,7 +94,7 @@ impl Plugin for TransformPlugin {
.register_type::<GlobalTransform>()
.add_plugin(ValidParentCheckPlugin::<GlobalTransform>::default())
// add transform systems to startup so the first update is "correct"
.configure_set(TransformSystem::TransformPropagate.in_set(CoreSet::PostUpdate))
.configure_set(TransformSystem::TransformPropagate.in_base_set(CoreSet::PostUpdate))
.edit_schedule(CoreSchedule::Startup, |schedule| {
schedule.configure_set(
TransformSystem::TransformPropagate.in_set(StartupSet::PostStartup),

View File

@ -101,14 +101,14 @@ impl Plugin for UiPlugin {
.register_type::<UiImage>()
.register_type::<Val>()
.register_type::<widget::Button>()
.configure_set(UiSystem::Focus.in_set(CoreSet::PreUpdate))
.configure_set(UiSystem::Flex.in_set(CoreSet::PostUpdate))
.configure_set(UiSystem::Stack.in_set(CoreSet::PostUpdate))
.configure_set(UiSystem::Focus.in_base_set(CoreSet::PreUpdate))
.configure_set(UiSystem::Flex.in_base_set(CoreSet::PostUpdate))
.configure_set(UiSystem::Stack.in_base_set(CoreSet::PostUpdate))
.add_system(ui_focus_system.in_set(UiSystem::Focus).after(InputSystem))
// add these systems to front because these must run before transform update systems
.add_system(
widget::text_system
.in_set(CoreSet::PostUpdate)
.in_base_set(CoreSet::PostUpdate)
.before(UiSystem::Flex)
.after(ModifiesWindows)
// Potential conflict: `Assets<Image>`
@ -123,7 +123,7 @@ impl Plugin for UiPlugin {
)
.add_system(
widget::update_image_calculated_size_system
.in_set(CoreSet::PostUpdate)
.in_base_set(CoreSet::PostUpdate)
.before(UiSystem::Flex)
// Potential conflicts: `Assets<Image>`
// They run independently since `widget::image_node_system` will only ever observe
@ -142,7 +142,7 @@ impl Plugin for UiPlugin {
.add_system(
update_clipping_system
.after(TransformSystem::TransformPropagate)
.in_set(CoreSet::PostUpdate),
.in_base_set(CoreSet::PostUpdate),
);
crate::render::build_ui_render(app);

View File

@ -93,16 +93,16 @@ impl Plugin for WindowPlugin {
match self.exit_condition {
ExitCondition::OnPrimaryClosed => {
app.add_system(exit_on_primary_closed.in_set(CoreSet::PostUpdate));
app.add_system(exit_on_primary_closed.in_base_set(CoreSet::PostUpdate));
}
ExitCondition::OnAllClosed => {
app.add_system(exit_on_all_closed.in_set(CoreSet::PostUpdate));
app.add_system(exit_on_all_closed.in_base_set(CoreSet::PostUpdate));
}
ExitCondition::DontExit => {}
}
if self.close_when_requested {
app.add_system(close_when_requested.in_set(CoreSet::PostUpdate));
app.add_system(close_when_requested.in_base_set(CoreSet::PostUpdate));
}
// Register event types

View File

@ -70,7 +70,7 @@ impl Plugin for WinitPlugin {
app.init_non_send_resource::<WinitWindows>()
.init_resource::<WinitSettings>()
.set_runner(winit_runner)
.configure_set(ModifiesWindows.in_set(CoreSet::PostUpdate))
.configure_set(ModifiesWindows.in_base_set(CoreSet::PostUpdate))
// exit_on_all_closed only uses the query to determine if the query is empty,
// and so doesn't care about ordering relative to changed_window
.add_systems(

View File

@ -6,7 +6,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(sprite_movement.in_set(CoreSet::Update))
.add_system(sprite_movement)
.run();
}

View File

@ -6,7 +6,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_startup_system(setup)
.add_system(sprite_movement.in_set(CoreSet::Update))
.add_system(sprite_movement)
.run();
}

View File

@ -7,7 +7,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // prevents blurry sprites
.add_startup_system(setup)
.add_system(animate_sprite.in_set(CoreSet::Update))
.add_system(animate_sprite)
.run();
}

View File

@ -14,9 +14,9 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_translation.in_set(CoreSet::Update))
.add_system(animate_rotation.in_set(CoreSet::Update))
.add_system(animate_scale.in_set(CoreSet::Update))
.add_system(animate_translation)
.add_system(animate_rotation)
.add_system(animate_scale)
.run();
}

View File

@ -12,7 +12,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_startup_system(setup)
.add_system(rotate.in_set(CoreSet::Update))
.add_system(rotate)
.run();
}

View File

@ -18,7 +18,7 @@ fn main() {
app.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(example_control_system.in_set(CoreSet::Update));
.add_system(example_control_system);
// Unfortunately, MSAA and HDR are not supported simultaneously under WebGL.
// Since this example uses HDR, we must disable MSAA for WASM builds, at least

View File

@ -11,7 +11,7 @@ fn main() {
.add_plugins(DefaultPlugins)
.add_startup_system(setup_scene)
.add_system(update_bloom_settings)
.add_system(bounce_spheres.in_set(CoreSet::Update))
.add_system(bounce_spheres)
.run();
}

View File

@ -9,8 +9,8 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(movement.in_set(CoreSet::Update))
.add_system(animate_light_direction.in_set(CoreSet::Update))
.add_system(movement)
.add_system(animate_light_direction)
.run();
}

View File

@ -16,7 +16,7 @@ fn main() {
.insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_light_direction.in_set(CoreSet::Update))
.add_system(animate_light_direction)
.run();
}

View File

@ -13,7 +13,7 @@ fn main() {
.insert_resource(Msaa::default())
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(cycle_msaa.in_set(CoreSet::Update))
.add_system(cycle_msaa)
.run();
}

View File

@ -7,7 +7,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(rotator_system.in_set(CoreSet::Update))
.add_system(rotator_system)
.run();
}

View File

@ -18,8 +18,8 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(cube_rotator_system.in_set(CoreSet::Update))
.add_system(rotator_system.in_set(CoreSet::Update))
.add_system(cube_rotator_system)
.add_system(rotator_system)
.run();
}

View File

@ -23,7 +23,7 @@ fn main() {
.add_system(adjust_point_light_biases)
.add_system(toggle_light)
.add_system(adjust_directional_light_biases)
.add_system(camera_controller.in_set(CoreSet::Update))
.add_system(camera_controller)
.run();
}

View File

@ -11,7 +11,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(set_camera_viewports.in_set(CoreSet::Update))
.add_system(set_camera_viewports)
.run();
}

View File

@ -7,7 +7,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(move_scene_entities.in_set(CoreSet::Update))
.add_system(move_scene_entities)
.run();
}

View File

@ -15,7 +15,7 @@ fn main() {
})
.add_startup_system(setup)
.add_system(setup_scene_once_loaded)
.add_system(keyboard_animation_control.in_set(CoreSet::Update))
.add_system(keyboard_animation_control)
.run();
}

View File

@ -21,7 +21,7 @@ fn main() {
..default()
})
.add_startup_system(setup)
.add_system(joint_animation.in_set(CoreSet::Update))
.add_system(joint_animation)
.run();
}

View File

@ -13,7 +13,7 @@ fn main() {
..default()
})
.add_startup_system(setup)
.add_system(joint_animation.in_set(CoreSet::Update))
.add_system(joint_animation)
.run();
}

View File

@ -240,6 +240,7 @@ fn print_at_end_round(mut counter: Local<u32>) {
/// A group of related system sets, used for controlling the order of systems
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
#[system_set(base)]
enum MySet {
BeforeRound,
AfterRound,
@ -281,21 +282,21 @@ fn main() {
// add_system(system) adds systems to the Update system set by default
// However we can manually specify the set if we want to. The following is equivalent to
// add_system(score_system)
.add_system(score_system.in_set(CoreSet::Update))
.add_system(score_system)
// There are other `CoreSets`, such as `Last` which runs at the very end of each run.
.add_system(print_at_end_round.in_set(CoreSet::Last))
.add_system(print_at_end_round.in_base_set(CoreSet::Last))
// We can also create new system sets, and order them relative to other system sets.
// Here is what our games stage order will look like:
// "before_round": new_player_system, new_round_system
// "update": print_message_system, score_system
// "after_round": score_check_system, game_over_system
.configure_set(MySet::BeforeRound.no_default_set().before(CoreSet::Update))
.configure_set(MySet::AfterRound.no_default_set().after(CoreSet::Update))
.add_system(new_round_system.in_set(MySet::BeforeRound))
.configure_set(MySet::BeforeRound.before(CoreSet::Update))
.configure_set(MySet::AfterRound.after(CoreSet::Update))
.add_system(new_round_system.in_base_set(MySet::BeforeRound))
.add_system(
new_player_system
.after(new_round_system)
.in_set(MySet::BeforeRound),
.in_base_set(MySet::BeforeRound),
)
.add_system(exclusive_player_system.in_set(MySet::BeforeRound))
.add_system(score_check_system.in_set(MySet::AfterRound))

View File

@ -8,7 +8,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(rotate.in_set(CoreSet::Update))
.add_system(rotate)
.run();
}

View File

@ -15,7 +15,7 @@ fn main() {
.add_startup_system(generate_bodies)
.insert_resource(FixedTime::new_from_secs(DELTA_TIME))
.add_systems_to_schedule(CoreSchedule::FixedUpdate, (interact_bodies, integrate))
.add_system(look_at_star.in_set(CoreSet::Update))
.add_system(look_at_star)
.insert_resource(ClearColor(Color::BLACK))
.run();
}

View File

@ -70,7 +70,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(spawn_system)
.add_system(move_system.in_set(CoreSet::Update))
.add_system(bounce_system.in_set(CoreSet::Update))
.add_system(move_system)
.add_system(bounce_system)
.run();
}

View File

@ -15,8 +15,8 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(remove_component.in_set(CoreSet::Update))
.add_system(react_on_removal.in_set(CoreSet::PostUpdate))
.add_system(remove_component)
.add_system(react_on_removal.in_base_set(CoreSet::PostUpdate))
.run();
}

View File

@ -13,9 +13,9 @@ fn main() {
.add_plugins(DefaultPlugins)
.add_startup_system(setup_contributor_selection)
.add_startup_system(setup)
.add_system(velocity_system.in_set(CoreSet::Update))
.add_system(move_system.in_set(CoreSet::Update))
.add_system(collision_system.in_set(CoreSet::Update))
.add_system(velocity_system)
.add_system(move_system)
.add_system(collision_system)
.add_system(select_system)
.init_resource::<SelectionState>()
.run();

View File

@ -14,7 +14,7 @@ fn main() {
}))
.add_startup_system(setup_scene)
.add_startup_system(setup_music)
.add_system(touch_camera.in_set(CoreSet::Update))
.add_system(touch_camera)
.add_system(button_handler)
.run();
}

View File

@ -11,7 +11,7 @@ fn main() {
.add_plugins(DefaultPlugins)
.add_plugin(MaterialPlugin::<CustomMaterial>::default())
.add_startup_system(setup)
.add_system(rotate_camera.in_set(CoreSet::Update))
.add_system(rotate_camera)
.run();
}

View File

@ -29,7 +29,7 @@ fn main() {
..default()
})
.add_startup_system(setup)
.add_system(rotate.in_set(CoreSet::Update))
.add_system(rotate)
.add_system(update)
.run();
}

View File

@ -44,9 +44,9 @@ fn main() {
color: Color::WHITE,
})
.add_startup_system(setup)
.add_system(mouse_handler.in_set(CoreSet::Update))
.add_system(movement_system.in_set(CoreSet::Update))
.add_system(collision_system.in_set(CoreSet::Update))
.add_system(mouse_handler)
.add_system(movement_system)
.add_system(collision_system)
.add_system(counter_system)
.add_system_to_schedule(CoreSchedule::FixedUpdate, scheduled_spawner)
.insert_resource(FixedTime::new_from_secs(0.2))

View File

@ -33,13 +33,9 @@ fn main() {
..default()
}))
.add_startup_system(setup)
.add_system(animate_sprite.in_set(CoreSet::Update))
.add_system(animate_sprite)
.add_system(print_sprite_count)
.add_system(
move_camera
.in_set(CoreSet::Update)
.after(print_sprite_count),
)
.add_system(move_camera.after(print_sprite_count))
.run();
}

View File

@ -31,8 +31,8 @@ fn main() {
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup)
.add_system(move_camera.in_set(CoreSet::Update))
.add_system(print_mesh_count.in_set(CoreSet::Update))
.add_system(move_camera)
.add_system(print_mesh_count)
.run();
}

View File

@ -27,7 +27,7 @@ fn main() {
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup)
.add_system(move_camera.in_set(CoreSet::Update))
.add_system(move_camera)
.add_system(print_light_count)
.add_plugin(LogVisibleLights)
.run();

View File

@ -188,7 +188,7 @@ fn main() {
.add_startup_system(setup)
// Updating transforms *must* be done before `CoreSet::PostUpdate`
// or the hierarchy will momentarily be in an invalid state.
.add_system(update.in_set(CoreSet::Update))
.add_system(update)
.run();
}

View File

@ -131,10 +131,10 @@ fn main() {
.add_startup_system(setup_sticks)
.add_startup_system(setup_triggers)
.add_startup_system(setup_connected)
.add_system(update_buttons.in_set(CoreSet::Update))
.add_system(update_button_values.in_set(CoreSet::Update))
.add_system(update_axes.in_set(CoreSet::Update))
.add_system(update_connected.in_set(CoreSet::Update))
.add_system(update_buttons)
.add_system(update_button_values)
.add_system(update_axes)
.add_system(update_connected)
.run();
}

View File

@ -42,7 +42,7 @@ fn main() {
.add_plugin(CameraControllerPlugin)
.add_plugin(SceneViewerPlugin)
.add_startup_system(setup)
.add_system(setup_scene_after_load.in_set(CoreSet::PreUpdate));
.add_system(setup_scene_after_load.in_base_set(CoreSet::PreUpdate));
app.run();
}

View File

@ -57,13 +57,13 @@ pub struct SceneViewerPlugin;
impl Plugin for SceneViewerPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<CameraTracker>()
.add_system(scene_load_check.in_set(CoreSet::PreUpdate))
.add_system(update_lights.in_set(CoreSet::Update))
.add_system(camera_tracker.in_set(CoreSet::Update));
.add_system(scene_load_check.in_base_set(CoreSet::PreUpdate))
.add_system(update_lights)
.add_system(camera_tracker);
#[cfg(feature = "animation")]
app.add_system(start_animation.in_set(CoreSet::Update))
.add_system(keyboard_animation_control.in_set(CoreSet::Update));
app.add_system(start_animation)
.add_system(keyboard_animation_control);
}
}

View File

@ -14,7 +14,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(rotate_cube.in_set(CoreSet::Update))
.add_system(rotate_cube)
.run();
}

View File

@ -30,8 +30,8 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(change_scale_direction.in_set(CoreSet::Update))
.add_system(scale_cube.in_set(CoreSet::Update))
.add_system(change_scale_direction)
.add_system(scale_cube)
.run();
}

View File

@ -25,9 +25,9 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(move_cube.in_set(CoreSet::Update))
.add_system(rotate_cube.in_set(CoreSet::Update))
.add_system(scale_down_sphere_proportional_to_cube_travel_distance.in_set(CoreSet::Update))
.add_system(move_cube)
.add_system(rotate_cube)
.add_system(scale_down_sphere_proportional_to_cube_travel_distance)
.run();
}

View File

@ -26,7 +26,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(move_cube.in_set(CoreSet::Update))
.add_system(move_cube)
.run();
}

View File

@ -8,7 +8,7 @@ fn main() {
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app())
.add_startup_system(setup)
.add_system(relative_cursor_position_system.in_set(CoreSet::Update))
.add_system(relative_cursor_position_system)
.run();
}

View File

@ -13,8 +13,8 @@ fn main() {
.add_plugins(DefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_startup_system(setup)
.add_system(text_update_system.in_set(CoreSet::Update))
.add_system(text_color_system.in_set(CoreSet::Update))
.add_system(text_update_system)
.add_system(text_color_system)
.run();
}

View File

@ -17,7 +17,7 @@ fn main() {
}))
.add_plugin(FrameTimeDiagnosticsPlugin)
.add_startup_system(infotext_system)
.add_system(change_text_system.in_set(CoreSet::Update))
.add_system(change_text_system)
.run();
}

View File

@ -12,7 +12,7 @@ fn main() {
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app())
.add_startup_system(setup)
.add_system(mouse_scroll.in_set(CoreSet::Update))
.add_system(mouse_scroll)
.run();
}

View File

@ -17,8 +17,8 @@ fn main() {
target_time: Timer::new(Duration::from_millis(SCALE_TIME), TimerMode::Once),
})
.add_startup_system(setup)
.add_system(change_scaling.in_set(CoreSet::Update))
.add_system(apply_scaling.after(change_scaling).in_set(CoreSet::Update))
.add_system(change_scaling)
.add_system(apply_scaling.after(change_scaling))
.run();
}

View File

@ -34,9 +34,9 @@ fn main() {
..default()
}))
.add_startup_system(test_setup::setup)
.add_system(test_setup::cycle_modes.in_set(CoreSet::Update))
.add_system(test_setup::rotate_cube.in_set(CoreSet::Update))
.add_system(test_setup::update_text.in_set(CoreSet::Update))
.add_system(test_setup::cycle_modes)
.add_system(test_setup::rotate_cube)
.add_system(test_setup::update_text)
.add_system(update_winit)
.run();
}