From aefe1f0739bb395d2454e05a1a812acab767892c Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Fri, 17 Mar 2023 18:45:34 -0700 Subject: [PATCH] Schedule-First: the new and improved add_systems (#8079) Co-authored-by: Mike --- .../bevy_ecs/components/archetype_updates.rs | 2 +- benches/benches/bevy_ecs/empty_archetypes.rs | 6 +- .../bevy_ecs/scheduling/run_condition.rs | 8 +- .../bevy_ecs/scheduling/running_systems.rs | 2 +- .../benches/bevy_ecs/scheduling/schedule.rs | 10 +- crates/bevy_animation/src/lib.rs | 9 +- crates/bevy_app/src/app.rs | 276 ++---- crates/bevy_app/src/ci_testing.rs | 4 +- crates/bevy_app/src/config.rs | 294 ------ crates/bevy_app/src/lib.rs | 201 +--- crates/bevy_app/src/main_schedule.rs | 168 ++++ crates/bevy_asset/src/asset_server.rs | 13 +- crates/bevy_asset/src/assets.rs | 16 +- crates/bevy_asset/src/debug_asset_server.rs | 4 +- .../asset_count_diagnostics_plugin.rs | 4 +- crates/bevy_asset/src/lib.rs | 42 +- crates/bevy_audio/src/lib.rs | 9 +- crates/bevy_core/src/lib.rs | 8 +- crates/bevy_core_pipeline/src/bloom/mod.rs | 25 +- crates/bevy_core_pipeline/src/core_2d/mod.rs | 21 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 25 +- crates/bevy_core_pipeline/src/fxaa/mod.rs | 4 +- .../bevy_core_pipeline/src/msaa_writeback.rs | 7 +- .../bevy_core_pipeline/src/tonemapping/mod.rs | 7 +- .../bevy_core_pipeline/src/upscaling/mod.rs | 7 +- .../src/entity_count_diagnostics_plugin.rs | 4 +- .../src/frame_time_diagnostics_plugin.rs | 4 +- crates/bevy_diagnostic/src/lib.rs | 6 +- .../src/log_diagnostics_plugin.rs | 4 +- .../system_information_diagnostics_plugin.rs | 4 +- crates/bevy_ecs/README.md | 2 +- crates/bevy_ecs/examples/change_detection.rs | 2 +- crates/bevy_ecs/examples/events.rs | 2 +- crates/bevy_ecs/macros/src/lib.rs | 2 +- crates/bevy_ecs/macros/src/set.rs | 69 +- crates/bevy_ecs/src/event.rs | 2 +- crates/bevy_ecs/src/lib.rs | 5 +- crates/bevy_ecs/src/schedule/condition.rs | 64 +- crates/bevy_ecs/src/schedule/config.rs | 894 +++++++++--------- crates/bevy_ecs/src/schedule/graph_utils.rs | 44 +- crates/bevy_ecs/src/schedule/mod.rs | 224 ++--- crates/bevy_ecs/src/schedule/schedule.rs | 411 +++----- crates/bevy_ecs/src/schedule/set.rs | 24 - crates/bevy_ecs/src/system/combinator.rs | 2 +- crates/bevy_ecs/src/system/commands/mod.rs | 4 +- crates/bevy_ecs/src/system/mod.rs | 4 +- crates/bevy_ecs/src/system/system.rs | 2 +- crates/bevy_ecs/src/system/system_param.rs | 2 +- crates/bevy_ecs/src/system/system_piping.rs | 12 +- crates/bevy_gilrs/src/lib.rs | 12 +- .../src/valid_parent_check_plugin.rs | 8 +- crates/bevy_input/src/common_conditions.rs | 6 +- crates/bevy_input/src/lib.rs | 9 +- crates/bevy_pbr/src/lib.rs | 143 +-- crates/bevy_pbr/src/material.rs | 23 +- crates/bevy_pbr/src/prepass/mod.rs | 30 +- crates/bevy_pbr/src/render/fog.rs | 9 +- crates/bevy_pbr/src/render/mesh.rs | 19 +- crates/bevy_pbr/src/wireframe.rs | 3 +- crates/bevy_render/src/camera/camera.rs | 3 +- crates/bevy_render/src/camera/mod.rs | 10 +- crates/bevy_render/src/camera/projection.rs | 27 +- crates/bevy_render/src/extract_component.rs | 13 +- crates/bevy_render/src/extract_resource.rs | 4 +- crates/bevy_render/src/globals.rs | 8 +- crates/bevy_render/src/lib.rs | 57 +- crates/bevy_render/src/pipelined_rendering.rs | 5 +- crates/bevy_render/src/render_asset.rs | 18 +- crates/bevy_render/src/texture/mod.rs | 7 +- crates/bevy_render/src/view/mod.rs | 21 +- crates/bevy_render/src/view/visibility/mod.rs | 101 +- crates/bevy_render/src/view/window.rs | 10 +- crates/bevy_scene/src/lib.rs | 5 +- crates/bevy_sprite/src/lib.rs | 9 +- crates/bevy_sprite/src/mesh2d/material.rs | 21 +- crates/bevy_sprite/src/mesh2d/mesh.rs | 17 +- crates/bevy_text/src/lib.rs | 11 +- crates/bevy_time/src/common_conditions.rs | 15 +- crates/bevy_time/src/fixed_timestep.rs | 10 +- crates/bevy_time/src/lib.rs | 11 +- crates/bevy_time/src/time.rs | 2 +- .../src/components/global_transform.rs | 2 +- .../src/components/transform.rs | 2 +- crates/bevy_transform/src/lib.rs | 55 +- crates/bevy_transform/src/systems.rs | 4 +- crates/bevy_ui/src/accessibility.rs | 18 +- crates/bevy_ui/src/lib.rs | 37 +- crates/bevy_ui/src/render/mod.rs | 19 +- crates/bevy_ui/src/stack.rs | 2 +- crates/bevy_window/src/lib.rs | 17 +- crates/bevy_winit/src/accessibility.rs | 17 +- crates/bevy_winit/src/lib.rs | 6 +- crates/bevy_winit/src/web_resize.rs | 4 +- errors/B0001.md | 6 +- errors/B0002.md | 4 +- errors/B0003.md | 6 +- errors/B0004.md | 4 +- examples/2d/2d_shapes.rs | 2 +- examples/2d/bloom_2d.rs | 3 +- examples/2d/mesh2d.rs | 2 +- examples/2d/mesh2d_manual.rs | 10 +- examples/2d/mesh2d_vertex_color_texture.rs | 2 +- examples/2d/move_sprite.rs | 3 +- examples/2d/pixel_perfect.rs | 3 +- examples/2d/rotation.rs | 10 +- examples/2d/sprite.rs | 2 +- examples/2d/sprite_flipping.rs | 2 +- examples/2d/sprite_sheet.rs | 3 +- examples/2d/text2d.rs | 11 +- examples/2d/texture_atlas.rs | 8 +- examples/2d/transparency_2d.rs | 2 +- examples/3d/3d_scene.rs | 2 +- examples/3d/3d_shapes.rs | 3 +- examples/3d/atmospheric_fog.rs | 7 +- examples/3d/blend_modes.rs | 3 +- examples/3d/bloom_3d.rs | 7 +- examples/3d/fog.rs | 7 +- examples/3d/fxaa.rs | 3 +- examples/3d/lighting.rs | 3 +- examples/3d/lines.rs | 2 +- examples/3d/load_gltf.rs | 3 +- examples/3d/msaa.rs | 3 +- examples/3d/orthographic.rs | 2 +- examples/3d/parenting.rs | 3 +- examples/3d/pbr.rs | 3 +- examples/3d/render_to_texture.rs | 3 +- examples/3d/shadow_biases.rs | 17 +- examples/3d/shadow_caster_receiver.rs | 3 +- examples/3d/skybox.rs | 17 +- examples/3d/spherical_area_lights.rs | 2 +- examples/3d/split_screen.rs | 3 +- examples/3d/spotlight.rs | 3 +- examples/3d/texture.rs | 2 +- examples/3d/tonemapping.rs | 32 +- examples/3d/transparency_3d.rs | 4 +- examples/3d/two_passes.rs | 2 +- examples/3d/update_gltf_scene.rs | 3 +- examples/3d/vertex_colors.rs | 2 +- examples/3d/wireframe.rs | 2 +- examples/animation/animated_fox.rs | 10 +- examples/animation/animated_transform.rs | 2 +- examples/animation/custom_skinned_mesh.rs | 3 +- examples/animation/gltf_skinned_mesh.rs | 3 +- examples/app/custom_loop.rs | 2 +- examples/app/drag_and_drop.rs | 2 +- examples/app/headless.rs | 4 +- examples/app/logs.rs | 2 +- examples/app/plugin.rs | 3 +- examples/app/plugin_group.rs | 4 +- examples/app/return_after_run.rs | 2 +- examples/app/without_winit.rs | 2 +- examples/asset/asset_loading.rs | 2 +- examples/asset/custom_asset.rs | 3 +- examples/asset/custom_asset_io.rs | 2 +- examples/asset/hot_asset_reloading.rs | 2 +- examples/async_tasks/async_compute.rs | 4 +- .../external_source_external_thread.rs | 3 +- examples/audio/audio.rs | 2 +- examples/audio/audio_control.rs | 3 +- examples/audio/decodable.rs | 2 +- examples/audio/spatial_audio_2d.rs | 3 +- examples/audio/spatial_audio_3d.rs | 3 +- examples/diagnostics/custom_diagnostic.rs | 3 +- examples/ecs/apply_system_buffers.rs | 24 +- examples/ecs/component_change_detection.rs | 11 +- examples/ecs/custom_query_param.rs | 3 +- examples/ecs/ecs_guide.rs | 81 +- examples/ecs/event.rs | 2 +- examples/ecs/fixed_timestep.rs | 10 +- examples/ecs/generic_system.rs | 21 +- examples/ecs/hierarchy.rs | 3 +- examples/ecs/iter_combinations.rs | 8 +- examples/ecs/nondeterministic_system_order.rs | 45 +- examples/ecs/parallel_query.rs | 3 +- examples/ecs/removal_detection.rs | 8 +- examples/ecs/run_conditions.rs | 65 +- examples/ecs/startup_system.rs | 4 +- examples/ecs/state.rs | 23 +- examples/ecs/system_closure.rs | 8 +- examples/ecs/system_param.rs | 3 +- examples/ecs/system_piping.rs | 19 +- examples/ecs/timers.rs | 3 +- examples/games/alien_cake_addict.rs | 26 +- examples/games/breakout.rs | 13 +- examples/games/contributors.rs | 17 +- examples/games/game_menu.rs | 75 +- examples/hello_world.rs | 2 +- examples/input/char_input_events.rs | 2 +- examples/input/gamepad_input.rs | 2 +- examples/input/gamepad_input_events.rs | 2 +- examples/input/keyboard_input.rs | 2 +- examples/input/keyboard_input_events.rs | 2 +- examples/input/keyboard_modifiers.rs | 2 +- examples/input/mouse_grab.rs | 2 +- examples/input/mouse_input.rs | 2 +- examples/input/mouse_input_events.rs | 2 +- examples/input/text_input.rs | 19 +- examples/input/touch_input.rs | 2 +- examples/input/touch_input_events.rs | 2 +- examples/mobile/src/lib.rs | 4 +- examples/reflection/generic_reflection.rs | 2 +- examples/reflection/reflection.rs | 2 +- examples/reflection/reflection_types.rs | 2 +- examples/reflection/trait_reflection.rs | 2 +- examples/scene/scene.rs | 7 +- examples/shader/animate_shader.rs | 2 +- examples/shader/array_texture.rs | 3 +- .../shader/compute_shader_game_of_life.rs | 6 +- examples/shader/custom_vertex_attribute.rs | 2 +- examples/shader/post_process_pass.rs | 5 +- examples/shader/post_processing.rs | 3 +- examples/shader/shader_defs.rs | 2 +- examples/shader/shader_instancing.rs | 15 +- examples/shader/shader_material.rs | 2 +- examples/shader/shader_material_glsl.rs | 2 +- .../shader_material_screenspace_texture.rs | 3 +- examples/shader/shader_prepass.rs | 3 +- examples/shader/texture_binding_array.rs | 2 +- examples/stress_tests/bevymark.rs | 19 +- .../stress_tests/many_animated_sprites.rs | 15 +- examples/stress_tests/many_buttons.rs | 3 +- examples/stress_tests/many_cubes.rs | 3 +- examples/stress_tests/many_foxes.rs | 15 +- examples/stress_tests/many_glyphs.rs | 2 +- examples/stress_tests/many_lights.rs | 7 +- examples/stress_tests/many_sprites.rs | 10 +- examples/stress_tests/text_pipeline.rs | 3 +- examples/stress_tests/transform_hierarchy.rs | 10 +- examples/tools/gamepad_viewer.rs | 20 +- .../scene_viewer/camera_controller_plugin.rs | 2 +- examples/tools/scene_viewer/main.rs | 4 +- .../tools/scene_viewer/scene_viewer_plugin.rs | 19 +- examples/transforms/3d_rotation.rs | 3 +- examples/transforms/scale.rs | 3 +- examples/transforms/transform.rs | 15 +- examples/transforms/translation.rs | 3 +- examples/ui/button.rs | 3 +- examples/ui/font_atlas_debug.rs | 3 +- examples/ui/relative_cursor_position.rs | 3 +- examples/ui/text.rs | 3 +- examples/ui/text_debug.rs | 3 +- examples/ui/text_layout.rs | 2 +- examples/ui/transparency_ui.rs | 2 +- examples/ui/ui.rs | 3 +- examples/ui/ui_scaling.rs | 10 +- examples/ui/window_fallthrough.rs | 4 +- examples/ui/z_index.rs | 2 +- examples/window/clear_color.rs | 3 +- examples/window/low_power.rs | 17 +- examples/window/multiple_windows.rs | 4 +- examples/window/scale_factor_override.rs | 11 +- examples/window/transparent_window.rs | 6 +- examples/window/window_resizing.rs | 4 +- examples/window/window_settings.rs | 17 +- src/lib.rs | 2 +- tests/how_to_test_systems.rs | 8 +- tests/window/minimising.rs | 4 +- tests/window/resizing.rs | 23 +- 258 files changed, 2177 insertions(+), 2798 deletions(-) delete mode 100644 crates/bevy_app/src/config.rs create mode 100644 crates/bevy_app/src/main_schedule.rs diff --git a/benches/benches/bevy_ecs/components/archetype_updates.rs b/benches/benches/bevy_ecs/components/archetype_updates.rs index 53cc22f6a1..30f8ca6fcc 100644 --- a/benches/benches/bevy_ecs/components/archetype_updates.rs +++ b/benches/benches/bevy_ecs/components/archetype_updates.rs @@ -9,7 +9,7 @@ fn setup(system_count: usize) -> (World, Schedule) { fn empty() {} let mut schedule = Schedule::new(); for _ in 0..system_count { - schedule.add_system(empty); + schedule.add_systems(empty); } schedule.run(&mut world); (world, schedule) diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs index 0d01bdcc32..315a2d2ce2 100644 --- a/benches/benches/bevy_ecs/empty_archetypes.rs +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -154,7 +154,7 @@ fn empty_archetypes(criterion: &mut Criterion) { let mut group = criterion.benchmark_group("empty_archetypes"); for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { let (mut world, mut schedule) = setup(true, |schedule| { - schedule.add_system(iter); + schedule.add_systems(iter); }); add_archetypes(&mut world, archetype_count); world.clear_entities(); @@ -185,7 +185,7 @@ fn empty_archetypes(criterion: &mut Criterion) { } for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { let (mut world, mut schedule) = setup(true, |schedule| { - schedule.add_system(for_each); + schedule.add_systems(for_each); }); add_archetypes(&mut world, archetype_count); world.clear_entities(); @@ -216,7 +216,7 @@ fn empty_archetypes(criterion: &mut Criterion) { } for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] { let (mut world, mut schedule) = setup(true, |schedule| { - schedule.add_system(par_for_each); + schedule.add_systems(par_for_each); }); add_archetypes(&mut world, archetype_count); world.clear_entities(); diff --git a/benches/benches/bevy_ecs/scheduling/run_condition.rs b/benches/benches/bevy_ecs/scheduling/run_condition.rs index 367cd7839e..a18bdd8c97 100644 --- a/benches/benches/bevy_ecs/scheduling/run_condition.rs +++ b/benches/benches/bevy_ecs/scheduling/run_condition.rs @@ -19,7 +19,7 @@ pub fn run_condition_yes(criterion: &mut Criterion) { fn empty() {} for amount in 0..21 { let mut schedule = Schedule::new(); - schedule.add_system(empty.run_if(yes)); + schedule.add_systems(empty.run_if(yes)); for _ in 0..amount { schedule.add_systems((empty, empty, empty, empty, empty).distributive_run_if(yes)); } @@ -42,7 +42,7 @@ pub fn run_condition_no(criterion: &mut Criterion) { fn empty() {} for amount in 0..21 { let mut schedule = Schedule::new(); - schedule.add_system(empty.run_if(no)); + schedule.add_systems(empty.run_if(no)); for _ in 0..amount { schedule.add_systems((empty, empty, empty, empty, empty).distributive_run_if(no)); } @@ -72,7 +72,7 @@ pub fn run_condition_yes_with_query(criterion: &mut Criterion) { } for amount in 0..21 { let mut schedule = Schedule::new(); - schedule.add_system(empty.run_if(yes_with_query)); + schedule.add_systems(empty.run_if(yes_with_query)); for _ in 0..amount { schedule.add_systems( (empty, empty, empty, empty, empty).distributive_run_if(yes_with_query), @@ -101,7 +101,7 @@ pub fn run_condition_yes_with_resource(criterion: &mut Criterion) { } for amount in 0..21 { let mut schedule = Schedule::new(); - schedule.add_system(empty.run_if(yes_with_resource)); + schedule.add_systems(empty.run_if(yes_with_resource)); for _ in 0..amount { schedule.add_systems( (empty, empty, empty, empty, empty).distributive_run_if(yes_with_resource), diff --git a/benches/benches/bevy_ecs/scheduling/running_systems.rs b/benches/benches/bevy_ecs/scheduling/running_systems.rs index 99a0827c2a..151b96d4a6 100644 --- a/benches/benches/bevy_ecs/scheduling/running_systems.rs +++ b/benches/benches/bevy_ecs/scheduling/running_systems.rs @@ -23,7 +23,7 @@ pub fn empty_systems(criterion: &mut Criterion) { for amount in 0..5 { let mut schedule = Schedule::new(); for _ in 0..amount { - schedule.add_system(empty); + schedule.add_systems(empty); } schedule.run(&mut world); group.bench_function(&format!("{:03}_systems", amount), |bencher| { diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index 5d2ab87677..e670c23d74 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -1,4 +1,4 @@ -use bevy_app::App; +use bevy_app::{App, Update}; use bevy_ecs::prelude::*; use criterion::Criterion; @@ -72,7 +72,7 @@ pub fn build_schedule(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(15)); // Method: generate a set of `graph_size` systems which have a One True Ordering. - // Add system to the schedule with full constraints. Hopefully this should be maximimally + // Add system to the schedule with full constraints. Hopefully this should be maximally // difficult for bevy to figure out. let labels: Vec<_> = (0..1000).map(|i| NumSet(i)).collect(); @@ -83,7 +83,7 @@ pub fn build_schedule(criterion: &mut Criterion) { bencher.iter(|| { let mut app = App::new(); for _ in 0..graph_size { - app.add_system(empty_system); + app.add_systems(Update, empty_system); } app.update(); }); @@ -93,7 +93,7 @@ pub fn build_schedule(criterion: &mut Criterion) { group.bench_function(format!("{graph_size}_schedule"), |bencher| { bencher.iter(|| { let mut app = App::new(); - app.add_system(empty_system.in_set(DummySet)); + app.add_systems(Update, empty_system.in_set(DummySet)); // Build a fully-connected dependency graph describing the One True Ordering. // Not particularly realistic but this can be refined later. @@ -105,7 +105,7 @@ pub fn build_schedule(criterion: &mut Criterion) { for label in &labels[i + 1..graph_size] { sys = sys.before(*label); } - app.add_system(sys); + app.add_systems(Update, sys); } // Run the app for a single frame. // This is necessary since dependency resolution does not occur until the game runs. diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 7312a5fcf6..0e9534d63b 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use std::time::Duration; -use bevy_app::{App, CoreSet, Plugin}; +use bevy_app::{App, Plugin, PostUpdate}; use bevy_asset::{AddAsset, Assets, Handle}; use bevy_core::Name; use bevy_ecs::prelude::*; @@ -550,10 +550,9 @@ impl Plugin for AnimationPlugin { app.add_asset::() .register_asset_reflect::() .register_type::() - .add_system( - animation_player - .in_base_set(CoreSet::PostUpdate) - .before(TransformSystem::TransformPropagate), + .add_systems( + PostUpdate, + animation_player.before(TransformSystem::TransformPropagate), ); } } diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 94271f9920..d7a4da63cd 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,13 +1,12 @@ use crate::{ - CoreSchedule, CoreSet, IntoSystemAppConfig, IntoSystemAppConfigs, Plugin, PluginGroup, - StartupSet, SystemAppConfig, + First, Main, MainSchedulePlugin, Plugin, PluginGroup, Startup, StateTransition, Update, }; pub use bevy_derive::AppLabel; use bevy_ecs::{ prelude::*, schedule::{ apply_state_transition, common_conditions::run_once as run_once_condition, - run_enter_schedule, BoxedScheduleLabel, IntoSystemConfig, IntoSystemSetConfigs, + run_enter_schedule, BoxedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs, ScheduleLabel, }, }; @@ -53,7 +52,7 @@ pub(crate) enum AppError { /// # /// fn main() { /// App::new() -/// .add_system(hello_world_system) +/// .add_systems(Update, hello_world_system) /// .run(); /// } /// @@ -74,12 +73,10 @@ pub struct App { pub runner: Box, // Send bound is required to make App Send /// The schedule that systems are added to by default. /// - /// This is initially set to [`CoreSchedule::Main`]. - pub default_schedule_label: BoxedScheduleLabel, - /// The schedule that controls the outer loop of schedule execution. + /// The schedule that runs the main loop of schedule execution. /// - /// This is initially set to [`CoreSchedule::Outer`]. - pub outer_schedule_label: BoxedScheduleLabel, + /// This is initially set to [`Main`]. + pub main_schedule_label: BoxedScheduleLabel, sub_apps: HashMap, plugin_registry: Vec>, plugin_name_added: HashSet, @@ -104,7 +101,7 @@ impl Debug for App { /// # Example /// /// ```rust -/// # use bevy_app::{App, AppLabel, SubApp, CoreSchedule}; +/// # use bevy_app::{App, AppLabel, SubApp, Main}; /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::schedule::ScheduleLabel; /// @@ -122,12 +119,10 @@ impl Debug for App { /// // create a app with a resource and a single schedule /// let mut sub_app = App::empty(); /// // add an outer schedule that runs the main schedule -/// sub_app.add_simple_outer_schedule(); /// sub_app.insert_resource(Val(100)); /// /// // initialize main schedule -/// sub_app.init_schedule(CoreSchedule::Main); -/// sub_app.add_system(|counter: Res| { +/// sub_app.add_systems(Main, |counter: Res| { /// // since we assigned the value from the main world in extract /// // we see that value instead of 100 /// assert_eq!(counter.0, 10); @@ -169,7 +164,7 @@ impl SubApp { pub fn run(&mut self) { self.app .world - .run_schedule_ref(&*self.app.outer_schedule_label); + .run_schedule_ref(&*self.app.main_schedule_label); self.app.world.clear_trackers(); } @@ -195,8 +190,7 @@ impl Default for App { #[cfg(feature = "bevy_reflect")] app.init_resource::(); - app.add_default_schedules(); - + app.add_plugin(MainSchedulePlugin); app.add_event::(); #[cfg(feature = "bevy_ci_testing")] @@ -211,8 +205,6 @@ impl Default for App { impl App { /// Creates a new [`App`] with some default structure to enable core engine features. /// This is the preferred constructor for most use cases. - /// - /// This calls [`App::add_default_schedules`]. pub fn new() -> App { App::default() } @@ -229,8 +221,7 @@ impl App { sub_apps: HashMap::default(), plugin_registry: Vec::default(), plugin_name_added: Default::default(), - default_schedule_label: Box::new(CoreSchedule::Main), - outer_schedule_label: Box::new(CoreSchedule::Outer), + main_schedule_label: Box::new(Main), building_plugin_depth: 0, } } @@ -240,9 +231,8 @@ impl App { /// This method also updates sub apps. /// See [`insert_sub_app`](Self::insert_sub_app) for more details. /// - /// The schedule run by this method is determined by the [`outer_schedule_label`](App) field. - /// In normal usage, this is [`CoreSchedule::Outer`], which will run [`CoreSchedule::Startup`] - /// the first time the app is run, then [`CoreSchedule::Main`] on every call of this method. + /// The schedule run by this method is determined by the [`main_schedule_label`](App) field. + /// By default this is [`Main`]. /// /// # Panics /// @@ -251,7 +241,7 @@ impl App { { #[cfg(feature = "trace")] let _bevy_frame_update_span = info_span!("main app").entered(); - self.world.run_schedule_ref(&*self.outer_schedule_label); + self.world.run_schedule_ref(&*self.main_schedule_label); } for (_label, sub_app) in self.sub_apps.iter_mut() { #[cfg(feature = "trace")] @@ -317,13 +307,13 @@ impl App { /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules /// for each state variant (if they don't already exist), an instance of [`apply_state_transition::`] in - /// [`CoreSet::StateTransitions`] so that transitions happen before [`CoreSet::Update`] and - /// a instance of [`run_enter_schedule::`] in [`CoreSet::StateTransitions`] with a + /// [`StateTransition`] so that transitions happen before [`Update`] and + /// a instance of [`run_enter_schedule::`] in [`StateTransition`] with a /// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the /// initial state. /// /// This also adds an [`OnUpdate`] system set for each state variant, - /// which runs during [`CoreSet::Update`] after the transitions are applied. + /// which runs during [`Update`] after the transitions are applied. /// These system sets only run if the [`State`] resource matches the respective state variant. /// /// If you would like to control how other systems run based on the current state, @@ -332,31 +322,19 @@ impl App { /// Note that you can also apply state transitions at other points in the schedule /// by adding the [`apply_state_transition`] system manually. pub fn add_state(&mut self) -> &mut Self { - self.init_resource::>(); - self.init_resource::>(); - - let mut schedules = self.world.resource_mut::(); - - let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) else { - let schedule_label = &self.default_schedule_label; - panic!("Default schedule {schedule_label:?} does not exist.") - }; - - default_schedule.add_systems( - ( - run_enter_schedule::.run_if(run_once_condition()), - apply_state_transition::, - ) - .chain() - .in_base_set(CoreSet::StateTransitions), - ); + self.init_resource::>() + .init_resource::>() + .add_systems( + StateTransition, + ( + run_enter_schedule::.run_if(run_once_condition()), + apply_state_transition::, + ) + .chain(), + ); for variant in S::variants() { - default_schedule.configure_set( - OnUpdate(variant.clone()) - .in_base_set(CoreSet::Update) - .run_if(in_state(variant)), - ); + self.configure_set(Update, OnUpdate(variant.clone()).run_if(in_state(variant))); } // The OnEnter, OnExit, and OnTransition schedules are lazily initialized @@ -382,30 +360,15 @@ impl App { /// # /// app.add_system(my_system); /// ``` - pub fn add_system(&mut self, system: impl IntoSystemAppConfig) -> &mut Self { - let mut schedules = self.world.resource_mut::(); - - let SystemAppConfig { system, schedule } = system.into_app_config(); - - if let Some(schedule_label) = schedule { - if let Some(schedule) = schedules.get_mut(&*schedule_label) { - schedule.add_system(system); - } else { - let mut schedule = Schedule::new(); - schedule.add_system(system); - schedules.insert(schedule_label, schedule); - } - } else if let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) { - default_schedule.add_system(system); - } else { - let schedule_label = &self.default_schedule_label; - panic!("Default schedule {schedule_label:?} does not exist.") - } - - self + #[deprecated( + since = "0.11.0", + note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Update, your_system).`" + )] + pub fn add_system(&mut self, system: impl IntoSystemConfigs) -> &mut Self { + self.add_systems(Update, system) } - /// Adds a system to the default system set and schedule of the app's [`Schedules`]. + /// Adds a system to the given schedule in this app's [`Schedules`]. /// /// # Examples /// @@ -418,36 +381,27 @@ impl App { /// # fn system_b() {} /// # fn system_c() {} /// # - /// app.add_systems((system_a, system_b, system_c)); + /// app.add_systems(Update, (system_a, system_b, system_c)); /// ``` - pub fn add_systems(&mut self, systems: impl IntoSystemAppConfigs) -> &mut Self { + pub fn add_systems( + &mut self, + schedule: impl ScheduleLabel, + systems: impl IntoSystemConfigs, + ) -> &mut Self { let mut schedules = self.world.resource_mut::(); - match systems.into_app_configs().0 { - crate::InnerConfigs::Blanket { systems, schedule } => { - let schedule = if let Some(label) = schedule { - schedules - .get_mut(&*label) - .unwrap_or_else(|| panic!("Schedule '{label:?}' does not exist.")) - } else { - let label = &*self.default_schedule_label; - schedules - .get_mut(label) - .unwrap_or_else(|| panic!("Default schedule '{label:?}' does not exist.")) - }; - schedule.add_systems(systems); - } - crate::InnerConfigs::Granular(systems) => { - for system in systems { - self.add_system(system); - } - } + if let Some(schedule) = schedules.get_mut(&schedule) { + schedule.add_systems(systems); + } else { + let mut new_schedule = Schedule::new(); + new_schedule.add_systems(systems); + schedules.insert(schedule, new_schedule); } self } - /// Adds a system to [`CoreSchedule::Startup`]. + /// Adds a system to [`Startup`]. /// /// These systems will run exactly once, at the start of the [`App`]'s lifecycle. /// To add a system that runs every frame, see [`add_system`](Self::add_system). @@ -463,13 +417,17 @@ impl App { /// } /// /// App::new() - /// .add_startup_system(my_startup_system); + /// .add_systems(Startup, my_startup_system); /// ``` - pub fn add_startup_system(&mut self, system: impl IntoSystemConfig) -> &mut Self { - self.add_system(system.in_schedule(CoreSchedule::Startup)) + #[deprecated( + since = "0.11.0", + note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Startup, your_system).`" + )] + pub fn add_startup_system(&mut self, system: impl IntoSystemConfigs) -> &mut Self { + self.add_systems(Startup, system) } - /// Adds a collection of systems to [`CoreSchedule::Startup`]. + /// Adds a collection of systems to [`Startup`]. /// /// # Examples /// @@ -482,88 +440,58 @@ impl App { /// # fn startup_system_b() {} /// # fn startup_system_c() {} /// # - /// app.add_startup_systems(( + /// app.add_systems(Startup, ( /// startup_system_a, /// startup_system_b, /// startup_system_c, /// )); /// ``` + #[deprecated( + since = "0.11.0", + note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Startup, your_system).`" + )] pub fn add_startup_systems(&mut self, systems: impl IntoSystemConfigs) -> &mut Self { - self.add_systems(systems.into_configs().in_schedule(CoreSchedule::Startup)) + self.add_systems(Startup, systems.into_configs()) } /// Configures a system set in the default schedule, adding the set if it does not exist. - pub fn configure_set(&mut self, set: impl IntoSystemSetConfig) -> &mut Self { - self.world - .resource_mut::() - .get_mut(&*self.default_schedule_label) - .unwrap() - .configure_set(set); + pub fn configure_set( + &mut self, + schedule: impl ScheduleLabel, + set: impl IntoSystemSetConfig, + ) -> &mut Self { + let mut schedules = self.world.resource_mut::(); + if let Some(schedule) = schedules.get_mut(&schedule) { + schedule.configure_set(set); + } else { + let mut new_schedule = Schedule::new(); + new_schedule.configure_set(set); + schedules.insert(schedule, new_schedule); + } self } /// Configures a collection of system sets in the default schedule, adding any sets that do not exist. - pub fn configure_sets(&mut self, sets: impl IntoSystemSetConfigs) -> &mut Self { - self.world - .resource_mut::() - .get_mut(&*self.default_schedule_label) - .unwrap() - .configure_sets(sets); - self - } - - /// Adds standardized schedules and labels to an [`App`]. - /// - /// Adding these schedules is necessary to make almost all core engine features work. - /// This is typically done implicitly by calling `App::default`, which is in turn called by - /// [`App::new`]. - /// - /// The schedules added are defined in the [`CoreSchedule`] enum, - /// and have a starting configuration defined by: - /// - /// - [`CoreSchedule::Outer`]: uses [`CoreSchedule::outer_schedule`] - /// - [`CoreSchedule::Startup`]: uses [`StartupSet::base_schedule`] - /// - [`CoreSchedule::Main`]: uses [`CoreSet::base_schedule`] - /// - [`CoreSchedule::FixedUpdate`]: no starting configuration - /// - /// # Examples - /// - /// ``` - /// use bevy_app::App; - /// use bevy_ecs::schedule::Schedules; - /// - /// let app = App::empty() - /// .init_resource::() - /// .add_default_schedules() - /// .update(); - /// ``` - pub fn add_default_schedules(&mut self) -> &mut Self { - self.add_schedule(CoreSchedule::Outer, CoreSchedule::outer_schedule()); - self.add_schedule(CoreSchedule::Startup, StartupSet::base_schedule()); - self.add_schedule(CoreSchedule::Main, CoreSet::base_schedule()); - self.init_schedule(CoreSchedule::FixedUpdate); - - self - } - - /// adds a single threaded outer schedule to the [`App`] that just runs the main schedule - pub fn add_simple_outer_schedule(&mut self) -> &mut Self { - fn run_main_schedule(world: &mut World) { - world.run_schedule(CoreSchedule::Main); + pub fn configure_sets( + &mut self, + schedule: impl ScheduleLabel, + sets: impl IntoSystemSetConfigs, + ) -> &mut Self { + let mut schedules = self.world.resource_mut::(); + if let Some(schedule) = schedules.get_mut(&schedule) { + schedule.configure_sets(sets); + } else { + let mut new_schedule = Schedule::new(); + new_schedule.configure_sets(sets); + schedules.insert(schedule, new_schedule); } - - self.edit_schedule(CoreSchedule::Outer, |schedule| { - schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded); - schedule.add_system(run_main_schedule); - }); - self } /// Setup the application to manage events of type `T`. /// /// This is done by adding a [`Resource`] of type [`Events::`], - /// and inserting an [`update_system`](Events::update_system) into [`CoreSet::First`]. + /// and inserting an [`update_system`](Events::update_system) into [`First`]. /// /// See [`Events`] for defining events. /// @@ -584,7 +512,7 @@ impl App { { if !self.world.contains_resource::>() { self.init_resource::>() - .add_system(Events::::update_system.in_base_set(CoreSet::First)); + .add_systems(First, Events::::update_system); } self } @@ -1025,7 +953,7 @@ mod tests { system::Commands, }; - use crate::{App, IntoSystemAppConfig, IntoSystemAppConfigs, Plugin}; + use crate::{App, Plugin}; struct PluginA; impl Plugin for PluginA { @@ -1097,31 +1025,11 @@ mod tests { commands.spawn_empty(); } - #[test] - fn add_system_should_create_schedule_if_it_does_not_exist() { - let mut app = App::new(); - app.add_system(foo.in_schedule(OnEnter(AppState::MainMenu))) - .add_state::(); - - app.world.run_schedule(OnEnter(AppState::MainMenu)); - assert_eq!(app.world.entities().len(), 1); - } - - #[test] - fn add_system_should_create_schedule_if_it_does_not_exist2() { - let mut app = App::new(); - app.add_state::() - .add_system(foo.in_schedule(OnEnter(AppState::MainMenu))); - - app.world.run_schedule(OnEnter(AppState::MainMenu)); - assert_eq!(app.world.entities().len(), 1); - } - #[test] fn add_systems_should_create_schedule_if_it_does_not_exist() { let mut app = App::new(); app.add_state::() - .add_systems((foo, bar).in_schedule(OnEnter(AppState::MainMenu))); + .add_systems(OnEnter(AppState::MainMenu), (foo, bar)); app.world.run_schedule(OnEnter(AppState::MainMenu)); assert_eq!(app.world.entities().len(), 2); @@ -1130,7 +1038,7 @@ mod tests { #[test] fn add_systems_should_create_schedule_if_it_does_not_exist2() { let mut app = App::new(); - app.add_systems((foo, bar).in_schedule(OnEnter(AppState::MainMenu))) + app.add_systems(OnEnter(AppState::MainMenu), (foo, bar)) .add_state::(); app.world.run_schedule(OnEnter(AppState::MainMenu)); diff --git a/crates/bevy_app/src/ci_testing.rs b/crates/bevy_app/src/ci_testing.rs index 17d8d929d6..f662a350a4 100644 --- a/crates/bevy_app/src/ci_testing.rs +++ b/crates/bevy_app/src/ci_testing.rs @@ -1,4 +1,4 @@ -use crate::{app::AppExit, App}; +use crate::{app::AppExit, App, Update}; use serde::Deserialize; use bevy_ecs::prelude::Resource; @@ -47,7 +47,7 @@ pub(crate) fn setup_app(app: &mut App) -> &mut App { }; app.insert_resource(config) - .add_system(ci_testing_exit_after); + .add_systems(Update, ci_testing_exit_after); app } diff --git a/crates/bevy_app/src/config.rs b/crates/bevy_app/src/config.rs deleted file mode 100644 index fb3a3fde1c..0000000000 --- a/crates/bevy_app/src/config.rs +++ /dev/null @@ -1,294 +0,0 @@ -use bevy_ecs::{ - all_tuples, - schedule::{ - BaseSystemSet, BoxedScheduleLabel, Condition, FreeSystemSet, IntoSystemConfig, - IntoSystemSet, ScheduleLabel, SystemConfig, SystemConfigs, - }, -}; - -use crate::CoreSchedule; - -/// A [`System`] with [`App`]-aware scheduling metadata. -/// -/// [`System`]: bevy_ecs::prelude::System -/// [`App`]: crate::App -pub struct SystemAppConfig { - pub(crate) system: SystemConfig, - pub(crate) schedule: Option, -} - -/// Types that can be converted into a [`SystemAppConfig`]. -/// -/// This has been implemented for all `System` trait objects -/// and all functions that convert into such. -pub trait IntoSystemAppConfig: Sized { - /// Converts into a [`SystemAppConfig`]. - fn into_app_config(self) -> SystemAppConfig; - - /// Adds the system to the provided `schedule`. - /// - /// If a schedule is not specified, it will be added to the [`App`]'s default schedule. - /// - /// [`App`]: crate::App - /// - /// # Panics - /// - /// If the system has already been assigned to a schedule. - #[track_caller] - fn in_schedule(self, schedule: impl ScheduleLabel) -> SystemAppConfig { - let mut config = self.into_app_config(); - if let Some(old_schedule) = &config.schedule { - panic!( - "Cannot add system to schedule '{schedule:?}': it is already in '{old_schedule:?}'." - ); - } - config.schedule = Some(Box::new(schedule)); - - config - } - - /// Adds the system to [`CoreSchedule::Startup`]. - /// This is a shorthand for `self.in_schedule(CoreSchedule::Startup)`. - /// - /// Systems in this schedule will run exactly once, at the start of the [`App`]'s lifecycle. - /// - /// [`App`]: crate::App - /// - /// # Examples - /// - /// ``` - /// # use bevy_app::prelude::*; - /// # use bevy_ecs::prelude::*; - /// # - /// fn my_startup_system(_commands: Commands) { - /// println!("My startup system"); - /// } - /// - /// App::new() - /// .add_system(my_startup_system.on_startup()) - /// .run(); - /// ``` - /// - /// # Panics - /// - /// If the system has already been assigned to a schedule. - #[inline] - fn on_startup(self) -> SystemAppConfig { - self.in_schedule(CoreSchedule::Startup) - } -} - -impl IntoSystemConfig<(), Self> for SystemAppConfig { - fn into_config(self) -> Self { - self - } - - #[track_caller] - fn in_set(self, set: impl FreeSystemSet) -> Self { - let Self { system, schedule } = self; - Self { - system: system.in_set(set), - schedule, - } - } - - #[track_caller] - fn in_base_set(self, set: impl BaseSystemSet) -> Self { - let Self { system, schedule } = self; - Self { - system: system.in_base_set(set), - schedule, - } - } - - fn no_default_base_set(self) -> Self { - let Self { system, schedule } = self; - Self { - system: system.no_default_base_set(), - schedule, - } - } - - fn before(self, set: impl IntoSystemSet) -> Self { - let Self { system, schedule } = self; - Self { - system: system.before(set), - schedule, - } - } - - fn after(self, set: impl IntoSystemSet) -> Self { - let Self { system, schedule } = self; - Self { - system: system.after(set), - schedule, - } - } - - fn run_if

(self, condition: impl Condition

) -> Self { - let Self { system, schedule } = self; - Self { - system: system.run_if(condition), - schedule, - } - } - - fn ambiguous_with(self, set: impl IntoSystemSet) -> Self { - let Self { system, schedule } = self; - Self { - system: system.ambiguous_with(set), - schedule, - } - } - - fn ambiguous_with_all(self) -> Self { - let Self { system, schedule } = self; - Self { - system: system.ambiguous_with_all(), - schedule, - } - } -} - -impl IntoSystemAppConfig<()> for SystemAppConfig { - fn into_app_config(self) -> SystemAppConfig { - self - } -} - -impl IntoSystemAppConfig for T -where - T: IntoSystemConfig, -{ - fn into_app_config(self) -> SystemAppConfig { - SystemAppConfig { - system: self.into_config(), - schedule: None, - } - } -} - -/// A collection of [`SystemAppConfig`]s. -pub struct SystemAppConfigs(pub(crate) InnerConfigs); - -pub(crate) enum InnerConfigs { - /// This came from an instance of `SystemConfigs`. - /// All systems are in the same schedule. - Blanket { - systems: SystemConfigs, - schedule: Option, - }, - /// This came from several separate instances of `SystemAppConfig`. - /// Each system gets its own schedule. - Granular(Vec), -} - -/// Types that can convert into [`SystemAppConfigs`]. -pub trait IntoSystemAppConfigs: Sized { - /// Converts to [`SystemAppConfigs`]. - fn into_app_configs(self) -> SystemAppConfigs; - - /// Adds the systems to the provided `schedule`. - /// - /// If a schedule with specified label does not exist, it will be created. - /// - /// If a schedule with the specified label does not exist, an empty one will be created. - /// - /// - /// [`App`]: crate::App - /// - /// # Panics - /// - /// If any of the systems have already been assigned to a schedule. - #[track_caller] - fn in_schedule(self, label: impl ScheduleLabel) -> SystemAppConfigs { - let mut configs = self.into_app_configs(); - - match &mut configs.0 { - InnerConfigs::Blanket { schedule, .. } => { - if schedule.is_some() { - panic!( - "Cannot add systems to the schedule '{label:?}: they are already in '{schedule:?}'" - ); - } - *schedule = Some(Box::new(label)); - } - InnerConfigs::Granular(configs) => { - for SystemAppConfig { schedule, .. } in configs { - if schedule.is_some() { - panic!( - "Cannot add system to the schedule '{label:?}': it is already in '{schedule:?}'." - ); - } - *schedule = Some(label.dyn_clone()); - } - } - } - - configs - } - - /// Adds the systems to [`CoreSchedule::Startup`]. - /// This is a shorthand for `self.in_schedule(CoreSchedule::Startup)`. - /// - /// # Examples - /// - /// ``` - /// # use bevy_app::prelude::*; - /// # use bevy_ecs::prelude::*; - /// # - /// # let mut app = App::new(); - /// # fn startup_system_a() {} - /// # fn startup_system_b() {} - /// # fn startup_system_c() {} - /// # - /// app.add_systems( - /// ( - /// startup_system_a, - /// startup_system_b, - /// startup_system_c, - /// ) - /// .on_startup() - /// ); - /// ``` - /// - /// # Panics - /// - /// If any of the systems have already been assigned to a schedule. - #[track_caller] - fn on_startup(self) -> SystemAppConfigs { - self.in_schedule(CoreSchedule::Startup) - } -} - -impl IntoSystemAppConfigs<()> for SystemAppConfigs { - fn into_app_configs(self) -> SystemAppConfigs { - self - } -} - -impl IntoSystemAppConfigs<()> for SystemConfigs { - fn into_app_configs(self) -> SystemAppConfigs { - SystemAppConfigs(InnerConfigs::Blanket { - systems: self, - schedule: None, - }) - } -} - -macro_rules! impl_system_collection { - ($(($param: ident, $sys: ident)),*) => { - impl<$($param, $sys),*> IntoSystemAppConfigs<($($param,)*)> for ($($sys,)*) - where - $($sys: IntoSystemAppConfig<$param>),* - { - #[allow(non_snake_case)] - fn into_app_configs(self) -> SystemAppConfigs { - let ($($sys,)*) = self; - SystemAppConfigs(InnerConfigs::Granular(vec![$($sys.into_app_config(),)*])) - } - } - } -} - -all_tuples!(impl_system_collection, 0, 15, P, S); diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index 165e9ebd70..8094d0458e 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -3,7 +3,7 @@ #![warn(missing_docs)] mod app; -mod config; +mod main_schedule; mod plugin; mod plugin_group; mod schedule_runner; @@ -13,7 +13,7 @@ mod ci_testing; pub use app::*; pub use bevy_derive::DynamicPlugin; -pub use config::*; +pub use main_schedule::*; pub use plugin::*; pub use plugin_group::*; pub use schedule_runner::*; @@ -26,197 +26,10 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ app::App, - config::{IntoSystemAppConfig, IntoSystemAppConfigs}, - CoreSchedule, CoreSet, DynamicPlugin, Plugin, PluginGroup, StartupSet, + main_schedule::{ + First, FixedUpdate, Last, Main, PostStartup, PostUpdate, PreStartup, PreUpdate, + Startup, StateTransition, Update, + }, + DynamicPlugin, Plugin, PluginGroup, }; } - -use bevy_ecs::{ - schedule::{ - apply_system_buffers, IntoSystemConfig, IntoSystemSetConfigs, Schedule, ScheduleLabel, - SystemSet, - }, - system::Local, - world::World, -}; - -/// The names of the default [`App`] schedules. -/// -/// The corresponding [`Schedule`](bevy_ecs::schedule::Schedule) objects are added by [`App::add_default_schedules`]. -#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] -pub enum CoreSchedule { - /// The schedule that runs once when the app starts. - Startup, - /// The schedule that contains the app logic that is evaluated each tick of [`App::update()`]. - Main, - /// The schedule that controls which schedules run. - /// - /// This is typically created using the [`CoreSchedule::outer_schedule`] method, - /// and does not need to manipulated during ordinary use. - Outer, - /// The schedule that contains systems which only run after a fixed period of time has elapsed. - /// - /// The exclusive `run_fixed_update_schedule` system runs this schedule during the [`CoreSet::FixedUpdate`] system set. - FixedUpdate, -} - -impl CoreSchedule { - /// An exclusive system that controls which schedule should be running. - /// - /// [`CoreSchedule::Main`] is always run. - /// - /// If this is the first time this system has been run, [`CoreSchedule::Startup`] will run before [`CoreSchedule::Main`]. - pub fn outer_loop(world: &mut World, mut run_at_least_once: Local) { - if !*run_at_least_once { - world.run_schedule(CoreSchedule::Startup); - *run_at_least_once = true; - } - - world.run_schedule(CoreSchedule::Main); - } - - /// Initializes a single threaded schedule for [`CoreSchedule::Outer`] that contains the [`outer_loop`](CoreSchedule::outer_loop) system. - pub fn outer_schedule() -> Schedule { - let mut schedule = Schedule::new(); - schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded); - schedule.add_system(Self::outer_loop); - schedule - } -} - -/// The names of the default [`App`] system sets. -/// -/// These are ordered in the same order they are listed. -/// -/// The corresponding [`SystemSets`](bevy_ecs::schedule::SystemSet) are added by [`App::add_default_schedules`]. -/// -/// The `*Flush` sets are assigned to the copy of [`apply_system_buffers`] -/// 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, - /// The copy of [`apply_system_buffers`] that runs immediately after `First`. - FirstFlush, - /// Runs before [`CoreSet::Update`]. - PreUpdate, - /// The copy of [`apply_system_buffers`] that runs immediately after `PreUpdate`. - PreUpdateFlush, - /// Applies [`State`](bevy_ecs::schedule::State) transitions - StateTransitions, - /// Runs systems that should only occur after a fixed period of time. - /// - /// The `run_fixed_update_schedule` system runs the [`CoreSchedule::FixedUpdate`] system in this system set. - FixedUpdate, - /// Responsible for doing most app logic. Systems should be registered here by default. - Update, - /// The copy of [`apply_system_buffers`] that runs immediately after `Update`. - UpdateFlush, - /// Runs after [`CoreSet::Update`]. - PostUpdate, - /// The copy of [`apply_system_buffers`] that runs immediately after `PostUpdate`. - PostUpdateFlush, - /// Runs after all other members of this set. - Last, - /// The copy of [`apply_system_buffers`] that runs immediately after `Last`. - LastFlush, -} - -impl CoreSet { - /// Sets up the base structure of [`CoreSchedule::Main`]. - /// - /// The sets defined in this enum are configured to run in order, - /// and a copy of [`apply_system_buffers`] is inserted into each `*Flush` set. - pub fn base_schedule() -> Schedule { - use CoreSet::*; - let mut schedule = Schedule::new(); - - // Create "stage-like" structure using buffer flushes + ordering - schedule - .set_default_base_set(Update) - .add_systems(( - apply_system_buffers.in_base_set(FirstFlush), - apply_system_buffers.in_base_set(PreUpdateFlush), - apply_system_buffers.in_base_set(UpdateFlush), - apply_system_buffers.in_base_set(PostUpdateFlush), - apply_system_buffers.in_base_set(LastFlush), - )) - .configure_sets( - ( - First, - FirstFlush, - PreUpdate, - PreUpdateFlush, - StateTransitions, - FixedUpdate, - Update, - UpdateFlush, - PostUpdate, - PostUpdateFlush, - Last, - LastFlush, - ) - .chain(), - ); - schedule - } -} - -/// The names of the default [`App`] startup sets, which live in [`CoreSchedule::Startup`]. -/// -/// The corresponding [`SystemSets`](bevy_ecs::schedule::SystemSet) are added by [`App::add_default_schedules`]. -/// -/// The `*Flush` sets are assigned to the copy of [`apply_system_buffers`] -/// 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 StartupSet { - /// Runs once before [`StartupSet::Startup`]. - PreStartup, - /// The copy of [`apply_system_buffers`] that runs immediately after `PreStartup`. - PreStartupFlush, - /// Runs once when an [`App`] starts up. - Startup, - /// The copy of [`apply_system_buffers`] that runs immediately after `Startup`. - StartupFlush, - /// Runs once after [`StartupSet::Startup`]. - PostStartup, - /// The copy of [`apply_system_buffers`] that runs immediately after `PostStartup`. - PostStartupFlush, -} - -impl StartupSet { - /// Sets up the base structure of [`CoreSchedule::Startup`]. - /// - /// The sets defined in this enum are configured to run in order, - /// and a copy of [`apply_system_buffers`] is inserted into each `*Flush` set. - pub fn base_schedule() -> Schedule { - use StartupSet::*; - let mut schedule = Schedule::new(); - schedule.set_default_base_set(Startup); - - // Create "stage-like" structure using buffer flushes + ordering - schedule.add_systems(( - apply_system_buffers.in_base_set(PreStartupFlush), - apply_system_buffers.in_base_set(StartupFlush), - apply_system_buffers.in_base_set(PostStartupFlush), - )); - - schedule.configure_sets( - ( - PreStartup, - PreStartupFlush, - Startup, - StartupFlush, - PostStartup, - PostStartupFlush, - ) - .chain(), - ); - - schedule - } -} diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs new file mode 100644 index 0000000000..2042b9e01f --- /dev/null +++ b/crates/bevy_app/src/main_schedule.rs @@ -0,0 +1,168 @@ +use crate::{App, Plugin}; +use bevy_ecs::{ + schedule::{ExecutorKind, Schedule, ScheduleLabel}, + system::{Local, Resource}, + world::{Mut, World}, +}; + +/// The schedule that contains the app logic that is evaluated each tick of [`App::update()`]. +/// +/// By default, it will run the following schedules in the given order: +/// +/// On the first run of the schedule (and only on the first run), it will run: +/// * [`PreStartup`] +/// * [`Startup`] +/// * [`PostStartup`] +/// +/// Then it will run: +/// * [`First`] +/// * [`PreUpdate`] +/// * [`StateTransition`] +/// * [`RunFixedUpdateLoop`] +/// * This will run [`FixedUpdate`] zero to many times, based on how much time has elapsed. +/// * [`Update`] +/// * [`PostUpdate`] +/// * [`Last`] +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Main; + +/// The schedule that runs before [`Startup`]. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PreStartup; + +/// The schedule that runs once when the app starts. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Startup; + +/// The schedule that runs once after [`Startup`]. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PostStartup; + +/// Runs first in the schedule. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct First; + +/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard +/// input OS events into an `Events` resource. This enables systems in [`Update`] to consume the events from the `Events` +/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event sytsem". +/// +/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready". +/// [`PreUpdate`] abstracts out "pre work implementation details". +/// +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PreUpdate; + +/// Runs [state transitions](bevy_ecs::schedule::States). +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct StateTransition; + +/// Runs the [`FixedUpdate`] schedule in a loop according until all relevant elapsed time has been "consumed". +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct RunFixedUpdateLoop; + +/// The schedule that contains systems which only run after a fixed period of time has elapsed. +/// +/// The exclusive `run_fixed_update_schedule` system runs this schedule. +/// This is run by the [`RunFixedUpdateLoop`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct FixedUpdate; + +/// The schedule that contains app logic. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Update; + +/// The schedule that contains logic that must run after [`Update`]. For example, synchronizing "local transforms" in a hierarchy +/// to "global" absolute transforms. This enables the [`PostUpdate`] transform-sync system to react to "local transform" changes in +/// [`Update`] without the [`Update`] systems needing to know about (or add scheduler dependencies for) the "global transform sync system". +/// +/// [`PostUpdate`] exists to do "engine/plugin response work" to things that happened in [`Update`]. +/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`]. +/// +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PostUpdate; + +/// Runs last in the schedule. +/// This is run by the [`Main`] schedule. +#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Last; + +/// Defines the schedules to be run for the [`Main`] schedule, including +/// their order. +#[derive(Resource, Debug)] +pub struct MainScheduleOrder { + /// The labels to run for the [`Main`] schedule (in the order they will be run). + pub labels: Vec>, +} + +impl Default for MainScheduleOrder { + fn default() -> Self { + Self { + labels: vec![ + Box::new(First), + Box::new(PreUpdate), + Box::new(StateTransition), + Box::new(RunFixedUpdateLoop), + Box::new(Update), + Box::new(PostUpdate), + Box::new(Last), + ], + } + } +} + +impl MainScheduleOrder { + /// Adds the given `schedule` after the `after` schedule + pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) { + let index = self + .labels + .iter() + .position(|current| (**current).eq(&after)) + .unwrap_or_else(|| panic!("Expected {after:?} to exist")); + self.labels.insert(index + 1, Box::new(schedule)); + } +} + +impl Main { + /// A system that runs the "main schedule" + pub fn run_main(world: &mut World, mut run_at_least_once: Local) { + if !*run_at_least_once { + let _ = world.try_run_schedule(PreStartup); + let _ = world.try_run_schedule(Startup); + let _ = world.try_run_schedule(PostStartup); + *run_at_least_once = true; + } + + world.resource_scope(|world, order: Mut| { + for label in &order.labels { + let _ = world.try_run_schedule_ref(&**label); + } + }); + } +} + +/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`]. +pub struct MainSchedulePlugin; + +impl Plugin for MainSchedulePlugin { + fn build(&self, app: &mut App) { + // simple "facilitator" schedules benefit from simpler single threaded scheduling + let mut main_schedule = Schedule::new(); + main_schedule.set_executor_kind(ExecutorKind::SingleThreaded); + let mut fixed_update_loop_schedule = Schedule::new(); + fixed_update_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded); + + app.add_schedule(Main, main_schedule) + .add_schedule(RunFixedUpdateLoop, fixed_update_loop_schedule) + .init_resource::() + .add_systems(Main, Main::run_main); + } +} diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index dd6a8ae307..e456c7eb44 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -644,7 +644,7 @@ pub fn free_unused_assets_system(asset_server: Res) { mod test { use super::*; use crate::{loader::LoadedAsset, update_asset_storage_system}; - use bevy_app::App; + use bevy_app::{App, Update}; use bevy_ecs::prelude::*; use bevy_reflect::TypeUuid; use bevy_utils::BoxedFuture; @@ -852,10 +852,13 @@ mod test { let mut app = App::new(); app.insert_resource(assets); app.insert_resource(asset_server); - app.add_systems(( - free_unused_assets_system.in_set(FreeUnusedAssets), - update_asset_storage_system::.after(FreeUnusedAssets), - )); + app.add_systems( + Update, + ( + free_unused_assets_system.in_set(FreeUnusedAssets), + update_asset_storage_system::.after(FreeUnusedAssets), + ), + ); fn load_asset(path: AssetPath, world: &World) -> HandleUntyped { let asset_server = world.resource::(); diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 40d390187f..3fffa1d413 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -1,6 +1,6 @@ use crate::{ - update_asset_storage_system, Asset, AssetLoader, AssetServer, AssetSet, Handle, HandleId, - RefChange, ReflectAsset, ReflectHandle, + update_asset_storage_system, Asset, AssetEvents, AssetLoader, AssetServer, Handle, HandleId, + LoadAssets, RefChange, ReflectAsset, ReflectHandle, }; use bevy_app::{App, AppTypeRegistry}; use bevy_ecs::prelude::*; @@ -331,10 +331,8 @@ impl AddAsset for App { }; self.insert_resource(assets) - .add_systems(( - Assets::::asset_event_system.in_base_set(AssetSet::AssetEvents), - update_asset_storage_system::.in_base_set(AssetSet::LoadAssets), - )) + .add_systems(LoadAssets, update_asset_storage_system::) + .add_systems(AssetEvents, Assets::::asset_event_system) .register_type::>() .add_event::>() } @@ -362,9 +360,9 @@ impl AddAsset for App { { #[cfg(feature = "debug_asset_server")] { - self.add_system( - crate::debug_asset_server::sync_debug_assets:: - .in_base_set(bevy_app::CoreSet::Update), + self.add_systems( + bevy_app::Update, + crate::debug_asset_server::sync_debug_assets::, ); let mut app = self .world diff --git a/crates/bevy_asset/src/debug_asset_server.rs b/crates/bevy_asset/src/debug_asset_server.rs index 7b506faeed..955fadb80f 100644 --- a/crates/bevy_asset/src/debug_asset_server.rs +++ b/crates/bevy_asset/src/debug_asset_server.rs @@ -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}; +use bevy_app::{App, Plugin, Update}; 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); + app.add_systems(Update, run_debug_asset_app); } } diff --git a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs index d36cd2969d..a5ce2546db 100644 --- a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs +++ b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs @@ -18,8 +18,8 @@ impl Default for AssetCountDiagnosticsPlugin { impl Plugin for AssetCountDiagnosticsPlugin { fn build(&self, app: &mut App) { - app.add_startup_system(Self::setup_system) - .add_system(Self::diagnostic_system); + app.add_systems(Startup, Self::setup_system) + .add_systems(Update, Self::diagnostic_system); } } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 59b0b2cb32..3265690165 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -46,18 +46,15 @@ pub use loader::*; pub use path::*; pub use reflect::*; -use bevy_app::prelude::*; -use bevy_ecs::prelude::*; +use bevy_app::{prelude::*, MainScheduleOrder}; +use bevy_ecs::schedule::ScheduleLabel; -/// [`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, - /// Asset events are generated. - AssetEvents, -} +/// Asset storages are updated. +#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] +pub struct LoadAssets; +/// Asset events are generated. +#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] +pub struct AssetEvents; /// Adds support for [`Assets`] to an App. /// @@ -106,24 +103,19 @@ impl Plugin for AssetPlugin { app.insert_resource(asset_server); } - app.register_type::(); - - app.configure_set( - AssetSet::LoadAssets - .before(CoreSet::PreUpdate) - .after(CoreSet::First), - ) - .configure_set( - AssetSet::AssetEvents - .after(CoreSet::PostUpdate) - .before(CoreSet::Last), - ) - .add_system(asset_server::free_unused_assets_system.in_base_set(CoreSet::PreUpdate)); + app.register_type::() + .add_systems(PreUpdate, asset_server::free_unused_assets_system); + app.init_schedule(LoadAssets); + app.init_schedule(AssetEvents); #[cfg(all( feature = "filesystem_watcher", all(not(target_arch = "wasm32"), not(target_os = "android")) ))] - app.add_system(io::filesystem_watcher_system.in_base_set(AssetSet::LoadAssets)); + app.add_systems(LoadAssets, io::filesystem_watcher_system); + + let mut order = app.world.resource_mut::(); + order.insert_after(First, LoadAssets); + order.insert_after(PostUpdate, AssetEvents); } } diff --git a/crates/bevy_audio/src/lib.rs b/crates/bevy_audio/src/lib.rs index af751a8364..468ff3d622 100644 --- a/crates/bevy_audio/src/lib.rs +++ b/crates/bevy_audio/src/lib.rs @@ -4,13 +4,13 @@ //! # use bevy_ecs::{system::Res, event::EventWriter}; //! # use bevy_audio::{Audio, AudioPlugin}; //! # use bevy_asset::{AssetPlugin, AssetServer}; -//! # use bevy_app::{App, AppExit, NoopPluginGroup as MinimalPlugins}; +//! # use bevy_app::{App, AppExit, NoopPluginGroup as MinimalPlugins, Startup}; //! fn main() { //! App::new() //! .add_plugins(MinimalPlugins) //! .add_plugin(AssetPlugin::default()) //! .add_plugin(AudioPlugin) -//! .add_startup_system(play_background_audio) +//! .add_systems(Startup, play_background_audio) //! .run(); //! } //! @@ -47,7 +47,6 @@ pub use sinks::*; use bevy_app::prelude::*; use bevy_asset::{AddAsset, Asset}; -use bevy_ecs::prelude::*; /// Adds support for audio playback to a Bevy Application /// @@ -62,7 +61,7 @@ impl Plugin for AudioPlugin { .add_asset::() .add_asset::() .init_resource::>() - .add_system(play_queued_audio_system::.in_base_set(CoreSet::PostUpdate)); + .add_systems(PostUpdate, play_queued_audio_system::); #[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))] app.init_asset_loader::(); @@ -78,6 +77,6 @@ impl AddAudioSource for App { self.add_asset::() .init_resource::>() .init_resource::>() - .add_system(play_queued_audio_system::.in_base_set(CoreSet::PostUpdate)) + .add_systems(PostUpdate, play_queued_audio_system::) } } diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 7e58685c9f..daf43cf86e 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -102,12 +102,12 @@ pub struct TaskPoolPlugin { } impl Plugin for TaskPoolPlugin { - fn build(&self, app: &mut App) { + fn build(&self, _app: &mut App) { // Setup the default bevy task pools self.task_pool_options.create_default_pools(); #[cfg(not(target_arch = "wasm32"))] - app.add_system(tick_global_task_pools.in_base_set(bevy_app::CoreSet::Last)); + _app.add_systems(Last, tick_global_task_pools); } } /// A dummy type that is [`!Send`](Send), to force systems to run on the main thread. @@ -124,7 +124,7 @@ fn tick_global_task_pools(_main_thread_marker: Option>) { /// Maintains a count of frames rendered since the start of the application. /// -/// [`FrameCount`] is incremented during [`CoreSet::Last`], providing predictable +/// [`FrameCount`] is incremented during [`Last`], providing predictable /// behaviour: it will be 0 during the first update, 1 during the next, and so forth. /// /// # Overflows @@ -142,7 +142,7 @@ pub struct FrameCountPlugin; impl Plugin for FrameCountPlugin { fn build(&self, app: &mut App) { app.init_resource::(); - app.add_system(update_frame_count.in_base_set(CoreSet::Last)); + app.add_systems(Last, update_frame_count); } } diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 5ff8cfba4e..62e577ea23 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -7,13 +7,7 @@ pub use settings::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}; use crate::{core_2d, core_3d}; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, HandleUntyped}; -use bevy_ecs::{ - prelude::{Component, Entity}, - query::{QueryState, With}, - schedule::IntoSystemConfig, - system::{Commands, Query, Res, ResMut}, - world::World, -}; +use bevy_ecs::prelude::*; use bevy_math::UVec2; use bevy_reflect::TypeUuid; use bevy_render::{ @@ -27,7 +21,7 @@ use bevy_render::{ renderer::{RenderContext, RenderDevice}, texture::{CachedTexture, TextureCache}, view::ViewTarget, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -71,12 +65,15 @@ impl Plugin for BloomPlugin { .init_resource::() .init_resource::>() .init_resource::>() - .add_systems(( - prepare_bloom_textures.in_set(RenderSet::Prepare), - prepare_downsampling_pipeline.in_set(RenderSet::Prepare), - prepare_upsampling_pipeline.in_set(RenderSet::Prepare), - queue_bloom_bind_groups.in_set(RenderSet::Queue), - )); + .add_systems( + Render, + ( + prepare_bloom_textures.in_set(RenderSet::Prepare), + prepare_downsampling_pipeline.in_set(RenderSet::Prepare), + prepare_upsampling_pipeline.in_set(RenderSet::Prepare), + queue_bloom_bind_groups.in_set(RenderSet::Queue), + ), + ); // Add bloom to the 3d render graph { diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index a94da4d0b3..e682257dc9 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -20,7 +20,7 @@ pub mod graph { pub use camera_2d::*; pub use main_pass_2d_node::*; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_render::{ camera::Camera, @@ -31,7 +31,7 @@ use bevy_render::{ DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, }, render_resource::CachedRenderPipelineId, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::FloatOrd; use std::ops::Range; @@ -52,13 +52,16 @@ impl Plugin for Core2dPlugin { render_app .init_resource::>() - .add_systems(( - extract_core_2d_camera_phases.in_schedule(ExtractSchedule), - sort_phase_system::.in_set(RenderSet::PhaseSort), - batch_phase_system:: - .after(sort_phase_system::) - .in_set(RenderSet::PhaseSort), - )); + .add_systems(ExtractSchedule, extract_core_2d_camera_phases) + .add_systems( + Render, + ( + sort_phase_system::.in_set(RenderSet::PhaseSort), + batch_phase_system:: + .after(sort_phase_system::) + .in_set(RenderSet::PhaseSort), + ), + ); let pass_node_2d = MainPass2dNode::new(&mut render_app.world); let tonemapping = TonemappingNode::new(&mut render_app.world); diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 166f6765e0..9bfa5486df 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -23,7 +23,7 @@ use std::cmp::Reverse; pub use camera_3d::*; pub use main_pass_3d_node::*; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_render::{ camera::{Camera, ExtractedCamera}, @@ -41,7 +41,7 @@ use bevy_render::{ renderer::RenderDevice, texture::TextureCache, view::ViewDepthTexture, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{FloatOrd, HashMap}; @@ -68,15 +68,18 @@ impl Plugin for Core3dPlugin { .init_resource::>() .init_resource::>() .init_resource::>() - .add_systems(( - extract_core_3d_camera_phases.in_schedule(ExtractSchedule), - prepare_core_3d_depth_textures - .in_set(RenderSet::Prepare) - .after(bevy_render::view::prepare_windows), - sort_phase_system::.in_set(RenderSet::PhaseSort), - sort_phase_system::.in_set(RenderSet::PhaseSort), - sort_phase_system::.in_set(RenderSet::PhaseSort), - )); + .add_systems(ExtractSchedule, extract_core_3d_camera_phases) + .add_systems( + Render, + ( + prepare_core_3d_depth_textures + .in_set(RenderSet::Prepare) + .after(bevy_render::view::prepare_windows), + sort_phase_system::.in_set(RenderSet::PhaseSort), + sort_phase_system::.in_set(RenderSet::PhaseSort), + sort_phase_system::.in_set(RenderSet::PhaseSort), + ), + ); let prepass_node = PrepassNode::new(&mut render_app.world); let pass_node_3d = MainPass3dNode::new(&mut render_app.world); diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index 0892299f24..943e2d39b8 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -14,7 +14,7 @@ use bevy_render::{ renderer::RenderDevice, texture::BevyDefault, view::{ExtractedView, ViewTarget}, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }; mod node; @@ -90,7 +90,7 @@ impl Plugin for FxaaPlugin { render_app .init_resource::() .init_resource::>() - .add_system(prepare_fxaa_pipelines.in_set(RenderSet::Prepare)); + .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare)); { let fxaa_node = FxaaNode::new(&mut render_app.world); diff --git a/crates/bevy_core_pipeline/src/msaa_writeback.rs b/crates/bevy_core_pipeline/src/msaa_writeback.rs index 2f8122d193..115b085efa 100644 --- a/crates/bevy_core_pipeline/src/msaa_writeback.rs +++ b/crates/bevy_core_pipeline/src/msaa_writeback.rs @@ -6,7 +6,7 @@ use bevy_render::{ render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotInfo, SlotType}, renderer::RenderContext, view::{Msaa, ViewTarget}, - RenderSet, + Render, RenderSet, }; use bevy_render::{render_resource::*, RenderApp}; @@ -20,7 +20,10 @@ impl Plugin for MsaaWritebackPlugin { return }; - render_app.add_system(queue_msaa_writeback_pipelines.in_set(RenderSet::Queue)); + render_app.add_systems( + Render, + queue_msaa_writeback_pipelines.in_set(RenderSet::Queue), + ); let msaa_writeback_2d = MsaaWritebackNode::new(&mut render_app.world); let msaa_writeback_3d = MsaaWritebackNode::new(&mut render_app.world); let mut graph = render_app.world.resource_mut::(); diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index d6c2f3a062..31ecd12177 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -10,7 +10,7 @@ use bevy_render::render_asset::RenderAssets; use bevy_render::renderer::RenderDevice; use bevy_render::texture::{CompressedImageFormats, Image, ImageSampler, ImageType}; use bevy_render::view::{ViewTarget, ViewUniform}; -use bevy_render::{render_resource::*, RenderApp, RenderSet}; +use bevy_render::{render_resource::*, Render, RenderApp, RenderSet}; mod node; @@ -94,7 +94,10 @@ impl Plugin for TonemappingPlugin { render_app .init_resource::() .init_resource::>() - .add_system(queue_view_tonemapping_pipelines.in_set(RenderSet::Queue)); + .add_systems( + Render, + queue_view_tonemapping_pipelines.in_set(RenderSet::Queue), + ); } } } diff --git a/crates/bevy_core_pipeline/src/upscaling/mod.rs b/crates/bevy_core_pipeline/src/upscaling/mod.rs index 441f9f7775..f3594397d5 100644 --- a/crates/bevy_core_pipeline/src/upscaling/mod.rs +++ b/crates/bevy_core_pipeline/src/upscaling/mod.rs @@ -3,7 +3,7 @@ use bevy_app::prelude::*; use bevy_ecs::prelude::*; use bevy_render::camera::{CameraOutputMode, ExtractedCamera}; use bevy_render::view::ViewTarget; -use bevy_render::{render_resource::*, RenderApp, RenderSet}; +use bevy_render::{render_resource::*, Render, RenderApp, RenderSet}; mod node; @@ -14,7 +14,10 @@ pub struct UpscalingPlugin; impl Plugin for UpscalingPlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_system(queue_view_upscaling_pipelines.in_set(RenderSet::Queue)); + render_app.add_systems( + Render, + queue_view_upscaling_pipelines.in_set(RenderSet::Queue), + ); } } } diff --git a/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs index fd6b21117c..5c4415c09b 100644 --- a/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs @@ -9,8 +9,8 @@ pub struct EntityCountDiagnosticsPlugin; impl Plugin for EntityCountDiagnosticsPlugin { fn build(&self, app: &mut App) { - app.add_startup_system(Self::setup_system) - .add_system(Self::diagnostic_system); + app.add_systems(Startup, Self::setup_system) + .add_systems(Update, Self::diagnostic_system); } } diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 8e26025bf2..4dbc3c4cfa 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -10,8 +10,8 @@ pub struct FrameTimeDiagnosticsPlugin; impl Plugin for FrameTimeDiagnosticsPlugin { fn build(&self, app: &mut bevy_app::App) { - app.add_startup_system(Self::setup_system) - .add_system(Self::diagnostic_system); + app.add_systems(Startup, Self::setup_system) + .add_systems(Update, Self::diagnostic_system); } } diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index b2d127f115..854f6e74cd 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -17,8 +17,10 @@ pub struct DiagnosticsPlugin; impl Plugin for DiagnosticsPlugin { fn build(&self, app: &mut App) { - app.init_resource::() - .add_startup_system(system_information_diagnostics_plugin::internal::log_system_info); + app.init_resource::().add_systems( + Startup, + system_information_diagnostics_plugin::internal::log_system_info, + ); } } diff --git a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs index dd80c8c8f2..65f212dcc6 100644 --- a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs @@ -37,9 +37,9 @@ impl Plugin for LogDiagnosticsPlugin { }); if self.debug { - app.add_system(Self::log_diagnostics_debug_system.in_base_set(CoreSet::PostUpdate)); + app.add_systems(PostUpdate, Self::log_diagnostics_debug_system); } else { - app.add_system(Self::log_diagnostics_system.in_base_set(CoreSet::PostUpdate)); + app.add_systems(PostUpdate, Self::log_diagnostics_system); } } } diff --git a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs index 096c9b13a6..b33cba5ca4 100644 --- a/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs @@ -14,8 +14,8 @@ use bevy_app::prelude::*; pub struct SystemInformationDiagnosticsPlugin; impl Plugin for SystemInformationDiagnosticsPlugin { fn build(&self, app: &mut App) { - app.add_startup_system(internal::setup_system) - .add_system(internal::diagnostic_system); + app.add_systems(Startup, internal::setup_system) + .add_systems(Update, internal::diagnostic_system); } } diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index ee9126e1b5..457f386e89 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -150,7 +150,7 @@ fn main() { let mut schedule = Schedule::default(); // Add our system to the schedule - schedule.add_system(movement); + schedule.add_systems(movement); // Run the schedule once. If your app has a "loop", you would run this once per loop schedule.run(&mut world); diff --git a/crates/bevy_ecs/examples/change_detection.rs b/crates/bevy_ecs/examples/change_detection.rs index 0abbf487a0..8bfa18d735 100644 --- a/crates/bevy_ecs/examples/change_detection.rs +++ b/crates/bevy_ecs/examples/change_detection.rs @@ -1,4 +1,4 @@ -use bevy_ecs::{prelude::*, schedule::IntoSystemConfig}; +use bevy_ecs::prelude::*; use rand::Rng; use std::ops::Deref; diff --git a/crates/bevy_ecs/examples/events.rs b/crates/bevy_ecs/examples/events.rs index 2dae9475cf..27bd13b5f6 100644 --- a/crates/bevy_ecs/examples/events.rs +++ b/crates/bevy_ecs/examples/events.rs @@ -16,7 +16,7 @@ fn main() { #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct FlushEvents; - schedule.add_system(Events::::update_system.in_set(FlushEvents)); + schedule.add_systems(Events::::update_system.in_set(FlushEvents)); // Add systems sending and receiving events after the events are flushed. schedule.add_systems(( diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index dbafe0d9e3..0e3374f757 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -469,7 +469,7 @@ pub fn derive_schedule_label(input: TokenStream) -> TokenStream { } /// Derive macro generating an impl of the trait `SystemSet`. -#[proc_macro_derive(SystemSet, attributes(system_set))] +#[proc_macro_derive(SystemSet)] pub fn derive_system_set(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let mut trait_path = bevy_ecs_path(); diff --git a/crates/bevy_ecs/macros/src/set.rs b/crates/bevy_ecs/macros/src/set.rs index 054735c858..66bfb601ec 100644 --- a/crates/bevy_ecs/macros/src/set.rs +++ b/crates/bevy_ecs/macros/src/set.rs @@ -1,9 +1,5 @@ use proc_macro::TokenStream; -use quote::{format_ident, quote, ToTokens}; -use syn::parse::{Parse, ParseStream}; - -pub static SYSTEM_SET_ATTRIBUTE_NAME: &str = "system_set"; -pub static BASE_ATTRIBUTE_NAME: &str = "base"; +use quote::quote; /// Derive a set trait /// @@ -12,55 +8,8 @@ pub static BASE_ATTRIBUTE_NAME: &str = "base"; /// - `input`: The [`syn::DeriveInput`] for the struct that we want to derive the set trait for /// - `trait_path`: The [`syn::Path`] to the set trait pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream { - let mut base_trait_path = trait_path.clone(); - let ident = &mut base_trait_path.segments.last_mut().unwrap().ident; - *ident = format_ident!("Base{ident}"); - - let mut free_trait_path = trait_path.clone(); - let ident = &mut free_trait_path.segments.last_mut().unwrap().ident; - *ident = format_ident!("Free{ident}"); - 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::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(), @@ -73,28 +22,12 @@ pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStrea .unwrap(), ); - let marker_impl = if is_base { - quote! { - impl #impl_generics #base_trait_path for #ident #ty_generics #where_clause {} - } - } else { - quote! { - impl #impl_generics #free_trait_path for #ident #ty_generics #where_clause {} - } - }; - (quote! { impl #impl_generics #trait_path for #ident #ty_generics #where_clause { - fn is_base(&self) -> bool { - #is_base - } - fn dyn_clone(&self) -> std::boxed::Box { std::boxed::Box::new(std::clone::Clone::clone(self)) } } - - #marker_impl }) .into() } diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 0bf6429175..3cdfc19445 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -939,7 +939,7 @@ mod tests { world.send_event(TestEvent { i: 4 }); let mut schedule = Schedule::new(); - schedule.add_system(|mut events: EventReader| { + schedule.add_systems(|mut events: EventReader| { let mut iter = events.iter(); assert_eq!(iter.next(), Some(&TestEvent { i: 0 })); diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 1c574befe7..8107de37e8 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -39,9 +39,8 @@ pub mod prelude { removal_detection::RemovedComponents, schedule::{ apply_state_transition, apply_system_buffers, common_conditions::*, Condition, - IntoSystemConfig, IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, - IntoSystemSetConfigs, NextState, OnEnter, OnExit, OnTransition, OnUpdate, Schedule, - Schedules, State, States, SystemSet, + IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, IntoSystemSetConfigs, NextState, + OnEnter, OnExit, OnTransition, OnUpdate, Schedule, Schedules, State, States, SystemSet, }, system::{ adapter as system_adapter, diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 5a93006a44..0ad7f05917 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -31,7 +31,7 @@ pub trait Condition: sealed::Condition { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # fn my_system() {} - /// app.add_system( + /// app.add_systems( /// // The `resource_equals` run condition will panic since we don't initialize `R`, /// // just like if we used `Res` in a system. /// my_system.run_if(resource_equals(R(0))), @@ -48,7 +48,7 @@ pub trait Condition: sealed::Condition { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # fn my_system() {} - /// app.add_system( + /// app.add_systems( /// // `resource_equals` will only get run if the resource `R` exists. /// my_system.run_if(resource_exists::().and_then(resource_equals(R(0)))), /// ); @@ -86,7 +86,7 @@ pub trait Condition: sealed::Condition { /// # let mut world = World::new(); /// # #[derive(Resource)] struct C(bool); /// # fn my_system(mut c: ResMut) { c.0 = true; } - /// app.add_system( + /// app.add_systems( /// // Only run the system if either `A` or `B` exist. /// my_system.run_if(resource_exists::().or_else(resource_exists::())), /// ); @@ -159,7 +159,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `run_once` will only return true the first time it's evaluated /// my_system.run_if(run_once()), /// ); @@ -199,7 +199,7 @@ pub mod common_conditions { /// # struct Counter(u8); /// # let mut app = Schedule::new(); /// # let mut world = World::new(); - /// app.add_system( + /// app.add_systems( /// // `resource_exsists` will only return true if the given resource exsists in the world /// my_system.run_if(resource_exists::()), /// ); @@ -239,7 +239,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `resource_equals` will only return true if the given resource equals the given value /// my_system.run_if(resource_equals(Counter(0))), /// ); @@ -276,7 +276,7 @@ pub mod common_conditions { /// # struct Counter(u8); /// # let mut app = Schedule::new(); /// # let mut world = World::new(); - /// app.add_system( + /// app.add_systems( /// // `resource_exists_and_equals` will only return true /// // if the given resource exsists and equals the given value /// my_system.run_if(resource_exists_and_equals(Counter(0))), @@ -319,7 +319,7 @@ pub mod common_conditions { /// # struct Counter(u8); /// # let mut app = Schedule::new(); /// # let mut world = World::new(); - /// app.add_system( + /// app.add_systems( /// // `resource_added` will only return true if the /// // given resource was just added /// my_system.run_if(resource_added::()), @@ -370,7 +370,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `resource_changed` will only return true if the /// // given resource was just changed (or added) /// my_system.run_if( @@ -423,7 +423,7 @@ pub mod common_conditions { /// # struct Counter(u8); /// # let mut app = Schedule::new(); /// # let mut world = World::new(); - /// app.add_system( + /// app.add_systems( /// // `resource_exists_and_changed` will only return true if the /// // given resource exsists and was just changed (or added) /// my_system.run_if( @@ -485,7 +485,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `resource_changed_or_removed` will only return true if the /// // given resource was just changed or removed (or added) /// my_system.run_if( @@ -555,7 +555,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `resource_removed` will only return true if the /// // given resource was just removed /// my_system.run_if(resource_removed::()), @@ -617,7 +617,7 @@ pub mod common_conditions { /// Paused, /// } /// - /// app.add_system( + /// app.add_systems( /// // `state_exists` will only return true if the /// // given state exsists /// my_system.run_if(state_exists::()), @@ -784,7 +784,7 @@ pub mod common_conditions { /// /// world.init_resource::>(); /// - /// app.add_system( + /// app.add_systems( /// // `state_changed` will only return true if the /// // given states value has just been updated or /// // the state has just been added @@ -826,9 +826,9 @@ pub mod common_conditions { /// # let mut world = World::new(); /// # world.init_resource::(); /// # world.init_resource::>(); - /// # app.add_system(Events::::update_system.before(my_system)); + /// # app.add_systems(Events::::update_system.before(my_system)); /// - /// app.add_system( + /// app.add_systems( /// my_system.run_if(on_event::()), /// ); /// @@ -868,7 +868,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// my_system.run_if(any_with_component::()), /// ); /// @@ -904,7 +904,7 @@ pub mod common_conditions { /// # let mut app = Schedule::new(); /// # let mut world = World::new(); /// # world.init_resource::(); - /// app.add_system( + /// app.add_systems( /// // `not` will inverse any condition you pass in. /// // Since the condition we choose always returns true /// // this system will never run @@ -1073,15 +1073,11 @@ where mod tests { use super::{common_conditions::*, Condition}; use crate as bevy_ecs; - use crate::{ - change_detection::ResMut, - component::Component, - schedule::{ - common_conditions::not, IntoSystemConfig, IntoSystemConfigs, Schedule, State, States, - }, - system::Local, - world::World, - }; + use crate::component::Component; + use crate::schedule::IntoSystemConfigs; + use crate::schedule::{common_conditions::not, State, States}; + use crate::system::Local; + use crate::{change_detection::ResMut, schedule::Schedule, world::World}; use bevy_ecs_macros::Resource; #[derive(Resource, Default)] @@ -1103,7 +1099,7 @@ mod tests { let mut schedule = Schedule::new(); // Run every other cycle - schedule.add_system(increment_counter.run_if(every_other_time)); + schedule.add_systems(increment_counter.run_if(every_other_time)); schedule.run(&mut world); schedule.run(&mut world); @@ -1113,7 +1109,7 @@ mod tests { assert_eq!(world.resource::().0, 2); // Run every other cycle oppsite to the last one - schedule.add_system(increment_counter.run_if(not(every_other_time))); + schedule.add_systems(increment_counter.run_if(not(every_other_time))); schedule.run(&mut world); schedule.run(&mut world); @@ -1130,9 +1126,9 @@ mod tests { let mut schedule = Schedule::new(); // Always run - schedule.add_system(increment_counter.run_if(every_other_time.or_else(|| true))); + schedule.add_systems(increment_counter.run_if(every_other_time.or_else(|| true))); // Run every other cycle - schedule.add_system(increment_counter.run_if(every_other_time.and_then(|| true))); + schedule.add_systems(increment_counter.run_if(every_other_time.and_then(|| true))); schedule.run(&mut world); assert_eq!(world.resource::().0, 2); @@ -1147,9 +1143,9 @@ mod tests { let mut schedule = Schedule::new(); // Run every other cycle - schedule.add_system(increment_counter.run_if(every_other_time).run_if(|| true)); + schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true)); // Never run - schedule.add_system(increment_counter.run_if(every_other_time).run_if(|| false)); + schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false)); schedule.run(&mut world); assert_eq!(world.resource::().0, 1); @@ -1166,7 +1162,7 @@ mod tests { // This should never run, if multiple run conditions worked // like an OR condition then it would always run - schedule.add_system( + schedule.add_systems( increment_counter .run_if(every_other_time) .run_if(not(every_other_time)), diff --git a/crates/bevy_ecs/src/schedule/config.rs b/crates/bevy_ecs/src/schedule/config.rs index ce0a45ae44..50aa7bfe90 100644 --- a/crates/bevy_ecs/src/schedule/config.rs +++ b/crates/bevy_ecs/src/schedule/config.rs @@ -5,57 +5,11 @@ use crate::{ condition::{BoxedCondition, Condition}, graph_utils::{Ambiguity, Dependency, DependencyKind, GraphInfo}, set::{BoxedSystemSet, IntoSystemSet, SystemSet}, + ScheduleLabel, }, system::{BoxedSystem, IntoSystem, System}, }; -use super::{BaseSystemSet, FreeSystemSet}; - -/// A [`SystemSet`] with scheduling metadata. -pub struct SystemSetConfig { - pub(super) set: BoxedSystemSet, - pub(super) graph_info: GraphInfo, - pub(super) conditions: Vec, -} - -impl SystemSetConfig { - fn new(set: BoxedSystemSet) -> Self { - // system type sets are automatically populated - // to avoid unintentionally broad changes, they cannot be configured - assert!( - set.system_type().is_none(), - "configuring system type sets is not allowed" - ); - - Self { - set, - graph_info: GraphInfo::system_set(), - conditions: Vec::new(), - } - } -} - -/// A [`System`] with scheduling metadata. -pub struct SystemConfig { - pub(super) system: BoxedSystem, - pub(super) graph_info: GraphInfo, - pub(super) conditions: Vec, -} - -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, - conditions: Vec::new(), - } - } -} - fn new_condition(condition: impl Condition) -> BoxedCondition { let condition_system = IntoSystem::into_system(condition); assert!( @@ -79,6 +33,370 @@ fn ambiguous_with(graph_info: &mut GraphInfo, set: BoxedSystemSet) { } } +impl IntoSystemConfigs for F +where + F: IntoSystem<(), (), Marker>, +{ + fn into_configs(self) -> SystemConfigs { + SystemConfigs::new_system(Box::new(IntoSystem::into_system(self))) + } +} + +impl IntoSystemConfigs<()> for BoxedSystem<(), ()> { + fn into_configs(self) -> SystemConfigs { + SystemConfigs::new_system(self) + } +} + +pub struct SystemConfig { + pub(crate) system: BoxedSystem, + pub(crate) graph_info: GraphInfo, + pub(crate) conditions: Vec, +} + +/// A collection of [`SystemConfig`]. +pub enum SystemConfigs { + SystemConfig(SystemConfig), + Configs { + configs: Vec, + /// If `true`, adds `before -> after` ordering constraints between the successive elements. + chained: bool, + }, +} + +impl SystemConfigs { + fn new_system(system: BoxedSystem) -> Self { + // include system in its default sets + let sets = system.default_system_sets().into_iter().collect(); + Self::SystemConfig(SystemConfig { + system, + graph_info: GraphInfo { + sets, + ..Default::default() + }, + conditions: Vec::new(), + }) + } + + fn in_set_inner(&mut self, set: BoxedSystemSet) { + match self { + SystemConfigs::SystemConfig(config) => { + config.graph_info.sets.push(set); + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.in_set_inner(set.dyn_clone()); + } + } + } + } + + fn before_inner(&mut self, set: BoxedSystemSet) { + match self { + SystemConfigs::SystemConfig(config) => { + config + .graph_info + .dependencies + .push(Dependency::new(DependencyKind::Before, set)); + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.before_inner(set.dyn_clone()); + } + } + } + } + + fn after_inner(&mut self, set: BoxedSystemSet) { + match self { + SystemConfigs::SystemConfig(config) => { + config + .graph_info + .dependencies + .push(Dependency::new(DependencyKind::After, set)); + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.after_inner(set.dyn_clone()); + } + } + } + } + + fn distributive_run_if_inner(&mut self, condition: impl Condition + Clone) { + match self { + SystemConfigs::SystemConfig(config) => { + config.conditions.push(new_condition(condition)); + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.distributive_run_if_inner(condition.clone()); + } + } + } + } + + fn ambiguous_with_inner(&mut self, set: BoxedSystemSet) { + match self { + SystemConfigs::SystemConfig(config) => { + ambiguous_with(&mut config.graph_info, set); + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.ambiguous_with_inner(set.dyn_clone()); + } + } + } + } + + fn ambiguous_with_all_inner(&mut self) { + match self { + SystemConfigs::SystemConfig(config) => { + config.graph_info.ambiguous_with = Ambiguity::IgnoreAll; + } + SystemConfigs::Configs { configs, .. } => { + for config in configs { + config.ambiguous_with_all_inner(); + } + } + } + } + + fn run_if_inner(&mut self, condition: BoxedCondition) { + match self { + SystemConfigs::SystemConfig(config) => { + config.conditions.push(condition); + } + SystemConfigs::Configs { .. } => { + todo!("run_if is not implemented for groups of systems yet") + } + } + } +} + +/// Types that can convert into a [`SystemConfigs`]. +pub trait IntoSystemConfigs +where + Self: Sized, +{ + /// Convert into a [`SystemConfigs`]. + #[doc(hidden)] + 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) + } + + /// Run before all systems in `set`. + fn before(self, set: impl IntoSystemSet) -> SystemConfigs { + self.into_configs().before(set) + } + + /// Run after all systems in `set`. + fn after(self, set: impl IntoSystemSet) -> SystemConfigs { + self.into_configs().after(set) + } + + /// Add a run condition to each contained system. + /// + /// Each system will receive its own clone of the [`Condition`] and will only run + /// if the `Condition` is true. + /// + /// Each individual condition will be evaluated at most once (per schedule run), + /// right before the corresponding system prepares to run. + /// + /// This is equivalent to calling [`run_if`](IntoSystemConfigs::run_if) on each individual + /// system, as shown below: + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # let mut app = Schedule::new(); + /// # fn a() {} + /// # fn b() {} + /// # fn condition() -> bool { true } + /// app.add_systems((a, b).distributive_run_if(condition)); + /// app.add_systems((a.run_if(condition), b.run_if(condition))); + /// ``` + /// + /// # Note + /// + /// Because the conditions are evaluated separately for each system, there is no guarantee + /// that all evaluations in a single schedule run will yield the same result. If another + /// system is run inbetween two evaluations it could cause the result of the condition to change. + /// + /// Use [`run_if`](IntoSystemSetConfig::run_if) on a [`SystemSet`] if you want to make sure + /// that either all or none of the systems are run, or you don't want to evaluate the run + /// condition for each contained system separately. + fn distributive_run_if(self, condition: impl Condition + Clone) -> SystemConfigs { + self.into_configs().distributive_run_if(condition) + } + + /// Run the systems only if the [`Condition`] is `true`. + /// + /// The `Condition` will be evaluated at most once (per schedule run), + /// the first time a system in this set prepares to run. + fn run_if(self, condition: impl Condition) -> SystemConfigs { + self.into_configs().run_if(condition) + } + + /// Suppress warnings and errors that would result from these systems having ambiguities + /// (conflicting access but indeterminate order) with systems in `set`. + fn ambiguous_with(self, set: impl IntoSystemSet) -> SystemConfigs { + self.into_configs().ambiguous_with(set) + } + + /// Suppress warnings and errors that would result from these systems having ambiguities + /// (conflicting access but indeterminate order) with any other system. + fn ambiguous_with_all(self) -> SystemConfigs { + self.into_configs().ambiguous_with_all() + } + + /// Treat this collection as a sequence of systems. + /// + /// Ordering constraints will be applied between the successive elements. + fn chain(self) -> SystemConfigs { + self.into_configs().chain() + } + + /// This used to add the system to `CoreSchedule::Startup`. + /// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::add_systems` with the `Startup` schedule: + /// Ex: `app.add_system(foo.on_startup())` -> `app.add_systems(Startup, foo)` + #[deprecated( + since = "0.11.0", + note = "`app.add_system(foo.on_startup())` has been deprecated in favor of `app.add_systems(Startup, foo)`. Please migrate to that API." + )] + fn on_startup(self) -> SystemConfigs { + panic!("`app.add_system(foo.on_startup())` has been deprecated in favor of `app.add_systems(Startup, foo)`. Please migrate to that API."); + } + + /// This used to add the system to the provided `schedule`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::add_systems`: + /// Ex: `app.add_system(foo.in_schedule(SomeSchedule))` -> `app.add_systems(SomeSchedule, foo)` + #[deprecated( + since = "0.11.0", + note = "`app.add_system(foo.in_schedule(SomeSchedule))` has been deprecated in favor of `app.add_systems(SomeSchedule, foo)`. Please migrate to that API." + )] + fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemConfigs { + panic!("`app.add_system(foo.in_schedule(SomeSchedule))` has been deprecated in favor of `app.add_systems(SomeSchedule, foo)`. Please migrate to that API."); + } +} + +impl IntoSystemConfigs<()> for SystemConfigs { + fn into_configs(self) -> Self { + self + } + + #[track_caller] + fn in_set(mut self, set: impl SystemSet) -> Self { + assert!( + set.system_type().is_none(), + "adding arbitrary systems to a system type set is not allowed" + ); + + self.in_set_inner(set.dyn_clone()); + + self + } + + fn before(mut self, set: impl IntoSystemSet) -> Self { + let set = set.into_system_set(); + self.before_inner(set.dyn_clone()); + self + } + + fn after(mut self, set: impl IntoSystemSet) -> Self { + let set = set.into_system_set(); + self.after_inner(set.dyn_clone()); + self + } + + fn distributive_run_if(mut self, condition: impl Condition + Clone) -> SystemConfigs { + self.distributive_run_if_inner(condition); + self + } + + fn ambiguous_with(mut self, set: impl IntoSystemSet) -> Self { + let set = set.into_system_set(); + self.ambiguous_with_inner(set.dyn_clone()); + self + } + + fn ambiguous_with_all(mut self) -> Self { + self.ambiguous_with_all_inner(); + self + } + + fn run_if(mut self, condition: impl Condition) -> SystemConfigs { + self.run_if_inner(new_condition(condition)); + self + } + + fn chain(mut self) -> Self { + match &mut self { + SystemConfigs::SystemConfig(_) => { /* no op */ } + SystemConfigs::Configs { chained, .. } => { + *chained = true; + } + } + self + } +} + +pub struct SystemConfigTupleMarker; + +macro_rules! impl_system_collection { + ($(($param: ident, $sys: ident)),*) => { + impl<$($param, $sys),*> IntoSystemConfigs<(SystemConfigTupleMarker, $($param,)*)> for ($($sys,)*) + where + $($sys: IntoSystemConfigs<$param>),* + { + #[allow(non_snake_case)] + fn into_configs(self) -> SystemConfigs { + let ($($sys,)*) = self; + SystemConfigs::Configs { + configs: vec![$($sys.into_configs(),)*], + chained: false, + } + } + } + } +} + +all_tuples!(impl_system_collection, 1, 20, P, S); + +/// A [`SystemSet`] with scheduling metadata. +pub struct SystemSetConfig { + pub(super) set: BoxedSystemSet, + pub(super) graph_info: GraphInfo, + pub(super) conditions: Vec, +} + +impl SystemSetConfig { + fn new(set: BoxedSystemSet) -> Self { + // system type sets are automatically populated + // to avoid unintentionally broad changes, they cannot be configured + assert!( + set.system_type().is_none(), + "configuring system type sets is not allowed" + ); + + Self { + set, + graph_info: GraphInfo::default(), + conditions: Vec::new(), + } + } +} + /// Types that can be converted into a [`SystemSetConfig`]. /// /// This has been implemented for all types that implement [`SystemSet`] and boxed trait objects. @@ -88,18 +406,9 @@ pub trait IntoSystemSetConfig: Sized { fn into_config(self) -> SystemSetConfig; /// Add to the provided `set`. #[track_caller] - fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfig { + fn in_set(self, set: impl SystemSet) -> SystemSetConfig { self.into_config().in_set(set) } - /// 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 BaseSystemSet) -> SystemSetConfig { - self.into_config().in_base_set(set) - } - /// Add this set to the schedules's default base set. - fn in_default_base_set(self) -> SystemSetConfig { - self.into_config().in_default_base_set() - } /// Run before all systems in `set`. fn before(self, set: impl IntoSystemSet) -> SystemSetConfig { self.into_config().before(set) @@ -125,6 +434,35 @@ pub trait IntoSystemSetConfig: Sized { fn ambiguous_with_all(self) -> SystemSetConfig { self.into_config().ambiguous_with_all() } + + /// This used to configure the set in the `CoreSchedule::Startup` schedule. + /// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::configure_set` with the `Startup` schedule: + /// Ex: `app.configure_set(MySet.on_startup())` -> `app.configure_set(Startup, MySet)` + #[deprecated( + since = "0.11.0", + note = "`app.configure_set(MySet.on_startup())` has been deprecated in favor of `app.configure_set(Startup, MySet)`. Please migrate to that API." + )] + fn on_startup(self) -> SystemSetConfigs { + panic!("`app.configure_set(MySet.on_startup())` has been deprecated in favor of `app.configure_set(Startup, MySet)`. Please migrate to that API."); + } + + /// This used to configure the set in the provided `schedule`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::configure_set`: + /// Ex: `app.configure_set(MySet.in_schedule(SomeSchedule))` -> `app.configure_set(SomeSchedule, MySet)` + #[deprecated( + since = "0.11.0", + note = "`app.configure_set(MySet.in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_set(SomeSchedule, MySet)`. Please migrate to that API." + )] + fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemSetConfigs { + panic!("`app.configure_set(MySet.in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_set(SomeSchedule, MySet)`. Please migrate to that API."); + } } impl IntoSystemSetConfig for S { @@ -150,41 +488,10 @@ impl IntoSystemSetConfig for SystemSetConfig { set.system_type().is_none(), "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 } - #[track_caller] - fn in_base_set(mut self, set: impl SystemSet) -> Self { - assert!( - set.system_type().is_none(), - "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 - } - fn before(mut self, set: impl IntoSystemSet) -> Self { self.graph_info.dependencies.push(Dependency::new( DependencyKind::Before, @@ -217,332 +524,6 @@ impl IntoSystemSetConfig for SystemSetConfig { } } -/// Types that can be converted into a [`SystemConfig`]. -/// -/// This has been implemented for boxed [`System`](crate::system::System) -/// trait objects and all functions that turn into such. -pub trait IntoSystemConfig: Sized -where - Config: IntoSystemConfig<(), Config>, -{ - /// Convert into a [`SystemConfig`]. - #[doc(hidden)] - fn into_config(self) -> Config; - /// Add to `set` membership. - #[track_caller] - fn in_set(self, set: impl FreeSystemSet) -> Config { - self.into_config().in_set(set) - } - /// 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 BaseSystemSet) -> Config { - self.into_config().in_base_set(set) - } - /// Don't add this system to the schedules's default set. - fn no_default_base_set(self) -> Config { - self.into_config().no_default_base_set() - } - /// Run before all systems in `set`. - fn before(self, set: impl IntoSystemSet) -> Config { - self.into_config().before(set) - } - /// Run after all systems in `set`. - fn after(self, set: impl IntoSystemSet) -> Config { - self.into_config().after(set) - } - /// Run only if the [`Condition`] is `true`. - /// - /// The `Condition` will be evaluated at most once (per schedule run), - /// when the system prepares to run. - fn run_if(self, condition: impl Condition) -> Config { - self.into_config().run_if(condition) - } - /// Suppress warnings and errors that would result from this system having ambiguities - /// (conflicting access but indeterminate order) with systems in `set`. - fn ambiguous_with(self, set: impl IntoSystemSet) -> Config { - self.into_config().ambiguous_with(set) - } - /// Suppress warnings and errors that would result from this system having ambiguities - /// (conflicting access but indeterminate order) with any other system. - fn ambiguous_with_all(self) -> Config { - self.into_config().ambiguous_with_all() - } -} - -impl IntoSystemConfig for F -where - F: IntoSystem<(), (), Marker>, -{ - fn into_config(self) -> SystemConfig { - SystemConfig::new(Box::new(IntoSystem::into_system(self))) - } -} - -impl IntoSystemConfig<()> for BoxedSystem<(), ()> { - fn into_config(self) -> SystemConfig { - SystemConfig::new(self) - } -} - -impl IntoSystemConfig<()> for SystemConfig { - fn into_config(self) -> Self { - self - } - - #[track_caller] - fn in_set(mut self, set: impl SystemSet) -> Self { - assert!( - set.system_type().is_none(), - "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 - } - - #[track_caller] - fn in_base_set(mut self, set: impl SystemSet) -> Self { - assert!( - set.system_type().is_none(), - "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 - } - - fn before(mut self, set: impl IntoSystemSet) -> Self { - self.graph_info.dependencies.push(Dependency::new( - DependencyKind::Before, - Box::new(set.into_system_set()), - )); - self - } - - fn after(mut self, set: impl IntoSystemSet) -> Self { - self.graph_info.dependencies.push(Dependency::new( - DependencyKind::After, - Box::new(set.into_system_set()), - )); - self - } - - fn run_if(mut self, condition: impl Condition) -> Self { - self.conditions.push(new_condition(condition)); - self - } - - fn ambiguous_with(mut self, set: impl IntoSystemSet) -> Self { - ambiguous_with(&mut self.graph_info, Box::new(set.into_system_set())); - self - } - - fn ambiguous_with_all(mut self) -> Self { - self.graph_info.ambiguous_with = Ambiguity::IgnoreAll; - self - } -} - -/// A collection of [`SystemConfig`]. -pub struct SystemConfigs { - pub(super) systems: Vec, - /// If `true`, adds `before -> after` ordering constraints between the successive elements. - pub(super) chained: bool, -} - -/// Types that can convert into a [`SystemConfigs`]. -pub trait IntoSystemConfigs -where - Self: Sized, -{ - /// Convert into a [`SystemConfigs`]. - #[doc(hidden)] - fn into_configs(self) -> SystemConfigs; - - /// Add these systems to the provided `set`. - #[track_caller] - fn in_set(self, set: impl FreeSystemSet) -> 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 BaseSystemSet) -> SystemConfigs { - self.into_configs().in_base_set(set) - } - - /// Run before all systems in `set`. - fn before(self, set: impl IntoSystemSet) -> SystemConfigs { - self.into_configs().before(set) - } - - /// Run after all systems in `set`. - fn after(self, set: impl IntoSystemSet) -> SystemConfigs { - self.into_configs().after(set) - } - - /// Add a run condition to each contained system. - /// - /// The [`Condition`] must be [`Clone`]. Each system will receive its own clone - /// of the `Condition` and will only run if the `Condition` is true. - /// - /// Each individual condition will be evaluated at most once (per schedule run), - /// right before the corresponding system prepares to run. - /// - /// This is equivalent to calling [`run_if`](IntoSystemConfig::run_if) on each individual - /// system, as shown below: - /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// # let mut app = Schedule::new(); - /// # fn a() {} - /// # fn b() {} - /// # fn condition() -> bool { true } - /// app.add_systems((a, b).distributive_run_if(condition)); - /// app.add_systems((a.run_if(condition), b.run_if(condition))); - /// ``` - /// - /// # Note - /// - /// Because the conditions are evaluated separately for each system, there is no guarantee - /// that all evaluations in a single schedule run will yield the same result. If another - /// system is run inbetween two evaluations it could cause the result of the condition to change. - /// - /// Use [`run_if`](IntoSystemSetConfig::run_if) on a [`SystemSet`] if you want to make sure - /// that either all or none of the systems are run, or you don't want to evaluate the run - /// condition for each contained system separately. - /// - /// The [`Condition`] is cloned for each system. - /// Cloned instances of [`FunctionSystem`](crate::system::FunctionSystem) will be de-initialized. - fn distributive_run_if(self, condition: impl Condition + Clone) -> SystemConfigs { - self.into_configs().distributive_run_if(condition) - } - - /// Suppress warnings and errors that would result from these systems having ambiguities - /// (conflicting access but indeterminate order) with systems in `set`. - fn ambiguous_with(self, set: impl IntoSystemSet) -> SystemConfigs { - self.into_configs().ambiguous_with(set) - } - - /// Suppress warnings and errors that would result from these systems having ambiguities - /// (conflicting access but indeterminate order) with any other system. - fn ambiguous_with_all(self) -> SystemConfigs { - self.into_configs().ambiguous_with_all() - } - - /// Treat this collection as a sequence of systems. - /// - /// Ordering constraints will be applied between the successive elements. - fn chain(self) -> SystemConfigs { - self.into_configs().chain() - } -} - -impl IntoSystemConfigs<()> for SystemConfigs { - fn into_configs(self) -> Self { - self - } - - #[track_caller] - fn in_set(mut self, set: impl SystemSet) -> Self { - assert!( - set.system_type().is_none(), - "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()); - } - - self - } - - #[track_caller] - fn in_base_set(mut self, set: impl SystemSet) -> Self { - assert!( - set.system_type().is_none(), - "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(mut self, set: impl IntoSystemSet) -> Self { - let set = set.into_system_set(); - for config in &mut self.systems { - config - .graph_info - .dependencies - .push(Dependency::new(DependencyKind::Before, set.dyn_clone())); - } - - self - } - - fn after(mut self, set: impl IntoSystemSet) -> Self { - let set = set.into_system_set(); - for config in &mut self.systems { - config - .graph_info - .dependencies - .push(Dependency::new(DependencyKind::After, set.dyn_clone())); - } - - self - } - - fn distributive_run_if(mut self, condition: impl Condition + Clone) -> SystemConfigs { - for config in &mut self.systems { - config.conditions.push(new_condition(condition.clone())); - } - - self - } - - fn ambiguous_with(mut self, set: impl IntoSystemSet) -> Self { - let set = set.into_system_set(); - for config in &mut self.systems { - ambiguous_with(&mut config.graph_info, set.dyn_clone()); - } - - self - } - - fn ambiguous_with_all(mut self) -> Self { - for config in &mut self.systems { - config.graph_info.ambiguous_with = Ambiguity::IgnoreAll; - } - - self - } - - fn chain(mut self) -> Self { - self.chained = true; - self - } -} - /// A collection of [`SystemSetConfig`]. pub struct SystemSetConfigs { pub(super) sets: Vec, @@ -561,16 +542,10 @@ where /// Add these system sets to the provided `set`. #[track_caller] - fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfigs { + 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 BaseSystemSet) -> SystemSetConfigs { - self.into_configs().in_base_set(set) - } - /// Run before all systems in `set`. fn before(self, set: impl IntoSystemSet) -> SystemSetConfigs { self.into_configs().before(set) @@ -599,6 +574,35 @@ where fn chain(self) -> SystemSetConfigs { self.into_configs().chain() } + + /// This used to configure the sets in the `CoreSchedule::Startup` schedule. + /// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::configure_sets` with the `Startup` schedule: + /// Ex: `app.configure_sets((A, B).on_startup())` -> `app.configure_sets(Startup, (A, B))` + #[deprecated( + since = "0.11.0", + note = "`app.configure_sets((A, B).on_startup())` has been deprecated in favor of `app.configure_sets(Startup, (A, B))`. Please migrate to that API." + )] + fn on_startup(self) -> SystemSetConfigs { + panic!("`app.configure_sets((A, B).on_startup())` has been deprecated in favor of `app.configure_sets(Startup, (A, B))`. Please migrate to that API."); + } + + /// This used to configure the sets in the provided `schedule`. + /// + /// # Panics + /// + /// Always panics. Please migrate to the new `App::configure_set`: + /// Ex: `app.configure_sets((A, B).in_schedule(SomeSchedule))` -> `app.configure_sets(SomeSchedule, (A, B))` + #[deprecated( + since = "0.11.0", + note = "`app.configure_sets((A, B).in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_sets(SomeSchedule, (A, B))`. Please migrate to that API." + )] + fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemSetConfigs { + panic!("`app.configure_sets((A, B).in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_sets(SomeSchedule, (A, B))`. Please migrate to that API."); + } } impl IntoSystemSetConfigs for SystemSetConfigs { @@ -612,42 +616,13 @@ impl IntoSystemSetConfigs for SystemSetConfigs { set.system_type().is_none(), "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.system_type().is_none(), - "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(mut self, set: impl IntoSystemSet) -> Self { let set = set.into_system_set(); for config in &mut self.sets { @@ -695,24 +670,6 @@ impl IntoSystemSetConfigs for SystemSetConfigs { } } -macro_rules! impl_system_collection { - ($(($param: ident, $sys: ident)),*) => { - impl<$($param, $sys),*> IntoSystemConfigs<($($param,)*)> for ($($sys,)*) - where - $($sys: IntoSystemConfig<$param>),* - { - #[allow(non_snake_case)] - fn into_configs(self) -> SystemConfigs { - let ($($sys,)*) = self; - SystemConfigs { - systems: vec![$($sys.into_config(),)*], - chained: false, - } - } - } - } -} - macro_rules! impl_system_set_collection { ($($set: ident),*) => { impl<$($set: IntoSystemSetConfig),*> IntoSystemSetConfigs for ($($set,)*) @@ -729,5 +686,4 @@ macro_rules! impl_system_set_collection { } } -all_tuples!(impl_system_collection, 0, 15, P, S); all_tuples!(impl_system_set_collection, 0, 15, S); diff --git a/crates/bevy_ecs/src/schedule/graph_utils.rs b/crates/bevy_ecs/src/schedule/graph_utils.rs index f7e28e6bb5..4a0311c8be 100644 --- a/crates/bevy_ecs/src/schedule/graph_utils.rs +++ b/crates/bevy_ecs/src/schedule/graph_utils.rs @@ -67,56 +67,14 @@ pub(crate) enum Ambiguity { IgnoreAll, } -#[derive(Clone)] +#[derive(Clone, Default)] pub(crate) struct GraphInfo { pub(crate) sets: Vec, pub(crate) dependencies: Vec, pub(crate) ambiguous_with: Ambiguity, - pub(crate) add_default_base_set: bool, pub(crate) base_set: Option, } -impl Default for GraphInfo { - fn default() -> Self { - GraphInfo { - sets: Vec::new(), - base_set: None, - dependencies: Vec::new(), - ambiguous_with: Ambiguity::default(), - 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); - } - } -} - /// Converts 2D row-major pair of indices into a 1D array index. pub(crate) fn index(row: usize, col: usize, num_cols: usize) -> usize { debug_assert!(col < num_cols); diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 0a5f92a4dd..05aaba9f8b 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -23,7 +23,7 @@ mod tests { use std::sync::atomic::{AtomicU32, Ordering}; pub use crate as bevy_ecs; - pub use crate::schedule::{IntoSystemConfig, IntoSystemSetConfig, Schedule, SystemSet}; + pub use crate::schedule::{IntoSystemSetConfig, Schedule, SystemSet}; pub use crate::system::{Res, ResMut}; pub use crate::{prelude::World, system::Resource}; @@ -75,7 +75,7 @@ mod tests { world.init_resource::(); - schedule.add_system(make_function_system(0)); + schedule.add_systems(make_function_system(0)); schedule.run(&mut world); assert_eq!(world.resource::().0, vec![0]); @@ -88,7 +88,7 @@ mod tests { world.init_resource::(); - schedule.add_system(make_exclusive_system(0)); + schedule.add_systems(make_exclusive_system(0)); schedule.run(&mut world); assert_eq!(world.resource::().0, vec![0]); @@ -108,7 +108,7 @@ mod tests { for _ in 0..thread_count { let inner = barrier.clone(); - schedule.add_system(move || { + schedule.add_systems(move || { inner.wait(); }); } @@ -195,6 +195,50 @@ mod tests { schedule.run(&mut world); assert_eq!(world.resource::().0, vec![0, 1, 2, 3]); } + + #[test] + fn add_systems_correct_order_nested() { + let mut world = World::new(); + let mut schedule = Schedule::new(); + + world.init_resource::(); + + schedule.add_systems( + ( + (make_function_system(0), make_function_system(1)).chain(), + make_function_system(2), + (make_function_system(3), make_function_system(4)).chain(), + ( + make_function_system(5), + (make_function_system(6), make_function_system(7)), + ), + ( + (make_function_system(8), make_function_system(9)).chain(), + make_function_system(10), + ), + ) + .chain(), + ); + + schedule.run(&mut world); + let order = &world.resource::().0; + assert_eq!( + &order[0..5], + &[0, 1, 2, 3, 4], + "first five items should be exactly ordered" + ); + let unordered = &order[5..8]; + assert!( + unordered.contains(&5) && unordered.contains(&6) && unordered.contains(&7), + "unordered must be 5, 6, and 7 in any order" + ); + let partially_ordered = &order[8..11]; + assert!( + partially_ordered == [8, 9, 10] || partially_ordered == [10, 8, 9], + "partially_ordered must be [8, 9, 10] or [10, 8, 9]" + ); + assert!(order.len() == 11, "must have exacty 11 order entries"); + } } mod conditions { @@ -210,7 +254,7 @@ mod tests { world.init_resource::(); world.init_resource::(); - schedule.add_system( + schedule.add_systems( make_function_system(0).run_if(|condition: Res| condition.0), ); @@ -256,7 +300,7 @@ mod tests { world.init_resource::(); world.init_resource::(); - schedule.add_system( + schedule.add_systems( make_exclusive_system(0).run_if(|condition: Res| condition.0), ); @@ -294,13 +338,13 @@ mod tests { world.init_resource::(); schedule.configure_set(TestSet::A.run_if(|| false).run_if(|| false)); - schedule.add_system(counting_system.in_set(TestSet::A)); + schedule.add_systems(counting_system.in_set(TestSet::A)); schedule.configure_set(TestSet::B.run_if(|| true).run_if(|| false)); - schedule.add_system(counting_system.in_set(TestSet::B)); + schedule.add_systems(counting_system.in_set(TestSet::B)); schedule.configure_set(TestSet::C.run_if(|| false).run_if(|| true)); - schedule.add_system(counting_system.in_set(TestSet::C)); + schedule.add_systems(counting_system.in_set(TestSet::C)); schedule.configure_set(TestSet::D.run_if(|| true).run_if(|| true)); - schedule.add_system(counting_system.in_set(TestSet::D)); + schedule.add_systems(counting_system.in_set(TestSet::D)); schedule.run(&mut world); assert_eq!(world.resource::().0.load(Ordering::Relaxed), 1); @@ -314,13 +358,13 @@ mod tests { world.init_resource::(); schedule.configure_set(TestSet::A.run_if(|| false)); - schedule.add_system(counting_system.in_set(TestSet::A).run_if(|| false)); + schedule.add_systems(counting_system.in_set(TestSet::A).run_if(|| false)); schedule.configure_set(TestSet::B.run_if(|| true)); - schedule.add_system(counting_system.in_set(TestSet::B).run_if(|| false)); + schedule.add_systems(counting_system.in_set(TestSet::B).run_if(|| false)); schedule.configure_set(TestSet::C.run_if(|| false)); - schedule.add_system(counting_system.in_set(TestSet::C).run_if(|| true)); + schedule.add_systems(counting_system.in_set(TestSet::C).run_if(|| true)); schedule.configure_set(TestSet::D.run_if(|| true)); - schedule.add_system(counting_system.in_set(TestSet::D).run_if(|| true)); + schedule.add_systems(counting_system.in_set(TestSet::D).run_if(|| true)); schedule.run(&mut world); assert_eq!(world.resource::().0.load(Ordering::Relaxed), 1); @@ -337,7 +381,7 @@ mod tests { world.init_resource::(); let mut schedule = Schedule::default(); - schedule.add_system( + schedule.add_systems( counting_system .run_if(|res1: Res| res1.is_changed()) .run_if(|res2: Res| res2.is_changed()), @@ -391,7 +435,7 @@ mod tests { .run_if(|res2: Res| res2.is_changed()), ); - schedule.add_system(counting_system.in_set(TestSet::A)); + schedule.add_systems(counting_system.in_set(TestSet::A)); // both resource were just added. schedule.run(&mut world); @@ -438,7 +482,7 @@ mod tests { schedule .configure_set(TestSet::A.run_if(|res1: Res| res1.is_changed())); - schedule.add_system( + schedule.add_systems( counting_system .run_if(|res2: Res| res2.is_changed()) .in_set(TestSet::A), @@ -544,7 +588,7 @@ mod tests { assert!(result.is_ok()); // Schedule another `foo`. - schedule.add_system(foo); + schedule.add_systems(foo); // When there are multiple instances of `foo`, dependencies on // `foo` are no longer allowed. Too much ambiguity. @@ -556,11 +600,11 @@ mod tests { // same goes for `ambiguous_with` let mut schedule = Schedule::new(); - schedule.add_system(foo); - schedule.add_system(bar.ambiguous_with(foo)); + schedule.add_systems(foo); + schedule.add_systems(bar.ambiguous_with(foo)); let result = schedule.initialize(&mut world); assert!(result.is_ok()); - schedule.add_system(foo); + schedule.add_systems(foo); let result = schedule.initialize(&mut world); assert!(matches!( result, @@ -626,7 +670,7 @@ mod tests { fn foo() {} // Add `foo` to both `A` and `C`. - schedule.add_system(foo.in_set(TestSet::A).in_set(TestSet::C)); + schedule.add_systems(foo.in_set(TestSet::A).in_set(TestSet::C)); // Order `A -> B -> C`. schedule.configure_sets(( @@ -664,140 +708,4 @@ 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, - } - - #[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 allow_same_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::A)) - .add_system(named_system.in_set(Normal::X).in_set(Normal::Y)); - - let result = schedule.initialize(&mut world); - assert!(matches!(result, Ok(()))); - - let mut schedule = Schedule::new(); - schedule - .configure_set(Normal::X.in_base_set(Base::A)) - .configure_set(Normal::Y.in_base_set(Base::A).in_set(Normal::X)); - - let result = schedule.initialize(&mut world); - assert!(matches!(result, Ok(()))); - } - - #[test] - fn default_base_set_ordering() { - let mut world = World::default(); - let mut schedule = Schedule::default(); - - world.init_resource::(); - - schedule - .set_default_base_set(Base::A) - .configure_set(Base::A.before(Base::B)) - .add_systems(( - make_function_system(0).in_base_set(Base::B), - make_function_system(1), - )); - schedule.run(&mut world); - - assert_eq!(world.resource::().0, vec![1, 0]); - } - } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 2f75213510..303ac8dd29 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -43,17 +43,11 @@ impl Schedules { /// and the old schedule is returned. Otherwise, `None` is returned. pub fn insert(&mut self, label: impl ScheduleLabel, schedule: Schedule) -> Option { let label = label.dyn_clone(); - if self.inner.contains_key(&label) { - warn!("schedule with label {:?} already exists", label); - } self.inner.insert(label, schedule) } /// Removes the schedule corresponding to the `label` from the map, returning it if it existed. pub fn remove(&mut self, label: &dyn ScheduleLabel) -> Option { - if !self.inner.contains_key(label) { - warn!("schedule with label {:?} not found", label); - } self.inner.remove(label) } @@ -62,9 +56,6 @@ impl Schedules { &mut self, label: &dyn ScheduleLabel, ) -> Option<(Box, Schedule)> { - if !self.inner.contains_key(label) { - warn!("schedule with label {:?} not found", label); - } self.inner.remove_entry(label) } @@ -134,7 +125,7 @@ fn make_executor(kind: ExecutorKind) -> Box { /// fn main() { /// let mut world = World::new(); /// let mut schedule = Schedule::default(); -/// schedule.add_system(hello_world); +/// schedule.add_systems(hello_world); /// /// schedule.run(&mut world); /// } @@ -183,21 +174,16 @@ 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(&mut self, system: impl IntoSystemConfig) -> &mut Self { - self.graph.add_system(system); + #[deprecated(since = "0.11.0", note = "please use `add_systems` instead")] + pub fn add_system(&mut self, system: impl IntoSystemConfigs) -> &mut Self { + self.graph.add_systems_inner(system.into_configs(), false); self } /// Add a collection of systems to the schedule. pub fn add_systems(&mut self, systems: impl IntoSystemConfigs) -> &mut Self { - self.graph.add_systems(systems); + self.graph.add_systems_inner(systems.into_configs(), false); self } @@ -346,29 +332,14 @@ impl Dag { } } -/// Describes which base set (i.e. [`SystemSet`] where [`SystemSet::is_base`] returns true) -/// a system belongs to. -/// -/// Note that this is only populated once [`ScheduleGraph::build_schedule`] is called. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub 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, - base_set_membership: BaseSetMembership::Uncalculated, - } + Self { inner: set } } pub fn name(&self) -> String { @@ -383,14 +354,12 @@ impl SystemSetNode { /// A [`BoxedSystem`] with metadata, stored in a [`ScheduleGraph`]. struct SystemNode { inner: Option, - base_set_membership: BaseSetMembership, } impl SystemNode { pub fn new(system: BoxedSystem) -> Self { Self { inner: Some(system), - base_set_membership: BaseSetMembership::Uncalculated, } } @@ -401,10 +370,6 @@ impl SystemNode { 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`]. @@ -416,7 +381,6 @@ pub struct ScheduleGraph { system_set_conditions: Vec>>, system_set_ids: HashMap, uninit: Vec<(NodeId, usize)>, - maybe_default_base_set: Vec, hierarchy: Dag, dependency: Dag, dependency_flattened: Dag, @@ -426,7 +390,6 @@ pub struct ScheduleGraph { conflicting_systems: Vec<(NodeId, NodeId, Vec)>, changed: bool, settings: ScheduleBuildSettings, - default_base_set: Option, } impl ScheduleGraph { @@ -437,7 +400,6 @@ 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(), @@ -448,7 +410,6 @@ impl ScheduleGraph { conflicting_systems: Vec::new(), changed: false, settings: default(), - default_base_set: None, } } @@ -491,44 +452,29 @@ impl ScheduleGraph { } /// Returns an iterator over all systems in this schedule. - /// - /// Note that the [`BaseSetMembership`] will only be initialized after [`ScheduleGraph::build_schedule`] is called. pub fn systems( &self, - ) -> impl Iterator< - Item = ( - NodeId, - &dyn System, - BaseSetMembership, - &[BoxedCondition], - ), - > { + ) -> impl Iterator, &[BoxedCondition])> { self.systems .iter() .zip(self.system_conditions.iter()) .enumerate() .filter_map(|(i, (system_node, condition))| { let system = system_node.inner.as_deref()?; - let base_set_membership = system_node.base_set_membership; let condition = condition.as_ref()?.as_slice(); - Some((NodeId::System(i), system, base_set_membership, condition)) + Some((NodeId::System(i), system, condition)) }) } /// Returns an iterator over all system sets in this schedule. - /// - /// Note that the [`BaseSetMembership`] will only be initialized after [`ScheduleGraph::build_schedule`] is called. - pub fn system_sets( - &self, - ) -> impl Iterator { + pub fn system_sets(&self) -> impl Iterator { self.system_set_ids.iter().map(|(_, node_id)| { let set_node = &self.system_sets[node_id.index()]; let set = &*set_node.inner; - let base_set_membership = set_node.base_set_membership; let conditions = self.system_set_conditions[node_id.index()] .as_deref() .unwrap_or(&[]); - (*node_id, set, base_set_membership, conditions) + (*node_id, set, conditions) }) } @@ -556,47 +502,144 @@ impl ScheduleGraph { &self.conflicting_systems } - fn add_systems(&mut self, systems: impl IntoSystemConfigs) { - let SystemConfigs { systems, chained } = systems.into_configs(); - let mut system_iter = systems.into_iter(); - if chained { - let Some(prev) = system_iter.next() else { return }; - let mut prev_id = self.add_system_inner(prev).unwrap(); - for next in system_iter { - let next_id = self.add_system_inner(next).unwrap(); - self.dependency.graph.add_edge(prev_id, next_id, ()); - prev_id = next_id; + /// Adds the systems to the graph. Returns a vector of all node ids contained the nested `SystemConfigs` + /// if `ancestor_chained` is true. Also returns true if "densely chained", meaning that all nested items + /// are linearly chained in the order they are defined + fn add_systems_inner( + &mut self, + configs: SystemConfigs, + ancestor_chained: bool, + ) -> AddSystemsInnerResult { + match configs { + SystemConfigs::SystemConfig(config) => { + let node_id = self.add_system_inner(config).unwrap(); + if ancestor_chained { + AddSystemsInnerResult { + densely_chained: true, + nodes: vec![node_id], + } + } else { + AddSystemsInnerResult { + densely_chained: true, + nodes: Vec::new(), + } + } } - } else { - for system in system_iter { - self.add_system_inner(system).unwrap(); + SystemConfigs::Configs { configs, chained } => { + let mut config_iter = configs.into_iter(); + let mut nodes_in_scope = Vec::new(); + let mut densely_chained = true; + if chained { + let Some(prev) = config_iter.next() else { + return AddSystemsInnerResult { + nodes: Vec::new(), + densely_chained: true + } + }; + let mut previous_result = self.add_systems_inner(prev, true); + densely_chained = previous_result.densely_chained; + for current in config_iter { + let current_result = self.add_systems_inner(current, true); + densely_chained = densely_chained && current_result.densely_chained; + match ( + previous_result.densely_chained, + current_result.densely_chained, + ) { + // Both groups are "densely" chained, so we can simplify the graph by only + // chaining the last in the previous list to the first in the current list + (true, true) => { + let last_in_prev = previous_result.nodes.last().unwrap(); + let first_in_current = current_result.nodes.first().unwrap(); + self.dependency.graph.add_edge( + *last_in_prev, + *first_in_current, + (), + ); + } + // The previous group is "densely" chained, so we can simplify the graph by only + // chaining the last item from the previous list to every item in the current list + (true, false) => { + let last_in_prev = previous_result.nodes.last().unwrap(); + for current_node in ¤t_result.nodes { + self.dependency.graph.add_edge( + *last_in_prev, + *current_node, + (), + ); + } + } + // The current list is currently "densely" chained, so we can simplify the graph by + // only chaining every item in the previous list to the first item in the current list + (false, true) => { + let first_in_current = current_result.nodes.first().unwrap(); + for previous_node in &previous_result.nodes { + self.dependency.graph.add_edge( + *previous_node, + *first_in_current, + (), + ); + } + } + // Neither of the lists are "densely" chained, so we must chain every item in the first + // list to every item in the second list + (false, false) => { + for previous_node in &previous_result.nodes { + for current_node in ¤t_result.nodes { + self.dependency.graph.add_edge( + *previous_node, + *current_node, + (), + ); + } + } + } + } + + if ancestor_chained { + nodes_in_scope.append(&mut previous_result.nodes); + } + + previous_result = current_result; + } + + // ensure the last config's nodes are added + if ancestor_chained { + nodes_in_scope.append(&mut previous_result.nodes); + } + } else { + let more_than_one_entry = config_iter.len() > 1; + for config in config_iter { + let result = self.add_systems_inner(config, ancestor_chained); + densely_chained = densely_chained && result.densely_chained; + if ancestor_chained { + nodes_in_scope.extend(result.nodes); + } + } + + // an "unchained" SystemConfig is only densely chained if it has exactly one densely chained entry + if more_than_one_entry { + densely_chained = false; + } + } + + AddSystemsInnerResult { + nodes: nodes_in_scope, + densely_chained, + } } } } - fn add_system(&mut self, system: impl IntoSystemConfig) { - self.add_system_inner(system).unwrap(); - } - - fn add_system_inner( - &mut self, - system: impl IntoSystemConfig, - ) -> Result { - let SystemConfig { - system, - graph_info, - conditions, - } = system.into_config(); - + fn add_system_inner(&mut self, config: SystemConfig) -> Result { let id = NodeId::System(self.systems.len()); // graph updates are immediate - self.update_graphs(id, graph_info, false)?; + self.update_graphs(id, config.graph_info)?; // system init has to be deferred (need `&mut World`) self.uninit.push((id, 0)); - self.systems.push(SystemNode::new(system)); - self.system_conditions.push(Some(conditions)); + self.systems.push(SystemNode::new(config.system)); + self.system_conditions.push(Some(config.conditions)); Ok(id) } @@ -639,7 +682,7 @@ impl ScheduleGraph { }; // graph updates are immediate - self.update_graphs(id, graph_info, set.is_base())?; + self.update_graphs(id, graph_info)?; // system init has to be deferred (need `&mut World`) let system_set_conditions = @@ -724,7 +767,6 @@ 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)?; @@ -734,8 +776,6 @@ impl ScheduleGraph { sets, dependencies, ambiguous_with, - base_set, - add_default_base_set, .. } = graph_info; @@ -749,30 +789,6 @@ 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); } @@ -832,149 +848,15 @@ 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, 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 = 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 { - if first_set != calculated_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) - } - /// Build a [`SystemSchedule`] optimized for scheduler access from the [`ScheduleGraph`]. /// /// This method also - /// - calculates [`BaseSetMembership`] /// - checks for dependency or hierarchy cycles /// - checks for system access conflicts and reports ambiguities pub fn build_schedule( &mut self, components: &Components, ) -> Result { - 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 self.hierarchy.topsort = self .topsort_graph(&self.hierarchy.graph, ReportCycles::Hierarchy) @@ -1340,17 +1222,14 @@ impl ScheduleGraph { Ok(()) } +} - fn set_default_base_set(&mut self, set: Option) { - 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; - } - } +/// Values returned by `ScheduleGraph::add_systems_inner` +struct AddSystemsInnerResult { + /// All nodes contained inside this add_systems_inner call's SystemConfigs hierarchy + nodes: Vec, + /// True if and only if all nodes are "densely chained" + densely_chained: bool, } /// Used to select the appropriate reporting function. diff --git a/crates/bevy_ecs/src/schedule/set.rs b/crates/bevy_ecs/src/schedule/set.rs index 23058a3de9..56deb5c398 100644 --- a/crates/bevy_ecs/src/schedule/set.rs +++ b/crates/bevy_ecs/src/schedule/set.rs @@ -23,34 +23,10 @@ pub trait SystemSet: DynHash + Debug + Send + Sync + 'static { None } - /// 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; } -/// A marker trait for `SystemSet` types where [`is_base`] returns `true`. -/// This should only be implemented for types that satisfy this requirement. -/// It is automatically implemented for base set types by `#[derive(SystemSet)]`. -/// -/// [`is_base`]: SystemSet::is_base -pub trait BaseSystemSet: SystemSet {} - -/// A marker trait for `SystemSet` types where [`is_base`] returns `false`. -/// This should only be implemented for types that satisfy this requirement. -/// It is automatically implemented for non-base set types by `#[derive(SystemSet)]`. -/// -/// [`is_base`]: SystemSet::is_base -pub trait FreeSystemSet: SystemSet {} - impl PartialEq for dyn SystemSet { fn eq(&self, other: &Self) -> bool { self.dyn_eq(other.as_dyn_eq()) diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index ec782e715b..671a29431c 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -50,7 +50,7 @@ use super::{ReadOnlySystem, System}; /// # world.init_resource::(); /// # /// # let mut app = Schedule::new(); -/// app.add_system(my_system.run_if(Xor::new( +/// app.add_systems(my_system.run_if(Xor::new( /// IntoSystem::into_system(resource_equals(A(1))), /// IntoSystem::into_system(resource_equals(B(1))), /// // The name of the combined system. diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 73df904177..fb0f1b388f 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -582,9 +582,9 @@ impl<'w, 's> Commands<'w, 's> { /// # world.init_resource::(); /// # /// # let mut setup_schedule = Schedule::new(); -/// # setup_schedule.add_system(setup); +/// # setup_schedule.add_systems(setup); /// # let mut assert_schedule = Schedule::new(); -/// # assert_schedule.add_system(assert_names); +/// # assert_schedule.add_systems(assert_names); /// # /// # setup_schedule.run(&mut world); /// # assert_schedule.run(&mut world); diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 6b622ca85d..19a68b1713 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -64,7 +64,7 @@ //! //! // Configure this system to run in between the other two systems //! // using explicit dependencies. -//! schedule.add_system(print_mid.after(print_first).before(print_last)); +//! schedule.add_systems(print_mid.after(print_first).before(print_last)); //! // Prints "Hello, World!" //! schedule.run(&mut world); //! @@ -214,7 +214,7 @@ mod tests { fn run_system>(world: &mut World, system: S) { let mut schedule = Schedule::default(); - schedule.add_system(system); + schedule.add_systems(system); schedule.run(world); } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 307dd2bc7d..b8c11b6262 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; /// /// Systems are executed in parallel, in opportunistic order; data access is managed automatically. /// It's possible to specify explicit execution order between specific systems, -/// see [`IntoSystemConfig`](crate::schedule::IntoSystemConfig). +/// see [`IntoSystemConfigs`](crate::schedule::IntoSystemConfigs). pub trait System: Send + Sync + 'static { /// The system's input. See [`In`](crate::system::In) for /// [`FunctionSystem`](crate::system::FunctionSystem)s. diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8f54f0b28c..47f668d8ae 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -674,7 +674,7 @@ unsafe impl SystemParam for &'_ World { /// move |mut val| val.0 = value.0 /// } /// -/// // .add_system(reset_to_system(my_config)) +/// // .add_systems(reset_to_system(my_config)) /// # assert_is_system(reset_to_system(Config(10))); /// ``` pub struct Local<'s, T: FromWorld + Send + 'static>(pub(crate) &'s mut T); diff --git a/crates/bevy_ecs/src/system/system_piping.rs b/crates/bevy_ecs/src/system/system_piping.rs index b1551e64f3..dc1f3dc55a 100644 --- a/crates/bevy_ecs/src/system/system_piping.rs +++ b/crates/bevy_ecs/src/system/system_piping.rs @@ -138,7 +138,7 @@ pub mod adapter { /// /// // Building a new schedule/app... /// let mut sched = Schedule::default(); - /// sched.add_system( + /// sched.add_systems( /// // Panic if the load system returns an error. /// load_save_system.pipe(system_adapter::unwrap) /// ) @@ -169,7 +169,7 @@ pub mod adapter { /// /// // Building a new schedule/app... /// let mut sched = Schedule::default(); - /// sched.add_system( + /// sched.add_systems( /// // Prints system information. /// data_pipe_system.pipe(system_adapter::info) /// ) @@ -196,7 +196,7 @@ pub mod adapter { /// /// // Building a new schedule/app... /// let mut sched = Schedule::default(); - /// sched.add_system( + /// sched.add_systems( /// // Prints debug data from system. /// parse_message_system.pipe(system_adapter::dbg) /// ) @@ -223,7 +223,7 @@ pub mod adapter { /// /// // Building a new schedule/app... /// # let mut sched = Schedule::default(); - /// sched.add_system( + /// sched.add_systems( /// // Prints system warning if system returns an error. /// warning_pipe_system.pipe(system_adapter::warn) /// ) @@ -251,7 +251,7 @@ pub mod adapter { /// use bevy_ecs::prelude::*; /// // Building a new schedule/app... /// let mut sched = Schedule::default(); - /// sched.add_system( + /// sched.add_systems( /// // Prints system error if system fails. /// parse_error_message_system.pipe(system_adapter::error) /// ) @@ -287,7 +287,7 @@ pub mod adapter { /// /// // Building a new schedule/app... /// # let mut sched = Schedule::default(); sched - /// .add_system( + /// .add_systems( /// // If the system fails, just move on and try again next frame. /// fallible_system.pipe(system_adapter::ignore) /// ) diff --git a/crates/bevy_gilrs/src/lib.rs b/crates/bevy_gilrs/src/lib.rs index f011710100..abbf34307c 100644 --- a/crates/bevy_gilrs/src/lib.rs +++ b/crates/bevy_gilrs/src/lib.rs @@ -1,7 +1,7 @@ mod converter; mod gilrs_system; -use bevy_app::{App, CoreSet, Plugin, StartupSet}; +use bevy_app::{App, Plugin, PreStartup, PreUpdate}; use bevy_ecs::prelude::*; use bevy_input::InputSystem; use bevy_utils::tracing::error; @@ -20,14 +20,8 @@ impl Plugin for GilrsPlugin { { Ok(gilrs) => { app.insert_non_send_resource(gilrs) - .add_startup_system( - gilrs_event_startup_system.in_base_set(StartupSet::PreStartup), - ) - .add_system( - gilrs_event_system - .before(InputSystem) - .in_base_set(CoreSet::PreUpdate), - ); + .add_systems(PreStartup, gilrs_event_startup_system) + .add_systems(PreUpdate, gilrs_event_system.before(InputSystem)); } Err(err) => error!("Failed to start Gilrs. {}", err), } diff --git a/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs b/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs index 557450f9af..ad8c6c1ec3 100644 --- a/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs +++ b/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use bevy_app::{App, CoreSet, Plugin}; +use bevy_app::{App, Last, Plugin}; use bevy_core::Name; use bevy_ecs::prelude::*; use bevy_log::warn; @@ -96,10 +96,10 @@ impl Default for ValidParentCheckPlugin { impl Plugin for ValidParentCheckPlugin { fn build(&self, app: &mut App) { - app.init_resource::>().add_system( + app.init_resource::>().add_systems( + Last, check_hierarchy_component_has_valid_parent:: - .run_if(resource_equals(ReportHierarchyIssue::::new(true))) - .in_base_set(CoreSet::Last), + .run_if(resource_equals(ReportHierarchyIssue::::new(true))), ); } } diff --git a/crates/bevy_input/src/common_conditions.rs b/crates/bevy_input/src/common_conditions.rs index dd93d2bdf1..8b85b8a858 100644 --- a/crates/bevy_input/src/common_conditions.rs +++ b/crates/bevy_input/src/common_conditions.rs @@ -11,7 +11,7 @@ use std::hash::Hash; /// fn main() { /// App::new() /// .add_plugins(DefaultPlugins) -/// .add_system(pause_menu.run_if(input_toggle_active(false, KeyCode::Escape))) +/// .add_systems(Update, pause_menu.run_if(input_toggle_active(false, KeyCode::Escape))) /// .run(); /// } /// @@ -33,7 +33,7 @@ use std::hash::Hash; /// App::new() /// .add_plugins(DefaultPlugins) /// .init_resource::() -/// .add_system(pause_menu.run_if(|paused: Res| paused.0)) +/// .add_systems(Update, pause_menu.run_if(|paused: Res| paused.0)) /// .run(); /// } /// @@ -75,7 +75,7 @@ where /// fn main() { /// App::new() /// .add_plugins(DefaultPlugins) -/// .add_system(jump.run_if(input_just_pressed(KeyCode::Space))) +/// .add_systems(Update, jump.run_if(input_just_pressed(KeyCode::Space))) /// .run(); /// } /// diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 7fc988171c..9da05a2e25 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -53,18 +53,18 @@ pub struct InputSystem; impl Plugin for InputPlugin { fn build(&self, app: &mut App) { - app.configure_set(InputSystem.in_base_set(CoreSet::PreUpdate)) + app // keyboard .add_event::() .init_resource::>() .init_resource::>() - .add_system(keyboard_input_system.in_set(InputSystem)) + .add_systems(PreUpdate, keyboard_input_system.in_set(InputSystem)) // mouse .add_event::() .add_event::() .add_event::() .init_resource::>() - .add_system(mouse_button_input_system.in_set(InputSystem)) + .add_systems(PreUpdate, mouse_button_input_system.in_set(InputSystem)) // gamepad .add_event::() .add_event::() @@ -76,6 +76,7 @@ impl Plugin for InputPlugin { .init_resource::>() .init_resource::>() .add_systems( + PreUpdate, ( gamepad_event_system, gamepad_connection_system.after(gamepad_event_system), @@ -91,7 +92,7 @@ impl Plugin for InputPlugin { // touch .add_event::() .init_resource::() - .add_system(touch_screen_input_system.in_set(InputSystem)); + .add_systems(PreUpdate, touch_screen_input_system.in_set(InputSystem)); // Register common types app.register_type::(); diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 544984d5c1..bf47c70450 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -55,7 +55,7 @@ use bevy_render::{ render_phase::sort_phase_system, render_resource::Shader, view::{ViewSet, VisibilitySystems}, - ExtractSchedule, RenderApp, RenderSet, + ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::TransformSystem; use environment_map::EnvironmentMapPlugin; @@ -176,59 +176,58 @@ impl Plugin for PbrPlugin { .init_resource::() .add_plugin(ExtractResourcePlugin::::default()) .configure_sets( + PostUpdate, ( SimulationLightSystems::AddClusters, - SimulationLightSystems::AddClustersFlush - .after(SimulationLightSystems::AddClusters) - .before(SimulationLightSystems::AssignLightsToClusters), + SimulationLightSystems::AddClustersFlush, SimulationLightSystems::AssignLightsToClusters, - SimulationLightSystems::CheckLightVisibility, - SimulationLightSystems::UpdateDirectionalLightCascades, - SimulationLightSystems::UpdateLightFrusta, ) - .in_base_set(CoreSet::PostUpdate), + .chain(), ) .add_plugin(FogPlugin) - .add_systems(( - add_clusters.in_set(SimulationLightSystems::AddClusters), - apply_system_buffers.in_set(SimulationLightSystems::AddClustersFlush), - assign_lights_to_clusters - .in_set(SimulationLightSystems::AssignLightsToClusters) - .after(TransformSystem::TransformPropagate) - .after(VisibilitySystems::CheckVisibility) - .after(CameraUpdateSystem), - update_directional_light_cascades - .in_set(SimulationLightSystems::UpdateDirectionalLightCascades) - .after(TransformSystem::TransformPropagate) - .after(CameraUpdateSystem), - update_directional_light_frusta - .in_set(SimulationLightSystems::UpdateLightFrusta) - // This must run after CheckVisibility because it relies on ComputedVisibility::is_visible() - .after(VisibilitySystems::CheckVisibility) - .after(TransformSystem::TransformPropagate) - .after(SimulationLightSystems::UpdateDirectionalLightCascades) - // We assume that no entity will be both a directional light and a spot light, - // so these systems will run independently of one another. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(update_spot_light_frusta), - update_point_light_frusta - .in_set(SimulationLightSystems::UpdateLightFrusta) - .after(TransformSystem::TransformPropagate) - .after(SimulationLightSystems::AssignLightsToClusters), - update_spot_light_frusta - .in_set(SimulationLightSystems::UpdateLightFrusta) - .after(TransformSystem::TransformPropagate) - .after(SimulationLightSystems::AssignLightsToClusters), - check_light_mesh_visibility - .in_set(SimulationLightSystems::CheckLightVisibility) - .after(VisibilitySystems::CalculateBoundsFlush) - .after(TransformSystem::TransformPropagate) - .after(SimulationLightSystems::UpdateLightFrusta) - // NOTE: This MUST be scheduled AFTER the core renderer visibility check - // because that resets entity ComputedVisibility for the first view - // which would override any results from this otherwise - .after(VisibilitySystems::CheckVisibility), - )); + .add_systems( + PostUpdate, + ( + add_clusters.in_set(SimulationLightSystems::AddClusters), + apply_system_buffers.in_set(SimulationLightSystems::AddClustersFlush), + assign_lights_to_clusters + .in_set(SimulationLightSystems::AssignLightsToClusters) + .after(TransformSystem::TransformPropagate) + .after(VisibilitySystems::CheckVisibility) + .after(CameraUpdateSystem), + update_directional_light_cascades + .in_set(SimulationLightSystems::UpdateDirectionalLightCascades) + .after(TransformSystem::TransformPropagate) + .after(CameraUpdateSystem), + update_directional_light_frusta + .in_set(SimulationLightSystems::UpdateLightFrusta) + // This must run after CheckVisibility because it relies on ComputedVisibility::is_visible() + .after(VisibilitySystems::CheckVisibility) + .after(TransformSystem::TransformPropagate) + .after(SimulationLightSystems::UpdateDirectionalLightCascades) + // We assume that no entity will be both a directional light and a spot light, + // so these systems will run independently of one another. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(update_spot_light_frusta), + update_point_light_frusta + .in_set(SimulationLightSystems::UpdateLightFrusta) + .after(TransformSystem::TransformPropagate) + .after(SimulationLightSystems::AssignLightsToClusters), + update_spot_light_frusta + .in_set(SimulationLightSystems::UpdateLightFrusta) + .after(TransformSystem::TransformPropagate) + .after(SimulationLightSystems::AssignLightsToClusters), + check_light_mesh_visibility + .in_set(SimulationLightSystems::CheckLightVisibility) + .after(VisibilitySystems::CalculateBoundsFlush) + .after(TransformSystem::TransformPropagate) + .after(SimulationLightSystems::UpdateLightFrusta) + // NOTE: This MUST be scheduled AFTER the core renderer visibility check + // because that resets entity ComputedVisibility for the first view + // which would override any results from this otherwise + .after(VisibilitySystems::CheckVisibility), + ), + ); app.world .resource_mut::>() @@ -248,31 +247,39 @@ impl Plugin for PbrPlugin { // Extract the required data from the main world render_app - .configure_set(RenderLightSystems::PrepareLights.in_set(RenderSet::Prepare)) - .configure_set(RenderLightSystems::PrepareClusters.in_set(RenderSet::Prepare)) - .configure_set(RenderLightSystems::QueueShadows.in_set(RenderSet::Queue)) + .configure_sets( + Render, + ( + RenderLightSystems::PrepareLights.in_set(RenderSet::Prepare), + RenderLightSystems::PrepareClusters.in_set(RenderSet::Prepare), + RenderLightSystems::QueueShadows.in_set(RenderSet::Queue), + ), + ) .add_systems( + ExtractSchedule, ( render::extract_clusters.in_set(RenderLightSystems::ExtractClusters), render::extract_lights.in_set(RenderLightSystems::ExtractLights), - ) - .in_schedule(ExtractSchedule), + ), + ) + .add_systems( + Render, + ( + render::prepare_lights + .before(ViewSet::PrepareUniforms) + .in_set(RenderLightSystems::PrepareLights), + // A sync is needed after prepare_lights, before prepare_view_uniforms, + // because prepare_lights creates new views for shadow mapping + apply_system_buffers + .in_set(RenderSet::Prepare) + .after(RenderLightSystems::PrepareLights) + .before(ViewSet::PrepareUniforms), + render::prepare_clusters + .after(render::prepare_lights) + .in_set(RenderLightSystems::PrepareClusters), + sort_phase_system::.in_set(RenderSet::PhaseSort), + ), ) - .add_systems(( - render::prepare_lights - .before(ViewSet::PrepareUniforms) - .in_set(RenderLightSystems::PrepareLights), - // A sync is needed after prepare_lights, before prepare_view_uniforms, - // because prepare_lights creates new views for shadow mapping - apply_system_buffers - .in_set(RenderSet::Prepare) - .after(RenderLightSystems::PrepareLights) - .before(ViewSet::PrepareUniforms), - render::prepare_clusters - .after(render::prepare_lights) - .in_set(RenderLightSystems::PrepareClusters), - sort_phase_system::.in_set(RenderSet::PhaseSort), - )) .init_resource::() .init_resource::() .init_resource::(); diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 0072474083..be28f2e13a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -3,7 +3,7 @@ use crate::{ MeshUniform, PrepassPipelinePlugin, PrepassPlugin, RenderLightSystems, SetMeshBindGroup, SetMeshViewBindGroup, Shadow, }; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; use bevy_core_pipeline::{ core_3d::{AlphaMask3d, Opaque3d, Transparent3d}, @@ -35,7 +35,7 @@ use bevy_render::{ renderer::RenderDevice, texture::FallbackImage, view::{ExtractedView, Msaa, VisibleEntities}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{tracing::error, HashMap, HashSet}; use std::hash::Hash; @@ -199,14 +199,17 @@ where .init_resource::>() .init_resource::>() .init_resource::>>() - .add_systems(( - extract_materials::.in_schedule(ExtractSchedule), - prepare_materials:: - .in_set(RenderSet::Prepare) - .after(PrepareAssetSet::PreAssetPrepare), - render::queue_shadows::.in_set(RenderLightSystems::QueueShadows), - queue_material_meshes::.in_set(RenderSet::Queue), - )); + .add_systems(ExtractSchedule, extract_materials::) + .add_systems( + Render, + ( + prepare_materials:: + .in_set(RenderSet::Prepare) + .after(PrepareAssetSet::PreAssetPrepare), + render::queue_shadows::.in_set(RenderLightSystems::QueueShadows), + queue_material_meshes::.in_set(RenderSet::Queue), + ), + ); } // PrepassPipelinePlugin is required for shadow mapping and the optional PrepassPlugin diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 2766f985d8..b2a50c5f37 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{IntoSystemAppConfig, Plugin}; +use bevy_app::Plugin; use bevy_asset::{load_internal_asset, AssetServer, Handle, HandleUntyped}; use bevy_core_pipeline::{ prelude::Camera3d, @@ -39,7 +39,7 @@ use bevy_render::{ renderer::RenderDevice, texture::{FallbackImagesDepth, FallbackImagesMsaa, TextureCache}, view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{tracing::error, HashMap}; @@ -102,7 +102,10 @@ where }; render_app - .add_system(queue_prepass_view_bind_group::.in_set(RenderSet::Queue)) + .add_systems( + Render, + queue_prepass_view_bind_group::.in_set(RenderSet::Queue), + ) .init_resource::>() .init_resource::() .init_resource::>>(); @@ -130,15 +133,18 @@ where }; render_app - .add_systems(( - extract_camera_prepass_phase.in_schedule(ExtractSchedule), - prepare_prepass_textures - .in_set(RenderSet::Prepare) - .after(bevy_render::view::prepare_windows), - queue_prepass_material_meshes::.in_set(RenderSet::Queue), - sort_phase_system::.in_set(RenderSet::PhaseSort), - sort_phase_system::.in_set(RenderSet::PhaseSort), - )) + .add_systems(ExtractSchedule, extract_camera_prepass_phase) + .add_systems( + Render, + ( + prepare_prepass_textures + .in_set(RenderSet::Prepare) + .after(bevy_render::view::prepare_windows), + queue_prepass_material_meshes::.in_set(RenderSet::Queue), + sort_phase_system::.in_set(RenderSet::PhaseSort), + sort_phase_system::.in_set(RenderSet::PhaseSort), + ), + ) .init_resource::>() .init_resource::>() .add_render_command::>() diff --git a/crates/bevy_pbr/src/render/fog.rs b/crates/bevy_pbr/src/render/fog.rs index 2c658bb7a1..efa9a4c15f 100644 --- a/crates/bevy_pbr/src/render/fog.rs +++ b/crates/bevy_pbr/src/render/fog.rs @@ -8,7 +8,7 @@ use bevy_render::{ render_resource::{DynamicUniformBuffer, Shader, ShaderType}, renderer::{RenderDevice, RenderQueue}, view::ExtractedView, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }; use crate::{FogFalloff, FogSettings}; @@ -142,8 +142,11 @@ impl Plugin for FogPlugin { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .add_system(prepare_fog.in_set(RenderFogSystems::PrepareFog)) - .configure_set(RenderFogSystems::PrepareFog.in_set(RenderSet::Prepare)); + .add_systems(Render, prepare_fog.in_set(RenderFogSystems::PrepareFog)) + .configure_set( + Render, + RenderFogSystems::PrepareFog.in_set(RenderSet::Prepare), + ); } } } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 1faf66bc4c..a76ff0f470 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -4,7 +4,7 @@ use crate::{ ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, }; -use bevy_app::{IntoSystemAppConfigs, Plugin}; +use bevy_app::Plugin; use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; use bevy_core_pipeline::{ prepass::ViewPrepassTextures, @@ -36,7 +36,7 @@ use bevy_render::{ FallbackImagesMsaa, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, view::{ComputedVisibility, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; use std::num::NonZeroU64; @@ -107,12 +107,15 @@ impl Plugin for MeshRenderPlugin { render_app .init_resource::() .init_resource::() - .add_systems((extract_meshes, extract_skinned_meshes).in_schedule(ExtractSchedule)) - .add_systems(( - prepare_skinned_meshes.in_set(RenderSet::Prepare), - queue_mesh_bind_group.in_set(RenderSet::Queue), - queue_mesh_view_bind_groups.in_set(RenderSet::Queue), - )); + .add_systems(ExtractSchedule, (extract_meshes, extract_skinned_meshes)) + .add_systems( + Render, + ( + prepare_skinned_meshes.in_set(RenderSet::Prepare), + queue_mesh_bind_group.in_set(RenderSet::Queue), + queue_mesh_view_bind_groups.in_set(RenderSet::Queue), + ), + ); } } } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 34d93c9185..7e885f9231 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -7,6 +7,7 @@ use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_reflect::std_traits::ReflectDefault; use bevy_reflect::{Reflect, TypeUuid}; use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy_render::Render; use bevy_render::{ extract_resource::{ExtractResource, ExtractResourcePlugin}, mesh::{Mesh, MeshVertexBufferLayout}, @@ -47,7 +48,7 @@ impl Plugin for WireframePlugin { .add_render_command::() .init_resource::() .init_resource::>() - .add_system(queue_wireframes.in_set(RenderSet::Queue)); + .add_systems(Render, queue_wireframes.in_set(RenderSet::Queue)); } } } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 27687fc863..8b0e44ab0b 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -492,7 +492,7 @@ impl NormalizedRenderTarget { /// The system function is generic over the camera projection type, and only instances of /// [`OrthographicProjection`] and [`PerspectiveProjection`] are automatically added to /// the app, as well as the runtime-selected [`Projection`]. -/// The system runs during [`CoreSet::PostUpdate`]. +/// The system runs during [`PostUpdate`](bevy_app::PostUpdate). /// /// ## World Resources /// @@ -502,7 +502,6 @@ impl NormalizedRenderTarget { /// [`OrthographicProjection`]: crate::camera::OrthographicProjection /// [`PerspectiveProjection`]: crate::camera::PerspectiveProjection /// [`Projection`]: crate::camera::Projection -/// [`CoreSet::PostUpdate`]: bevy_app::CoreSet::PostUpdate pub fn camera_system( mut window_resized_events: EventReader, mut window_created_events: EventReader, diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 91ea69d941..44d72cb03e 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -7,9 +7,9 @@ pub use camera::*; pub use camera_driver_node::*; pub use projection::*; -use crate::{render_graph::RenderGraph, ExtractSchedule, RenderApp, RenderSet}; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; -use bevy_ecs::schedule::IntoSystemConfig; +use crate::{render_graph::RenderGraph, ExtractSchedule, Render, RenderApp, RenderSet}; +use bevy_app::{App, Plugin}; +use bevy_ecs::schedule::IntoSystemConfigs; #[derive(Default)] pub struct CameraPlugin; @@ -29,8 +29,8 @@ impl Plugin for CameraPlugin { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .add_system(extract_cameras.in_schedule(ExtractSchedule)) - .add_system(sort_cameras.in_set(RenderSet::Prepare)); + .add_systems(ExtractSchedule, extract_cameras) + .add_systems(Render, sort_cameras.in_set(RenderSet::Prepare)); let camera_driver_node = CameraDriverNode::new(&mut render_app.world); let mut render_graph = render_app.world.resource_mut::(); render_graph.add_node(crate::main_graph::node::CAMERA_DRIVER, camera_driver_node); diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index fd82cd72da..ad3731be45 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use bevy_app::{App, CoreSchedule, CoreSet, IntoSystemAppConfig, Plugin, StartupSet}; +use bevy_app::{App, Plugin, PostStartup, PostUpdate}; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_math::{Mat4, Rect, Vec2}; use bevy_reflect::{ @@ -27,25 +27,24 @@ pub struct CameraUpdateSystem; impl Plugin for CameraProjectionPlugin { fn build(&self, app: &mut App) { app.register_type::() - .edit_schedule(CoreSchedule::Startup, |schedule| { - schedule.configure_set(CameraUpdateSystem.in_base_set(StartupSet::PostStartup)); - }) - .configure_set(CameraUpdateSystem.in_base_set(CoreSet::PostUpdate)) - .add_systems(( - crate::camera::camera_system:: - .on_startup() - .in_set(CameraUpdateSystem) - // We assume that each camera will only have one projection, - // so we can ignore ambiguities with all other monomorphizations. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(CameraUpdateSystem), + .add_systems( + PostStartup, crate::camera::camera_system:: .in_set(CameraUpdateSystem) // We assume that each camera will only have one projection, // so we can ignore ambiguities with all other monomorphizations. // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. .ambiguous_with(CameraUpdateSystem), - )); + ) + .add_systems( + PostUpdate, + crate::camera::camera_system:: + .in_set(CameraUpdateSystem) + // We assume that each camera will only have one projection, + // so we can ignore ambiguities with all other monomorphizations. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(CameraUpdateSystem), + ); } } diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 2b70a897a1..606c8ccbe0 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -2,9 +2,9 @@ use crate::{ render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType}, renderer::{RenderDevice, RenderQueue}, view::ComputedVisibility, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_asset::{Asset, Handle}; use bevy_ecs::{ component::Component, @@ -83,7 +83,10 @@ impl Plugin for UniformComponentP if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .insert_resource(ComponentUniforms::::default()) - .add_system(prepare_uniform_components::.in_set(RenderSet::Prepare)); + .add_systems( + Render, + prepare_uniform_components::.in_set(RenderSet::Prepare), + ); } } } @@ -180,9 +183,9 @@ impl Plugin for ExtractComponentPlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { if self.only_extract_visible { - render_app.add_system(extract_visible_components::.in_schedule(ExtractSchedule)); + render_app.add_systems(ExtractSchedule, extract_visible_components::); } else { - render_app.add_system(extract_components::.in_schedule(ExtractSchedule)); + render_app.add_systems(ExtractSchedule, extract_components::); } } } diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index a1e8b122f0..ea8f7e81fd 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; pub use bevy_render_macros::ExtractResource; @@ -32,7 +32,7 @@ impl Default for ExtractResourcePlugin { impl Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_system(extract_resource::.in_schedule(ExtractSchedule)); + render_app.add_systems(ExtractSchedule, extract_resource::); } } } diff --git a/crates/bevy_render/src/globals.rs b/crates/bevy_render/src/globals.rs index 6b9fbdbc7f..ef585000b8 100644 --- a/crates/bevy_render/src/globals.rs +++ b/crates/bevy_render/src/globals.rs @@ -3,9 +3,9 @@ use crate::{ prelude::Shader, render_resource::{ShaderType, UniformBuffer}, renderer::{RenderDevice, RenderQueue}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_app::{App, IntoSystemAppConfigs, Plugin}; +use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, HandleUntyped}; use bevy_core::FrameCount; use bevy_ecs::prelude::*; @@ -26,8 +26,8 @@ impl Plugin for GlobalsPlugin { render_app .init_resource::() .init_resource:: { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app + .init_resource::>() + .init_resource::>() + .init_resource::>() + .add_systems(ExtractSchedule, extract_render_asset::) .configure_sets( + Render, ( PrepareAssetSet::PreAssetPrepare, PrepareAssetSet::AssetPrepare, @@ -89,13 +94,10 @@ impl Plugin for RenderAssetPlugin { .chain() .in_set(RenderSet::Prepare), ) - .init_resource::>() - .init_resource::>() - .init_resource::>() - .add_systems(( - extract_render_asset::.in_schedule(ExtractSchedule), + .add_systems( + Render, prepare_assets::.in_set(self.prepare_asset_set.clone()), - )); + ); } } } diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index f9b32a1eb7..1f4f45a83b 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -33,7 +33,7 @@ pub use texture_cache::*; use crate::{ render_asset::{PrepareAssetSet, RenderAssetPlugin}, renderer::RenderDevice, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }; use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, Assets}; @@ -115,7 +115,10 @@ impl Plugin for ImagePlugin { .init_resource::() .init_resource::() .init_resource::() - .add_system(update_texture_cache_system.in_set(RenderSet::Cleanup)); + .add_systems( + Render, + update_texture_cache_system.in_set(RenderSet::Cleanup), + ); } } } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index fa8bf29603..39bc2d1419 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -14,7 +14,7 @@ use crate::{ render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, renderer::{RenderDevice, RenderQueue}, texture::{BevyDefault, TextureCache}, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }; use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; @@ -55,14 +55,17 @@ impl Plugin for ViewPlugin { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .configure_set(ViewSet::PrepareUniforms.in_set(RenderSet::Prepare)) - .add_systems(( - prepare_view_uniforms.in_set(ViewSet::PrepareUniforms), - prepare_view_targets - .after(WindowSystem::Prepare) - .in_set(RenderSet::Prepare) - .after(crate::render_asset::prepare_assets::), - )); + .configure_set(Render, ViewSet::PrepareUniforms.in_set(RenderSet::Prepare)) + .add_systems( + Render, + ( + prepare_view_uniforms.in_set(ViewSet::PrepareUniforms), + prepare_view_targets + .after(WindowSystem::Prepare) + .in_set(RenderSet::Prepare) + .after(crate::render_asset::prepare_assets::), + ), + ); } } } diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 3163f7a831..25f0fc02fa 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -2,7 +2,7 @@ mod render_layers; pub use render_layers::*; -use bevy_app::{CoreSet, Plugin}; +use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; use bevy_ecs::prelude::*; use bevy_hierarchy::{Children, Parent}; @@ -91,8 +91,8 @@ impl ComputedVisibility { /// Whether this entity is visible to something this frame. This is true if and only if [`Self::is_visible_in_hierarchy`] and [`Self::is_visible_in_view`] /// are true. This is the canonical method to call to determine if an entity should be drawn. - /// This value is updated in [`CoreSet::PostUpdate`] by the [`VisibilitySystems::CheckVisibility`] system set. - /// Reading it during [`CoreSet::Update`] will yield the value from the previous frame. + /// This value is updated in [`PostUpdate`] by the [`VisibilitySystems::CheckVisibility`] system set. + /// Reading it during [`Update`](bevy_app::Update) will yield the value from the previous frame. #[inline] pub fn is_visible(&self) -> bool { self.flags.bits == ComputedVisibilityFlags::all().bits @@ -100,7 +100,7 @@ impl ComputedVisibility { /// Whether this entity is visible in the entity hierarchy, which is determined by the [`Visibility`] component. /// This takes into account "visibility inheritance". If any of this entity's ancestors (see [`Parent`]) are hidden, this entity - /// will be hidden as well. This value is updated in the [`VisibilitySystems::VisibilityPropagate`], which lives under the [`CoreSet::PostUpdate`] set. + /// will be hidden as well. This value is updated in the [`VisibilitySystems::VisibilityPropagate`], which lives in the [`PostUpdate`] schedule. #[inline] pub fn is_visible_in_hierarchy(&self) -> bool { self.flags @@ -110,8 +110,8 @@ impl ComputedVisibility { /// Whether this entity is visible in _any_ view (Cameras, Lights, etc). Each entity type (and view type) should choose how to set this /// value. For cameras and drawn entities, this will take into account [`RenderLayers`]. /// - /// This value is reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] during [`CoreSet::PostUpdate`]. - /// Each entity type then chooses how to set this field in the [`VisibilitySystems::CheckVisibility`] system set, under [`CoreSet::PostUpdate`]. + /// This value is reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] during [`PostUpdate`]. + /// Each entity type then chooses how to set this field in the [`VisibilitySystems::CheckVisibility`] system set, in [`PostUpdate`]. /// Meshes might use frustum culling to decide if they are visible in a view. /// Other entities might just set this to `true` every frame. #[inline] @@ -210,52 +210,49 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; - app.configure_set(CalculateBounds.in_base_set(CoreSet::PostUpdate)) + app // We add an AABB component in CalculateBounds, which must be ready on the same frame. - .add_system(apply_system_buffers.in_set(CalculateBoundsFlush)) - .configure_set( - CalculateBoundsFlush - .after(CalculateBounds) - .in_base_set(CoreSet::PostUpdate), + .add_systems( + PostUpdate, + apply_system_buffers.in_set(CalculateBoundsFlush), ) - .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_systems(( - calculate_bounds.in_set(CalculateBounds), - update_frusta:: - .in_set(UpdateOrthographicFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate) - // We assume that no camera will have more than one projection component, - // so these systems will run independently of one another. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(update_frusta::) - .ambiguous_with(update_frusta::), - update_frusta:: - .in_set(UpdatePerspectiveFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate) - // We assume that no camera will have more than one projection component, - // so these systems will run independently of one another. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(update_frusta::), - update_frusta:: - .in_set(UpdateProjectionFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate), - visibility_propagate_system.in_set(VisibilityPropagate), - check_visibility - .in_set(CheckVisibility) - .after(CalculateBoundsFlush) - .after(UpdateOrthographicFrusta) - .after(UpdatePerspectiveFrusta) - .after(UpdateProjectionFrusta) - .after(VisibilityPropagate) - .after(TransformSystem::TransformPropagate), - )); + .configure_set(PostUpdate, CalculateBoundsFlush.after(CalculateBounds)) + .add_systems( + PostUpdate, + ( + calculate_bounds.in_set(CalculateBounds), + update_frusta:: + .in_set(UpdateOrthographicFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate) + // We assume that no camera will have more than one projection component, + // so these systems will run independently of one another. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(update_frusta::) + .ambiguous_with(update_frusta::), + update_frusta:: + .in_set(UpdatePerspectiveFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate) + // We assume that no camera will have more than one projection component, + // so these systems will run independently of one another. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(update_frusta::), + update_frusta:: + .in_set(UpdateProjectionFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate), + visibility_propagate_system.in_set(VisibilityPropagate), + check_visibility + .in_set(CheckVisibility) + .after(CalculateBoundsFlush) + .after(UpdateOrthographicFrusta) + .after(UpdatePerspectiveFrusta) + .after(UpdateProjectionFrusta) + .after(VisibilityPropagate) + .after(TransformSystem::TransformPropagate), + ), + ); } } @@ -457,7 +454,7 @@ mod test { #[test] fn visibility_propagation() { let mut app = App::new(); - app.add_system(visibility_propagate_system); + app.add_systems(Update, visibility_propagate_system); let root1 = app .world @@ -576,7 +573,7 @@ mod test { #[test] fn visibility_propagation_unconditional_visible() { let mut app = App::new(); - app.add_system(visibility_propagate_system); + app.add_systems(Update, visibility_propagate_system); let root1 = app .world diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index ca9fac5aab..8118e4fd06 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -1,9 +1,9 @@ use crate::{ render_resource::TextureView, renderer::{RenderAdapter, RenderDevice, RenderInstance}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; use bevy_window::{ @@ -32,9 +32,9 @@ impl Plugin for WindowRenderPlugin { .init_resource::() .init_resource::() .init_non_send_resource::() - .add_system(extract_windows.in_schedule(ExtractSchedule)) - .configure_set(WindowSystem::Prepare.in_set(RenderSet::Prepare)) - .add_system(prepare_windows.in_set(WindowSystem::Prepare)); + .add_systems(ExtractSchedule, extract_windows) + .configure_set(Render, WindowSystem::Prepare.in_set(RenderSet::Prepare)) + .add_systems(Render, prepare_windows.in_set(WindowSystem::Prepare)); } } } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index 2d7ef33951..3488064f10 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -24,7 +24,6 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::AddAsset; -use bevy_ecs::prelude::*; #[derive(Default)] pub struct ScenePlugin; @@ -36,9 +35,9 @@ impl Plugin for ScenePlugin { .add_asset::() .init_asset_loader::() .init_resource::() - .add_system(scene_spawner_system) + .add_systems(Update, scene_spawner_system) // Systems `*_bundle_spawner` must run before `scene_spawner_system` - .add_system(scene_spawner.in_base_set(CoreSet::PreUpdate)); + .add_systems(PreUpdate, scene_spawner); } } diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index d7a02c3ff7..b624b7bad8 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -34,7 +34,7 @@ use bevy_reflect::TypeUuid; use bevy_render::{ render_phase::AddRenderCommand, render_resource::{Shader, SpecializedRenderPipelines}, - ExtractSchedule, RenderApp, RenderSet, + ExtractSchedule, Render, RenderApp, RenderSet, }; #[derive(Default)] @@ -71,13 +71,14 @@ impl Plugin for SpritePlugin { .init_resource::() .add_render_command::() .add_systems( + ExtractSchedule, ( extract_sprites.in_set(SpriteSystem::ExtractSprites), extract_sprite_events, - ) - .in_schedule(ExtractSchedule), + ), ) - .add_system( + .add_systems( + Render, queue_sprites .in_set(RenderSet::Queue) .ambiguous_with(queue_material2d_meshes::), diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 4ee87cff02..ed03db9a48 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -1,4 +1,4 @@ -use bevy_app::{App, IntoSystemAppConfig, Plugin}; +use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; use bevy_core_pipeline::{ core_2d::Transparent2d, @@ -32,7 +32,7 @@ use bevy_render::{ renderer::RenderDevice, texture::FallbackImage, view::{ComputedVisibility, ExtractedView, Msaa, Visibility, VisibleEntities}, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::{FloatOrd, HashMap, HashSet}; @@ -161,13 +161,16 @@ where .init_resource::>() .init_resource::>() .init_resource::>>() - .add_systems(( - extract_materials_2d::.in_schedule(ExtractSchedule), - prepare_materials_2d:: - .in_set(RenderSet::Prepare) - .after(PrepareAssetSet::PreAssetPrepare), - queue_material2d_meshes::.in_set(RenderSet::Queue), - )); + .add_systems(ExtractSchedule, extract_materials_2d::) + .add_systems( + Render, + ( + prepare_materials_2d:: + .in_set(RenderSet::Prepare) + .after(PrepareAssetSet::PreAssetPrepare), + queue_material2d_meshes::.in_set(RenderSet::Queue), + ), + ); } } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index a001b00dc3..070898df55 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -1,4 +1,4 @@ -use bevy_app::{IntoSystemAppConfig, Plugin}; +use bevy_app::Plugin; use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; use bevy_ecs::{ @@ -22,7 +22,7 @@ use bevy_render::{ view::{ ComputedVisibility, ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, }, - Extract, ExtractSchedule, RenderApp, RenderSet, + Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; @@ -103,11 +103,14 @@ impl Plugin for Mesh2dRenderPlugin { render_app .init_resource::() .init_resource::>() - .add_systems(( - extract_mesh2d.in_schedule(ExtractSchedule), - queue_mesh2d_bind_group.in_set(RenderSet::Queue), - queue_mesh2d_view_bind_groups.in_set(RenderSet::Queue), - )); + .add_systems(ExtractSchedule, extract_mesh2d) + .add_systems( + Render, + ( + queue_mesh2d_bind_group.in_set(RenderSet::Queue), + queue_mesh2d_view_bind_groups.in_set(RenderSet::Queue), + ), + ); } } } diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 767b926490..e740e525a2 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -79,9 +79,9 @@ impl Plugin for TextPlugin { .init_resource::() .init_resource::() .insert_resource(TextPipeline::default()) - .add_system( + .add_systems( + PostUpdate, update_text2d_layout - .in_base_set(CoreSet::PostUpdate) // Potential conflict: `Assets` // In practice, they run independently since `bevy_render::camera_update_system` // will only ever observe its own render target, and `update_text2d_layout` @@ -90,10 +90,9 @@ impl Plugin for TextPlugin { ); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_system( - extract_text2d_sprite - .after(SpriteSystem::ExtractSprites) - .in_schedule(ExtractSchedule), + render_app.add_systems( + ExtractSchedule, + extract_text2d_sprite.after(SpriteSystem::ExtractSprites), ); } } diff --git a/crates/bevy_time/src/common_conditions.rs b/crates/bevy_time/src/common_conditions.rs index 1f5625515e..974de24dfa 100644 --- a/crates/bevy_time/src/common_conditions.rs +++ b/crates/bevy_time/src/common_conditions.rs @@ -8,14 +8,14 @@ use bevy_utils::Duration; /// If used for a fixed timestep system, use [`on_fixed_timer`] instead. /// /// ```rust,no_run -/// # use bevy_app::{App, IntoSystemAppConfig, NoopPluginGroup as DefaultPlugins, PluginGroup}; -/// # use bevy_ecs::schedule::IntoSystemConfig; +/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update}; +/// # use bevy_ecs::schedule::IntoSystemConfigs; /// # use bevy_utils::Duration; /// # use bevy_time::common_conditions::on_timer; /// fn main() { /// App::new() /// .add_plugins(DefaultPlugins) -/// .add_system(tick.run_if(on_timer(Duration::from_secs(1)))) +/// .add_systems(Update, tick.run_if(on_timer(Duration::from_secs(1)))) /// .run(); /// } /// fn tick() { @@ -46,16 +46,15 @@ pub fn on_timer(duration: Duration) -> impl FnMut(Res() .init_resource::() - .add_systems(( - // This pair of systems has an ambiguous order, - // as their data access conflicts, and there's no order between them. - reads_a, - writes_a, - // This pair of systems has conflicting data access, - // but it's resolved with an explicit ordering: - // the .after relationship here means that we will always double after adding. - adds_one_to_b, - doubles_b.after(adds_one_to_b), - // This system isn't ambiguous with adds_one_to_b, - // due to the transitive ordering created by our constraints: - // if A is before B is before C, then A must be before C as well. - reads_b.after(doubles_b), - // This system will conflict with all of our writing systems - // but we've silenced its ambiguity with adds_one_to_b. - // This should only be done in the case of clear false positives: - // leave a comment in your code justifying the decision! - reads_a_and_b.ambiguous_with(adds_one_to_b), - )) + .add_systems( + Update, + ( + // This pair of systems has an ambiguous order, + // as their data access conflicts, and there's no order between them. + reads_a, + writes_a, + // This pair of systems has conflicting data access, + // but it's resolved with an explicit ordering: + // the .after relationship here means that we will always double after adding. + adds_one_to_b, + doubles_b.after(adds_one_to_b), + // This system isn't ambiguous with adds_one_to_b, + // due to the transitive ordering created by our constraints: + // if A is before B is before C, then A must be before C as well. + reads_b.after(doubles_b), + // This system will conflict with all of our writing systems + // but we've silenced its ambiguity with adds_one_to_b. + // This should only be done in the case of clear false positives: + // leave a comment in your code justifying the decision! + reads_a_and_b.ambiguous_with(adds_one_to_b), + ), + ) // Be mindful, internal ambiguities are reported too! // If there are any ambiguities due solely to DefaultPlugins, // or between DefaultPlugins and any of your third party plugins, diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 987962a164..d68f50b1f7 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -69,6 +69,7 @@ fn bounce_system(windows: Query<&Window>, mut sprites: Query<(&Transform, &mut V fn main() { App::new() .add_plugins(DefaultPlugins) - .add_systems((spawn_system.on_startup(), move_system, bounce_system)) + .add_systems(Startup, spawn_system) + .add_systems(Update, (move_system, bounce_system)) .run(); } diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 96f0c6e1ca..b7d339afa8 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -14,11 +14,9 @@ fn main() { // `CoreSet::Update', and the system that reacts on the removal in `CoreSet::PostUpdate`. App::new() .add_plugins(DefaultPlugins) - .add_systems(( - setup.on_startup(), - remove_component, - react_on_removal.in_base_set(CoreSet::PostUpdate), - )) + .add_systems(Startup, setup) + .add_systems(Update, remove_component) + .add_systems(PostUpdate, react_on_removal) .run(); } diff --git a/examples/ecs/run_conditions.rs b/examples/ecs/run_conditions.rs index 557aa4b50b..413982d6e0 100644 --- a/examples/ecs/run_conditions.rs +++ b/examples/ecs/run_conditions.rs @@ -11,37 +11,40 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .init_resource::() - .add_systems(( - increment_input_counter - // The common_conditions module has a few useful run conditions - // for checking resources and states. These are included in the prelude. - .run_if(resource_exists::()) - // This is a custom run condition, defined using a system that returns - // a `bool` and which has read-only `SystemParam`s. - // Both run conditions must return `true` in order for the system to run. - // Note that this second run condition will be evaluated even if the first returns `false`. - .run_if(has_user_input), - print_input_counter - // `.and_then()` is a run condition combinator that only evaluates the second condition - // if the first condition returns `true`. This behavior is known as "short-circuiting", - // and is how the `&&` operator works in Rust (as well as most C-family languages). - // In this case, the short-circuiting behavior prevents the second run condition from - // panicking if the `InputCounter` resource has not been initialized. - .run_if(resource_exists::().and_then( - // This is a custom run condition in the form of a closure. - // This is useful for small, simple run conditions you don't need to reuse. - // All the normal rules still apply: all parameters must be read only except for local parameters. - |counter: Res| counter.is_changed() && !counter.is_added(), - )), - print_time_message - // This function returns a custom run condition, much like the common conditions module. - // It will only return true once 2 seconds have passed. - .run_if(time_passed(2.0)) - // You can use the `not` condition from the common_conditions module - // to inverse a run condition. In this case it will return true if - // less than 2.5 seconds have elapsed since the app started. - .run_if(not(time_passed(2.5))), - )) + .add_systems( + Update, + ( + increment_input_counter + // The common_conditions module has a few useful run conditions + // for checking resources and states. These are included in the prelude. + .run_if(resource_exists::()) + // This is a custom run condition, defined using a system that returns + // a `bool` and which has read-only `SystemParam`s. + // Both run conditions must return `true` in order for the system to run. + // Note that this second run condition will be evaluated even if the first returns `false`. + .run_if(has_user_input), + print_input_counter + // `.and_then()` is a run condition combinator that only evaluates the second condition + // if the first condition returns `true`. This behavior is known as "short-circuiting", + // and is how the `&&` operator works in Rust (as well as most C-family languages). + // In this case, the short-circuiting behavior prevents the second run condition from + // panicking if the `InputCounter` resource has not been initialized. + .run_if(resource_exists::().and_then( + // This is a custom run condition in the form of a closure. + // This is useful for small, simple run conditions you don't need to reuse. + // All the normal rules still apply: all parameters must be read only except for local parameters. + |counter: Res| counter.is_changed() && !counter.is_added(), + )), + print_time_message + // This function returns a custom run condition, much like the common conditions module. + // It will only return true once 2 seconds have passed. + .run_if(time_passed(2.0)) + // You can use the `not` condition from the common_conditions module + // to inverse a run condition. In this case it will return true if + // less than 2.5 seconds have elapsed since the app started. + .run_if(not(time_passed(2.5))), + ), + ) .run(); } diff --git a/examples/ecs/startup_system.rs b/examples/ecs/startup_system.rs index f1a3e892cd..bb303e85ad 100644 --- a/examples/ecs/startup_system.rs +++ b/examples/ecs/startup_system.rs @@ -4,8 +4,8 @@ use bevy::prelude::*; fn main() { App::new() - .add_startup_system(startup_system) - .add_system(normal_system) + .add_systems(Startup, startup_system) + .add_systems(Update, normal_system) .run(); } diff --git a/examples/ecs/state.rs b/examples/ecs/state.rs index ae7f9d9950..b61ba01adb 100644 --- a/examples/ecs/state.rs +++ b/examples/ecs/state.rs @@ -11,19 +11,20 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_state::() - .add_system(setup.on_startup()) - // This system runs when we enter `AppState::Menu`, during `CoreSet::StateTransitions`. + .add_systems(Startup, setup) + // This system runs when we enter `AppState::Menu`, during the `StateTransitions` schedule. // All systems from the exit schedule of the state we're leaving are run first, // and then all systems from the enter schedule of the state we're entering are run second. - .add_system(setup_menu.in_schedule(OnEnter(AppState::Menu))) - // By contrast, on_update systems are stored in the main schedule, during CoreSet::Update, - // and simply check the value of the `State` resource to see if they should run each frame. - .add_systems(( - menu.in_set(OnUpdate(AppState::Menu)), - cleanup_menu.in_schedule(OnExit(AppState::Menu)), - setup_game.in_schedule(OnEnter(AppState::InGame)), - )) - .add_systems((movement, change_color).in_set(OnUpdate(AppState::InGame))) + .add_systems(OnEnter(AppState::Menu), setup_menu) + // By contrast, update systems are stored in the `Update` schedule. They simply + // check the value of the `State` resource to see if they should run each frame. + .add_systems(Update, menu.in_set(OnUpdate(AppState::Menu))) + .add_systems(OnExit(AppState::Menu), cleanup_menu) + .add_systems(OnEnter(AppState::InGame), setup_game) + .add_systems( + Update, + (movement, change_color).in_set(OnUpdate(AppState::InGame)), + ) .run(); } diff --git a/examples/ecs/system_closure.rs b/examples/ecs/system_closure.rs index 952d690b2b..7c6c077f64 100644 --- a/examples/ecs/system_closure.rs +++ b/examples/ecs/system_closure.rs @@ -27,15 +27,15 @@ fn main() { App::new() .add_plugin(LogPlugin::default()) // we can use a closure as a system - .add_system(simple_closure) + .add_systems(Update, simple_closure) // or we can use a more complex closure, and pass an argument to initialize a Local variable. - .add_system(complex_closure("foo".into())) + .add_systems(Update, complex_closure("foo".into())) // we can also inline a closure - .add_system(|| { + .add_systems(Update, || { info!("Hello from an inlined closure!"); }) // or use variables outside a closure - .add_system(move || { + .add_systems(Update, move || { info!( "Hello from an inlined closure that captured the 'outside_variable'! {:?}", outside_variable diff --git a/examples/ecs/system_param.rs b/examples/ecs/system_param.rs index 4304ec1699..ebe5c1713a 100644 --- a/examples/ecs/system_param.rs +++ b/examples/ecs/system_param.rs @@ -5,7 +5,8 @@ use bevy::{ecs::system::SystemParam, prelude::*}; fn main() { App::new() .insert_resource(PlayerCount(0)) - .add_systems((spawn.on_startup(), count_players)) + .add_systems(Startup, spawn) + .add_systems(Update, count_players) .run(); } diff --git a/examples/ecs/system_piping.rs b/examples/ecs/system_piping.rs index c0a772d6a4..c3416c3994 100644 --- a/examples/ecs/system_piping.rs +++ b/examples/ecs/system_piping.rs @@ -15,14 +15,17 @@ fn main() { level: Level::TRACE, filter: "".to_string(), }) - .add_systems(( - parse_message_system.pipe(handler_system), - data_pipe_system.pipe(info), - parse_message_system.pipe(dbg), - warning_pipe_system.pipe(warn), - parse_error_message_system.pipe(error), - parse_message_system.pipe(ignore), - )) + .add_systems( + Update, + ( + parse_message_system.pipe(handler_system), + data_pipe_system.pipe(info), + parse_message_system.pipe(dbg), + warning_pipe_system.pipe(warn), + parse_error_message_system.pipe(error), + parse_message_system.pipe(ignore), + ), + ) .run(); } diff --git a/examples/ecs/timers.rs b/examples/ecs/timers.rs index 8376f39a0b..7b3f09c808 100644 --- a/examples/ecs/timers.rs +++ b/examples/ecs/timers.rs @@ -6,7 +6,8 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .init_resource::() - .add_systems((setup.on_startup(), countdown, print_when_completed)) + .add_systems(Startup, setup) + .add_systems(Update, (countdown, print_when_completed)) .run(); } diff --git a/examples/games/alien_cake_addict.rs b/examples/games/alien_cake_addict.rs index 7fbc2961c8..ed9fb30529 100644 --- a/examples/games/alien_cake_addict.rs +++ b/examples/games/alien_cake_addict.rs @@ -17,18 +17,17 @@ struct BonusSpawnTimer(Timer); fn main() { App::new() + .add_plugins(DefaultPlugins) .init_resource::() .insert_resource(BonusSpawnTimer(Timer::from_seconds( 5.0, TimerMode::Repeating, ))) - .add_plugins(DefaultPlugins) .add_state::() - .add_systems(( - setup_cameras.on_startup(), - setup.in_schedule(OnEnter(GameState::Playing)), - )) + .add_systems(Startup, setup_cameras) + .add_systems(OnEnter(GameState::Playing), setup) .add_systems( + Update, ( move_player, focus_camera, @@ -38,13 +37,16 @@ fn main() { ) .in_set(OnUpdate(GameState::Playing)), ) - .add_systems(( - teardown.in_schedule(OnExit(GameState::Playing)), - display_score.in_schedule(OnEnter(GameState::GameOver)), - gameover_keyboard.in_set(OnUpdate(GameState::GameOver)), - teardown.in_schedule(OnExit(GameState::GameOver)), - bevy::window::close_on_esc, - )) + .add_systems(OnExit(GameState::Playing), teardown) + .add_systems(OnEnter(GameState::GameOver), display_score) + .add_systems( + Update, + ( + gameover_keyboard.in_set(OnUpdate(GameState::GameOver)), + bevy::window::close_on_esc, + ), + ) + .add_systems(OnExit(GameState::GameOver), teardown) .run(); } diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index 206da2fe4a..a10b194edb 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -52,10 +52,13 @@ fn main() { .add_plugins(DefaultPlugins) .insert_resource(Scoreboard { score: 0 }) .insert_resource(ClearColor(BACKGROUND_COLOR)) - .add_startup_system(setup) .add_event::() + // Configure how frequently our gameplay systems are run + .insert_resource(FixedTime::new_from_secs(1.0 / 60.0)) + .add_systems(Startup, setup) // Add our gameplay simulation systems to the fixed timestep schedule .add_systems( + FixedUpdate, ( check_for_collisions, apply_velocity.before(check_for_collisions), @@ -63,13 +66,9 @@ fn main() { .before(check_for_collisions) .after(apply_velocity), play_collision_sound.after(check_for_collisions), - ) - .in_schedule(CoreSchedule::FixedUpdate), + ), ) - // Configure how frequently our gameplay systems are run - .insert_resource(FixedTime::new_from_secs(1.0 / 60.0)) - .add_system(update_scoreboard) - .add_system(bevy::window::close_on_esc) + .add_systems(Update, (update_scoreboard, bevy::window::close_on_esc)) .run(); } diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index f18ae839f1..f76e0778f0 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -11,14 +11,17 @@ use std::{ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_startup_systems((setup_contributor_selection, setup)) - .add_systems(( - velocity_system, - move_system, - collision_system, - select_system, - )) .init_resource::() + .add_systems(Startup, (setup_contributor_selection, setup)) + .add_systems( + Update, + ( + velocity_system, + move_system, + collision_system, + select_system, + ), + ) .run(); } diff --git a/examples/games/game_menu.rs b/examples/games/game_menu.rs index 8dc39e701d..3e38e34f38 100644 --- a/examples/games/game_menu.rs +++ b/examples/games/game_menu.rs @@ -33,9 +33,9 @@ fn main() { // Insert as resource the initial value for the settings resources .insert_resource(DisplayQuality::Medium) .insert_resource(Volume(7)) - .add_startup_system(setup) // Declare the game state, whose starting value is determined by the `Default` trait .add_state::() + .add_systems(Startup, setup) // Adds the plugins for each state .add_plugin(splash::SplashPlugin) .add_plugin(menu::MenuPlugin) @@ -58,14 +58,13 @@ mod splash { impl Plugin for SplashPlugin { fn build(&self, app: &mut App) { // As this plugin is managing the splash screen, it will focus on the state `GameState::Splash` - app.add_systems(( + app // When entering the state, spawn everything needed for this screen - splash_setup.in_schedule(OnEnter(GameState::Splash)), + .add_systems(OnEnter(GameState::Splash), splash_setup) // While in this state, run the `countdown` system - countdown.in_set(OnUpdate(GameState::Splash)), + .add_systems(Update, countdown.in_set(OnUpdate(GameState::Splash))) // When exiting the state, despawn everything that was spawned for this screen - despawn_screen::.in_schedule(OnExit(GameState::Splash)), - )); + .add_systems(OnExit(GameState::Splash), despawn_screen::); } } @@ -131,11 +130,9 @@ mod game { impl Plugin for GamePlugin { fn build(&self, app: &mut App) { - app.add_systems(( - game_setup.in_schedule(OnEnter(GameState::Game)), - game.in_set(OnUpdate(GameState::Game)), - despawn_screen::.in_schedule(OnExit(GameState::Game)), - )); + app.add_systems(OnEnter(GameState::Game), game_setup) + .add_systems(Update, game.in_set(OnUpdate(GameState::Game))) + .add_systems(OnExit(GameState::Game), despawn_screen::); } } @@ -268,33 +265,47 @@ mod menu { // entering the `GameState::Menu` state. // Current screen in the menu is handled by an independent state from `GameState` .add_state::() - .add_system(menu_setup.in_schedule(OnEnter(GameState::Menu))) + .add_systems(OnEnter(GameState::Menu), menu_setup) // Systems to handle the main menu screen - .add_systems(( - main_menu_setup.in_schedule(OnEnter(MenuState::Main)), - despawn_screen::.in_schedule(OnExit(MenuState::Main)), - )) + .add_systems(OnEnter(MenuState::Main), main_menu_setup) + .add_systems(OnExit(MenuState::Main), despawn_screen::) // Systems to handle the settings menu screen - .add_systems(( - settings_menu_setup.in_schedule(OnEnter(MenuState::Settings)), - despawn_screen::.in_schedule(OnExit(MenuState::Settings)), - )) + .add_systems(OnEnter(MenuState::Settings), settings_menu_setup) + .add_systems( + OnExit(MenuState::Settings), + despawn_screen::, + ) // Systems to handle the display settings screen - .add_systems(( - display_settings_menu_setup.in_schedule(OnEnter(MenuState::SettingsDisplay)), - setting_button::.in_set(OnUpdate(MenuState::SettingsDisplay)), - despawn_screen:: - .in_schedule(OnExit(MenuState::SettingsDisplay)), - )) + .add_systems( + OnEnter(MenuState::SettingsDisplay), + display_settings_menu_setup, + ) + .add_systems( + Update, + ( + setting_button:: + .in_set(OnUpdate(MenuState::SettingsDisplay)), + ), + ) + .add_systems( + OnExit(MenuState::SettingsDisplay), + despawn_screen::, + ) // Systems to handle the sound settings screen - .add_systems(( - sound_settings_menu_setup.in_schedule(OnEnter(MenuState::SettingsSound)), + .add_systems(OnEnter(MenuState::SettingsSound), sound_settings_menu_setup) + .add_systems( + Update, setting_button::.in_set(OnUpdate(MenuState::SettingsSound)), - despawn_screen:: - .in_schedule(OnExit(MenuState::SettingsSound)), - )) + ) + .add_systems( + OnExit(MenuState::SettingsSound), + despawn_screen::, + ) // Common systems to all screens that handles buttons behaviour - .add_systems((menu_action, button_system).in_set(OnUpdate(GameState::Menu))); + .add_systems( + Update, + (menu_action, button_system).in_set(OnUpdate(GameState::Menu)), + ); } } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 3f09f5c4c7..9b5dc363de 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; fn main() { - App::new().add_system(hello_world_system).run(); + App::new().add_systems(Update, hello_world_system).run(); } fn hello_world_system() { diff --git a/examples/input/char_input_events.rs b/examples/input/char_input_events.rs index 1fbb7af53b..141f4ce806 100644 --- a/examples/input/char_input_events.rs +++ b/examples/input/char_input_events.rs @@ -5,7 +5,7 @@ use bevy::{prelude::*, window::ReceivedCharacter}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(print_char_event_system) + .add_systems(Update, print_char_event_system) .run(); } diff --git a/examples/input/gamepad_input.rs b/examples/input/gamepad_input.rs index 10adf077e7..9abc87bafd 100644 --- a/examples/input/gamepad_input.rs +++ b/examples/input/gamepad_input.rs @@ -5,7 +5,7 @@ use bevy::{input::gamepad::GamepadButton, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(gamepad_system) + .add_systems(Update, gamepad_system) .run(); } diff --git a/examples/input/gamepad_input_events.rs b/examples/input/gamepad_input_events.rs index a53976b196..1e21f7d0f3 100644 --- a/examples/input/gamepad_input_events.rs +++ b/examples/input/gamepad_input_events.rs @@ -10,7 +10,7 @@ use bevy::{ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_systems((gamepad_events, gamepad_ordered_events)) + .add_systems(Update, (gamepad_events, gamepad_ordered_events)) .run(); } diff --git a/examples/input/keyboard_input.rs b/examples/input/keyboard_input.rs index 27c2592927..1072e3695e 100644 --- a/examples/input/keyboard_input.rs +++ b/examples/input/keyboard_input.rs @@ -5,7 +5,7 @@ use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(keyboard_input_system) + .add_systems(Update, keyboard_input_system) .run(); } diff --git a/examples/input/keyboard_input_events.rs b/examples/input/keyboard_input_events.rs index 1353ed3587..bac3b68316 100644 --- a/examples/input/keyboard_input_events.rs +++ b/examples/input/keyboard_input_events.rs @@ -5,7 +5,7 @@ use bevy::{input::keyboard::KeyboardInput, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(print_keyboard_event_system) + .add_systems(Update, print_keyboard_event_system) .run(); } diff --git a/examples/input/keyboard_modifiers.rs b/examples/input/keyboard_modifiers.rs index a7242e7d5f..271e133d7a 100644 --- a/examples/input/keyboard_modifiers.rs +++ b/examples/input/keyboard_modifiers.rs @@ -5,7 +5,7 @@ use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(keyboard_input_system) + .add_systems(Update, keyboard_input_system) .run(); } diff --git a/examples/input/mouse_grab.rs b/examples/input/mouse_grab.rs index 988e6e4571..33e2574180 100644 --- a/examples/input/mouse_grab.rs +++ b/examples/input/mouse_grab.rs @@ -5,7 +5,7 @@ use bevy::{prelude::*, window::CursorGrabMode}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(grab_mouse) + .add_systems(Update, grab_mouse) .run(); } diff --git a/examples/input/mouse_input.rs b/examples/input/mouse_input.rs index 918a6713c2..0e812646ea 100644 --- a/examples/input/mouse_input.rs +++ b/examples/input/mouse_input.rs @@ -5,7 +5,7 @@ use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(mouse_click_system) + .add_systems(Update, mouse_click_system) .run(); } diff --git a/examples/input/mouse_input_events.rs b/examples/input/mouse_input_events.rs index 3d3cf40e34..c15a60b0e1 100644 --- a/examples/input/mouse_input_events.rs +++ b/examples/input/mouse_input_events.rs @@ -8,7 +8,7 @@ use bevy::{ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(print_mouse_events_system) + .add_systems(Update, print_mouse_events_system) .run(); } diff --git a/examples/input/text_input.rs b/examples/input/text_input.rs index e083f76d25..97e90ad033 100644 --- a/examples/input/text_input.rs +++ b/examples/input/text_input.rs @@ -9,14 +9,17 @@ use bevy::{input::keyboard::KeyboardInput, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_systems(( - setup_scene.on_startup(), - toggle_ime, - listen_ime_events, - listen_received_character_events, - listen_keyboard_input_events, - bubbling_text, - )) + .add_systems(Startup, setup_scene) + .add_systems( + Update, + ( + toggle_ime, + listen_ime_events, + listen_received_character_events, + listen_keyboard_input_events, + bubbling_text, + ), + ) .run(); } diff --git a/examples/input/touch_input.rs b/examples/input/touch_input.rs index b2427aebc4..e3fa437c56 100644 --- a/examples/input/touch_input.rs +++ b/examples/input/touch_input.rs @@ -5,7 +5,7 @@ use bevy::{input::touch::*, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(touch_system) + .add_systems(Update, touch_system) .run(); } diff --git a/examples/input/touch_input_events.rs b/examples/input/touch_input_events.rs index 665abd271e..b8e918334e 100644 --- a/examples/input/touch_input_events.rs +++ b/examples/input/touch_input_events.rs @@ -5,7 +5,7 @@ use bevy::{input::touch::*, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_system(touch_event_system) + .add_systems(Update, touch_event_system) .run(); } diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index 66ae669676..c964835211 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -12,8 +12,8 @@ fn main() { }), ..default() })) - .add_startup_systems((setup_scene, setup_music)) - .add_systems((touch_camera, button_handler)) + .add_systems(Startup, (setup_scene, setup_music)) + .add_systems(Update, (touch_camera, button_handler)) .run(); } diff --git a/examples/reflection/generic_reflection.rs b/examples/reflection/generic_reflection.rs index b91a4fa073..854bbb5ca1 100644 --- a/examples/reflection/generic_reflection.rs +++ b/examples/reflection/generic_reflection.rs @@ -8,7 +8,7 @@ fn main() { .add_plugins(DefaultPlugins) // You must manually register each instance of a generic type .register_type::>() - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/reflection/reflection.rs b/examples/reflection/reflection.rs index 447db38a3e..bd23d5a1a8 100644 --- a/examples/reflection/reflection.rs +++ b/examples/reflection/reflection.rs @@ -18,7 +18,7 @@ fn main() { .add_plugins(DefaultPlugins) .register_type::() .register_type::() - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index db9dc4b61d..584f45822a 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/reflection/trait_reflection.rs b/examples/reflection/trait_reflection.rs index c675abd003..218d0da98e 100644 --- a/examples/reflection/trait_reflection.rs +++ b/examples/reflection/trait_reflection.rs @@ -5,8 +5,8 @@ use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_startup_system(setup) .register_type::() + .add_systems(Startup, setup) .run(); } diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index 33a6c426ef..e6477d7ded 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -14,8 +14,11 @@ fn main() { })) .register_type::() .register_type::() - .add_startup_systems((save_scene_system, load_scene_system, infotext_system)) - .add_system(log_system) + .add_systems( + Startup, + (save_scene_system, load_scene_system, infotext_system), + ) + .add_systems(Update, log_system) .run(); } diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index cb5d09038a..c4add68af9 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -7,7 +7,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/shader/array_texture.rs b/examples/shader/array_texture.rs index 5066779818..ee2ebe3070 100644 --- a/examples/shader/array_texture.rs +++ b/examples/shader/array_texture.rs @@ -11,7 +11,8 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_systems((setup.on_startup(), create_array_texture)) + .add_systems(Startup, setup) + .add_systems(Update, create_array_texture) .run(); } diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 736e0b3f11..29a41b3e83 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -11,7 +11,7 @@ use bevy::{ render_graph::{self, RenderGraph}, render_resource::*, renderer::{RenderContext, RenderDevice}, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }, window::WindowPlugin, }; @@ -32,7 +32,7 @@ fn main() { ..default() })) .add_plugin(GameOfLifeComputePlugin) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } @@ -74,7 +74,7 @@ impl Plugin for GameOfLifeComputePlugin { let render_app = app.sub_app_mut(RenderApp); render_app .init_resource::() - .add_system(queue_bind_group.in_set(RenderSet::Queue)); + .add_systems(Render, queue_bind_group.in_set(RenderSet::Queue)); let mut render_graph = render_app.world.resource_mut::(); render_graph.add_node("game_of_life", GameOfLifeNode::default()); diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs index e98ca56a24..628e145f64 100644 --- a/examples/shader/custom_vertex_attribute.rs +++ b/examples/shader/custom_vertex_attribute.rs @@ -17,7 +17,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/shader/post_process_pass.rs b/examples/shader/post_process_pass.rs index ca91b7ee44..0bb3b8aaa4 100644 --- a/examples/shader/post_process_pass.rs +++ b/examples/shader/post_process_pass.rs @@ -39,9 +39,8 @@ fn main() { ..default() })) .add_plugin(PostProcessPlugin) - .add_startup_system(setup) - .add_system(rotate) - .add_system(update_settings) + .add_systems(Startup, setup) + .add_systems(Update, (rotate, update_settings)) .run(); } diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index ce62816fca..d23200c911 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -23,7 +23,8 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(Material2dPlugin::::default()) - .add_systems((setup.on_startup(), main_camera_cube_rotator_system)) + .add_systems(Startup, setup) + .add_systems(Update, main_camera_cube_rotator_system) .run(); } diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index 4735406ace..fe38783215 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -16,7 +16,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 902860aaa7..dd533e32af 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -19,7 +19,7 @@ use bevy::{ render_resource::*, renderer::RenderDevice, view::{ExtractedView, NoFrustumCulling}, - RenderApp, RenderSet, + Render, RenderApp, RenderSet, }, }; use bytemuck::{Pod, Zeroable}; @@ -28,7 +28,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(CustomMaterialPlugin) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } @@ -85,10 +85,13 @@ impl Plugin for CustomMaterialPlugin { .add_render_command::() .init_resource::() .init_resource::>() - .add_systems(( - queue_custom.in_set(RenderSet::Queue), - prepare_instance_buffers.in_set(RenderSet::Prepare), - )); + .add_systems( + Render, + ( + queue_custom.in_set(RenderSet::Queue), + prepare_instance_buffers.in_set(RenderSet::Prepare), + ), + ); } } diff --git a/examples/shader/shader_material.rs b/examples/shader/shader_material.rs index d501d6c092..ae8ea78e54 100644 --- a/examples/shader/shader_material.rs +++ b/examples/shader/shader_material.rs @@ -10,7 +10,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index 01409b2634..3afae668f2 100644 --- a/examples/shader/shader_material_glsl.rs +++ b/examples/shader/shader_material_glsl.rs @@ -16,7 +16,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index 71b73ae998..7ead718c14 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -10,7 +10,8 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) - .add_systems((setup.on_startup(), rotate_camera)) + .add_systems(Startup, setup) + .add_systems(Update, rotate_camera) .run(); } diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index 0271977128..07b7f1890a 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -28,7 +28,8 @@ fn main() { prepass_enabled: false, ..default() }) - .add_systems((setup.on_startup(), rotate, toggle_prepass_view)) + .add_systems(Startup, setup) + .add_systems(Update, (rotate, toggle_prepass_view)) .run(); } diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 94e96d7762..0a88718ca7 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -33,7 +33,7 @@ fn main() { } app.add_plugin(MaterialPlugin::::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index e4131e029f..83c1abfa56 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -43,14 +43,17 @@ fn main() { count: 0, color: Color::WHITE, }) - .add_systems(( - setup.on_startup(), - mouse_handler, - movement_system, - collision_system, - counter_system, - scheduled_spawner.in_schedule(CoreSchedule::FixedUpdate), - )) + .add_systems(Startup, setup) + .add_systems(FixedUpdate, scheduled_spawner) + .add_systems( + Update, + ( + mouse_handler, + movement_system, + collision_system, + counter_system, + ), + ) .insert_resource(FixedTime::new_from_secs(0.2)) .run(); } diff --git a/examples/stress_tests/many_animated_sprites.rs b/examples/stress_tests/many_animated_sprites.rs index a5d6b4e938..e933763f44 100644 --- a/examples/stress_tests/many_animated_sprites.rs +++ b/examples/stress_tests/many_animated_sprites.rs @@ -32,12 +32,15 @@ fn main() { }), ..default() })) - .add_systems(( - setup.on_startup(), - animate_sprite, - print_sprite_count, - move_camera.after(print_sprite_count), - )) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + animate_sprite, + print_sprite_count, + move_camera.after(print_sprite_count), + ), + ) .run(); } diff --git a/examples/stress_tests/many_buttons.rs b/examples/stress_tests/many_buttons.rs index 826aa5395c..f25175cd11 100644 --- a/examples/stress_tests/many_buttons.rs +++ b/examples/stress_tests/many_buttons.rs @@ -21,7 +21,8 @@ fn main() { .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) .init_resource::() - .add_systems((setup.on_startup(), button_system)) + .add_systems(Startup, setup) + .add_systems(Update, button_system) .run(); } diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index 4a87d6f4dc..6d0d7db3f1 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -30,7 +30,8 @@ fn main() { })) .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) - .add_systems((setup.on_startup(), move_camera, print_mesh_count)) + .add_systems(Startup, setup) + .add_systems(Update, (move_camera, print_mesh_count)) .run(); } diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index 2b42d8602f..9244861d8b 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -41,12 +41,15 @@ fn main() { color: Color::WHITE, brightness: 1.0, }) - .add_systems(( - setup.on_startup(), - setup_scene_once_loaded, - keyboard_animation_control, - update_fox_rings.after(keyboard_animation_control), - )) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + setup_scene_once_loaded, + keyboard_animation_control, + update_fox_rings.after(keyboard_animation_control), + ), + ) .run(); } diff --git a/examples/stress_tests/many_glyphs.rs b/examples/stress_tests/many_glyphs.rs index b5c7fc0f37..ec320e7d81 100644 --- a/examples/stress_tests/many_glyphs.rs +++ b/examples/stress_tests/many_glyphs.rs @@ -20,7 +20,7 @@ fn main() { })) .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index 0060ab354d..c00e8c6cf5 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -8,7 +8,7 @@ use bevy::{ math::{DVec2, DVec3}, pbr::{ExtractedPointLight, GlobalLightMeta}, prelude::*, - render::{camera::ScalingMode, RenderApp, RenderSet}, + render::{camera::ScalingMode, Render, RenderApp, RenderSet}, window::{PresentMode, WindowPlugin}, }; use rand::{thread_rng, Rng}; @@ -26,7 +26,8 @@ fn main() { })) .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) - .add_systems((setup.on_startup(), move_camera, print_light_count)) + .add_systems(Startup, setup) + .add_systems(Update, (move_camera, print_light_count)) .add_plugin(LogVisibleLights) .run(); } @@ -154,7 +155,7 @@ impl Plugin for LogVisibleLights { Err(_) => return, }; - render_app.add_system(print_visible_light_count.in_set(RenderSet::Prepare)); + render_app.add_systems(Render, print_visible_light_count.in_set(RenderSet::Prepare)); } } diff --git a/examples/stress_tests/many_sprites.rs b/examples/stress_tests/many_sprites.rs index f22359d6d9..7ab23d1b48 100644 --- a/examples/stress_tests/many_sprites.rs +++ b/examples/stress_tests/many_sprites.rs @@ -40,11 +40,11 @@ fn main() { }), ..default() })) - .add_systems(( - setup.on_startup(), - print_sprite_count, - move_camera.after(print_sprite_count), - )) + .add_systems(Startup, setup) + .add_systems( + Update, + (print_sprite_count, move_camera.after(print_sprite_count)), + ) .run(); } diff --git a/examples/stress_tests/text_pipeline.rs b/examples/stress_tests/text_pipeline.rs index f5396ca90c..6229772d00 100644 --- a/examples/stress_tests/text_pipeline.rs +++ b/examples/stress_tests/text_pipeline.rs @@ -20,7 +20,8 @@ fn main() { })) .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) - .add_systems((spawn.on_startup(), update_text_bounds)) + .add_systems(Startup, spawn) + .add_systems(Update, update_text_bounds) .run(); } diff --git a/examples/stress_tests/transform_hierarchy.rs b/examples/stress_tests/transform_hierarchy.rs index 785f225bef..47985fbbf6 100644 --- a/examples/stress_tests/transform_hierarchy.rs +++ b/examples/stress_tests/transform_hierarchy.rs @@ -185,10 +185,10 @@ fn main() { .insert_resource(cfg) .add_plugins(MinimalPlugins) .add_plugin(TransformPlugin::default()) - .add_startup_system(setup) + .add_systems(Startup, setup) // Updating transforms *must* be done before `CoreSet::PostUpdate` // or the hierarchy will momentarily be in an invalid state. - .add_system(update) + .add_systems(Update, update) .run(); } @@ -242,10 +242,10 @@ struct UpdateFilter { /// update component with some per-component value #[derive(Component)] -struct Update(f32); +struct UpdateValue(f32); /// update positions system -fn update(time: Res