Skip to content

Commit 82d0e84

Browse files
authored
Explicit execution order ambiguities API (bevyengine#1469)
Explicit execution order ambiguities API.
1 parent a5d2501 commit 82d0e84

File tree

4 files changed

+217
-3
lines changed

4 files changed

+217
-3
lines changed

crates/bevy_ecs/src/schedule/stage.rs

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,9 +482,20 @@ fn topological_order(
482482
/// Returns vector containing all pairs of indices of systems with ambiguous execution order.
483483
/// Systems must be topologically sorted beforehand.
484484
fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize)> {
485+
let mut ambiguity_set_labels = HashMap::default();
486+
for set in systems.iter().flat_map(|c| c.ambiguity_sets()) {
487+
let len = ambiguity_set_labels.len();
488+
ambiguity_set_labels.entry(set).or_insert(len);
489+
}
490+
let mut all_ambiguity_sets = Vec::<FixedBitSet>::with_capacity(systems.len());
485491
let mut all_dependencies = Vec::<FixedBitSet>::with_capacity(systems.len());
486492
let mut all_dependants = Vec::<FixedBitSet>::with_capacity(systems.len());
487493
for (index, container) in systems.iter().enumerate() {
494+
let mut ambiguity_sets = FixedBitSet::with_capacity(ambiguity_set_labels.len());
495+
for set in container.ambiguity_sets() {
496+
ambiguity_sets.insert(ambiguity_set_labels[set]);
497+
}
498+
all_ambiguity_sets.push(ambiguity_sets);
488499
let mut dependencies = FixedBitSet::with_capacity(systems.len());
489500
for &dependency in container.dependencies() {
490501
dependencies.union_with(&all_dependencies[dependency]);
@@ -522,7 +533,10 @@ fn find_ambiguities(systems: &[impl SystemContainer]) -> Vec<(usize, usize)> {
522533
for index_b in full_bitset.difference(&relations)
523534
/*.take(index_a)*/
524535
{
525-
if !processed.contains(index_b) && !systems[index_a].is_compatible(&systems[index_b]) {
536+
if !processed.contains(index_b)
537+
&& all_ambiguity_sets[index_a].is_disjoint(&all_ambiguity_sets[index_b])
538+
&& !systems[index_a].is_compatible(&systems[index_b])
539+
{
526540
ambiguities.push((index_a, index_b));
527541
}
528542
}
@@ -1208,6 +1222,27 @@ mod tests {
12081222
);
12091223
assert_eq!(ambiguities.len(), 2);
12101224

1225+
let mut stage = SystemStage::parallel()
1226+
.with_system(component.system().label("0"))
1227+
.with_system(
1228+
resource
1229+
.system()
1230+
.label("1")
1231+
.after("0")
1232+
.in_ambiguity_set("a"),
1233+
)
1234+
.with_system(empty.system().label("2"))
1235+
.with_system(component.system().label("3").after("2").before("4"))
1236+
.with_system(resource.system().label("4").in_ambiguity_set("a"));
1237+
stage.initialize_systems(&mut world, &mut resources);
1238+
stage.rebuild_orders_and_dependencies();
1239+
let ambiguities = find_ambiguities_labels(&stage.parallel);
1240+
assert!(
1241+
ambiguities.contains(&("0".into(), "3".into()))
1242+
|| ambiguities.contains(&("3".into(), "0".into()))
1243+
);
1244+
assert_eq!(ambiguities.len(), 1);
1245+
12111246
let mut stage = SystemStage::parallel()
12121247
.with_system(component.system().label("0").before("2"))
12131248
.with_system(component.system().label("1").before("2"))
@@ -1248,6 +1283,30 @@ mod tests {
12481283
);
12491284
assert_eq!(ambiguities.len(), 1);
12501285

1286+
let mut stage = SystemStage::parallel()
1287+
.with_system(component.system().label("0").before("1").before("2"))
1288+
.with_system(component.system().label("1").in_ambiguity_set("a"))
1289+
.with_system(component.system().label("2").in_ambiguity_set("a"))
1290+
.with_system(component.system().label("3").after("1").after("2"));
1291+
stage.initialize_systems(&mut world, &mut resources);
1292+
stage.rebuild_orders_and_dependencies();
1293+
let ambiguities = find_ambiguities_labels(&stage.parallel);
1294+
assert_eq!(ambiguities.len(), 0);
1295+
1296+
let mut stage = SystemStage::parallel()
1297+
.with_system(component.system().label("0").before("1").before("2"))
1298+
.with_system(component.system().label("1").in_ambiguity_set("a"))
1299+
.with_system(component.system().label("2").in_ambiguity_set("b"))
1300+
.with_system(component.system().label("3").after("1").after("2"));
1301+
stage.initialize_systems(&mut world, &mut resources);
1302+
stage.rebuild_orders_and_dependencies();
1303+
let ambiguities = find_ambiguities_labels(&stage.parallel);
1304+
assert!(
1305+
ambiguities.contains(&("1".into(), "2".into()))
1306+
|| ambiguities.contains(&("2".into(), "1".into()))
1307+
);
1308+
assert_eq!(ambiguities.len(), 1);
1309+
12511310
let mut stage = SystemStage::parallel()
12521311
.with_system(
12531312
component
@@ -1300,6 +1359,76 @@ mod tests {
13001359
);
13011360
assert_eq!(ambiguities.len(), 6);
13021361

1362+
let mut stage = SystemStage::parallel()
1363+
.with_system(
1364+
component
1365+
.system()
1366+
.label("0")
1367+
.before("1")
1368+
.before("2")
1369+
.before("3")
1370+
.before("4"),
1371+
)
1372+
.with_system(component.system().label("1").in_ambiguity_set("a"))
1373+
.with_system(component.system().label("2").in_ambiguity_set("a"))
1374+
.with_system(component.system().label("3").in_ambiguity_set("a"))
1375+
.with_system(component.system().label("4").in_ambiguity_set("a"))
1376+
.with_system(
1377+
component
1378+
.system()
1379+
.label("5")
1380+
.after("1")
1381+
.after("2")
1382+
.after("3")
1383+
.after("4"),
1384+
);
1385+
stage.initialize_systems(&mut world, &mut resources);
1386+
stage.rebuild_orders_and_dependencies();
1387+
let ambiguities = find_ambiguities_labels(&stage.parallel);
1388+
assert_eq!(ambiguities.len(), 0);
1389+
1390+
let mut stage = SystemStage::parallel()
1391+
.with_system(
1392+
component
1393+
.system()
1394+
.label("0")
1395+
.before("1")
1396+
.before("2")
1397+
.before("3")
1398+
.before("4"),
1399+
)
1400+
.with_system(component.system().label("1").in_ambiguity_set("a"))
1401+
.with_system(component.system().label("2").in_ambiguity_set("a"))
1402+
.with_system(
1403+
component
1404+
.system()
1405+
.label("3")
1406+
.in_ambiguity_set("a")
1407+
.in_ambiguity_set("b"),
1408+
)
1409+
.with_system(component.system().label("4").in_ambiguity_set("b"))
1410+
.with_system(
1411+
component
1412+
.system()
1413+
.label("5")
1414+
.after("1")
1415+
.after("2")
1416+
.after("3")
1417+
.after("4"),
1418+
);
1419+
stage.initialize_systems(&mut world, &mut resources);
1420+
stage.rebuild_orders_and_dependencies();
1421+
let ambiguities = find_ambiguities_labels(&stage.parallel);
1422+
assert!(
1423+
ambiguities.contains(&("1".into(), "4".into()))
1424+
|| ambiguities.contains(&("4".into(), "1".into()))
1425+
);
1426+
assert!(
1427+
ambiguities.contains(&("2".into(), "4".into()))
1428+
|| ambiguities.contains(&("4".into(), "2".into()))
1429+
);
1430+
assert_eq!(ambiguities.len(), 2);
1431+
13031432
let mut stage = SystemStage::parallel()
13041433
.with_system(empty.exclusive_system().label("0"))
13051434
.with_system(empty.exclusive_system().label("1").after("0"))
@@ -1349,5 +1478,44 @@ mod tests {
13491478
|| ambiguities.contains(&("5".into(), "2".into()))
13501479
);
13511480
assert_eq!(ambiguities.len(), 6);
1481+
1482+
let mut stage = SystemStage::parallel()
1483+
.with_system(empty.exclusive_system().label("0").before("1").before("3"))
1484+
.with_system(empty.exclusive_system().label("1").in_ambiguity_set("a"))
1485+
.with_system(empty.exclusive_system().label("2").after("1"))
1486+
.with_system(empty.exclusive_system().label("3").in_ambiguity_set("a"))
1487+
.with_system(empty.exclusive_system().label("4").after("3").before("5"))
1488+
.with_system(empty.exclusive_system().label("5").in_ambiguity_set("a"))
1489+
.with_system(empty.exclusive_system().label("6").after("2").after("5"));
1490+
stage.initialize_systems(&mut world, &mut resources);
1491+
stage.rebuild_orders_and_dependencies();
1492+
let ambiguities = find_ambiguities_labels(&stage.exclusive_at_start);
1493+
assert!(
1494+
ambiguities.contains(&("2".into(), "3".into()))
1495+
|| ambiguities.contains(&("3".into(), "2".into()))
1496+
);
1497+
assert!(
1498+
ambiguities.contains(&("1".into(), "4".into()))
1499+
|| ambiguities.contains(&("4".into(), "1".into()))
1500+
);
1501+
assert!(
1502+
ambiguities.contains(&("2".into(), "4".into()))
1503+
|| ambiguities.contains(&("4".into(), "2".into()))
1504+
);
1505+
assert!(
1506+
ambiguities.contains(&("2".into(), "5".into()))
1507+
|| ambiguities.contains(&("5".into(), "2".into()))
1508+
);
1509+
assert_eq!(ambiguities.len(), 4);
1510+
1511+
let mut stage = SystemStage::parallel()
1512+
.with_system(empty.exclusive_system().label("0").in_ambiguity_set("a"))
1513+
.with_system(empty.exclusive_system().label("1").in_ambiguity_set("a"))
1514+
.with_system(empty.exclusive_system().label("2").in_ambiguity_set("a"))
1515+
.with_system(empty.exclusive_system().label("3").in_ambiguity_set("a"));
1516+
stage.initialize_systems(&mut world, &mut resources);
1517+
stage.rebuild_orders_and_dependencies();
1518+
let ambiguities = find_ambiguities_labels(&stage.exclusive_at_start);
1519+
assert_eq!(ambiguities.len(), 0);
13521520
}
13531521
}

crates/bevy_ecs/src/schedule/system_container.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub(super) trait SystemContainer {
1010
fn label(&self) -> &Option<Cow<'static, str>>;
1111
fn before(&self) -> &[Cow<'static, str>];
1212
fn after(&self) -> &[Cow<'static, str>];
13+
fn ambiguity_sets(&self) -> &[Cow<'static, str>];
1314
fn is_compatible(&self, other: &Self) -> bool;
1415
}
1516

@@ -20,6 +21,7 @@ pub(super) struct ExclusiveSystemContainer {
2021
label: Option<Cow<'static, str>>,
2122
before: Vec<Cow<'static, str>>,
2223
after: Vec<Cow<'static, str>>,
24+
ambiguity_sets: Vec<Cow<'static, str>>,
2325
}
2426

2527
impl ExclusiveSystemContainer {
@@ -31,6 +33,7 @@ impl ExclusiveSystemContainer {
3133
label: descriptor.label,
3234
before: descriptor.before,
3335
after: descriptor.after,
36+
ambiguity_sets: descriptor.ambiguity_sets,
3437
}
3538
}
3639

@@ -72,6 +75,10 @@ impl SystemContainer for ExclusiveSystemContainer {
7275
&self.after
7376
}
7477

78+
fn ambiguity_sets(&self) -> &[Cow<'static, str>] {
79+
&self.ambiguity_sets
80+
}
81+
7582
fn is_compatible(&self, _: &Self) -> bool {
7683
false
7784
}
@@ -85,6 +92,7 @@ pub struct ParallelSystemContainer {
8592
label: Option<Cow<'static, str>>,
8693
before: Vec<Cow<'static, str>>,
8794
after: Vec<Cow<'static, str>>,
95+
ambiguity_sets: Vec<Cow<'static, str>>,
8896
}
8997

9098
impl SystemContainer for ParallelSystemContainer {
@@ -120,6 +128,10 @@ impl SystemContainer for ParallelSystemContainer {
120128
&self.after
121129
}
122130

131+
fn ambiguity_sets(&self) -> &[Cow<'static, str>] {
132+
&self.ambiguity_sets
133+
}
134+
123135
fn is_compatible(&self, other: &Self) -> bool {
124136
self.system()
125137
.component_access()
@@ -144,6 +156,7 @@ impl ParallelSystemContainer {
144156
label: descriptor.label,
145157
before: descriptor.before,
146158
after: descriptor.after,
159+
ambiguity_sets: descriptor.ambiguity_sets,
147160
}
148161
}
149162

crates/bevy_ecs/src/schedule/system_descriptor.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub struct ParallelSystemDescriptor {
7575
pub(crate) label: Option<Cow<'static, str>>,
7676
pub(crate) before: Vec<Cow<'static, str>>,
7777
pub(crate) after: Vec<Cow<'static, str>>,
78+
pub(crate) ambiguity_sets: Vec<Cow<'static, str>>,
7879
}
7980

8081
fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescriptor {
@@ -83,6 +84,7 @@ fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescrip
8384
label: None,
8485
before: Vec::new(),
8586
after: Vec::new(),
87+
ambiguity_sets: Vec::new(),
8688
}
8789
}
8890

@@ -95,6 +97,10 @@ pub trait ParallelSystemDescriptorCoercion {
9597

9698
/// Specifies that the system should run after the system with given label.
9799
fn after(self, label: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor;
100+
101+
/// Specifies that the system is exempt from execution order ambiguity detection
102+
/// with other systems in this set.
103+
fn in_ambiguity_set(self, set: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor;
98104
}
99105

100106
impl ParallelSystemDescriptorCoercion for ParallelSystemDescriptor {
@@ -112,6 +118,11 @@ impl ParallelSystemDescriptorCoercion for ParallelSystemDescriptor {
112118
self.after.push(label.into());
113119
self
114120
}
121+
122+
fn in_ambiguity_set(mut self, set: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor {
123+
self.ambiguity_sets.push(set.into());
124+
self
125+
}
115126
}
116127

117128
impl<S> ParallelSystemDescriptorCoercion for S
@@ -129,6 +140,10 @@ where
129140
fn after(self, label: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor {
130141
new_parallel_descriptor(Box::new(self)).after(label)
131142
}
143+
144+
fn in_ambiguity_set(self, set: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor {
145+
new_parallel_descriptor(Box::new(self)).in_ambiguity_set(set)
146+
}
132147
}
133148

134149
impl ParallelSystemDescriptorCoercion for BoxedSystem<(), ()> {
@@ -143,6 +158,10 @@ impl ParallelSystemDescriptorCoercion for BoxedSystem<(), ()> {
143158
fn after(self, label: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor {
144159
new_parallel_descriptor(self).after(label)
145160
}
161+
162+
fn in_ambiguity_set(self, set: impl Into<Cow<'static, str>>) -> ParallelSystemDescriptor {
163+
new_parallel_descriptor(self).in_ambiguity_set(set)
164+
}
146165
}
147166

148167
#[derive(Debug, Clone, Copy)]
@@ -158,6 +177,7 @@ pub struct ExclusiveSystemDescriptor {
158177
pub(crate) label: Option<Cow<'static, str>>,
159178
pub(crate) before: Vec<Cow<'static, str>>,
160179
pub(crate) after: Vec<Cow<'static, str>>,
180+
pub(crate) ambiguity_sets: Vec<Cow<'static, str>>,
161181
pub(crate) insertion_point: InsertionPoint,
162182
}
163183

@@ -167,6 +187,7 @@ fn new_exclusive_descriptor(system: Box<dyn ExclusiveSystem>) -> ExclusiveSystem
167187
label: None,
168188
before: Vec::new(),
169189
after: Vec::new(),
190+
ambiguity_sets: Vec::new(),
170191
insertion_point: InsertionPoint::AtStart,
171192
}
172193
}
@@ -181,6 +202,10 @@ pub trait ExclusiveSystemDescriptorCoercion {
181202
/// Specifies that the system should run after the system with given label.
182203
fn after(self, label: impl Into<Cow<'static, str>>) -> ExclusiveSystemDescriptor;
183204

205+
/// Specifies that the system is exempt from execution order ambiguity detection
206+
/// with other systems in this set.
207+
fn in_ambiguity_set(self, set: impl Into<Cow<'static, str>>) -> ExclusiveSystemDescriptor;
208+
184209
/// Specifies that the system should run with other exclusive systems at the start of stage.
185210
fn at_start(self) -> ExclusiveSystemDescriptor;
186211

@@ -208,6 +233,11 @@ impl ExclusiveSystemDescriptorCoercion for ExclusiveSystemDescriptor {
208233
self
209234
}
210235

236+
fn in_ambiguity_set(mut self, set: impl Into<Cow<'static, str>>) -> ExclusiveSystemDescriptor {
237+
self.ambiguity_sets.push(set.into());
238+
self
239+
}
240+
211241
fn at_start(mut self) -> ExclusiveSystemDescriptor {
212242
self.insertion_point = InsertionPoint::AtStart;
213243
self
@@ -240,6 +270,10 @@ where
240270
new_exclusive_descriptor(Box::new(self)).after(label)
241271
}
242272

273+
fn in_ambiguity_set(self, set: impl Into<Cow<'static, str>>) -> ExclusiveSystemDescriptor {
274+
new_exclusive_descriptor(Box::new(self)).in_ambiguity_set(set)
275+
}
276+
243277
fn at_start(self) -> ExclusiveSystemDescriptor {
244278
new_exclusive_descriptor(Box::new(self)).at_start()
245279
}

crates/bevy_transform/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ pub mod prelude {
77
}
88

99
use bevy_app::{prelude::*, startup_stage};
10-
use bevy_ecs::IntoSystem;
11-
use bevy_ecs::ParallelSystemDescriptorCoercion;
10+
use bevy_ecs::{IntoSystem, ParallelSystemDescriptorCoercion};
1211
use bevy_reflect::RegisterTypeBuilder;
1312
use prelude::{parent_update_system, Children, GlobalTransform, Parent, PreviousParent, Transform};
1413

0 commit comments

Comments
 (0)