Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System Stepping implemented as Resource #8453

Merged
merged 40 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dce6a9a
Add `Schedule.label`
dmlary Mar 27, 2023
8db83c3
WIP: Add bevy_ecs::schedule::stepping
dmlary Mar 27, 2023
4ac6dfa
Update `ScheduleExecutor` for stepping
dmlary Mar 28, 2023
6714bcf
Unify `Stepping` updates into single enum
dmlary Mar 29, 2023
e5661c7
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary Mar 29, 2023
031acd4
Stepping::continue_frame() works; test updates
dmlary Mar 31, 2023
2a96883
Stepping dynamic schedule order detection
dmlary Apr 1, 2023
9004864
combine build step list functions
dmlary Apr 2, 2023
545c5f0
move stepping frame cursor into Stepping
dmlary Apr 4, 2023
9d85b2e
WIP: add stepping to breakout
dmlary Apr 4, 2023
62fa980
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Apr 4, 2023
bca6c85
Added stepping to breakout
dmlary Apr 17, 2023
315a88c
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary Apr 17, 2023
625e690
Stepping cursor changed to use label & NodeId
dmlary Apr 20, 2023
4a6cea5
Add cursor tests, and Stepping _node() methods
dmlary Apr 21, 2023
16a9e59
Updates from code-review
dmlary Apr 22, 2023
a9ebeb5
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary Apr 22, 2023
1cb1c58
merge fixes, and CI fixes
dmlary Apr 22, 2023
4b8b507
Added `bevy_debug_stepping` feature
dmlary Apr 27, 2023
d746299
Fix breakout example so stepping help shows on start
dmlary Apr 27, 2023
8fb59a3
Merge branch 'main' into stepping-resource
dmlary Apr 27, 2023
5b3a77c
update docs/cargo_features.md
dmlary Apr 27, 2023
8ec0e20
`pub(crate)` for `Schedule::executor()`
dmlary Apr 27, 2023
bfd76a1
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary May 1, 2023
ed8db01
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary May 5, 2023
709a23b
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary May 21, 2023
2f8ba58
Merge remote-tracking branch 'upstream/main' into stepping-resource
dmlary Jun 8, 2023
0912a84
merge main
dmlary Oct 26, 2023
7e3f2ad
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Nov 29, 2023
806f0ad
stepping: cleanup clippy warning
dmlary Nov 29, 2023
383c6fc
fix for toml file
dmlary Nov 30, 2023
bd18fb4
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Dec 24, 2023
7a841c8
add system_stepping example; fix stepping issue
dmlary Jan 6, 2024
4242192
update examples/README.md; add LogPlugin to system_stepping
dmlary Jan 6, 2024
39b48f7
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Jan 6, 2024
5f88d6b
convert `bevy_internal` use in system_stepping example
dmlary Jan 6, 2024
7cc52fa
Fixes per code-review comments
dmlary Jan 6, 2024
3c767ab
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Jan 9, 2024
6cdc0c5
Merge remote-tracking branch 'upstream' into stepping-resource
dmlary Jan 28, 2024
b455384
Minor style tweaks
cart Feb 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ impl App {
schedule.add_systems(systems);
} else {
let mut new_schedule = Schedule::new();
new_schedule.set_label(schedule.dyn_clone());
new_schedule.add_systems(systems);
schedules.insert(schedule, new_schedule);
}
Expand Down Expand Up @@ -459,6 +460,7 @@ impl App {
schedule.configure_set(set);
} else {
let mut new_schedule = Schedule::new();
new_schedule.set_label(schedule.dyn_clone());
new_schedule.configure_set(set);
schedules.insert(schedule, new_schedule);
}
Expand All @@ -476,6 +478,7 @@ impl App {
schedule.configure_sets(sets);
} else {
let mut new_schedule = Schedule::new();
new_schedule.set_label(schedule.dyn_clone());
new_schedule.configure_sets(sets);
schedules.insert(schedule, new_schedule);
}
Expand Down Expand Up @@ -869,8 +872,11 @@ impl App {
/// # Warning
/// This method will overwrite any existing schedule at that label.
/// To avoid this behavior, use the `init_schedule` method instead.
pub fn add_schedule(&mut self, label: impl ScheduleLabel, schedule: Schedule) -> &mut Self {
pub fn add_schedule(&mut self, label: impl ScheduleLabel, mut schedule: Schedule) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if schedule.label().is_none() {
schedule.set_label(label.dyn_clone());
}
dmlary marked this conversation as resolved.
Show resolved Hide resolved
schedules.insert(label, schedule);

self
Expand All @@ -882,7 +888,9 @@ impl App {
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(&label) {
schedules.insert(label, Schedule::new());
let mut schedule = Schedule::new();
schedule.set_label(label.dyn_clone());
schedules.insert(label, schedule);
}
self
}
Expand Down Expand Up @@ -912,7 +920,9 @@ impl App {
let mut schedules = self.world.resource_mut::<Schedules>();

if schedules.get(&label).is_none() {
schedules.insert(label.dyn_clone(), Schedule::new());
let mut schedule = Schedule::new();
schedule.set_label(label.dyn_clone());
schedules.insert(label.dyn_clone(), schedule);
}

let schedule = schedules.get_mut(&label).unwrap();
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_app/src/main_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub struct MainSchedulePlugin;

impl Plugin for MainSchedulePlugin {
fn build(&self, app: &mut App) {
use bevy_ecs::schedule::{IntoSystemConfigs, Stepping};

// simple "facilitator" schedules benefit from simpler single threaded scheduling
let mut main_schedule = Schedule::new();
main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
Expand All @@ -163,6 +165,6 @@ impl Plugin for MainSchedulePlugin {
app.add_schedule(Main, main_schedule)
.add_schedule(RunFixedUpdateLoop, fixed_update_loop_schedule)
.init_resource::<MainScheduleOrder>()
.add_systems(Main, Main::run_main);
.add_systems(Main, (Stepping::begin_frame, Main::run_main).chain());
}
}
7 changes: 6 additions & 1 deletion crates/bevy_ecs/src/schedule/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ use crate::{
pub(super) trait SystemExecutor: Send + Sync {
fn kind(&self) -> ExecutorKind;
fn init(&mut self, schedule: &SystemSchedule);
fn run(&mut self, schedule: &mut SystemSchedule, world: &mut World);
fn run(
&mut self,
schedule: &mut SystemSchedule,
skip_systems: Option<FixedBitSet>,
world: &mut World,
);
fn set_apply_final_buffers(&mut self, value: bool);
}

Expand Down
31 changes: 30 additions & 1 deletion crates/bevy_ecs/src/schedule/executor/multi_threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,12 @@ impl SystemExecutor for MultiThreadedExecutor {
self.num_dependencies_remaining = Vec::with_capacity(sys_count);
}

fn run(&mut self, schedule: &mut SystemSchedule, world: &mut World) {
fn run(
&mut self,
schedule: &mut SystemSchedule,
skip_systems: Option<FixedBitSet>,
world: &mut World,
) {
// reset counts
self.num_systems = schedule.systems.len();
if self.num_systems == 0 {
Expand All @@ -179,6 +184,30 @@ impl SystemExecutor for MultiThreadedExecutor {
}
}

// If stepping is enabled, make sure we skip those systems that should
// not be run.
if let Some(mut skipped_systems) = skip_systems {
debug_assert_eq!(skipped_systems.len(), self.completed_systems.len());
// mark skipped systems as completed
self.completed_systems |= &skipped_systems;
self.num_completed_systems = self.completed_systems.count_ones(..);

// signal the dependencies for each of the skipped systems, as
// though they had run
for system_index in skipped_systems.ones() {
self.signal_dependents(system_index);
}

// Finally, we need to clear all skipped systems from the ready
// list.
//
// We invert the skipped system mask to get the list of systems
// that should be run. Then we bitwise AND it with the ready list,
// resulting in a list of ready systems that aren't skipped.
skipped_systems.toggle_range(..);
self.ready_systems &= skipped_systems;
}

let thread_executor = world
.get_resource::<MainThreadExecutor>()
.map(|e| e.0.clone());
Expand Down
14 changes: 13 additions & 1 deletion crates/bevy_ecs/src/schedule/executor/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,19 @@ impl SystemExecutor for SimpleExecutor {
self.completed_systems = FixedBitSet::with_capacity(sys_count);
}

fn run(&mut self, schedule: &mut SystemSchedule, world: &mut World) {
fn run(
&mut self,
schedule: &mut SystemSchedule,
skip_systems: Option<FixedBitSet>,
world: &mut World,
) {
// If stepping is enabled, make sure we skip those systems that should
// not be run.
if let Some(skipped_systems) = skip_systems {
// mark skipped systems as completed
self.completed_systems |= &skipped_systems;
}

for system_index in 0..schedule.systems.len() {
#[cfg(feature = "trace")]
let name = schedule.systems[system_index].name();
Expand Down
14 changes: 13 additions & 1 deletion crates/bevy_ecs/src/schedule/executor/single_threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,19 @@ impl SystemExecutor for SingleThreadedExecutor {
self.unapplied_systems = FixedBitSet::with_capacity(sys_count);
}

fn run(&mut self, schedule: &mut SystemSchedule, world: &mut World) {
fn run(
&mut self,
schedule: &mut SystemSchedule,
skip_systems: Option<FixedBitSet>,
world: &mut World,
) {
// If stepping is enabled, make sure we skip those systems that should
// not be run.
if let Some(skipped_systems) = skip_systems {
// mark skipped systems as completed
self.completed_systems |= &skipped_systems;
}

for system_index in 0..schedule.systems.len() {
#[cfg(feature = "trace")]
let name = schedule.systems[system_index].name();
Expand Down
99 changes: 99 additions & 0 deletions crates/bevy_ecs/src/schedule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod graph_utils;
mod schedule;
mod set;
mod state;
mod stepping;

pub use self::condition::*;
pub use self::config::*;
Expand Down Expand Up @@ -708,4 +709,102 @@ mod tests {
assert!(matches!(result, Err(ScheduleBuildError::Ambiguity)));
}
}

mod stepping {
use super::*;
use bevy_ecs::system::SystemState;

#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct TestSchedule;

/// verify the [`SimpleExecutor`] supports stepping
#[test]
fn simple_executor() {
// create a test schedule
let mut schedule = Schedule::default();
schedule
.set_label(TestSchedule)
.set_executor_kind(ExecutorKind::Simple)
.add_systems(|| panic!("test failed; system should not run"));

// Add our schedule to stepping & and enable stepping; this should
// prevent any systems in the schedule from running
let mut stepping = Stepping::default();
stepping.add_schedule(TestSchedule).enable();

// create a world, and add the stepping resource
let mut world = World::default();
world.insert_resource(stepping);

// start a new frame by running ihe begin_frame() system
let mut system_state: SystemState<Option<ResMut<Stepping>>> =
SystemState::new(&mut world);
let res = system_state.get_mut(&mut world);
Stepping::begin_frame(res);

// now run the schedule; this will panic if the executor doesn't
// handle stepping
schedule.run(&mut world);
}
dmlary marked this conversation as resolved.
Show resolved Hide resolved

/// verify the [`SingleThreadedExecutor`] supports stepping
#[test]
fn single_threaded_executor() {
// create a test schedule
let mut schedule = Schedule::default();
schedule
.set_label(TestSchedule)
.set_executor_kind(ExecutorKind::SingleThreaded)
.add_systems(|| panic!("test failed; system should not run"));

// Add our schedule to stepping & and enable stepping; this should
// prevent any systems in the schedule from running
let mut stepping = Stepping::default();
stepping.add_schedule(TestSchedule).enable();

// create a world, and add the stepping resource
let mut world = World::default();
world.insert_resource(stepping);

// start a new frame by running ihe begin_frame() system
let mut system_state: SystemState<Option<ResMut<Stepping>>> =
SystemState::new(&mut world);
let res = system_state.get_mut(&mut world);
Stepping::begin_frame(res);

// now run the schedule; this will panic if the executor doesn't
// handle stepping
schedule.run(&mut world);
}

/// verify the [`MultiThreadedExecutor`] supports stepping
#[test]
fn multi_threaded_executor() {
// create a test schedule
let mut schedule = Schedule::default();
schedule
.set_label(TestSchedule)
.set_executor_kind(ExecutorKind::MultiThreaded)
.add_systems(|| panic!("test failed; system should not run"));

// Add our schedule to stepping & and enable stepping; this should
// prevent any systems in the schedule from running
let mut stepping = Stepping::default();
stepping.add_schedule(TestSchedule).enable();

// create a world, and add the stepping resource
let mut world = World::default();
world.insert_resource(stepping);

// start a new frame by running ihe begin_frame() system
let mut system_state: SystemState<Option<ResMut<Stepping>>> =
SystemState::new(&mut world);
let res = system_state.get_mut(&mut world);
Stepping::begin_frame(res);

// now run the schedule; this will panic if the executor doesn't
// handle stepping
schedule.run(&mut world);
}
}
}
Loading