diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index ac28a8c29a11b..4b81fd455b495 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -23,19 +23,55 @@ pub const CHECK_TICK_THRESHOLD: u32 = 518_400_000; /// Changes stop being detected once they become this old. pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1); +/// Types that can read change detection information. +/// This change detection is controlled by [`DetectChangesMut`] types such as [`ResMut`]. +/// +/// ## Example +/// Using types that implement [`DetectChanges`], such as [`Res`], provide +/// a way to query if a value has been mutated in another system. +/// +/// ``` +/// use bevy_ecs::prelude::*; +/// +/// #[derive(Resource)] +/// struct MyResource(u32); +/// +/// fn my_system(mut resource: Res) { +/// if resource.is_changed() { +/// println!("My component was mutated!"); +/// } +/// } +/// ``` +pub trait DetectChanges { + /// Returns `true` if this value was added after the system last ran. + fn is_added(&self) -> bool; + + /// Returns `true` if this value was added or mutably dereferenced after the system last ran. + fn is_changed(&self) -> bool; + + /// Returns the change tick recording the previous time this data was changed. + /// + /// Note that components and resources are also marked as changed upon insertion. + /// + /// For comparison, the previous change tick of a system can be read using the + /// [`SystemChangeTick`](crate::system::SystemChangeTick) + /// [`SystemParam`](crate::system::SystemParam). + fn last_changed(&self) -> u32; +} + /// Types that implement reliable change detection. /// /// ## Example -/// Using types that implement [`DetectChanges`], such as [`ResMut`], provide +/// Using types that implement [`DetectChangesMut`], such as [`ResMut`], provide /// a way to query if a value has been mutated in another system. -/// Normally change detecting is triggered by either [`DerefMut`] or [`AsMut`], however -/// it can be manually triggered via [`DetectChanges::set_changed`]. +/// Normally change detection is triggered by either [`DerefMut`] or [`AsMut`], however +/// it can be manually triggered via [`set_if_neq`](`DetectChangesMut::set_changed`). /// /// To ensure that changes are only triggered when the value actually differs, /// check if the value would change before assignment, such as by checking that `new != old`. /// You must be *sure* that you are not mutably dereferencing in this process. /// -/// [`set_if_neq`](DetectChanges::set_if_neq) is a helper +/// [`set_if_neq`](DetectChangesMut::set_if_neq) is a helper /// method for this common functionality. /// /// ``` @@ -53,18 +89,12 @@ pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1); /// } /// ``` /// -pub trait DetectChanges { +pub trait DetectChangesMut: DetectChanges { /// The type contained within this smart pointer /// - /// For example, for `Res` this would be `T`. + /// For example, for `ResMut` this would be `T`. type Inner: ?Sized; - /// Returns `true` if this value was added after the system last ran. - fn is_added(&self) -> bool; - - /// Returns `true` if this value was added or mutably dereferenced after the system last ran. - fn is_changed(&self) -> bool; - /// Flags this value as having been changed. /// /// Mutably accessing this smart pointer will automatically flag this value as having been changed. @@ -73,21 +103,12 @@ pub trait DetectChanges { /// **Note**: This operation cannot be undone. fn set_changed(&mut self); - /// Returns the change tick recording the previous time this data was changed. - /// - /// Note that components and resources are also marked as changed upon insertion. - /// - /// For comparison, the previous change tick of a system can be read using the - /// [`SystemChangeTick`](crate::system::SystemChangeTick) - /// [`SystemParam`](crate::system::SystemParam). - fn last_changed(&self) -> u32; - /// Manually sets the change tick recording the previous time this data was mutated. /// /// # Warning /// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies. - /// If you merely want to flag this data as changed, use [`set_changed`](DetectChanges::set_changed) instead. - /// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChanges::bypass_change_detection) instead. + /// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead. + /// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead. fn set_last_changed(&mut self, last_change_tick: u32); /// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick. @@ -113,8 +134,6 @@ pub trait DetectChanges { macro_rules! change_detection_impl { ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => { impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> { - type Inner = $target; - #[inline] fn is_added(&self) -> bool { self.ticks @@ -129,6 +148,35 @@ macro_rules! change_detection_impl { .is_older_than(self.ticks.last_change_tick, self.ticks.change_tick) } + #[inline] + fn last_changed(&self) -> u32 { + self.ticks.last_change_tick + } + } + + impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> { + type Target = $target; + + #[inline] + fn deref(&self) -> &Self::Target { + self.value + } + } + + impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> { + #[inline] + fn as_ref(&self) -> &$target { + self.deref() + } + } + } +} + +macro_rules! change_detection_mut_impl { + ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => { + impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> { + type Inner = $target; + #[inline] fn set_changed(&mut self) { self.ticks @@ -136,11 +184,6 @@ macro_rules! change_detection_impl { .set_changed(self.ticks.change_tick); } - #[inline] - fn last_changed(&self) -> u32 { - self.ticks.last_change_tick - } - #[inline] fn set_last_changed(&mut self, last_change_tick: u32) { self.ticks.last_change_tick = last_change_tick @@ -165,15 +208,6 @@ macro_rules! change_detection_impl { } } - impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> { - type Target = $target; - - #[inline] - fn deref(&self) -> &Self::Target { - self.value - } - } - impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { @@ -182,13 +216,6 @@ macro_rules! change_detection_impl { } } - impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> { - #[inline] - fn as_ref(&self) -> &$target { - self.deref() - } - } - impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> { #[inline] fn as_mut(&mut self) -> &mut $target { @@ -214,12 +241,12 @@ macro_rules! impl_methods { #[doc = stringify!($name)] /// `, but you need a `Mut`. /// - /// Note that calling [`DetectChanges::set_last_changed`] on the returned value + /// Note that calling [`DetectChangesMut::set_last_changed`] on the returned value /// will not affect the original. pub fn reborrow(&mut self) -> Mut<'_, $target> { Mut { value: self.value, - ticks: Ticks { + ticks: TicksMut { added: self.ticks.added, changed: self.ticks.changed, last_change_tick: self.ticks.last_change_tick, @@ -231,7 +258,7 @@ macro_rules! impl_methods { /// Maps to an inner value by applying a function to the contained reference, without flagging a change. /// /// You should never modify the argument passed to the closure -- if you want to modify the data - /// without flagging a change, consider using [`DetectChanges::bypass_change_detection`] to make your intent explicit. + /// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit. /// /// ```rust /// # use bevy_ecs::prelude::*; @@ -275,14 +302,40 @@ macro_rules! impl_debug { }; } +#[derive(Clone)] pub(crate) struct Ticks<'a> { + pub(crate) added: &'a Tick, + pub(crate) changed: &'a Tick, + pub(crate) last_change_tick: u32, + pub(crate) change_tick: u32, +} + +impl<'a> Ticks<'a> { + /// # Safety + /// This should never alias the underlying ticks with a mutable one such as `TicksMut`. + #[inline] + pub(crate) unsafe fn from_tick_cells( + cells: TickCells<'a>, + last_change_tick: u32, + change_tick: u32, + ) -> Self { + Self { + added: cells.added.deref(), + changed: cells.changed.deref(), + last_change_tick, + change_tick, + } + } +} + +pub(crate) struct TicksMut<'a> { pub(crate) added: &'a mut Tick, pub(crate) changed: &'a mut Tick, pub(crate) last_change_tick: u32, pub(crate) change_tick: u32, } -impl<'a> Ticks<'a> { +impl<'a> TicksMut<'a> { /// # Safety /// This should never alias the underlying ticks. All access must be unique. #[inline] @@ -300,6 +353,71 @@ impl<'a> Ticks<'a> { } } +impl<'a> From> for Ticks<'a> { + fn from(ticks: TicksMut<'a>) -> Self { + Ticks { + added: ticks.added, + changed: ticks.changed, + last_change_tick: ticks.last_change_tick, + change_tick: ticks.change_tick, + } + } +} + +/// Shared borrow of a [`Resource`]. +/// +/// See the [`Resource`] documentation for usage. +/// +/// If you need a unique mutable borrow, use [`ResMut`] instead. +/// +/// # Panics +/// +/// Panics when used as a [`SystemParameter`](crate::system::SystemParam) if the resource does not exist. +/// +/// Use `Option>` instead if the resource might not always exist. +pub struct Res<'w, T: ?Sized + Resource> { + pub(crate) value: &'w T, + pub(crate) ticks: Ticks<'w>, +} + +impl<'w, T: Resource> Res<'w, T> { + // no it shouldn't clippy + #[allow(clippy::should_implement_trait)] + pub fn clone(this: &Self) -> Self { + Self { + value: this.value, + ticks: this.ticks.clone(), + } + } + + pub fn into_inner(self) -> &'w T { + self.value + } +} + +impl<'w, T: Resource> From> for Res<'w, T> { + fn from(res: ResMut<'w, T>) -> Self { + Self { + value: res.value, + ticks: res.ticks.into(), + } + } +} + +impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T> +where + &'a T: IntoIterator, +{ + type Item = <&'a T as IntoIterator>::Item; + type IntoIter = <&'a T as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.value.into_iter() + } +} +change_detection_impl!(Res<'w, T>, T, Resource); +impl_debug!(Res<'w, T>, Resource); + /// Unique mutable borrow of a [`Resource`]. /// /// See the [`Resource`] documentation for usage. @@ -313,7 +431,7 @@ impl<'a> Ticks<'a> { /// Use `Option>` instead if the resource might not always exist. pub struct ResMut<'a, T: ?Sized + Resource> { pub(crate) value: &'a mut T, - pub(crate) ticks: Ticks<'a>, + pub(crate) ticks: TicksMut<'a>, } impl<'w, 'a, T: Resource> IntoIterator for &'a ResMut<'w, T> @@ -342,6 +460,7 @@ where } change_detection_impl!(ResMut<'a, T>, T, Resource); +change_detection_mut_impl!(ResMut<'a, T>, T, Resource); impl_methods!(ResMut<'a, T>, T, Resource); impl_debug!(ResMut<'a, T>, Resource); @@ -370,10 +489,11 @@ impl<'a, T: Resource> From> for Mut<'a, T> { /// Use `Option>` instead if the resource might not always exist. pub struct NonSendMut<'a, T: ?Sized + 'static> { pub(crate) value: &'a mut T, - pub(crate) ticks: Ticks<'a>, + pub(crate) ticks: TicksMut<'a>, } change_detection_impl!(NonSendMut<'a, T>, T,); +change_detection_mut_impl!(NonSendMut<'a, T>, T,); impl_methods!(NonSendMut<'a, T>, T,); impl_debug!(NonSendMut<'a, T>,); @@ -388,10 +508,46 @@ impl<'a, T: 'static> From> for Mut<'a, T> { } } +/// Shared borrow of an entity's component with access to change detection. +/// Similar to [`Mut`] but is immutable and so doesn't require unique access. +pub struct Ref<'a, T: ?Sized> { + pub(crate) value: &'a T, + pub(crate) ticks: Ticks<'a>, +} + +impl<'a, T: ?Sized> Ref<'a, T> { + pub fn into_inner(self) -> &'a T { + self.value + } +} + +impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T> +where + &'a T: IntoIterator, +{ + type Item = <&'a T as IntoIterator>::Item; + type IntoIter = <&'a T as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.value.into_iter() + } +} +change_detection_impl!(Ref<'a, T>, T,); +impl_debug!(Ref<'a, T>,); + /// Unique mutable borrow of an entity's component pub struct Mut<'a, T: ?Sized> { pub(crate) value: &'a mut T, - pub(crate) ticks: Ticks<'a>, + pub(crate) ticks: TicksMut<'a>, +} + +impl<'a, T: ?Sized> From> for Ref<'a, T> { + fn from(mut_ref: Mut<'a, T>) -> Self { + Self { + value: mut_ref.value, + ticks: mut_ref.ticks.into(), + } + } } impl<'w, 'a, T> IntoIterator for &'a Mut<'w, T> @@ -420,6 +576,7 @@ where } change_detection_impl!(Mut<'a, T>, T,); +change_detection_mut_impl!(Mut<'a, T>, T,); impl_methods!(Mut<'a, T>, T,); impl_debug!(Mut<'a, T>,); @@ -433,13 +590,13 @@ impl_debug!(Mut<'a, T>,); /// or are defined outside of rust this can be used. pub struct MutUntyped<'a> { pub(crate) value: PtrMut<'a>, - pub(crate) ticks: Ticks<'a>, + pub(crate) ticks: TicksMut<'a>, } impl<'a> MutUntyped<'a> { /// Returns the pointer to the value, marking it as changed. /// - /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChanges::bypass_change_detection). + /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChangesMut::bypass_change_detection). #[inline] pub fn into_inner(mut self) -> PtrMut<'a> { self.set_changed(); @@ -449,13 +606,13 @@ impl<'a> MutUntyped<'a> { /// Returns a [`MutUntyped`] with a smaller lifetime. /// This is useful if you have `&mut MutUntyped`, but you need a `MutUntyped`. /// - /// Note that calling [`DetectChanges::set_last_changed`] on the returned value + /// Note that calling [`DetectChangesMut::set_last_changed`] on the returned value /// will not affect the original. #[inline] pub fn reborrow(&mut self) -> MutUntyped { MutUntyped { value: self.value.reborrow(), - ticks: Ticks { + ticks: TicksMut { added: self.ticks.added, changed: self.ticks.changed, last_change_tick: self.ticks.last_change_tick, @@ -466,7 +623,7 @@ impl<'a> MutUntyped<'a> { /// Returns a pointer to the value without taking ownership of this smart pointer, marking it as changed. /// - /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChanges::bypass_change_detection). + /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChangesMut::bypass_change_detection). #[inline] pub fn as_mut(&mut self) -> PtrMut<'_> { self.set_changed(); @@ -481,8 +638,6 @@ impl<'a> MutUntyped<'a> { } impl<'a> DetectChanges for MutUntyped<'a> { - type Inner = PtrMut<'a>; - #[inline] fn is_added(&self) -> bool { self.ticks @@ -498,13 +653,17 @@ impl<'a> DetectChanges for MutUntyped<'a> { } #[inline] - fn set_changed(&mut self) { - self.ticks.changed.set_changed(self.ticks.change_tick); + fn last_changed(&self) -> u32 { + self.ticks.last_change_tick } +} + +impl<'a> DetectChangesMut for MutUntyped<'a> { + type Inner = PtrMut<'a>; #[inline] - fn last_changed(&self) -> u32 { - self.ticks.last_change_tick + fn set_changed(&mut self) { + self.ticks.changed.set_changed(self.ticks.change_tick); } #[inline] @@ -545,7 +704,9 @@ mod tests { use crate::{ self as bevy_ecs, - change_detection::{Mut, NonSendMut, ResMut, Ticks, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE}, + change_detection::{ + Mut, NonSendMut, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE, + }, component::{Component, ComponentTicks, Tick}, query::ChangeTrackers, system::{IntoSystem, Query, System}, @@ -553,6 +714,7 @@ mod tests { }; use super::DetectChanges; + use super::DetectChangesMut; #[derive(Component, PartialEq)] struct C; @@ -656,7 +818,7 @@ mod tests { added: Tick::new(1), changed: Tick::new(2), }; - let ticks = Ticks { + let ticks = TicksMut { added: &mut component_ticks.added, changed: &mut component_ticks.changed, last_change_tick: 3, @@ -681,7 +843,7 @@ mod tests { added: Tick::new(1), changed: Tick::new(2), }; - let ticks = Ticks { + let ticks = TicksMut { added: &mut component_ticks.added, changed: &mut component_ticks.changed, last_change_tick: 3, @@ -710,7 +872,7 @@ mod tests { added: Tick::new(1), changed: Tick::new(2), }; - let ticks = Ticks { + let ticks = TicksMut { added: &mut component_ticks.added, changed: &mut component_ticks.changed, last_change_tick, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 02780adff3318..47d93beab837d 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -28,7 +28,7 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ bundle::Bundle, - change_detection::DetectChanges, + change_detection::{DetectChanges, DetectChangesMut}, component::Component, entity::Entity, event::{EventReader, EventWriter, Events}, diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 86eecfa6ec9bf..5733ffd262829 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,11 +1,11 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - change_detection::Ticks, + change_detection::{Ticks, TicksMut}, component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType, Tick}, entity::Entity, query::{Access, DebugCheckedUnwrap, FilteredAccess}, storage::{ComponentSparseSet, Table, TableRow}, - world::{Mut, World}, + world::{Mut, Ref, World}, }; use bevy_ecs_macros::all_tuples; pub use bevy_ecs_macros::WorldQuery; @@ -653,6 +653,167 @@ unsafe impl WorldQuery for &T { /// SAFETY: access is read only unsafe impl ReadOnlyWorldQuery for &T {} +#[doc(hidden)] +pub struct RefFetch<'w, T> { + // T::Storage = TableStorage + table_data: Option<( + ThinSlicePtr<'w, UnsafeCell>, + ThinSlicePtr<'w, UnsafeCell>, + ThinSlicePtr<'w, UnsafeCell>, + )>, + // T::Storage = SparseStorage + sparse_set: Option<&'w ComponentSparseSet>, + + last_change_tick: u32, + change_tick: u32, +} + +/// SAFETY: `Self` is the same as `Self::ReadOnly` +unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { + type Fetch<'w> = RefFetch<'w, T>; + type Item<'w> = Ref<'w, T>; + type ReadOnly = Self; + type State = ComponentId; + + fn shrink<'wlong: 'wshort, 'wshort>(item: Ref<'wlong, T>) -> Ref<'wshort, T> { + item + } + + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { + StorageType::Table => true, + StorageType::SparseSet => false, + } + }; + + const IS_ARCHETYPAL: bool = true; + + unsafe fn init_fetch<'w>( + world: &'w World, + &component_id: &ComponentId, + last_change_tick: u32, + change_tick: u32, + ) -> RefFetch<'w, T> { + RefFetch { + table_data: None, + sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { + world + .storages() + .sparse_sets + .get(component_id) + .debug_checked_unwrap() + }), + last_change_tick, + change_tick, + } + } + + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + RefFetch { + table_data: fetch.table_data, + sparse_set: fetch.sparse_set, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut RefFetch<'w, T>, + component_id: &ComponentId, + _archetype: &'w Archetype, + table: &'w Table, + ) { + if Self::IS_DENSE { + Self::set_table(fetch, component_id, table); + } + } + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut RefFetch<'w, T>, + &component_id: &ComponentId, + table: &'w Table, + ) { + let column = table.get_column(component_id).debug_checked_unwrap(); + fetch.table_data = Some(( + column.get_data_slice().into(), + column.get_added_ticks_slice().into(), + column.get_changed_ticks_slice().into(), + )); + } + + #[inline(always)] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + ) -> Self::Item<'w> { + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + let (table_components, added_ticks, changed_ticks) = + fetch.table_data.debug_checked_unwrap(); + Ref { + value: table_components.get(table_row.index()).deref(), + ticks: Ticks { + added: added_ticks.get(table_row.index()).deref(), + changed: changed_ticks.get(table_row.index()).deref(), + change_tick: fetch.change_tick, + last_change_tick: fetch.last_change_tick, + }, + } + } + StorageType::SparseSet => { + let (component, ticks) = fetch + .sparse_set + .debug_checked_unwrap() + .get_with_ticks(entity) + .debug_checked_unwrap(); + Ref { + value: component.deref(), + ticks: Ticks::from_tick_cells(ticks, fetch.last_change_tick, fetch.change_tick), + } + } + } + } + + fn update_component_access( + &component_id: &ComponentId, + access: &mut FilteredAccess, + ) { + assert!( + !access.access().has_write(component_id), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component_id); + } + + fn update_archetype_component_access( + &component_id: &ComponentId, + archetype: &Archetype, + access: &mut Access, + ) { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component_id) { + access.add_read(archetype_component_id); + } + } + + fn init_state(world: &mut World) -> ComponentId { + world.init_component::() + } + + fn matches_component_set( + &state: &ComponentId, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + set_contains_id(state) + } +} + +/// SAFETY: access is read only +unsafe impl<'__w, T: Component> ReadOnlyWorldQuery for Ref<'__w, T> {} + #[doc(hidden)] pub struct WriteFetch<'w, T> { // T::Storage = TableStorage @@ -755,7 +916,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { fetch.table_data.debug_checked_unwrap(); Mut { value: table_components.get(table_row.index()).deref_mut(), - ticks: Ticks { + ticks: TicksMut { added: added_ticks.get(table_row.index()).deref_mut(), changed: changed_ticks.get(table_row.index()).deref_mut(), change_tick: fetch.change_tick, @@ -771,7 +932,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { .debug_checked_unwrap(); Mut { value: component.assert_unique().deref_mut(), - ticks: Ticks::from_tick_cells(ticks, fetch.last_change_tick, fetch.change_tick), + ticks: TicksMut::from_tick_cells( + ticks, + fetch.last_change_tick, + fetch.change_tick, + ), } } } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index aaa46d1209cbd..5fd37e11534b4 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -130,15 +130,14 @@ pub fn assert_is_system>(sys: S) mod tests { use std::any::TypeId; - use crate::prelude::StageLabel; - use crate::{ self as bevy_ecs, archetype::{ArchetypeComponentId, Archetypes}, bundle::Bundles, + change_detection::DetectChanges, component::{Component, Components}, entity::{Entities, Entity}, - prelude::AnyOf, + prelude::{AnyOf, StageLabel}, query::{Added, Changed, Or, With, Without}, schedule::{Schedule, Stage, SystemStage}, system::{ diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index a229925eca6fc..1699358810604 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1,9 +1,9 @@ -pub use crate::change_detection::{NonSendMut, ResMut}; +pub use crate::change_detection::{NonSendMut, Res, ResMut}; use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, - change_detection::Ticks, - component::{Component, ComponentId, ComponentTicks, Components, Tick}, + change_detection::{Ticks, TicksMut}, + component::{Component, ComponentId, ComponentTicks, Components}, entity::{Entities, Entity}, query::{ Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery, @@ -404,103 +404,6 @@ impl_param_set!(); /// ``` pub trait Resource: Send + Sync + 'static {} -/// Shared borrow of a [`Resource`]. -/// -/// See the [`Resource`] documentation for usage. -/// -/// If you need a unique mutable borrow, use [`ResMut`] instead. -/// -/// # Panics -/// -/// Panics when used as a [`SystemParameter`](SystemParam) if the resource does not exist. -/// -/// Use `Option>` instead if the resource might not always exist. -pub struct Res<'w, T: Resource> { - value: &'w T, - added: &'w Tick, - changed: &'w Tick, - last_change_tick: u32, - change_tick: u32, -} - -impl<'w, T: Resource> Debug for Res<'w, T> -where - T: Debug, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("Res").field(&self.value).finish() - } -} - -impl<'w, T: Resource> Res<'w, T> { - // no it shouldn't clippy - #[allow(clippy::should_implement_trait)] - pub fn clone(this: &Self) -> Self { - Self { - value: this.value, - added: this.added, - changed: this.changed, - last_change_tick: this.last_change_tick, - change_tick: this.change_tick, - } - } - - /// Returns `true` if the resource was added after the system last ran. - pub fn is_added(&self) -> bool { - self.added - .is_older_than(self.last_change_tick, self.change_tick) - } - - /// Returns `true` if the resource was added or mutably dereferenced after the system last ran. - pub fn is_changed(&self) -> bool { - self.changed - .is_older_than(self.last_change_tick, self.change_tick) - } - - pub fn into_inner(self) -> &'w T { - self.value - } -} - -impl<'w, T: Resource> Deref for Res<'w, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.value - } -} - -impl<'w, T: Resource> AsRef for Res<'w, T> { - #[inline] - fn as_ref(&self) -> &T { - self.deref() - } -} - -impl<'w, T: Resource> From> for Res<'w, T> { - fn from(res: ResMut<'w, T>) -> Self { - Self { - value: res.value, - added: res.ticks.added, - changed: res.ticks.changed, - change_tick: res.ticks.change_tick, - last_change_tick: res.ticks.last_change_tick, - } - } -} - -impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T> -where - &'a T: IntoIterator, -{ - type Item = <&'a T as IntoIterator>::Item; - type IntoIter = <&'a T as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.value.into_iter() - } -} - // SAFETY: Res only reads a single World resource unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} @@ -551,10 +454,12 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { }); Res { value: ptr.deref(), - added: ticks.added.deref(), - changed: ticks.changed.deref(), - last_change_tick: system_meta.last_change_tick, - change_tick, + ticks: Ticks { + added: ticks.added.deref(), + changed: ticks.changed.deref(), + last_change_tick: system_meta.last_change_tick, + change_tick, + }, } } } @@ -582,10 +487,12 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { .get_resource_with_ticks(component_id) .map(|(ptr, ticks)| Res { value: ptr.deref(), - added: ticks.added.deref(), - changed: ticks.changed.deref(), - last_change_tick: system_meta.last_change_tick, - change_tick, + ticks: Ticks { + added: ticks.added.deref(), + changed: ticks.changed.deref(), + last_change_tick: system_meta.last_change_tick, + change_tick, + }, }) } } @@ -640,7 +547,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { }); ResMut { value: value.value, - ticks: Ticks { + ticks: TicksMut { added: value.ticks.added, changed: value.ticks.changed, last_change_tick: system_meta.last_change_tick, @@ -670,7 +577,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { .get_resource_unchecked_mut_with_id(component_id) .map(|value| ResMut { value: value.value, - ticks: Ticks { + ticks: TicksMut { added: value.ticks.added, changed: value.ticks.changed, last_change_tick: system_meta.last_change_tick, @@ -1161,7 +1068,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { }); NonSendMut { value: ptr.assert_unique().deref_mut(), - ticks: Ticks::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), + ticks: TicksMut::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), } } } @@ -1186,7 +1093,7 @@ unsafe impl<'a, T: 'static> SystemParam for Option> { .get_non_send_with_ticks(component_id) .map(|(ptr, ticks)| NonSendMut { value: ptr.assert_unique().deref_mut(), - ticks: Ticks::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), + ticks: TicksMut::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), }) } } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index f88e599a94982..c2928aa93dd51 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, ArchetypeId, Archetypes}, bundle::{Bundle, BundleInfo}, - change_detection::{MutUntyped, Ticks}, + change_detection::{MutUntyped, TicksMut}, component::{ Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, TickCells, @@ -161,7 +161,7 @@ impl<'w> EntityRef<'w> { // - returned component is of type T // - Caller guarantees that this reference will not alias. value: value.assert_unique().deref_mut::(), - ticks: Ticks::from_tick_cells(ticks, last_change_tick, change_tick), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), }) } } @@ -347,7 +347,7 @@ impl<'w> EntityMut<'w> { ) .map(|(value, ticks)| Mut { value: value.assert_unique().deref_mut::(), - ticks: Ticks::from_tick_cells( + ticks: TicksMut::from_tick_cells( ticks, self.world.last_change_tick(), self.world.read_change_tick(), @@ -1057,7 +1057,7 @@ pub(crate) unsafe fn get_mut( ) .map(|(value, ticks)| Mut { value: value.assert_unique().deref_mut::(), - ticks: Ticks::from_tick_cells(ticks, last_change_tick, change_tick), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), }) } @@ -1075,7 +1075,7 @@ pub(crate) unsafe fn get_mut_by_id( get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map( |(value, ticks)| MutUntyped { value: value.assert_unique(), - ticks: Ticks::from_tick_cells(ticks, world.last_change_tick(), change_tick), + ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), }, ) } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 3146f2a97f60c..8380c22a50933 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2,7 +2,7 @@ mod entity_ref; mod spawn_batch; mod world_cell; -pub use crate::change_detection::Mut; +pub use crate::change_detection::{Mut, Ref}; pub use entity_ref::*; pub use spawn_batch::*; pub use world_cell::*; @@ -10,7 +10,7 @@ pub use world_cell::*; use crate::{ archetype::{ArchetypeComponentId, ArchetypeId, ArchetypeRow, Archetypes}, bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, - change_detection::{MutUntyped, Ticks}, + change_detection::{MutUntyped, TicksMut}, component::{ Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells, }, @@ -1277,7 +1277,7 @@ impl World { let mut value = unsafe { ptr.read::() }; let value_mut = Mut { value: &mut value, - ticks: Ticks { + ticks: TicksMut { added: &mut ticks.added, changed: &mut ticks.changed, last_change_tick, @@ -1358,7 +1358,11 @@ impl World { let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; Some(Mut { value: ptr.assert_unique().deref_mut(), - ticks: Ticks::from_tick_cells(ticks, self.last_change_tick(), self.read_change_tick()), + ticks: TicksMut::from_tick_cells( + ticks, + self.last_change_tick(), + self.read_change_tick(), + ), }) } @@ -1393,7 +1397,7 @@ impl World { .get_with_ticks()?; Some(Mut { value: ptr.assert_unique().deref_mut(), - ticks: Ticks { + ticks: TicksMut { added: ticks.added.deref_mut(), changed: ticks.changed.deref_mut(), last_change_tick: self.last_change_tick(), @@ -1585,10 +1589,11 @@ impl World { let change_tick = self.change_tick(); let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; - // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. - // - index is in-bounds because the column is initialized and non-empty - // - no other reference to the ticks of the same row can exist at the same time - let ticks = unsafe { Ticks::from_tick_cells(ticks, self.last_change_tick(), change_tick) }; + let ticks = + // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. + // - index is in-bounds because the column is initialized and non-empty + // - no other reference to the ticks of the same row can exist at the same time + unsafe { TicksMut::from_tick_cells(ticks, self.last_change_tick(), change_tick) }; Some(MutUntyped { // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`. @@ -1628,10 +1633,11 @@ impl World { let change_tick = self.change_tick(); let (ptr, ticks) = self.get_non_send_with_ticks(component_id)?; - // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. - // - index is in-bounds because the column is initialized and non-empty - // - no other reference to the ticks of the same row can exist at the same time - let ticks = unsafe { Ticks::from_tick_cells(ticks, self.last_change_tick(), change_tick) }; + let ticks = + // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. + // - index is in-bounds because the column is initialized and non-empty + // - no other reference to the ticks of the same row can exist at the same time + unsafe { TicksMut::from_tick_cells(ticks, self.last_change_tick(), change_tick) }; Some(MutUntyped { // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`. @@ -1755,7 +1761,7 @@ impl FromWorld for T { mod tests { use super::World; use crate::{ - change_detection::DetectChanges, + change_detection::DetectChangesMut, component::{ComponentDescriptor, ComponentInfo, StorageType}, ptr::OwningPtr, system::Resource, diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index d0f17aa53d339..807521a141469 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,7 +1,7 @@ use crate::{Axis, Input}; use bevy_ecs::event::{EventReader, EventWriter}; use bevy_ecs::{ - change_detection::DetectChanges, + change_detection::DetectChangesMut, system::{Res, ResMut, Resource}, }; use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect}; diff --git a/crates/bevy_input/src/input.rs b/crates/bevy_input/src/input.rs index 3d1a1ecf5a9ad..a22145ea64f5d 100644 --- a/crates/bevy_input/src/input.rs +++ b/crates/bevy_input/src/input.rs @@ -36,11 +36,11 @@ use bevy_ecs::schedule::State; /// * Call the [`Input::clear`] method at each frame start, before processing events. /// /// Note: Calling `clear` from a [`ResMut`] will trigger change detection. -/// It may be preferable to use [`DetectChanges::bypass_change_detection`] +/// It may be preferable to use [`DetectChangesMut::bypass_change_detection`] /// to avoid causing the resource to always be marked as changed. /// ///[`ResMut`]: bevy_ecs::system::ResMut -///[`DetectChanges::bypass_change_detection`]: bevy_ecs::change_detection::DetectChanges::bypass_change_detection +///[`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection #[derive(Debug, Clone, Resource, Reflect)] #[reflect(Default)] pub struct Input { diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index 021646015be7c..83b35fe47e0a8 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -1,5 +1,5 @@ use crate::{ButtonState, Input}; -use bevy_ecs::{change_detection::DetectChanges, event::EventReader, system::ResMut}; +use bevy_ecs::{change_detection::DetectChangesMut, event::EventReader, system::ResMut}; use bevy_reflect::{FromReflect, Reflect}; #[cfg(feature = "serialize")] diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index bf86ed55b22ea..9fbac1ce937ff 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -1,5 +1,5 @@ use crate::{ButtonState, Input}; -use bevy_ecs::{change_detection::DetectChanges, event::EventReader, system::ResMut}; +use bevy_ecs::{change_detection::DetectChangesMut, event::EventReader, system::ResMut}; use bevy_math::Vec2; use bevy_reflect::{FromReflect, Reflect}; diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 3fa92750421c8..74e590faf6e6f 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use bevy_app::{App, Plugin}; -#[cfg(debug_assertions)] +use bevy_ecs::change_detection::DetectChanges; use bevy_ecs::system::Local; use bevy_ecs::system::{Commands, Res, ResMut, Resource}; pub use bevy_render_macros::ExtractResource; diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 43715a8cbd4ae..94c8e37b812ba 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -2,6 +2,7 @@ mod convert; use crate::{CalculatedSize, Node, Style, UiScale}; use bevy_ecs::{ + change_detection::DetectChanges, entity::Entity, event::EventReader, query::{Changed, ReadOnlyWorldQuery, With, Without}, diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index aa5327c13ad3a..06889ead589e7 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -1,6 +1,6 @@ use crate::{camera_config::UiCameraConfig, CalculatedClip, Node, UiStack}; use bevy_ecs::{ - change_detection::DetectChanges, + change_detection::DetectChangesMut, entity::Entity, prelude::Component, query::WorldQuery,