Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ecs/nondeterministic_system_order.rs
6592 views
1
//! By default, Bevy systems run in parallel with each other.
2
//! Unless the order is explicitly specified, their relative order is nondeterministic.
3
//!
4
//! In many cases, this doesn't matter and is in fact desirable!
5
//! Consider two systems, one which writes to resource A, and the other which writes to resource B.
6
//! By allowing their order to be arbitrary, we can evaluate them greedily, based on the data that is free.
7
//! Because their data accesses are **compatible**, there is no **observable** difference created based on the order they are run.
8
//!
9
//! But if instead we have two systems mutating the same data, or one reading it and the other mutating,
10
//! then the actual observed value will vary based on the nondeterministic order of evaluation.
11
//! These observable conflicts are called **system execution order ambiguities**.
12
//!
13
//! This example demonstrates how you might detect and resolve (or silence) these ambiguities.
14
15
use bevy::{
16
ecs::schedule::{LogLevel, ScheduleBuildSettings},
17
prelude::*,
18
};
19
20
fn main() {
21
App::new()
22
// We can modify the reporting strategy for system execution order ambiguities on a per-schedule basis.
23
// You must do this for each schedule you want to inspect; child schedules executed within an inspected
24
// schedule do not inherit this modification.
25
.edit_schedule(Update, |schedule| {
26
schedule.set_build_settings(ScheduleBuildSettings {
27
ambiguity_detection: LogLevel::Warn,
28
..default()
29
});
30
})
31
.init_resource::<A>()
32
.init_resource::<B>()
33
.add_systems(
34
Update,
35
(
36
// This pair of systems has an ambiguous order,
37
// as their data access conflicts, and there's no order between them.
38
reads_a,
39
writes_a,
40
// This pair of systems has conflicting data access,
41
// but it's resolved with an explicit ordering:
42
// the .after relationship here means that we will always double after adding.
43
adds_one_to_b,
44
doubles_b.after(adds_one_to_b),
45
// This system isn't ambiguous with adds_one_to_b,
46
// due to the transitive ordering created by our constraints:
47
// if A is before B is before C, then A must be before C as well.
48
reads_b.after(doubles_b),
49
// This system will conflict with all of our writing systems
50
// but we've silenced its ambiguity with adds_one_to_b.
51
// This should only be done in the case of clear false positives:
52
// leave a comment in your code justifying the decision!
53
reads_a_and_b.ambiguous_with(adds_one_to_b),
54
),
55
)
56
// Be mindful, internal ambiguities are reported too!
57
// If there are any ambiguities due solely to DefaultPlugins,
58
// or between DefaultPlugins and any of your third party plugins,
59
// please file a bug with the repo responsible!
60
// Only *you* can prevent nondeterministic bugs due to greedy parallelism.
61
.add_plugins(DefaultPlugins)
62
.run();
63
}
64
65
#[derive(Resource, Debug, Default)]
66
struct A(usize);
67
68
#[derive(Resource, Debug, Default)]
69
struct B(usize);
70
71
// Data access is determined solely on the basis of the types of the system's parameters
72
// Every implementation of the `SystemParam` and `WorldQuery` traits must declare which data is used
73
// and whether or not it is mutably accessed.
74
fn reads_a(_a: Res<A>) {}
75
76
fn writes_a(mut a: ResMut<A>) {
77
a.0 += 1;
78
}
79
80
fn adds_one_to_b(mut b: ResMut<B>) {
81
b.0 = b.0.saturating_add(1);
82
}
83
84
fn doubles_b(mut b: ResMut<B>) {
85
// This will overflow pretty rapidly otherwise
86
b.0 = b.0.saturating_mul(2);
87
}
88
89
fn reads_b(b: Res<B>) {
90
// This invariant is always true,
91
// because we've fixed the system order so doubling always occurs after adding.
92
assert!((b.0.is_multiple_of(2)) || (b.0 == usize::MAX));
93
}
94
95
fn reads_a_and_b(a: Res<A>, b: Res<B>) {
96
// Only display the first few steps to avoid burying the ambiguities in the console
97
if b.0 < 10 {
98
info!("{}, {}", a.0, b.0);
99
}
100
}
101
102