mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Fixed criteria-less systems being re-ran unnecessarily (#1754)
Fixes #1753. The problem was introduced while reworking the logic around stages' own criteria. Before #1675 they used to be stored and processed inline with the systems' criteria, and systems without criteria used that of their stage. After, criteria-less systems think they should run, always. This PR more or less restores previous behavior; a less cludge solution can wait until after 0.5 - ideally, until stageless. Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
bf053218bf
commit
500d7469e7
2 changed files with 133 additions and 84 deletions
|
@ -747,12 +747,6 @@ impl Stage for SystemStage {
|
|||
self.world_id = Some(world.id());
|
||||
}
|
||||
|
||||
if let ShouldRun::No | ShouldRun::NoAndCheckAgain =
|
||||
self.stage_run_criteria.should_run(world)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if self.systems_modified {
|
||||
self.initialize_systems(world);
|
||||
self.rebuild_orders_and_dependencies();
|
||||
|
@ -767,101 +761,127 @@ impl Stage for SystemStage {
|
|||
self.executor_modified = false;
|
||||
}
|
||||
|
||||
// Evaluate run criteria.
|
||||
for index in 0..self.run_criteria.len() {
|
||||
let (run_criteria, tail) = self.run_criteria.split_at_mut(index);
|
||||
let mut criteria = &mut tail[0];
|
||||
match &mut criteria.inner {
|
||||
RunCriteriaInner::Single(system) => criteria.should_run = system.run((), world),
|
||||
RunCriteriaInner::Piped {
|
||||
input: parent,
|
||||
system,
|
||||
..
|
||||
} => criteria.should_run = system.run(run_criteria[*parent].should_run, world),
|
||||
}
|
||||
}
|
||||
let mut run_stage_loop = true;
|
||||
while run_stage_loop {
|
||||
let should_run = self.stage_run_criteria.should_run(world);
|
||||
match should_run {
|
||||
ShouldRun::No => return,
|
||||
ShouldRun::NoAndCheckAgain => continue,
|
||||
ShouldRun::YesAndCheckAgain => (),
|
||||
ShouldRun::Yes => {
|
||||
run_stage_loop = false;
|
||||
}
|
||||
};
|
||||
|
||||
let mut has_work = true;
|
||||
while has_work {
|
||||
fn should_run(
|
||||
container: &impl SystemContainer,
|
||||
run_criteria: &[RunCriteriaContainer],
|
||||
) -> bool {
|
||||
matches!(
|
||||
container
|
||||
.run_criteria()
|
||||
.map(|index| run_criteria[index].should_run),
|
||||
None | Some(ShouldRun::Yes) | Some(ShouldRun::YesAndCheckAgain)
|
||||
)
|
||||
}
|
||||
|
||||
// Run systems that want to be at the start of stage.
|
||||
for container in &mut self.exclusive_at_start {
|
||||
if should_run(container, &self.run_criteria) {
|
||||
container.system_mut().run(world);
|
||||
// Evaluate system run criteria.
|
||||
for index in 0..self.run_criteria.len() {
|
||||
let (run_criteria, tail) = self.run_criteria.split_at_mut(index);
|
||||
let mut criteria = &mut tail[0];
|
||||
match &mut criteria.inner {
|
||||
RunCriteriaInner::Single(system) => criteria.should_run = system.run((), world),
|
||||
RunCriteriaInner::Piped {
|
||||
input: parent,
|
||||
system,
|
||||
..
|
||||
} => criteria.should_run = system.run(run_criteria[*parent].should_run, world),
|
||||
}
|
||||
}
|
||||
|
||||
// Run parallel systems using the executor.
|
||||
// TODO: hard dependencies, nested sets, whatever... should be evaluated here.
|
||||
for container in &mut self.parallel {
|
||||
container.should_run = should_run(container, &self.run_criteria);
|
||||
}
|
||||
self.executor.run_systems(&mut self.parallel, world);
|
||||
let mut run_system_loop = true;
|
||||
let mut default_should_run = ShouldRun::Yes;
|
||||
while run_system_loop {
|
||||
run_system_loop = false;
|
||||
|
||||
// Run systems that want to be between parallel systems and their command buffers.
|
||||
for container in &mut self.exclusive_before_commands {
|
||||
if should_run(container, &self.run_criteria) {
|
||||
container.system_mut().run(world);
|
||||
fn should_run(
|
||||
container: &impl SystemContainer,
|
||||
run_criteria: &[RunCriteriaContainer],
|
||||
default: ShouldRun,
|
||||
) -> bool {
|
||||
matches!(
|
||||
container
|
||||
.run_criteria()
|
||||
.map(|index| run_criteria[index].should_run)
|
||||
.unwrap_or(default),
|
||||
ShouldRun::Yes | ShouldRun::YesAndCheckAgain
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply parallel systems' buffers.
|
||||
for container in &mut self.parallel {
|
||||
if container.should_run {
|
||||
container.system_mut().apply_buffers(world);
|
||||
// Run systems that want to be at the start of stage.
|
||||
for container in &mut self.exclusive_at_start {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
container.system_mut().run(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run systems that want to be at the end of stage.
|
||||
for container in &mut self.exclusive_at_end {
|
||||
if should_run(container, &self.run_criteria) {
|
||||
container.system_mut().run(world);
|
||||
// Run parallel systems using the executor.
|
||||
// TODO: hard dependencies, nested sets, whatever... should be evaluated here.
|
||||
for container in &mut self.parallel {
|
||||
container.should_run =
|
||||
should_run(container, &self.run_criteria, default_should_run);
|
||||
}
|
||||
}
|
||||
self.executor.run_systems(&mut self.parallel, world);
|
||||
|
||||
// Check for old component and system change ticks
|
||||
self.check_change_ticks(world);
|
||||
// Run systems that want to be between parallel systems and their command buffers.
|
||||
for container in &mut self.exclusive_before_commands {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
container.system_mut().run(world);
|
||||
}
|
||||
}
|
||||
|
||||
// Reevaluate run criteria.
|
||||
has_work = false;
|
||||
let run_criteria = &mut self.run_criteria;
|
||||
for index in 0..run_criteria.len() {
|
||||
let (run_criteria, tail) = run_criteria.split_at_mut(index);
|
||||
let criteria = &mut tail[0];
|
||||
match criteria.should_run {
|
||||
ShouldRun::No => (),
|
||||
ShouldRun::Yes => criteria.should_run = ShouldRun::No,
|
||||
ShouldRun::YesAndCheckAgain | ShouldRun::NoAndCheckAgain => {
|
||||
match &mut criteria.inner {
|
||||
RunCriteriaInner::Single(system) => {
|
||||
criteria.should_run = system.run((), world)
|
||||
// Apply parallel systems' buffers.
|
||||
for container in &mut self.parallel {
|
||||
if container.should_run {
|
||||
container.system_mut().apply_buffers(world);
|
||||
}
|
||||
}
|
||||
|
||||
// Run systems that want to be at the end of stage.
|
||||
for container in &mut self.exclusive_at_end {
|
||||
if should_run(container, &self.run_criteria, default_should_run) {
|
||||
container.system_mut().run(world);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for old component and system change ticks
|
||||
self.check_change_ticks(world);
|
||||
|
||||
// Evaluate run criteria.
|
||||
let run_criteria = &mut self.run_criteria;
|
||||
for index in 0..run_criteria.len() {
|
||||
let (run_criteria, tail) = run_criteria.split_at_mut(index);
|
||||
let criteria = &mut tail[0];
|
||||
match criteria.should_run {
|
||||
ShouldRun::No => (),
|
||||
ShouldRun::Yes => criteria.should_run = ShouldRun::No,
|
||||
ShouldRun::YesAndCheckAgain | ShouldRun::NoAndCheckAgain => {
|
||||
match &mut criteria.inner {
|
||||
RunCriteriaInner::Single(system) => {
|
||||
criteria.should_run = system.run((), world)
|
||||
}
|
||||
RunCriteriaInner::Piped {
|
||||
input: parent,
|
||||
system,
|
||||
..
|
||||
} => {
|
||||
criteria.should_run =
|
||||
system.run(run_criteria[*parent].should_run, world)
|
||||
}
|
||||
}
|
||||
RunCriteriaInner::Piped {
|
||||
input: parent,
|
||||
system,
|
||||
..
|
||||
} => {
|
||||
criteria.should_run =
|
||||
system.run(run_criteria[*parent].should_run, world)
|
||||
match criteria.should_run {
|
||||
ShouldRun::Yes => {
|
||||
run_system_loop = true;
|
||||
}
|
||||
ShouldRun::YesAndCheckAgain | ShouldRun::NoAndCheckAgain => {
|
||||
run_system_loop = true;
|
||||
}
|
||||
ShouldRun::No => (),
|
||||
}
|
||||
}
|
||||
match criteria.should_run {
|
||||
ShouldRun::Yes | ShouldRun::YesAndCheckAgain => has_work = true,
|
||||
ShouldRun::No | ShouldRun::NoAndCheckAgain => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// after the first loop, default to not running systems without run criteria
|
||||
default_should_run = ShouldRun::No;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -652,4 +652,33 @@ mod test {
|
|||
&MyState::Final
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_1753() {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
enum AppState {
|
||||
Main,
|
||||
}
|
||||
|
||||
fn should_run_once(mut flag: ResMut<bool>, test_name: Res<&'static str>) {
|
||||
assert!(!*flag, "{:?}", *test_name);
|
||||
*flag = true;
|
||||
}
|
||||
|
||||
let mut world = World::new();
|
||||
world.insert_resource(State::new(AppState::Main));
|
||||
world.insert_resource(false);
|
||||
world.insert_resource("control");
|
||||
let mut stage = SystemStage::parallel().with_system(should_run_once.system());
|
||||
stage.run(&mut world);
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "after control");
|
||||
|
||||
world.insert_resource(false);
|
||||
world.insert_resource("test");
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system_set(State::<AppState>::get_driver())
|
||||
.with_system(should_run_once.system());
|
||||
stage.run(&mut world);
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "after test");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue