From 8c5b269931d91973f65b90b36da88f913fd5763e Mon Sep 17 00:00:00 2001 From: Trashtalk Date: Wed, 7 Aug 2024 01:46:06 +0200 Subject: [PATCH 1/2] implement Component for QueryState --- crates/bevy_ecs/src/archetype.rs | 5 +++++ crates/bevy_ecs/src/query/state.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index f0eda83a2293a..a38376d431665 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -19,11 +19,13 @@ //! [`Table`]: crate::storage::Table //! [`World::archetypes`]: crate::world::World::archetypes +use crate as bevy_ecs; use crate::{ bundle::BundleId, component::{ComponentId, Components, StorageType}, entity::{Entity, EntityLocation}, observer::Observers, + prelude::Event, storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow}, }; use std::{ @@ -108,6 +110,9 @@ impl ArchetypeId { } } +#[derive(Event)] +pub(crate) struct ArchetypeCreated(pub ArchetypeId); + #[derive(Copy, Clone, Eq, PartialEq)] pub(crate) enum ComponentStatus { Added, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index dcb20250c2466..3e080f05a2d5c 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1,15 +1,17 @@ use crate::{ - archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, + archetype::{Archetype, ArchetypeComponentId, ArchetypeCreated, ArchetypeGeneration, ArchetypeId}, batching::BatchingStrategy, - component::{ComponentId, Components, Tick}, + component::{Component, ComponentHooks, ComponentId, Components, StorageType, Tick}, entity::Entity, prelude::FromWorld, query::{ Access, DebugCheckedUnwrap, FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, }, + observer::{Observer, Trigger}, storage::{SparseSetIndex, TableId}, world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId}, }; +use crate::world::DeferredWorld; use bevy_utils::tracing::warn; #[cfg(feature = "trace")] use bevy_utils::tracing::Span; @@ -74,6 +76,27 @@ pub struct QueryState { par_iter_span: Span, } +impl Component for QueryState { + const STORAGE_TYPE: StorageType = StorageType::SparseSet; + + fn register_component_hooks(hooks: &mut ComponentHooks) { + hooks.on_add(|mut world, entity, _| { + let mut observer = Observer::new(|trigger: Trigger, + mut world: DeferredWorld| { + let archetype_id: ArchetypeId = trigger.event().0; + let archetype_ptr: *const Archetype = world.archetypes().get(archetype_id).unwrap(); + let mut entity = world.entity_mut(trigger.entity()); + let mut state = entity.get_mut::>().unwrap(); + unsafe { + state.new_archetype(&*archetype_ptr, &mut Access::::default()); + } + }); + observer.watch_entity(entity); + world.commands().spawn(observer); + }); + } +} + impl fmt::Debug for QueryState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("QueryState") From 7fb5a9b58bf7bb0e4cf38a504908ea19d8fbfe5b Mon Sep 17 00:00:00 2001 From: Trashtalk Date: Thu, 8 Aug 2024 15:23:16 +0200 Subject: [PATCH 2/2] made Query to use Entity QueryState --- crates/bevy_ecs/src/query/state.rs | 3 +- crates/bevy_ecs/src/system/query.rs | 82 ++++++++++++--------- crates/bevy_ecs/src/system/system_param.rs | 19 ++--- crates/bevy_ecs/src/world/deferred_world.rs | 3 +- 4 files changed, 58 insertions(+), 49 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 3e080f05a2d5c..f193aae1b99b3 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -58,6 +58,7 @@ pub(super) union StorageId { // SAFETY NOTE: // Do not add any new fields that use the `D` or `F` generic parameters as this may // make `QueryState::as_transmuted_state` unsound if not done with care. +#[derive(Clone)] pub struct QueryState { world_id: WorldId, pub(crate) archetype_generation: ArchetypeGeneration, @@ -88,7 +89,7 @@ impl Component for QueryState< let mut entity = world.entity_mut(trigger.entity()); let mut state = entity.get_mut::>().unwrap(); unsafe { - state.new_archetype(&*archetype_ptr, &mut Access::::default()); + state.new_archetype_internal(&*archetype_ptr); } }); observer.watch_entity(entity); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 215c8d7b1108b..16006d8f23d1e 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -8,6 +8,7 @@ use crate::{ }, world::unsafe_world_cell::UnsafeWorldCell, }; +use core::marker::PhantomData; use std::borrow::Borrow; /// [System parameter] that provides selective access to the [`Component`] data stored in a [`World`]. @@ -346,12 +347,13 @@ use std::borrow::Borrow; /// [`Table`]: crate::storage::Table /// [`With`]: crate::query::With /// [`Without`]: crate::query::Without -pub struct Query<'world, 'state, D: QueryData, F: QueryFilter = ()> { +pub struct Query<'world, 'state, D: QueryData + 'static, F: QueryFilter + 'static = ()> { // SAFETY: Must have access to the components registered in `state`. world: UnsafeWorldCell<'world>, - state: &'state QueryState, + state: Entity, last_run: Tick, this_run: Tick, + marker: PhantomData<&'state (D, F)>, } impl std::fmt::Debug for Query<'_, '_, D, F> { @@ -380,29 +382,37 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub(crate) unsafe fn new( world: UnsafeWorldCell<'w>, - state: &'s QueryState, + state: Entity, last_run: Tick, this_run: Tick, ) -> Self { - state.validate_world(world.id()); + let query_state = world.world_mut().get::>(state).unwrap(); + query_state.validate_world(world.id()); Self { world, state, last_run, this_run, + marker: PhantomData, } } + fn get_query_state(&self) -> &QueryState { + return unsafe { self.world.world_mut().get::>(self.state).unwrap() } + } + /// Returns another `Query` from this that fetches the read-only version of the query items. /// /// For example, `Query<(&mut D1, &D2, &mut D3), With>` will become `Query<(&D1, &D2, &D3), With>`. /// This can be useful when working around the borrow checker, /// or reusing functionality between systems via functions that accept query types. pub fn to_readonly(&self) -> Query<'_, 's, D::ReadOnly, F> { - let new_state = self.state.as_readonly(); + let mut query_state = self.get_query_state(); + let new_state = query_state.as_readonly().clone(); + let read_only_state = unsafe { self.world.world_mut() }.spawn(*new_state).id(); // SAFETY: This is memory safe because it turns the query immutable. - unsafe { Query::new(self.world, new_state, self.last_run, self.this_run) } + unsafe { Query::new(self.world, read_only_state, self.last_run, self.this_run) } } /// Returns an [`Iterator`] over the read-only query items. @@ -437,7 +447,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { - self.state + self.get_query_state() .as_readonly() .iter_unchecked_manual(self.world, self.last_run, self.this_run) } @@ -473,7 +483,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_mut(&mut self) -> QueryIter<'_, 's, D, F> { // SAFETY: `self.world` has permission to access the required components. unsafe { - self.state + self.get_query_state() .iter_unchecked_manual(self.world, self.last_run, self.this_run) } } @@ -508,7 +518,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { - self.state.as_readonly().iter_combinations_unchecked_manual( + self.get_query_state().as_readonly().iter_combinations_unchecked_manual( self.world, self.last_run, self.this_run, @@ -544,7 +554,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { ) -> QueryCombinationIter<'_, 's, D, F, K> { // SAFETY: `self.world` has permission to access the required components. unsafe { - self.state + self.get_query_state() .iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run) } } @@ -597,7 +607,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { - self.state.as_readonly().iter_many_unchecked_manual( + self.get_query_state().as_readonly().iter_many_unchecked_manual( entities, self.world, self.last_run, @@ -649,7 +659,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { { // SAFETY: `self.world` has permission to access the required components. unsafe { - self.state.iter_many_unchecked_manual( + self.get_query_state().iter_many_unchecked_manual( entities, self.world, self.last_run, @@ -677,7 +687,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { - self.state + self.get_query_state() .iter_unchecked_manual(self.world, self.last_run, self.this_run) } } @@ -703,7 +713,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { - self.state + self.get_query_state() .iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run) } } @@ -733,7 +743,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `self.world` has permission to access the required components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { - self.state.iter_many_unchecked_manual( + self.get_query_state().iter_many_unchecked_manual( entities, self.world, self.last_run, @@ -761,7 +771,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter(&self) -> QueryParIter<'_, '_, D::ReadOnly, F> { QueryParIter { world: self.world, - state: self.state.as_readonly(), + state: self.get_query_state().as_readonly(), last_run: self.last_run, this_run: self.this_run, batching_strategy: BatchingStrategy::new(), @@ -802,7 +812,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_mut(&mut self) -> QueryParIter<'_, '_, D, F> { QueryParIter { world: self.world, - state: self.state, + state: self.get_query_state(), last_run: self.last_run, this_run: self.this_run, batching_strategy: BatchingStrategy::new(), @@ -847,7 +857,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.as_readonly().get_unchecked_manual( + self.get_query_state().as_readonly().get_unchecked_manual( self.world, entity, self.last_run, @@ -875,7 +885,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `&self` ensures there is no mutable access to any components accessible to this query. // - `self.world` matches `self.state`. unsafe { - self.state + self.get_query_state() .get_many_read_only_manual(self.world, entities, self.last_run, self.this_run) } } @@ -964,7 +974,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state + self.get_query_state() .get_unchecked_manual(self.world, entity, self.last_run, self.this_run) } } @@ -985,7 +995,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { ) -> Result<[D::Item<'_>; N], QueryEntityError> { // SAFETY: scheduler ensures safe Query world access unsafe { - self.state + self.get_query_state() .get_many_unchecked_manual(self.world, entities, self.last_run, self.this_run) } } @@ -1066,7 +1076,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // SEMI-SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state + self.get_query_state() .get_unchecked_manual(self.world, entity, self.last_run, self.this_run) } } @@ -1138,7 +1148,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // the query ensures that the components it accesses are not mutably accessible somewhere else // and the query is read only. unsafe { - self.state.as_readonly().get_single_unchecked_manual( + self.get_query_state().as_readonly().get_single_unchecked_manual( self.world, self.last_run, self.this_run, @@ -1209,7 +1219,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // the query ensures mutable access to the components it accesses, and the query // is uniquely borrowed unsafe { - self.state + self.get_query_state() .get_single_unchecked_manual(self.world, self.last_run, self.this_run) } } @@ -1249,7 +1259,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { // - `&self` ensures that no one currently has write access. // - `self.world` matches `self.state`. unsafe { - self.state + self.get_query_state() .is_empty_unsafe_world_cell(self.world, self.last_run, self.this_run) } } @@ -1282,7 +1292,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn contains(&self, entity: Entity) -> bool { // SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access unsafe { - self.state + self.get_query_state() .as_nop() .get_unchecked_manual(self.world, entity, self.last_run, self.this_run) .is_ok() @@ -1369,12 +1379,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { &mut self, ) -> QueryLens<'_, NewD, NewF> { let components = self.world.components(); - let state = self.state.transmute_filtered::(components); + let state = self.get_query_state().transmute_filtered::(components); QueryLens { world: self.world, state, last_run: self.last_run, this_run: self.this_run, + marker: PhantomData, } } @@ -1462,13 +1473,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { ) -> QueryLens<'_, NewD, NewF> { let components = self.world.components(); let state = self - .state - .join_filtered::(components, other.state); + .get_query_state() + .join_filtered::(components, other.get_query_state()); QueryLens { world: self.world, state, last_run: self.last_run, this_run: self.this_run, + marker: PhantomData, } } } @@ -1529,7 +1541,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state.as_readonly().get_unchecked_manual( + self.get_query_state().as_readonly().get_unchecked_manual( self.world, entity, self.last_run, @@ -1566,7 +1578,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { - self.state + self.get_query_state() .as_readonly() .iter_unchecked_manual(self.world, self.last_run, self.this_run) } @@ -1576,11 +1588,12 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { /// Type returned from [`Query::transmute_lens`] containing the new [`QueryState`]. /// /// Call [`query`](QueryLens::query) or [`into`](Into::into) to construct the resulting [`Query`] -pub struct QueryLens<'w, Q: QueryData, F: QueryFilter = ()> { +pub struct QueryLens<'w, Q: QueryData + 'static, F: QueryFilter + 'static = ()> { world: UnsafeWorldCell<'w>, - state: QueryState, + state: Entity, last_run: Tick, this_run: Tick, + marker: PhantomData<(Q, F)>, } impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> { @@ -1588,9 +1601,10 @@ impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> { pub fn query(&mut self) -> Query<'w, '_, Q, F> { Query { world: self.world, - state: &self.state, + state: self.state, last_run: self.last_run, this_run: self.this_run, + marker: PhantomData } } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8898e34d092e1..9c0b333097544 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -10,6 +10,7 @@ use crate::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QueryState, ReadOnlyQueryData, }, + prelude::Entity, system::{Query, SystemMeta}, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FromWorld, World}, }; @@ -214,11 +215,11 @@ unsafe impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re // SAFETY: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. unsafe impl SystemParam for Query<'_, '_, D, F> { - type State = QueryState; + type State = Entity; type Item<'w, 's> = Query<'w, 's, D, F>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - let state = QueryState::new_with_access(world, &mut system_meta.archetype_component_access); + let state: QueryState = QueryState::new_with_access(world, &mut system_meta.archetype_component_access); assert_component_access_compatibility( &system_meta.name, std::any::type_name::(), @@ -230,15 +231,7 @@ unsafe impl SystemParam for Qu system_meta .component_access_set .add(state.component_access.clone()); - state - } - - unsafe fn new_archetype( - state: &mut Self::State, - archetype: &Archetype, - system_meta: &mut SystemMeta, - ) { - state.new_archetype(archetype, &mut system_meta.archetype_component_access); + world.spawn(state).id() } #[inline] @@ -251,7 +244,7 @@ unsafe impl SystemParam for Qu // SAFETY: We have registered all of the query's world accesses, // so the caller ensures that `world` has permission to access any // world data that the query needs. - unsafe { Query::new(world, state, system_meta.last_run, change_tick) } + unsafe { Query::new(world, *state, system_meta.last_run, change_tick) } } } @@ -280,7 +273,7 @@ impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static> BuildableSystemPa system_meta .component_access_set .add(state.component_access.clone()); - state + world.spawn(state).id() } } diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 92d953f3adbcc..6db845b8e818e 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -127,9 +127,10 @@ impl<'w> DeferredWorld<'w> { // SAFETY: We ran validate_world to ensure our state matches unsafe { let world_cell = self.world; + let id = world_cell.world_mut().spawn(*state).id(); Query::new( world_cell, - state, + id, world_cell.last_change_tick(), world_cell.change_tick(), )