Skip to content

schedule v3 ("stageless") #4391

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3adb0e2
SemiSafeCell
maniwani Jul 30, 2022
e606f8a
Update access.rs
maniwani Jul 30, 2022
0539c83
Update system params
maniwani Jul 30, 2022
b47d610
Update system
maniwani Jul 30, 2022
8e95317
remove ExclusiveSystem* internals
maniwani Jul 30, 2022
26a443c
Add some tests to show soundness
maniwani Jul 30, 2022
b9f5cc8
Update fixed_timestep.rs
maniwani Jul 31, 2022
6bdbf7b
Update extract_param.rs
maniwani Jul 31, 2022
ebacaf1
deprecate `.exclusive_system()`
maniwani Jul 31, 2022
0ceb33b
fold trait into SystemParamState, bikeshed to MaybeUnsafeCell and Wor…
maniwani Jul 31, 2022
e84cdc2
move back to `bevy_ptr`
maniwani Jul 31, 2022
9c6bc0c
move check from init to construction
maniwani Jul 31, 2022
03cedaf
into_mut -> into_cell_ref
maniwani Aug 1, 2022
0de338a
better trait method doc
maniwani Aug 1, 2022
b0df01b
couple nits
maniwani Aug 1, 2022
2aa494a
copy test from #4343
Apr 11, 2022
339089d
convert to using a concurrent queue for interior mutability
Apr 11, 2022
6c721bf
remove optimization
Apr 12, 2022
6843a5e
remove if statement
Apr 12, 2022
77020a6
check locality of scope_on_scope for nested spawns
Apr 12, 2022
6b3cb51
update some comments
Apr 12, 2022
5b14cc6
transmute scope ref to &'scope to get rid of Arc
Apr 12, 2022
301de77
clarify safety comment
Apr 12, 2022
4ad24f8
add a test for UB
Apr 12, 2022
462fe27
copy lifetimes from std scope
Apr 13, 2022
dc1fd72
add compile fail test
Apr 13, 2022
ff76517
clean up comments
Apr 13, 2022
6c59216
change single threaded task pool type signatures to match changes
Apr 14, 2022
45f25b1
fix doc comment
Apr 14, 2022
43b1c17
change Arc to immutable reference
Apr 14, 2022
9801cb9
add another compile fail test
hymm Apr 16, 2022
b49bad6
match lifetime of return value to lifetimes of futures
hymm Apr 18, 2022
cd95da7
rework executor code for performance
hymm Apr 22, 2022
52bf907
add invariance of lifetimes to single threaded executor
hymm Apr 23, 2022
c2ada7c
add some docs about the lifetimes
hymm Apr 23, 2022
c2e0485
fix doc link
hymm Apr 23, 2022
7601ff7
fix some issues from rebase
hymm Apr 25, 2022
a0e4749
run cargo fmt
hymm Apr 25, 2022
92243db
fix new clippy error
hymm Jun 4, 2022
70e0831
revert change to executor
hymm Jun 10, 2022
a7e2890
fix rebase formatting issues
hymm Jun 10, 2022
312e198
fix merge nit
maniwani Aug 1, 2022
da6f527
add `is_empty` method for accesses
maniwani Aug 5, 2022
6a664b9
make `MaybeUnsafeCell<T>` `Send` when `T: Sync`
maniwani Aug 5, 2022
a400e1b
add schedule_v3 module
maniwani Aug 1, 2022
f5f80cd
change ticks
maniwani Aug 5, 2022
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
2 changes: 1 addition & 1 deletion benches/benches/bevy_ecs/scheduling/run_criteria.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy_ecs::{
component::Component,
prelude::{ParallelSystemDescriptorCoercion, Res, RunCriteriaDescriptorCoercion},
prelude::{IntoSystemDescriptor, Res, RunCriteriaDescriptorCoercion},
schedule::{ShouldRun, Stage, SystemStage},
system::Query,
world::World,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_animation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bevy_ecs::{
entity::Entity,
prelude::Component,
reflect::ReflectComponent,
schedule::ParallelSystemDescriptorCoercion,
schedule::IntoSystemDescriptor,
system::{Query, Res},
};
use bevy_hierarchy::Children;
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{CoreStage, Plugin, PluginGroup, PluginGroupBuilder, StartupSchedule,
pub use bevy_derive::AppLabel;
use bevy_ecs::{
event::{Event, Events},
prelude::{FromWorld, IntoExclusiveSystem},
prelude::FromWorld,
schedule::{
IntoSystemDescriptor, Schedule, ShouldRun, Stage, StageLabel, State, StateData, SystemSet,
SystemStage,
Expand Down Expand Up @@ -78,7 +78,7 @@ impl Default for App {

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

#[cfg(feature = "bevy_ci_testing")]
{
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ fxhash = "0.2"
downcast-rs = "1.2"
serde = "1"

petgraph = "0.6"
bitvec = "1"
thiserror = "1"

[dev-dependencies]
rand = "0.8"

Expand Down
32 changes: 30 additions & 2 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,30 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
fn apply(&mut self, world: &mut World) {
self.0.apply(world)
}

fn world_access_level() -> WorldAccessLevel {
let mut exclusive = false;
let mut shared = false;
#(
match #param_fetch::world_access_level() {
WorldAccessLevel::Exclusive => {
exclusive = true;
}
WorldAccessLevel::Shared => {
shared = true;
}
WorldAccessLevel::None => (),
}
)*

if exclusive {
WorldAccessLevel::Exclusive
} else if shared {
WorldAccessLevel::Shared
} else {
WorldAccessLevel::None
}
}
}


Expand All @@ -266,7 +290,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
unsafe fn get_param(
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'w World,
world: MaybeUnsafeCell<'w, World>,
change_tick: u32,
) -> Self::Item {
ParamSet {
Expand Down Expand Up @@ -404,14 +428,18 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
fn apply(&mut self, world: &mut #path::world::World) {
self.state.apply(world)
}

fn world_access_level() -> #path::system::WorldAccessLevel {
TSystemParamState::world_access_level()
}
}

impl #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> #where_clause {
type Item = #struct_name #ty_generics;
unsafe fn get_param(
state: &'s mut Self,
system_meta: &#path::system::SystemMeta,
world: &'w #path::world::World,
world: #path::system::MaybeUnsafeCell<'w, #path::world::World>,
change_tick: u32,
) -> Self::Item {
#struct_name {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ use std::{
/// }
/// #
/// # bevy_ecs::system::assert_is_system(setup);
/// # bevy_ecs::system::IntoExclusiveSystem::exclusive_system(exclusive_system);
/// # bevy_ecs::system::assert_is_system(exclusive_system);
/// ```
///
/// It can be used to refer to a specific entity to apply [`EntityCommands`], or to call [`Query::get`] (or similar methods) to access its components.
Expand Down
11 changes: 6 additions & 5 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod query;
#[cfg(feature = "bevy_reflect")]
pub mod reflect;
pub mod schedule;
pub mod schedule_v3;
pub mod storage;
pub mod system;
pub mod world;
Expand All @@ -34,13 +35,13 @@ pub mod prelude {
event::{EventReader, EventWriter, Events},
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
schedule::{
AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, Schedule, Stage,
StageLabel, State, SystemLabel, SystemSet, SystemStage,
AmbiguitySetLabel, IntoSystemDescriptor, RunCriteria, RunCriteriaDescriptorCoercion,
RunCriteriaLabel, Schedule, Stage, StageLabel, State, SystemLabel, SystemSet,
SystemStage,
},
system::{
Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend,
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System,
Commands, In, IntoChainSystem, IntoSystem, Local, NonSend, NonSendMut,
ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System,
SystemParamFunction,
},
world::{FromWorld, Mut, World},
Expand Down
95 changes: 86 additions & 9 deletions crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ pub struct Access<T: SparseSetIndex> {
reads_and_writes: FixedBitSet,
/// The exclusively-accessed elements.
writes: FixedBitSet,
/// Is `true` if this has access to all elements in the collection?
/// Is `true` if this has access to all elements in the collection.
/// This field is a performance optimization for `&World` (also harder to mess up for soundness).
reads_all: bool,
/// Is `true` if this has exclusive access to all elements in the collection.
/// This field is a performance optimization for `&mut World` (also harder to mess up for soundness).
writes_all: bool,
marker: PhantomData<T>,
}

impl<T: SparseSetIndex> Default for Access<T> {
fn default() -> Self {
Self {
reads_all: false,
writes_all: false,
reads_and_writes: Default::default(),
writes: Default::default(),
marker: PhantomData,
Expand Down Expand Up @@ -64,7 +68,11 @@ impl<T: SparseSetIndex> Access<T> {

/// Returns `true` if this can exclusively access the element given by `index`.
pub fn has_write(&self, index: T) -> bool {
self.writes.contains(index.sparse_set_index())
if self.writes_all {
true
} else {
self.writes.contains(index.sparse_set_index())
}
}

/// Sets this as having access to all indexed elements (i.e. `&World`).
Expand All @@ -77,16 +85,29 @@ impl<T: SparseSetIndex> Access<T> {
self.reads_all
}

/// Sets this as having exclusive access to all indexed elements (i.e. `&mut World`).
pub fn write_all(&mut self) {
self.reads_all = true;
self.writes_all = true;
}

/// Returns `true` if this has exclusive access to all indexed elements (i.e. `&mut World`).
pub fn has_write_all(&self) -> bool {
self.writes_all
}

/// Removes all accesses.
pub fn clear(&mut self) {
self.reads_all = false;
self.writes_all = false;
self.reads_and_writes.clear();
self.writes.clear();
}

/// Adds all access from `other`.
pub fn extend(&mut self, other: &Access<T>) {
self.reads_all = self.reads_all || other.reads_all;
self.writes_all = self.writes_all || other.writes_all;
self.reads_and_writes.union_with(&other.reads_and_writes);
self.writes.union_with(&other.writes);
}
Expand All @@ -96,6 +117,12 @@ impl<T: SparseSetIndex> Access<T> {
/// `Access` instances are incompatible if one can write
/// an element that the other can read or write.
pub fn is_compatible(&self, other: &Access<T>) -> bool {
// All systems make a `&World` reference before running to update change detection info.
// Since exclusive systems produce a `&mut World`, we cannot let other systems run.
if self.writes_all || other.writes_all {
return false;
}

// Only systems that do not write data are compatible with systems that operate on `&World`.
if self.reads_all {
return other.writes.count_ones(..) == 0;
Expand All @@ -112,15 +139,31 @@ impl<T: SparseSetIndex> Access<T> {
/// Returns a vector of elements that the access and `other` cannot access at the same time.
pub fn get_conflicts(&self, other: &Access<T>) -> Vec<T> {
let mut conflicts = FixedBitSet::default();
if self.reads_all {
conflicts.extend(other.writes.ones());

if self.writes_all {
conflicts.extend(other.reads_and_writes.ones());
}

if other.reads_all {
conflicts.extend(self.writes.ones());
if other.writes_all {
conflicts.extend(self.reads_and_writes.ones());
}

if !(self.writes_all || other.writes_all) {
match (self.reads_all, other.reads_all) {
(false, false) => {
conflicts.extend(self.writes.intersection(&other.reads_and_writes));
conflicts.extend(self.reads_and_writes.intersection(&other.writes));
}
(false, true) => {
conflicts.extend(self.writes.ones());
}
(true, false) => {
conflicts.extend(other.writes.ones());
}
(true, true) => (),
}
}
conflicts.extend(self.writes.intersection(&other.reads_and_writes));
conflicts.extend(self.reads_and_writes.intersection(&other.writes));

conflicts
.ones()
.map(SparseSetIndex::get_sparse_set_index)
Expand All @@ -143,6 +186,11 @@ impl<T: SparseSetIndex> Access<T> {
pub fn writes(&self) -> impl Iterator<Item = T> + '_ {
self.writes.ones().map(T::get_sparse_set_index)
}

/// Returns `true` if this does not have access to any elements in the collection.
pub fn is_empty(&self) -> bool {
self.reads_and_writes.count_ones(..) == 0 && !self.reads_all && !self.writes_all
}
}

/// An [`Access`] that has been filtered to include and exclude certain combinations of elements.
Expand Down Expand Up @@ -266,6 +314,16 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
pub fn read_all(&mut self) {
self.access.read_all();
}

/// Sets the underlying unfiltered access as having exclusive access to all indexed elements.
pub fn write_all(&mut self) {
self.access.write_all();
}

/// Returns `true` if this does not have access to any elements in the collection.
pub fn is_empty(&self) -> bool {
self.access.is_empty()
}
}

/// A collection of [`FilteredAccess`] instances.
Expand Down Expand Up @@ -306,6 +364,20 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
true
}

/// Returns `true` if this and `filtered_access` can be active at the same time.
pub fn is_compatible_single(&self, filtered_access: &FilteredAccess<T>) -> bool {
if self.combined_access.is_compatible(filtered_access.access()) {
return true;
}
for filtered in &self.filtered_accesses {
if !filtered.is_compatible(filtered_access) {
return false;
}
}

true
}

/// Returns a vector of elements that this set and `other` cannot access at the same time.
pub fn get_conflicts(&self, other: &FilteredAccessSet<T>) -> Vec<T> {
// if the unfiltered access is incompatible, must check each pair
Expand All @@ -320,7 +392,7 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
conflicts.into_iter().collect()
}

/// Returns a vector of elements that this set and `other` cannot access at the same time.
/// Returns a vector of elements that this set and `filtered_access` cannot access at the same time.
pub fn get_conflicts_single(&self, filtered_access: &FilteredAccess<T>) -> Vec<T> {
// if the unfiltered access is incompatible, must check each pair
let mut conflicts = HashSet::new();
Expand Down Expand Up @@ -349,6 +421,11 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
self.combined_access.clear();
self.filtered_accesses.clear();
}

/// Returns `true` if this does not have access to any elements in the collection.
pub fn is_empty(&self) -> bool {
self.combined_access.is_empty()
}
}

impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ecs/src/schedule/executor.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{schedule::ParallelSystemContainer, world::World};
use crate::{schedule::FunctionSystemContainer, world::World};
use downcast_rs::{impl_downcast, Downcast};

pub trait ParallelSystemExecutor: Downcast + Send + Sync {
/// Called by `SystemStage` whenever `systems` have been changed.
fn rebuild_cached_data(&mut self, systems: &[ParallelSystemContainer]);
fn rebuild_cached_data(&mut self, systems: &[FunctionSystemContainer]);

fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World);
fn run_systems(&mut self, systems: &mut [FunctionSystemContainer], world: &mut World);
}

impl_downcast!(ParallelSystemExecutor);
Expand All @@ -14,9 +14,9 @@ impl_downcast!(ParallelSystemExecutor);
pub struct SingleThreadedExecutor;

impl ParallelSystemExecutor for SingleThreadedExecutor {
fn rebuild_cached_data(&mut self, _: &[ParallelSystemContainer]) {}
fn rebuild_cached_data(&mut self, _: &[FunctionSystemContainer]) {}

fn run_systems(&mut self, systems: &mut [ParallelSystemContainer], world: &mut World) {
fn run_systems(&mut self, systems: &mut [FunctionSystemContainer], world: &mut World) {
for system in systems {
if system.should_run() {
#[cfg(feature = "trace")]
Expand Down
Loading