Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 1 addition & 31 deletions crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,20 +625,6 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
self.filtered_accesses.push(filtered_access);
}

/// Adds a read access without filters to the set.
pub(crate) fn add_unfiltered_read(&mut self, index: T) {
let mut filter = FilteredAccess::default();
filter.add_read(index);
self.add(filter);
}

/// Adds a write access without filters to the set.
pub(crate) fn add_unfiltered_write(&mut self, index: T) {
let mut filter = FilteredAccess::default();
filter.add_write(index);
self.add(filter);
}

/// Adds all of the accesses from the passed set to `self`.
pub fn extend(&mut self, filtered_access_set: FilteredAccessSet<T>) {
self.combined_access
Expand Down Expand Up @@ -676,7 +662,7 @@ impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
#[cfg(test)]
mod tests {
use crate::query::access::AccessFilters;
use crate::query::{Access, FilteredAccess, FilteredAccessSet};
use crate::query::{Access, FilteredAccess};
use fixedbitset::FixedBitSet;
use std::marker::PhantomData;

Expand Down Expand Up @@ -728,22 +714,6 @@ mod tests {
assert_eq!(access_d.get_conflicts(&access_c), vec![0]);
}

#[test]
fn filtered_combined_access() {
let mut access_a = FilteredAccessSet::<usize>::default();
access_a.add_unfiltered_read(1);

let mut filter_b = FilteredAccess::<usize>::default();
filter_b.add_write(1);

let conflicts = access_a.get_conflicts_single(&filter_b);
assert_eq!(
&conflicts,
&[1_usize],
"access_a: {access_a:?}, filter_b: {filter_b:?}"
);
}

#[test]
fn filtered_access_extend() {
let mut access_a = FilteredAccess::<usize>::default();
Expand Down
24 changes: 19 additions & 5 deletions crates/bevy_ecs/src/schedule/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,11 +1360,25 @@ impl ScheduleGraph {
if system_a.is_exclusive() || system_b.is_exclusive() {
conflicting_systems.push((a, b, Vec::new()));
} else {
let access_a = system_a.component_access();
let access_b = system_b.component_access();
if !access_a.is_compatible(access_b) {
let conflicts: Vec<_> = access_a
.get_conflicts(access_b)
let component_access_a = system_a.component_access();
let component_access_b = system_b.component_access();
if !component_access_a.is_compatible(component_access_b) {
let conflicts: Vec<_> = component_access_a
.get_conflicts(component_access_b)
.into_iter()
.filter(|id| !ignored_ambiguities.contains(id))
.collect();

if !conflicts.is_empty() {
conflicting_systems.push((a, b, conflicts));
}
}

let resource_access_a = system_a.resource_access();
let resource_access_b = system_b.resource_access();
if !resource_access_a.is_compatible(resource_access_b) {
let conflicts: Vec<_> = resource_access_a
.get_conflicts(resource_access_b)
.into_iter()
.filter(|id| !ignored_ambiguities.contains(id))
.collect();
Expand Down
9 changes: 8 additions & 1 deletion crates/bevy_ecs/src/system/adapter_system.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::borrow::Cow;

use super::{ReadOnlySystem, System};
use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCell};
use crate::{
query::Access, schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCell,
};

/// Customizes the behavior of an [`AdapterSystem`]
///
Expand Down Expand Up @@ -89,6 +91,11 @@ where
self.system.component_access()
}

#[inline]
fn resource_access(&self) -> &Access<crate::component::ComponentId> {
self.system.resource_access()
}

#[inline]
fn archetype_component_access(
&self,
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_ecs/src/system/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub struct CombinatorSystem<Func, A, B> {
a: A,
b: B,
name: Cow<'static, str>,
resource_access: Access<ComponentId>,
component_access: Access<ComponentId>,
archetype_component_access: Access<ArchetypeComponentId>,
}
Expand All @@ -128,6 +129,7 @@ impl<Func, A, B> CombinatorSystem<Func, A, B> {
a,
b,
name,
resource_access: Access::new(),
component_access: Access::new(),
archetype_component_access: Access::new(),
}
Expand All @@ -151,6 +153,11 @@ where
&self.component_access
}

#[inline]
fn resource_access(&self) -> &Access<ComponentId> {
&self.resource_access
}

fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.archetype_component_access
}
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_ecs/src/system/exclusive_function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ where
self.system_meta.component_access_set.combined_access()
}

#[inline]
fn resource_access(&self) -> &Access<ComponentId> {
&self.system_meta.resource_access
}

#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_meta.archetype_component_access
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use super::{In, IntoSystem, ReadOnlySystem, SystemBuilder};
#[derive(Clone)]
pub struct SystemMeta {
pub(crate) name: Cow<'static, str>,
pub(crate) resource_access: Access<ComponentId>,
pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
// NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
Expand All @@ -38,6 +39,7 @@ impl SystemMeta {
let name = std::any::type_name::<T>();
Self {
name: name.into(),
resource_access: Access::default(),
archetype_component_access: Access::default(),
component_access_set: FilteredAccessSet::default(),
is_send: true,
Expand Down Expand Up @@ -513,6 +515,11 @@ where
self.system_meta.component_access_set.combined_access()
}

#[inline]
fn resource_access(&self) -> &Access<ComponentId> {
&self.system_meta.resource_access
}

#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_meta.archetype_component_access
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,13 +1087,16 @@ mod tests {
x.initialize(&mut world);
y.initialize(&mut world);

let conflicts = x.component_access().get_conflicts(y.component_access());
let component_conflicts = x.component_access().get_conflicts(y.component_access());
let d_id = world.components().get_id(TypeId::of::<D>()).unwrap();
assert_eq!(component_conflicts, vec![d_id]);

let resource_conflicts = x.resource_access().get_conflicts(y.resource_access());
let b_id = world
.components()
.get_resource_id(TypeId::of::<B>())
.unwrap();
let d_id = world.components().get_id(TypeId::of::<D>()).unwrap();
assert_eq!(conflicts, vec![b_id, d_id]);
assert_eq!(resource_conflicts, vec![b_id]);
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/system/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub trait System: Send + Sync + 'static {
}
/// Returns the system's component [`Access`].
fn component_access(&self) -> &Access<ComponentId>;

/// Returns the system's resource [`Access`].
fn resource_access(&self) -> &Access<ComponentId>;

/// Returns the system's archetype component [`Access`].
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId>;
/// Returns true if the system is [`Send`].
Expand Down
47 changes: 25 additions & 22 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,16 +500,13 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
let component_id = world.components.init_resource::<T>();
world.initialize_resource_internal(component_id);

let combined_access = system_meta.component_access_set.combined_access();
assert!(
!combined_access.has_write(component_id),
!system_meta.resource_access.has_write(component_id),
"error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(),
system_meta.name,
);
system_meta
.component_access_set
.add_unfiltered_read(component_id);
system_meta.resource_access.add_read(component_id);

let archetype_component_id = world
.get_resource_archetype_component_id(component_id)
Expand Down Expand Up @@ -592,19 +589,17 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
let component_id = world.components.init_resource::<T>();
world.initialize_resource_internal(component_id);

let combined_access = system_meta.component_access_set.combined_access();
if combined_access.has_write(component_id) {
let access = &system_meta.resource_access;
if access.has_write(component_id) {
panic!(
"error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(), system_meta.name);
} else if combined_access.has_read(component_id) {
} else if access.has_read(component_id) {
panic!(
"error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(), system_meta.name);
}
system_meta
.component_access_set
.add_unfiltered_write(component_id);
system_meta.resource_access.add_write(component_id);

let archetype_component_id = world
.get_resource_archetype_component_id(component_id)
Expand Down Expand Up @@ -703,7 +698,16 @@ unsafe impl SystemParam for &'_ World {
{
panic!("&World conflicts with a previous mutable system parameter. Allowing this would break Rust's mutability rules");
}

system_meta.component_access_set.add(filtered_access);

let mut resource_access: Access<ComponentId> = Access::default();
resource_access.read_all();
if !system_meta.resource_access.is_compatible(&resource_access) {
panic!("&World conflicts with a previous mutable system parameter. Allowing this would break Rust's mutability rules");
}

system_meta.resource_access.extend(&resource_access);
}

unsafe fn get_param<'w, 's>(
Expand All @@ -725,6 +729,8 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> {
fn init_state(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
system_meta.component_access_set.read_all();
system_meta.component_access_set.write_all();
system_meta.resource_access.read_all();
system_meta.resource_access.write_all();
system_meta.set_has_deferred();
}

Expand Down Expand Up @@ -1130,16 +1136,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
let component_id = world.components.init_non_send::<T>();
world.initialize_non_send_internal(component_id);

let combined_access = system_meta.component_access_set.combined_access();
assert!(
!combined_access.has_write(component_id),
! system_meta.resource_access.has_write(component_id),
"error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(),
system_meta.name,
);
system_meta
.component_access_set
.add_unfiltered_read(component_id);

system_meta.resource_access.add_read(component_id);

let archetype_component_id = world
.get_non_send_archetype_component_id(component_id)
Expand Down Expand Up @@ -1219,19 +1223,18 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
let component_id = world.components.init_non_send::<T>();
world.initialize_non_send_internal(component_id);

let combined_access = system_meta.component_access_set.combined_access();
if combined_access.has_write(component_id) {
let resource_access = &system_meta.resource_access;
if resource_access.has_write(component_id) {
panic!(
"error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(), system_meta.name);
} else if combined_access.has_read(component_id) {
} else if resource_access.has_read(component_id) {
panic!(
"error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002",
std::any::type_name::<T>(), system_meta.name);
}
system_meta
.component_access_set
.add_unfiltered_write(component_id);

system_meta.resource_access.add_write(component_id);

let archetype_component_id = world
.get_non_send_archetype_component_id(component_id)
Expand Down