1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use crate::{
archetype,
hlist::define_null,
registry::Registry,
resource,
system::schedule::{
sendable::SendableWorld,
Stage,
},
World,
};
use fnv::FnvBuildHasher;
use hashbrown::HashMap;
define_null!();
/// The stages within a schedule.
pub trait Stages<
'a,
R,
Resources,
QueryIndicesLists,
ResourceViewsIndicesLists,
DisjointIndicesLists,
EntryIndicesLists,
EntryViewsFilterIndicesLists,
>: Send where
R: Registry,
Resources: resource::Resources,
{
/// A list of booleans indicating whether each task within the first stage has already been run.
type HasRun: Send;
/// Run all of the stages, parallelizing as much work as possible.
///
/// The parallelization strategy involves two parts:
///
/// 1. Compile-time scheduling: at compile time, tasks are split into stages, where all tasks
/// in a stage can always be run in parallel with each other, no matter the `World`.
/// 2. Run-time optimization: at run-time, component claims on archetype tables within the
/// `World` are tracked when scheduling a single stage. Then, any tasks within the next stage
/// whose borrowed components do not interfere with the tasks in the current stage's dynamic
/// claims are run as well.
fn run(&mut self, world: &mut World<R, Resources>, has_run: Self::HasRun);
/// Attempt to run as many tasks within the first stage in the list as possible as add-ons to
/// the previous stage.
///
/// `borrowed_archetypes` contains a set of dynamic claims that are already borrowed by the
/// previous stage. This method respects those claims when evaluating whether new tasks can be
/// executed.
///
/// # Safety
/// `borrowed_archetypes` must accurately represent the dynamic claims already made on the
/// component columns within `world`.
unsafe fn run_add_ons(
&mut self,
world: SendableWorld<R, Resources>,
borrowed_archetypes: HashMap<archetype::IdentifierRef<R>, R::Claims, FnvBuildHasher>,
resource_claims: Resources::Claims,
) -> Self::HasRun;
/// Creates a new default set of booleans to indicate that each task within the first stage has
/// not been run.
fn new_has_run() -> Self::HasRun;
}
impl<R, Resources> Stages<'_, R, Resources, Null, Null, Null, Null, Null> for Null
where
R: Registry,
Resources: resource::Resources,
{
type HasRun = Null;
fn run(&mut self, _world: &mut World<R, Resources>, _has_run: Self::HasRun) {}
unsafe fn run_add_ons(
&mut self,
_world: SendableWorld<R, Resources>,
_borrowed_archetypes: HashMap<archetype::IdentifierRef<R>, R::Claims, FnvBuildHasher>,
_resource_claims: Resources::Claims,
) -> Self::HasRun {
Null
}
fn new_has_run() -> Self::HasRun {
Null
}
}
impl<
'a,
R,
Resources,
T,
U,
QueryIndicesList,
QueryIndicesLists,
ResourceViewsIndicesList,
ResourceViewsIndicesLists,
DisjointIndicesList,
DisjointIndicesLists,
EntryIndicesList,
EntryIndicesLists,
EntryViewsFilterIndicesList,
EntryViewsFilterIndicesLists,
>
Stages<
'a,
R,
Resources,
(QueryIndicesList, QueryIndicesLists),
(ResourceViewsIndicesList, ResourceViewsIndicesLists),
(DisjointIndicesList, DisjointIndicesLists),
(EntryIndicesList, EntryIndicesLists),
(EntryViewsFilterIndicesList, EntryViewsFilterIndicesLists),
> for (T, U)
where
R: Registry,
Resources: resource::Resources,
T: Stage<
'a,
R,
Resources,
QueryIndicesList,
ResourceViewsIndicesList,
DisjointIndicesList,
EntryIndicesList,
EntryViewsFilterIndicesList,
>,
U: Stages<
'a,
R,
Resources,
QueryIndicesLists,
ResourceViewsIndicesLists,
DisjointIndicesLists,
EntryIndicesLists,
EntryViewsFilterIndicesLists,
>,
{
type HasRun = T::HasRun;
fn run(&mut self, world: &mut World<R, Resources>, has_run: Self::HasRun) {
// Each stage is run sequentially. The tasks within a stage are parallelized.
let next_has_run = self.0.run(
// SAFETY: The pointer provided here is unique, being created from a mutable reference.
unsafe { SendableWorld::new(world) },
HashMap::default(),
Resources::Claims::default(),
has_run,
&mut self.1,
);
self.1.run(world, next_has_run);
}
unsafe fn run_add_ons(
&mut self,
world: SendableWorld<R, Resources>,
borrowed_archetypes: HashMap<archetype::IdentifierRef<R>, R::Claims, FnvBuildHasher>,
resource_claims: Resources::Claims,
) -> Self::HasRun {
// SAFETY: The safety contract of this method call is upheld by the safety contract of this
// method.
unsafe {
self.0
.run_add_ons(world, borrowed_archetypes, resource_claims)
}
}
fn new_has_run() -> Self::HasRun {
T::new_has_run()
}
}