Skip to content

Commit dd9ee4d

Browse files
committed
Expose system accesses and filters in BRP schedule.graph
1 parent 434f3e9 commit dd9ee4d

4 files changed

Lines changed: 183 additions & 26 deletions

File tree

crates/bevy_dev_tools/src/schedule_data/serde.rs

Lines changed: 135 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ pub struct ScheduleData {
7171
pub conflicts: Vec<SystemConflict>,
7272
}
7373

74+
/// A newtype for the index of a component.
75+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord, Hash)]
76+
pub struct ComponentIndex(pub u32);
77+
7478
/// Data about a component type.
7579
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
7680
pub struct ComponentData {
@@ -89,7 +93,65 @@ pub struct SystemData {
8993
pub exclusive: bool,
9094
/// Whether this system has deferred buffers to apply.
9195
pub deferred: bool,
92-
// TODO: Store the conditions specific to this system.
96+
97+
/// Combined access for the system
98+
pub combined_access: AccessData,
99+
/// Filtered accesses for the system
100+
pub filtered_accesses: Vec<FilteredAccessData>,
101+
}
102+
103+
/// Access wrapper
104+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
105+
pub struct AccessData {
106+
read_and_writes: Vec<u32>,
107+
writes: Vec<u32>,
108+
read_and_writes_inverted: bool,
109+
writes_inverted: bool,
110+
archetypal: Vec<u32>,
111+
}
112+
113+
impl AccessData {
114+
fn new(value: &bevy_ecs::query::Access, trace: &mut ComponentTrace) -> Self {
115+
Self {
116+
read_and_writes: trace.get_indexes(value.read_and_writes().iter()),
117+
writes: trace.get_indexes(value.writes().iter()),
118+
read_and_writes_inverted: value.read_and_writes_inverted(),
119+
writes_inverted: value.writes_inverted(),
120+
archetypal: trace.get_indexes(value.archetypal().iter()),
121+
}
122+
}
123+
}
124+
125+
/// `AccessFilters` wrapper
126+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
127+
pub struct AccessFiltersData {
128+
with: Vec<u32>,
129+
without: Vec<u32>,
130+
}
131+
132+
/// `FilteredAccess` wrapper
133+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
134+
pub struct FilteredAccessData {
135+
access: AccessData,
136+
required: Vec<u32>,
137+
filter_sets: Vec<AccessFiltersData>,
138+
}
139+
140+
impl FilteredAccessData {
141+
fn new(value: &bevy_ecs::query::FilteredAccess, trace: &mut ComponentTrace) -> Self {
142+
Self {
143+
access: AccessData::new(value.access(), trace),
144+
required: trace.get_indexes(value.required().iter()),
145+
filter_sets: value
146+
.filter_sets()
147+
.iter()
148+
.map(|f| AccessFiltersData {
149+
with: trace.get_indexes(f.with().iter()),
150+
without: trace.get_indexes(f.without().iter()),
151+
})
152+
.collect(),
153+
}
154+
}
93155
}
94156

95157
/// Data about a particular system set.
@@ -145,6 +207,49 @@ pub enum AccessConflict {
145207
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
146208
pub struct SystemSetIndex(pub u32);
147209

210+
// Helper to build the components used by systems in this schedule
211+
struct ComponentTrace<'a> {
212+
world_components: &'a Components,
213+
component_id_to_index: HashMap<ComponentId, usize>,
214+
components: Vec<ComponentData>,
215+
}
216+
217+
impl<'a> ComponentTrace<'a> {
218+
fn with_world_components(world_components: &'a Components) -> Self {
219+
Self {
220+
world_components,
221+
component_id_to_index: HashMap::new(),
222+
components: vec![],
223+
}
224+
}
225+
226+
fn get_index(&mut self, id: ComponentId) -> u32 {
227+
match self.component_id_to_index.entry(id) {
228+
Entry::Occupied(entry) => *entry.get() as _,
229+
Entry::Vacant(entry) => {
230+
let component = self
231+
.world_components
232+
.get_info(id)
233+
.expect("the component has already been registered by the system");
234+
235+
self.components.push(ComponentData {
236+
name: format!("{}", component.name()),
237+
});
238+
239+
*entry.insert(self.components.len() - 1) as _
240+
}
241+
}
242+
}
243+
244+
fn get_indexes(&mut self, ids: impl Iterator<Item = ComponentId>) -> Vec<u32> {
245+
ids.map(|id| self.get_index(id)).collect()
246+
}
247+
248+
fn get_indexes3<'b>(&mut self, ids: impl Iterator<Item = &'b ComponentId>) -> Vec<u32> {
249+
ids.map(|id| self.get_index(*id)).collect()
250+
}
251+
}
252+
148253
impl ScheduleData {
149254
/// Creates the data from the underlying [`Schedule`].
150255
///
@@ -154,6 +259,8 @@ impl ScheduleData {
154259
world_components: &Components,
155260
build_metadata: Option<&ScheduleBuildMetadata>,
156261
) -> Result<Self, ExtractAppDataError> {
262+
let mut component_trace = ComponentTrace::with_world_components(world_components);
263+
157264
let graph = schedule.graph();
158265

159266
let mut system_key_to_index = HashMap::new();
@@ -169,14 +276,20 @@ impl ScheduleData {
169276
}
170277

171278
let systems = schedule
172-
.systems()
279+
.systems_with_access()
173280
.map_err(|_| {
174281
ExtractAppDataError::ScheduleNotInitialized(format!("{:?}", schedule.label()))
175282
})?
176283
.enumerate()
177-
.map(|(index, (key, system))| {
284+
.map(|(index, (key, system_with_access))| {
178285
system_key_to_index.insert(key, index);
179286

287+
let system = system_with_access.system();
288+
let access = system_with_access.access();
289+
290+
let combined_access = access.combined_access();
291+
let filtered_accesses = access.filtered_accesses();
292+
180293
let flags = system.flags();
181294

182295
SystemData {
@@ -185,6 +298,11 @@ impl ScheduleData {
185298
== core::any::TypeId::of::<ApplyDeferred>(),
186299
exclusive: flags.contains(SystemStateFlags::EXCLUSIVE),
187300
deferred: flags.contains(SystemStateFlags::DEFERRED),
301+
combined_access: AccessData::new(combined_access, &mut component_trace),
302+
filtered_accesses: filtered_accesses
303+
.iter()
304+
.map(|fa| FilteredAccessData::new(fa, &mut component_trace))
305+
.collect(),
188306
}
189307
})
190308
.collect();
@@ -258,9 +376,6 @@ impl ScheduleData {
258376
);
259377
}
260378

261-
let mut component_id_to_index = HashMap::<ComponentId, usize>::new();
262-
let mut components = vec![];
263-
264379
let conflicts = graph
265380
.conflicting_systems()
266381
.iter()
@@ -279,29 +394,14 @@ impl ScheduleData {
279394
// The systems conflict on the world if there's no particular component IDs.
280395
AccessConflict::World
281396
} else {
282-
AccessConflict::Components(
283-
conflicts
284-
.iter()
285-
.map(|id| match component_id_to_index.entry(*id) {
286-
Entry::Occupied(entry) => *entry.get() as _,
287-
Entry::Vacant(entry) => {
288-
let component = world_components.get_info(*id).expect(
289-
"the component has already been registered by the system",
290-
);
291-
292-
components.push(ComponentData {
293-
name: format!("{}", component.name()),
294-
});
295-
*entry.insert(components.len() - 1) as _
296-
}
297-
})
298-
.collect(),
299-
)
397+
AccessConflict::Components(component_trace.get_indexes3(conflicts.iter()))
300398
},
301399
}
302400
})
303401
.collect();
304402

403+
let components = component_trace.components;
404+
305405
Ok(Self {
306406
name: format!("{:?}", schedule.label()),
307407
components,
@@ -337,8 +437,9 @@ pub mod tests {
337437
use bevy_platform::collections::HashMap;
338438

339439
use crate::schedule_data::serde::{
340-
AccessConflict, AppData, ComponentData, ExtractAppDataError, ScheduleData, ScheduleIndex,
341-
SystemConflict, SystemData, SystemSetData, SystemSetIndex,
440+
AccessConflict, AccessData, AppData, ComponentData, ExtractAppDataError,
441+
FilteredAccessData, ScheduleData, ScheduleIndex, SystemConflict, SystemData, SystemSetData,
442+
SystemSetIndex,
342443
};
343444

344445
fn app_data_from_app(app: &mut App) -> Result<AppData, ExtractAppDataError> {
@@ -517,6 +618,8 @@ pub mod tests {
517618
apply_deferred: false,
518619
exclusive: false,
519620
deferred: false,
621+
combined_access: AccessData::default(),
622+
filtered_accesses: vec![],
520623
}
521624
}
522625

@@ -704,18 +807,24 @@ pub mod tests {
704807
apply_deferred: false,
705808
exclusive: false,
706809
deferred: true,
810+
combined_access: AccessData::default(),
811+
filtered_accesses: vec![],
707812
},
708813
SystemData {
709814
name: "a1".into(),
710815
apply_deferred: false,
711816
exclusive: false,
712817
deferred: true,
818+
combined_access: AccessData::default(),
819+
filtered_accesses: vec![],
713820
},
714821
SystemData {
715822
name: "apply_deferred".into(),
716823
apply_deferred: true,
717824
exclusive: true,
718825
deferred: false,
826+
combined_access: AccessData::default(),
827+
filtered_accesses: vec![],
719828
},
720829
simple_system("b0"),
721830
simple_system("b1"),

crates/bevy_ecs/src/query/access.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,30 @@ impl Access {
395395
AccessConflicts::Individual(conflicts)
396396
}
397397

398+
/// All accessed components, or forbidden components if
399+
/// `Self::component_read_and_writes_inverted` is set.
400+
pub fn read_and_writes(&self) -> &ComponentIdSet {
401+
&self.read_and_writes
402+
}
403+
404+
/// All exclusively-accessed components, or components that may not be
405+
/// exclusively accessed if `Self::component_writes_inverted` is set.
406+
pub fn writes(&self) -> &ComponentIdSet {
407+
&self.writes
408+
}
409+
410+
/// Is `true` if this component can read all components *except* those
411+
/// present in `Self::read_and_writes`.
412+
pub fn read_and_writes_inverted(&self) -> bool {
413+
self.read_and_writes_inverted
414+
}
415+
416+
/// Is `true` if this component can write to all components *except* those
417+
/// present in `Self::writes`.
418+
pub fn writes_inverted(&self) -> bool {
419+
self.writes_inverted
420+
}
421+
398422
/// Returns the indices of the components that this has an archetypal access to.
399423
///
400424
/// These are components whose values are not accessed (and thus will never cause conflicts),

crates/bevy_ecs/src/schedule/node.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ impl SystemWithAccess {
5555
pub fn system(&self) -> &ScheduleSystem {
5656
&self.system
5757
}
58+
59+
/// Access
60+
pub fn access(&self) -> &FilteredAccessSet {
61+
&self.access
62+
}
5863
}
5964

6065
impl System for SystemWithAccess {

crates/bevy_ecs/src/schedule/schedule.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,25 @@ impl Schedule {
708708
Ok(iter)
709709
}
710710

711+
/// Sys
712+
pub fn systems_with_access(
713+
&self,
714+
) -> Result<impl Iterator<Item = (SystemKey, &SystemWithAccess)> + Sized, ScheduleNotInitialized>
715+
{
716+
if !self.executor_initialized {
717+
return Err(ScheduleNotInitialized);
718+
}
719+
720+
let iter = self
721+
.executable
722+
.system_ids
723+
.iter()
724+
.zip(&self.executable.systems)
725+
.map(|(&node_id, system)| (node_id, system));
726+
727+
Ok(iter)
728+
}
729+
711730
/// Returns the number of systems in this schedule.
712731
pub fn systems_len(&self) -> usize {
713732
if !self.executor_initialized {

0 commit comments

Comments
 (0)