From c98f097722efbe58edd84d80799a5c5ba73dbbd4 Mon Sep 17 00:00:00 2001 From: xiejiaen Date: Mon, 4 Nov 2024 20:32:41 +0800 Subject: [PATCH] mutate observer --- crates/bevy_ecs/src/change_detection.rs | 3 +-- crates/bevy_ecs/src/query/fetch.rs | 16 ++++-------- crates/bevy_ecs/src/query/filter.rs | 2 +- crates/bevy_ecs/src/query/world_query.rs | 2 +- crates/bevy_ecs/src/system/system_param.rs | 2 +- .../bevy_ecs/src/world/component_constants.rs | 2 +- crates/bevy_ecs/src/world/entity_change.rs | 9 +++---- crates/bevy_ecs/src/world/mod.rs | 22 ++++++++++------ .../bevy_ecs/src/world/unsafe_world_cell.rs | 6 +++-- crates/bevy_render/src/pipelined_rendering.rs | 1 - examples/ecs/responding_to_changes.rs | 26 +++++++------------ 11 files changed, 42 insertions(+), 49 deletions(-) diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index e196411cd3331..9672aef40e780 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -1,5 +1,6 @@ //! Types that detect when their internal data mutate. +use crate::world::entity_change::{EntityChange, EntityChanges}; use crate::{ component::{Tick, TickCells}, ptr::PtrMut, @@ -16,7 +17,6 @@ use { bevy_ptr::ThinSlicePtr, core::{cell::UnsafeCell, panic::Location}, }; -use crate::world::entity_change::{EntityChange, EntityChanges}; /// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans. /// @@ -1103,7 +1103,6 @@ where } } - change_detection_impl!(Mut<'w, T>, T,); change_detection_mut_with_onchange_impl!(Mut<'w, T>, T,); impl_methods_with_onchange!(Mut<'w, T>, T,); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 00559f51e89a7..6446f12dd8b6f 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,3 +1,4 @@ +use crate::world::entity_change::{EntityChange, EntityChanges}; use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundle, @@ -14,9 +15,8 @@ use crate::{ use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_utils::all_tuples; use core::{cell::UnsafeCell, marker::PhantomData}; -use std::cell::RefCell; use smallvec::SmallVec; -use crate::world::entity_change::{EntityChange, EntityChanges}; +use std::cell::RefCell; /// Types that can be fetched from a [`World`] using a [`Query`]. /// @@ -1431,7 +1431,7 @@ unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {} /// The [`WorldQuery::Fetch`] type for `&mut T`. pub struct WriteFetch<'w, T: Component> { component_id: ComponentId, - changes: &'w RefCell, + changes: &'w RefCell, components: StorageSwitch< T, // T::STORAGE_TYPE = StorageType::Table @@ -1571,10 +1571,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { let caller = unsafe { _callers.get(table_row.as_usize()) }; Mut { - on_change: Some(( - EntityChange::new(entity, fetch.component_id), - fetch.changes, - )), + on_change: Some((EntityChange::new(entity, fetch.component_id), fetch.changes)), value: component.deref_mut(), ticks: TicksMut { added: added.deref_mut(), @@ -1592,10 +1589,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe { sparse_set.get_with_ticks(entity).debug_checked_unwrap() }; Mut { - on_change: Some(( - EntityChange::new(entity, fetch.component_id), - fetch.changes, - )), + on_change: Some((EntityChange::new(entity, fetch.component_id), fetch.changes)), value: component.assert_unique().deref_mut(), ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run), #[cfg(feature = "track_change_detection")] diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 58814a6abb689..ba8eb86276c3c 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -277,7 +277,7 @@ unsafe impl WorldQuery for Without { } }; const IS_MUTATE: bool = false; - + #[inline] unsafe fn set_archetype( _fetch: &mut (), diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index 85e94b720cb02..88ffdfdab4842 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -77,7 +77,7 @@ pub unsafe trait WorldQuery { /// iterators. const IS_DENSE: bool; - /// Return true if (and only if) TODO + /// Return true if (and only if) this query will return a mutate access of any [`Component`] const IS_MUTATE: bool; /// Adjusts internal state to account for the next [`Archetype`]. This will always be called on diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index d59d043cee657..7216f01f84311 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1,3 +1,4 @@ +use super::Populated; pub use crate::change_detection::{NonSendMut, Res, ResMut}; use crate::{ archetype::{Archetype, Archetypes}, @@ -28,7 +29,6 @@ use core::{ marker::PhantomData, ops::{Deref, DerefMut}, }; -use super::Populated; /// A parameter that can be used in a [`System`](super::System). /// diff --git a/crates/bevy_ecs/src/world/component_constants.rs b/crates/bevy_ecs/src/world/component_constants.rs index 70efcf0a24cd5..2661cdf4f7802 100644 --- a/crates/bevy_ecs/src/world/component_constants.rs +++ b/crates/bevy_ecs/src/world/component_constants.rs @@ -49,4 +49,4 @@ pub struct OnRemove; #[derive(Event, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] -pub struct OnMutate; \ No newline at end of file +pub struct OnMutate; diff --git a/crates/bevy_ecs/src/world/entity_change.rs b/crates/bevy_ecs/src/world/entity_change.rs index 2427cde1f9bf4..e4ed8d49db570 100644 --- a/crates/bevy_ecs/src/world/entity_change.rs +++ b/crates/bevy_ecs/src/world/entity_change.rs @@ -1,7 +1,7 @@ -use std::cell::RefCell; -use bevy_utils::Parallel; use crate::component::ComponentId; use crate::entity::Entity; +use bevy_utils::Parallel; +use std::cell::RefCell; /// A shorthand for [`Vec`]. pub type EntityChanges = Vec; @@ -14,7 +14,6 @@ pub struct ParallelEntityChanges { } impl ParallelEntityChanges { - /// Returns a default `Changes` pub fn new() -> Self { @@ -35,7 +34,7 @@ impl ParallelEntityChanges { /// A Record hint which entity's component has changed #[derive(Copy, Clone, Debug)] pub struct EntityChange { - entity: Entity, + entity: Entity, component: ComponentId, } @@ -53,4 +52,4 @@ impl EntityChange { pub fn component(&self) -> ComponentId { self.component } -} \ No newline at end of file +} diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index b7642d50be0d5..16e54c0134b6f 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2,9 +2,9 @@ pub(crate) mod command_queue; -pub(crate) mod entity_change; mod component_constants; mod deferred_world; +pub(crate) mod entity_change; mod entity_fetch; mod entity_ref; pub mod error; @@ -63,9 +63,9 @@ use core::{ #[cfg(feature = "track_change_detection")] use bevy_ptr::UnsafeCellDeref; +use crate::world::entity_change::ParallelEntityChanges; use core::panic::Location; use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; -use crate::world::entity_change::ParallelEntityChanges; /// A [`World`] mutation. /// @@ -3024,12 +3024,18 @@ impl World { pub(crate) fn flush_entity_changes(&mut self) { let mut parallel_entity_changes = std::mem::take(&mut self.entity_changes); let mut deferred_world = DeferredWorld::from(&mut *self); - parallel_entity_changes.iter_mut().for_each(|entity_changes| { - entity_changes.drain(..).for_each(|entity_change| unsafe { - // SAFETY: [`OnMutate`] Event is ZST - deferred_world.trigger_observers(ON_MUTATE, entity_change.entity(), std::iter::once(entity_change.component())) - }) - }); + parallel_entity_changes + .iter_mut() + .for_each(|entity_changes| { + entity_changes.drain(..).for_each(|entity_change| unsafe { + // SAFETY: [`OnMutate`] Event is ZST + deferred_world.trigger_observers( + ON_MUTATE, + entity_change.entity(), + std::iter::once(entity_change.component()), + ) + }) + }); _ = std::mem::replace(&mut self.entity_changes, parallel_entity_changes); } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index db7b157c276e8..ce688a78713e2 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -3,6 +3,7 @@ #![warn(unsafe_op_in_unsafe_fn)] use super::{Mut, Ref, World, WorldId}; +use crate::world::entity_change::{EntityChange, EntityChanges}; use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, @@ -22,7 +23,6 @@ use bevy_ptr::Ptr; use bevy_ptr::UnsafeCellDeref; use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, ptr}; use std::cell::RefCell; -use crate::world::entity_change::{EntityChange, EntityChanges}; /// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid /// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule. @@ -332,7 +332,9 @@ impl<'w> UnsafeWorldCell<'w> { /// time as any other accesses to that same component. pub unsafe fn entity_changes(self) -> &'w RefCell { // SAFETY: The caller promises to only access world data allowed by this instance. - &unsafe { self.unsafe_world() }.entity_changes.get_local_ref_cell() + &unsafe { self.unsafe_world() } + .entity_changes + .get_local_ref_cell() } /// Retrieves an [`UnsafeEntityCell`] that exposes read and write operations for the given `entity`. diff --git a/crates/bevy_render/src/pipelined_rendering.rs b/crates/bevy_render/src/pipelined_rendering.rs index 824fbc405c56f..1b744db051c58 100644 --- a/crates/bevy_render/src/pipelined_rendering.rs +++ b/crates/bevy_render/src/pipelined_rendering.rs @@ -43,7 +43,6 @@ impl RenderAppChannels { self.app_to_render_sender.send_blocking(render_app).unwrap(); self.render_app_in_render_thread = true; } - } impl Drop for RenderAppChannels { diff --git a/examples/ecs/responding_to_changes.rs b/examples/ecs/responding_to_changes.rs index eff20be0150e2..948a27b3547a7 100644 --- a/examples/ecs/responding_to_changes.rs +++ b/examples/ecs/responding_to_changes.rs @@ -1,4 +1,3 @@ - //! Bevy has two primary ways to respond to changes in your ECS data: //! //! 1. **Change detection:** whenever a component or resource is mutated, it will be flagged as changed. @@ -192,11 +191,8 @@ fn update_counter_observer( trigger: Trigger, mut button_query: Query<(&mut CounterValue, &Interaction, &ChangeStrategy)>, ) { - let Ok(( - mut counter, - interaction, - change_strategy - )) = button_query.get_mut(trigger.entity()) else { + let Ok((mut counter, interaction, change_strategy)) = button_query.get_mut(trigger.entity()) + else { // Other entities may have the Interaction component, but we're only interested in these particular buttons. return; }; @@ -216,16 +212,14 @@ fn setup_ui(mut commands: Commands) { commands.spawn(Camera2d::default()); let root_node = commands - .spawn(( - Node { - width: Val::Percent(100.), - height: Val::Percent(100.), - flex_direction: FlexDirection::Column, - justify_content: JustifyContent::Center, - align_items: AlignItems::Center, - ..default() - }, - )) + .spawn((Node { + width: Val::Percent(100.), + height: Val::Percent(100.), + flex_direction: FlexDirection::Column, + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + },)) .id(); let changed_filter_button =