From b23f8b4058361c81813ca7c344cb25f0d72f2c57 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 4 Aug 2021 18:44:05 -0700 Subject: [PATCH 1/7] Split out SystemParamFetch lifetimes --- crates/bevy_ecs/macros/src/lib.rs | 25 +-- crates/bevy_ecs/src/event.rs | 16 +- crates/bevy_ecs/src/system/commands/mod.rs | 24 +-- crates/bevy_ecs/src/system/function_system.rs | 18 +- crates/bevy_ecs/src/system/query.rs | 18 +- crates/bevy_ecs/src/system/system_param.rs | 154 +++++++++--------- crates/bevy_render/src/draw.rs | 20 ++- crates/bevy_scene/src/command.rs | 4 +- .../src/hierarchy/child_builder.rs | 12 +- .../bevy_transform/src/hierarchy/hierarchy.rs | 2 +- 10 files changed, 150 insertions(+), 143 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 9755e92637da1..1d44c24f52034 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -189,22 +189,24 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { let queries = get_idents(|i| format!("Q{}", i), max_queries); let filters = get_idents(|i| format!("F{}", i), max_queries); let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries); + let state_lifetimes = get_lifetimes(|i| format!("'qs{}", i), max_queries); let mut query_fns = Vec::new(); let mut query_fn_muts = Vec::new(); for i in 0..max_queries { let query = &queries[i]; let filter = &filters[i]; let lifetime = &lifetimes[i]; + let state_lifetime = &state_lifetimes[i]; let fn_name = Ident::new(&format!("q{}", i), Span::call_site()); let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site()); let index = Index::from(i); query_fns.push(quote! { - pub fn #fn_name(&self) -> &Query<#lifetime, #query, #filter> { + pub fn #fn_name(&self) -> &Query<#lifetime, #state_lifetime, #query, #filter> { &self.0.#index } }); query_fn_muts.push(quote! { - pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query, #filter> { + pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> { &mut self.0.#index } }); @@ -214,10 +216,11 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { let query = &queries[0..query_count]; let filter = &filters[0..query_count]; let lifetime = &lifetimes[0..query_count]; + let state_lifetime = &state_lifetimes[0..query_count]; let query_fn = &query_fns[0..query_count]; let query_fn_mut = &query_fn_muts[0..query_count]; tokens.extend(TokenStream::from(quote! { - impl<#(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> + impl<#(#lifetime,)* #(#state_lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>; @@ -270,16 +273,16 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { fn default_config() {} } - impl<'a, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'a> for QuerySetState<(#(QueryState<#query, #filter>,)*)> + impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { - type Item = QuerySet<(#(Query<'a, #query, #filter>,)*)>; + type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { let (#(#query,)*) = &state.0; @@ -287,7 +290,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { } } - impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> + impl<#(#lifetime,)* #(#state_lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { #(#query_fn)* @@ -415,12 +418,12 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } } - impl #impl_generics #path::system::SystemParamFetch<'a> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> { + impl #impl_generics #path::system::SystemParamFetch<'w, 's> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> { type Item = #struct_name#ty_generics; unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &#path::system::SystemMeta, - world: &'a #path::world::World, + world: &'w #path::world::World, change_tick: u32, ) -> Self::Item { #struct_name { diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 0260da3a57d58..f088b27f1060a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -151,18 +151,20 @@ fn map_instance_event(event_instance: &EventInstance) -> &T { /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] -pub struct EventReader<'a, T: Component> { - last_event_count: Local<'a, (usize, PhantomData)>, - events: Res<'a, Events>, +pub struct EventReader<'w, 's, T: Component> { + last_event_count: Local<'s, (usize, PhantomData)>, + events: Res<'w, Events>, } /// Sends events of type `T`. #[derive(SystemParam)] -pub struct EventWriter<'a, T: Component> { - events: ResMut<'a, Events>, +pub struct EventWriter<'w, 's, T: Component> { + events: ResMut<'w, Events>, + #[system_param(ignore)] + marker: PhantomData<&'s usize>, } -impl<'a, T: Component> EventWriter<'a, T> { +impl<'w, 's, T: Component> EventWriter<'w, 's, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -252,7 +254,7 @@ fn internal_event_reader<'a, T>( } } -impl<'a, T: Component> EventReader<'a, T> { +impl<'w, 's, T: Component> EventReader<'w, 's, T> { /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// event counter, which means subsequent event reads will not include events that happened /// before now. diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 0488b09e4f770..97304959fcdcb 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -16,14 +16,14 @@ pub trait Command: Send + Sync + 'static { } /// A list of commands that will be run to modify a [`World`]. -pub struct Commands<'a> { - queue: &'a mut CommandQueue, - entities: &'a Entities, +pub struct Commands<'w, 's> { + queue: &'s mut CommandQueue, + entities: &'w Entities, } -impl<'a> Commands<'a> { +impl<'w, 's> Commands<'w, 's> { /// Create a new `Commands` from a queue and a world. - pub fn new(queue: &'a mut CommandQueue, world: &'a World) -> Self { + pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self { Self { queue, entities: world.entities(), @@ -50,7 +50,7 @@ impl<'a> Commands<'a> { /// } /// # example_system.system(); /// ``` - pub fn spawn(&mut self) -> EntityCommands<'a, '_> { + pub fn spawn<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> { let entity = self.entities.reserve_entity(); EntityCommands { entity, @@ -98,7 +98,7 @@ impl<'a> Commands<'a> { /// } /// # example_system.system(); /// ``` - pub fn spawn_bundle<'b, T: Bundle>(&'b mut self, bundle: T) -> EntityCommands<'a, 'b> { + pub fn spawn_bundle<'a, T: Bundle>(&'a mut self, bundle: T) -> EntityCommands<'w, 's, 'a> { let mut e = self.spawn(); e.insert_bundle(bundle); e @@ -123,7 +123,7 @@ impl<'a> Commands<'a> { /// } /// # example_system.system(); /// ``` - pub fn entity(&mut self, entity: Entity) -> EntityCommands<'a, '_> { + pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> { EntityCommands { entity, commands: self, @@ -159,12 +159,12 @@ impl<'a> Commands<'a> { } /// A list of commands that will be run to modify an [`Entity`]. -pub struct EntityCommands<'a, 'b> { +pub struct EntityCommands<'w, 's, 'a> { entity: Entity, - commands: &'b mut Commands<'a>, + commands: &'a mut Commands<'w, 's>, } -impl<'a, 'b> EntityCommands<'a, 'b> { +impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// Retrieves the current entity's unique [`Entity`] id. #[inline] pub fn id(&self) -> Entity { @@ -252,7 +252,7 @@ impl<'a, 'b> EntityCommands<'a, 'b> { } /// Returns the underlying `[Commands]`. - pub fn commands(&mut self) -> &mut Commands<'a> { + pub fn commands(&mut self) -> &mut Commands<'w, 's> { self.commands } } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index bb85fc8bbc019..1783c19e63b0a 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -87,7 +87,7 @@ impl SystemState { /// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only. #[inline] - pub fn get<'a>(&'a mut self, world: &'a World) -> >::Item + pub fn get<'w, 's>(&'s mut self, world: &'w World) -> >::Item where Param::Fetch: ReadOnlySystemParamFetch, { @@ -98,10 +98,10 @@ impl SystemState { /// Retrieve the mutable [`SystemParam`] values. #[inline] - pub fn get_mut<'a>( - &'a mut self, - world: &'a mut World, - ) -> >::Item { + pub fn get_mut<'w, 's>( + &'s mut self, + world: &'w mut World, + ) -> >::Item { self.validate_world_and_update_archetypes(world); // SAFE: World is uniquely borrowed and matches the World this SystemState was created with. unsafe { self.get_unchecked_manual(world) } @@ -142,10 +142,10 @@ impl SystemState { /// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was /// created with. #[inline] - pub unsafe fn get_unchecked_manual<'a>( - &'a mut self, - world: &'a World, - ) -> >::Item { + pub unsafe fn get_unchecked_manual<'w, 's>( + &'s mut self, + world: &'w World, + ) -> >::Item { let change_tick = world.increment_change_tick(); let param = ::get_param( &mut self.param_state, diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 05ff84a798ed6..efa245e19ffce 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -107,17 +107,17 @@ use thiserror::Error; /// /// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery) /// bevy has to offer. -pub struct Query<'w, Q: WorldQuery, F: WorldQuery = ()> +pub struct Query<'world, 'state, Q: WorldQuery, F: WorldQuery = ()> where F::Fetch: FilterFetch, { - pub(crate) world: &'w World, - pub(crate) state: &'w QueryState, + pub(crate) world: &'world World, + pub(crate) state: &'state QueryState, pub(crate) last_change_tick: u32, pub(crate) change_tick: u32, } -impl<'w, Q: WorldQuery, F: WorldQuery> Query<'w, Q, F> +impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> where F::Fetch: FilterFetch, { @@ -130,7 +130,7 @@ where #[inline] pub(crate) unsafe fn new( world: &'w World, - state: &'w QueryState, + state: &'s QueryState, last_change_tick: u32, change_tick: u32, ) -> Self { @@ -266,7 +266,7 @@ where /// /// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries. #[inline] - pub fn for_each<'s>(&'s self, f: impl FnMut(>::Item)) + pub fn for_each(&'s self, f: impl FnMut(>::Item)) where Q::Fetch: ReadOnlyFetch, { @@ -285,7 +285,7 @@ where /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot /// be chained like a normal [`Iterator`]. #[inline] - pub fn for_each_mut<'s>(&'s mut self, f: impl FnMut(>::Item)) { + pub fn for_each_mut(&'s mut self, f: impl FnMut(>::Item)) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { @@ -303,7 +303,7 @@ where /// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for /// write-queries. #[inline] - pub fn par_for_each<'s>( + pub fn par_for_each( &'s self, task_pool: &TaskPool, batch_size: usize, @@ -327,7 +327,7 @@ where /// Runs `f` on each query result in parallel using the given task pool. #[inline] - pub fn par_for_each_mut<'s>( + pub fn par_for_each_mut( &'s mut self, task_pool: &TaskPool, batch_size: usize, diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0499948755a93..b2cf44d471675 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -41,7 +41,7 @@ use std::{ /// # my_system.system(); /// ``` pub trait SystemParam: Sized { - type Fetch: for<'a> SystemParamFetch<'a>; + type Fetch: for<'w, 's> SystemParamFetch<'w, 's>; } /// The state of a [`SystemParam`]. @@ -80,21 +80,21 @@ pub unsafe trait SystemParamState: Send + Sync + 'static { /// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`] pub unsafe trait ReadOnlySystemParamFetch {} -pub trait SystemParamFetch<'a>: SystemParamState { +pub trait SystemParamFetch<'world, 'state>: SystemParamState { type Item; /// # Safety /// /// This call might access any of the input parameters in an unsafe way. Make sure the data /// access is safe in the context of the system scheduler. unsafe fn get_param( - state: &'a mut Self, + state: &'state mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'world World, change_tick: u32, ) -> Self::Item; } -impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'a, Q, F> +impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F> where F::Fetch: FilterFetch, { @@ -146,17 +146,17 @@ where fn default_config() {} } -impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'a> for QueryState +impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's> for QueryState where F::Fetch: FilterFetch, { - type Item = Query<'a, Q, F>; + type Item = Query<'w, 's, Q, F>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { Query::new(world, state, system_meta.last_change_tick, change_tick) @@ -286,14 +286,14 @@ unsafe impl SystemParamState for ResState { fn default_config() {} } -impl<'a, T: Component> SystemParamFetch<'a> for ResState { - type Item = Res<'a, T>; +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState { + type Item = Res<'w, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { let column = world @@ -334,14 +334,14 @@ unsafe impl SystemParamState for OptionResState { fn default_config() {} } -impl<'a, T: Component> SystemParamFetch<'a> for OptionResState { - type Item = Option>; +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState { + type Item = Option>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world @@ -400,14 +400,14 @@ unsafe impl SystemParamState for ResMutState { fn default_config() {} } -impl<'a, T: Component> SystemParamFetch<'a> for ResMutState { - type Item = ResMut<'a, T>; +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState { + type Item = ResMut<'w, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { let value = world @@ -447,14 +447,14 @@ unsafe impl SystemParamState for OptionResMutState { fn default_config() {} } -impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState { - type Item = Option>; +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState { + type Item = Option>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world @@ -470,7 +470,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState { } } -impl<'a> SystemParam for Commands<'a> { +impl<'w, 's> SystemParam for Commands<'w, 's> { type Fetch = CommandQueue; } @@ -492,14 +492,14 @@ unsafe impl SystemParamState for CommandQueue { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for CommandQueue { - type Item = Commands<'a>; +impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue { + type Item = Commands<'w, 's>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { Commands::new(state, world) @@ -582,14 +582,14 @@ unsafe impl SystemParamState for LocalState { } } -impl<'a, T: Component + FromWorld> SystemParamFetch<'a> for LocalState { - type Item = Local<'a, T>; +impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState { + type Item = Local<'s, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, _system_meta: &SystemMeta, - _world: &'a World, + _world: &'w World, _change_tick: u32, ) -> Self::Item { Local(&mut state.0) @@ -655,14 +655,14 @@ unsafe impl SystemParamState for RemovedComponentsState { fn default_config() {} } -impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState { - type Item = RemovedComponents<'a, T>; +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState { + type Item = RemovedComponents<'w, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { RemovedComponents { @@ -770,14 +770,14 @@ unsafe impl SystemParamState for NonSendState { fn default_config() {} } -impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState { - type Item = NonSend<'a, T>; +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState { + type Item = NonSend<'w, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -803,7 +803,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState { /// The [`SystemParamState`] of `Option>`. pub struct OptionNonSendState(NonSendState); -impl<'a, T: Component> SystemParam for Option> { +impl<'w, T: Component> SystemParam for Option> { type Fetch = OptionNonSendState; } @@ -820,14 +820,14 @@ unsafe impl SystemParamState for OptionNonSendState { fn default_config() {} } -impl<'a, T: 'static> SystemParamFetch<'a> for OptionNonSendState { - type Item = Option>; +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState { + type Item = Option>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -889,14 +889,14 @@ unsafe impl SystemParamState for NonSendMutState { fn default_config() {} } -impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState { - type Item = NonSendMut<'a, T>; +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState { + type Item = NonSendMut<'w, T>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -937,14 +937,14 @@ unsafe impl SystemParamState for OptionNonSendMutState { fn default_config() {} } -impl<'a, T: 'static> SystemParamFetch<'a> for OptionNonSendMutState { - type Item = Option>; +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState { + type Item = Option>; #[inline] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -982,14 +982,14 @@ unsafe impl SystemParamState for ArchetypesState { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for ArchetypesState { - type Item = &'a Archetypes; +impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState { + type Item = &'w Archetypes; #[inline] unsafe fn get_param( - _state: &'a mut Self, + _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { world.archetypes() @@ -1017,14 +1017,14 @@ unsafe impl SystemParamState for ComponentsState { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for ComponentsState { - type Item = &'a Components; +impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState { + type Item = &'w Components; #[inline] unsafe fn get_param( - _state: &'a mut Self, + _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { world.components() @@ -1052,14 +1052,14 @@ unsafe impl SystemParamState for EntitiesState { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for EntitiesState { - type Item = &'a Entities; +impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState { + type Item = &'w Entities; #[inline] unsafe fn get_param( - _state: &'a mut Self, + _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { world.entities() @@ -1087,14 +1087,14 @@ unsafe impl SystemParamState for BundlesState { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for BundlesState { - type Item = &'a Bundles; +impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState { + type Item = &'w Bundles; #[inline] unsafe fn get_param( - _state: &'a mut Self, + _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'a World, + world: &'w World, _change_tick: u32, ) -> Self::Item { world.bundles() @@ -1127,13 +1127,13 @@ unsafe impl SystemParamState for SystemChangeTickState { fn default_config() {} } -impl<'a> SystemParamFetch<'a> for SystemChangeTickState { +impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState { type Item = SystemChangeTick; unsafe fn get_param( - _state: &mut Self, + _state: &'s mut Self, system_meta: &SystemMeta, - _world: &World, + _world: &'w World, change_tick: u32, ) -> Self::Item { SystemChangeTick { @@ -1154,15 +1154,15 @@ macro_rules! impl_system_param_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] - impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) { + impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) { type Item = ($($param::Item,)*); #[inline] #[allow(clippy::unused_unit)] unsafe fn get_param( - state: &'a mut Self, + state: &'s mut Self, system_meta: &SystemMeta, - world: &'a World, + world: &'w World, change_tick: u32, ) -> Self::Item { diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 6faac65c32865..9d4b6ce54f834 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -14,7 +14,7 @@ use bevy_ecs::{ system::{Query, Res, ResMut, SystemParam}, }; use bevy_reflect::Reflect; -use std::{ops::Range, sync::Arc}; +use std::{marker::PhantomData, ops::Range, sync::Arc}; use thiserror::Error; /// A queued command for the renderer @@ -164,18 +164,20 @@ pub enum DrawError { } #[derive(SystemParam)] -pub struct DrawContext<'a> { - pub pipelines: ResMut<'a, Assets>, - pub shaders: ResMut<'a, Assets>, - pub asset_render_resource_bindings: ResMut<'a, AssetRenderResourceBindings>, - pub pipeline_compiler: ResMut<'a, PipelineCompiler>, - pub render_resource_context: Res<'a, Box>, - pub shared_buffers: ResMut<'a, SharedBuffers>, +pub struct DrawContext<'w, 's> { + pub pipelines: ResMut<'w, Assets>, + pub shaders: ResMut<'w, Assets>, + pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>, + pub pipeline_compiler: ResMut<'w, PipelineCompiler>, + pub render_resource_context: Res<'w, Box>, + pub shared_buffers: ResMut<'w, SharedBuffers>, #[system_param(ignore)] pub current_pipeline: Option>, + #[system_param(ignore)] + marker: PhantomData<&'s usize>, } -impl<'a> DrawContext<'a> { +impl<'w, 's> DrawContext<'w, 's> { pub fn get_uniform_buffer( &mut self, render_resource: &T, diff --git a/crates/bevy_scene/src/command.rs b/crates/bevy_scene/src/command.rs index 42554e91793df..aef43a2e7f0d0 100644 --- a/crates/bevy_scene/src/command.rs +++ b/crates/bevy_scene/src/command.rs @@ -23,7 +23,7 @@ pub trait SpawnSceneCommands { fn spawn_scene(&mut self, scene: Handle); } -impl<'a> SpawnSceneCommands for Commands<'a> { +impl<'w, 's> SpawnSceneCommands for Commands<'w, 's> { fn spawn_scene(&mut self, scene_handle: Handle) { self.add(SpawnScene { scene_handle }); } @@ -45,7 +45,7 @@ pub trait SpawnSceneAsChildCommands { fn spawn_scene(&mut self, scene: Handle) -> &mut Self; } -impl<'a, 'b> SpawnSceneAsChildCommands for ChildBuilder<'a, 'b> { +impl<'w, 's, 'a> SpawnSceneAsChildCommands for ChildBuilder<'w, 's, 'a> { fn spawn_scene(&mut self, scene_handle: Handle) -> &mut Self { self.add_command(SpawnSceneAsChild { scene_handle, diff --git a/crates/bevy_transform/src/hierarchy/child_builder.rs b/crates/bevy_transform/src/hierarchy/child_builder.rs index 71fc54aeb24ce..3e625f4e856f0 100644 --- a/crates/bevy_transform/src/hierarchy/child_builder.rs +++ b/crates/bevy_transform/src/hierarchy/child_builder.rs @@ -40,8 +40,8 @@ pub struct PushChildren { children: SmallVec<[Entity; 8]>, } -pub struct ChildBuilder<'a, 'b> { - commands: &'b mut Commands<'a>, +pub struct ChildBuilder<'w, 's, 'a> { + commands: &'a mut Commands<'w, 's>, push_children: PushChildren, } @@ -71,14 +71,14 @@ impl Command for PushChildren { } } -impl<'a, 'b> ChildBuilder<'a, 'b> { - pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'a, '_> { +impl<'w, 's, 'a> ChildBuilder<'w, 's, 'a> { + pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'w, 's, '_> { let e = self.commands.spawn_bundle(bundle); self.push_children.children.push(e.id()); e } - pub fn spawn(&mut self) -> EntityCommands<'a, '_> { + pub fn spawn(&mut self) -> EntityCommands<'w, 's, '_> { let e = self.commands.spawn(); self.push_children.children.push(e.id()); e @@ -100,7 +100,7 @@ pub trait BuildChildren { fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self; } -impl<'a, 'b> BuildChildren for EntityCommands<'a, 'b> { +impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> { fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self { let parent = self.id(); let push_children = { diff --git a/crates/bevy_transform/src/hierarchy/hierarchy.rs b/crates/bevy_transform/src/hierarchy/hierarchy.rs index d50f241e43d74..b8e1bf45329d1 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy.rs @@ -47,7 +47,7 @@ pub trait DespawnRecursiveExt { fn despawn_recursive(&mut self); } -impl<'a, 'b> DespawnRecursiveExt for EntityCommands<'a, 'b> { +impl<'w, 's, 'a> DespawnRecursiveExt for EntityCommands<'w, 's, 'a> { /// Despawns the provided entity and its children. fn despawn_recursive(&mut self) { let entity = self.id(); From b163c50c7e4f98e216e9d9a9be4b042dfbf88156 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 5 Aug 2021 14:28:10 -0700 Subject: [PATCH 2/7] expose read-only world-only lifetimes --- crates/bevy_ecs/src/query/state.rs | 24 +++++++++++----------- crates/bevy_ecs/src/system/query.rs | 6 +++--- crates/bevy_ecs/src/system/system_param.rs | 4 ++++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 6349d3101b2a0..df7cba0eb1762 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -117,11 +117,11 @@ where } #[inline] - pub fn get<'w>( - &mut self, + pub fn get<'w, 's>( + &'s mut self, world: &'w World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> + ) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { @@ -130,11 +130,11 @@ where } #[inline] - pub fn get_mut<'w>( - &mut self, + pub fn get_mut<'w, 's>( + &'s mut self, world: &'w mut World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { // SAFETY: query has unique world access unsafe { self.get_unchecked(world, entity) } } @@ -144,11 +144,11 @@ where /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn get_unchecked<'w>( - &mut self, + pub unsafe fn get_unchecked<'w, 's>( + &'s mut self, world: &'w World, entity: Entity, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { self.validate_world_and_update_archetypes(world); self.get_unchecked_manual( world, @@ -161,13 +161,13 @@ where /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. - pub unsafe fn get_unchecked_manual<'w>( - &self, + pub unsafe fn get_unchecked_manual<'w, 's>( + &'s self, world: &'w World, entity: Entity, last_change_tick: u32, change_tick: u32, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<>::Item, QueryEntityError> { let location = world .entities .get(entity) diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index efa245e19ffce..0e3fb1b4e2004 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -146,7 +146,7 @@ where /// /// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries. #[inline] - pub fn iter(&self) -> QueryIter<'_, '_, Q, F> + pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -351,7 +351,7 @@ where /// /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries. #[inline] - pub fn get(&self, entity: Entity) -> Result<::Item, QueryEntityError> + pub fn get(&'s self, entity: Entity) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { @@ -511,7 +511,7 @@ where /// ``` /// /// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries. - pub fn single(&self) -> Result<>::Item, QuerySingleError> + pub fn single(&'s self) -> Result<>::Item, QuerySingleError> where Q::Fetch: ReadOnlyFetch, { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index b2cf44d471675..98e9145ec63d7 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -228,6 +228,10 @@ impl<'w, T: Component> Res<'w, T> { self.ticks .is_changed(self.last_change_tick, self.change_tick) } + + pub fn into_inner(self) -> &'w T { + self.value + } } impl<'w, T: Component> Deref for Res<'w, T> { From 99cbf96356e37647cd26b26fee8969d8f4f75bc5 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 5 Aug 2021 14:28:34 -0700 Subject: [PATCH 3/7] temporary system safety tests (to prove aliased muts arent possible) --- crates/bevy_ecs/src/system/mod.rs | 151 ++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 92b03c1f4e223..5f981af12af77 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -624,4 +624,155 @@ mod tests { ); } } + + #[test] + fn system_safety_test() { + struct A(usize); + struct B(usize); + + fn system(mut query: Query<&mut A>, e: Res) { + { + let mut iter = query.iter_mut(); + let a = &mut *iter.next().unwrap(); + + let mut iter2 = query.iter_mut(); + let b = &mut *iter2.next().unwrap(); + + // this should fail to compile + println!("{}", a.0); + } + + { + let mut a1 = query.get_mut(*e).unwrap(); + + let mut a2 = query.get_mut(*e).unwrap(); + + // this should fail to compile + println!("{} {}", a1.0, a2.0); + } + } + + fn query_set(mut queries: QuerySet<(Query<&mut A>,Query<&A>)>, e: Res) { + { + + let q2 = queries.q0_mut(); + let mut iter2 = q2.iter_mut(); + let mut b = iter2.next().unwrap(); + + let q1 = queries.q1(); + let mut iter = q1.iter(); + let a = &*iter.next().unwrap(); + + // this should fail to compile + b.0 = a.0 + } + + { + + let q1 = queries.q1(); + let mut iter = q1.iter(); + let a = &*iter.next().unwrap(); + + let q2 = queries.q0_mut(); + let mut iter2 = q2.iter_mut(); + let mut b = iter2.next().unwrap(); + + // this should fail to compile (but currently doesn't) + b.0 = a.0; + } + { + + let q2 = queries.q0_mut(); + let mut b = q2.get_mut(*e).unwrap(); + + let q1 = queries.q1(); + let a = q1.get(*e).unwrap(); + + // this should fail to compile + b.0 = a.0 + } + + { + let q1 = queries.q1(); + let a = q1.get(*e).unwrap(); + + let q2 = queries.q0_mut(); + let mut b = q2.get_mut(*e).unwrap(); + // this should fail to compile (but currently doesn't) + b.0 = a.0 + } + } + + let mut world = World::default(); + world.spawn().insert_bundle((A(1), B(1))); + run_system(&mut world, system); + } + + /// this test exists to show that read-only world-only queries can return data that lives as long as 'world + #[test] + fn long_life_test() { + struct Holder<'w> { + value: &'w A, + } + + struct State { + state: SystemState>, + state_q: SystemState>, + } + + impl State { + fn hold_res<'w>(&mut self, world: &'w World) -> Holder<'w> { + let a = self.state.get(&world); + Holder { + value: a.into_inner(), + } + } + fn hold_component<'w>(&mut self, world: &'w World, entity: Entity) -> Holder<'w> { + let q = self.state_q.get(&world); + let a = q.get(entity).unwrap(); + Holder { value: a } + } + fn hold_components<'w>(&mut self, world: &'w World) -> Vec> { + let mut components = Vec::new(); + let q = self.state_q.get(&world); + for a in q.iter() { + components.push(Holder { value: a }); + } + components + } + } + } + + #[test] + fn system_state_safety_test() { + struct A(usize); + struct B(usize); + + struct State { + state_r: SystemState>, + state_w: SystemState>, + } + + impl State { + fn get_component<'w>(&mut self, world: &'w mut World, entity: Entity) { + let q1 = self.state_r.get(&world); + let a1 = q1.get(entity).unwrap(); + + let mut q2 = self.state_w.get_mut(world); + let a2 = q2.get_mut(entity).unwrap(); + // this should fail to compile + println!("{}", a1.0); + } + + fn get_components<'w>(&mut self, world: &'w mut World) { + let q1 = self.state_r.get(&world); + let a1 = q1.iter().next().unwrap(); + + let mut q2 = self.state_w.get_mut(world); + let a2 = q2.iter_mut().next().unwrap(); + // this should fail to compile + println!("{}", a1.0); + } + } + } } From 03cf17ca779947e0aff5ee1195490efe7218570c Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Aug 2021 15:57:57 -0700 Subject: [PATCH 4/7] Rework QuerySet to be safe --- crates/bevy_ecs/macros/src/lib.rs | 48 +++++++------------ crates/bevy_ecs/src/system/mod.rs | 42 ++++++++-------- crates/bevy_ecs/src/system/query.rs | 38 ++++++++------- crates/bevy_ecs/src/system/system_param.rs | 13 +++-- crates/bevy_render/src/camera/camera.rs | 9 ++-- crates/bevy_render/src/mesh/mesh.rs | 11 +++-- .../nodes/render_resources_node.rs | 33 +++++++------ crates/bevy_render/src/wireframe/mod.rs | 12 ++--- crates/bevy_text/src/text2d.rs | 10 ++-- crates/bevy_ui/src/widget/text.rs | 10 ++-- examples/ecs/system_param.rs | 8 ++-- examples/game/alien_cake_addict.rs | 12 +++-- 12 files changed, 128 insertions(+), 118 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 1d44c24f52034..8371de3e4b160 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -9,8 +9,8 @@ use syn::{ parse_macro_input, punctuated::Punctuated, token::Comma, - Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt, - Path, Result, Token, + Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result, + Token, }; struct AllTuples { @@ -176,38 +176,26 @@ fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec { .collect::>() } -fn get_lifetimes(fmt_string: fn(usize) -> String, count: usize) -> Vec { - (0..count) - .map(|i| Lifetime::new(&fmt_string(i), Span::call_site())) - .collect::>() -} - #[proc_macro] pub fn impl_query_set(_input: TokenStream) -> TokenStream { let mut tokens = TokenStream::new(); let max_queries = 4; let queries = get_idents(|i| format!("Q{}", i), max_queries); let filters = get_idents(|i| format!("F{}", i), max_queries); - let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries); - let state_lifetimes = get_lifetimes(|i| format!("'qs{}", i), max_queries); - let mut query_fns = Vec::new(); let mut query_fn_muts = Vec::new(); for i in 0..max_queries { let query = &queries[i]; let filter = &filters[i]; - let lifetime = &lifetimes[i]; - let state_lifetime = &state_lifetimes[i]; let fn_name = Ident::new(&format!("q{}", i), Span::call_site()); - let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site()); let index = Index::from(i); - query_fns.push(quote! { - pub fn #fn_name(&self) -> &Query<#lifetime, #state_lifetime, #query, #filter> { - &self.0.#index - } - }); query_fn_muts.push(quote! { - pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> { - &mut self.0.#index + pub fn #fn_name(&mut self) -> Query<'_, '_, #query, #filter> { + // SAFE: systems run without conflicts with other systems. + // Conflicting queries in QuerySet are not accessible at the same time + // QuerySets are guaranteed to not conflict with other SystemParams + unsafe { + Query::new(self.world, &self.query_states.#index, self.last_change_tick, self.change_tick) + } } }); } @@ -215,12 +203,9 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { for query_count in 1..=max_queries { let query = &queries[0..query_count]; let filter = &filters[0..query_count]; - let lifetime = &lifetimes[0..query_count]; - let state_lifetime = &state_lifetimes[0..query_count]; - let query_fn = &query_fns[0..query_count]; let query_fn_mut = &query_fn_muts[0..query_count]; tokens.extend(TokenStream::from(quote! { - impl<#(#lifetime,)* #(#state_lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)> + impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>; @@ -276,7 +261,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { - type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>; + type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>; #[inline] unsafe fn get_param( @@ -285,15 +270,18 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { world: &'w World, change_tick: u32, ) -> Self::Item { - let (#(#query,)*) = &state.0; - QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*)) + QuerySet { + query_states: &state.0, + world, + last_change_tick: system_meta.last_change_tick, + change_tick, + } } } - impl<#(#lifetime,)* #(#state_lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)> + impl<'w, 's, #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)> where #(#filter::Fetch: FilterFetch,)* { - #(#query_fn)* #(#query_fn_mut)* } })); diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 5f981af12af77..f26a37a7dde35 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -24,7 +24,7 @@ mod tests { bundle::Bundles, component::Components, entity::{Entities, Entity}, - query::{Added, Changed, Or, With, Without}, + query::{Added, Changed, Or, QueryState, With, Without}, schedule::{Schedule, Stage, SystemStage}, system::{ ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet, @@ -131,9 +131,9 @@ mod tests { // Regression test for issue #762 fn query_system( mut ran: ResMut, - set: QuerySet<( - Query<(), Or<(Changed, Changed)>>, - Query<(), Or<(Added, Added)>>, + mut set: QuerySet<( + QueryState<(), Or<(Changed, Changed)>>, + QueryState<(), Or<(Added, Added)>>, )>, ) { let changed = set.q0().iter().count(); @@ -236,7 +236,7 @@ mod tests { #[test] fn query_set_system() { - fn sys(mut _set: QuerySet<(Query<&mut A>, Query<&A>)>) {} + fn sys(mut _set: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) {} let mut world = World::default(); run_system(&mut world, sys); } @@ -244,7 +244,7 @@ mod tests { #[test] #[should_panic] fn conflicting_query_with_query_set_system() { - fn sys(_query: Query<&mut A>, _set: QuerySet<(Query<&mut A>, Query<&B>)>) {} + fn sys(_query: Query<&mut A>, _set: QuerySet<(QueryState<&mut A>, QueryState<&B>)>) {} let mut world = World::default(); run_system(&mut world, sys); @@ -253,7 +253,11 @@ mod tests { #[test] #[should_panic] fn conflicting_query_sets_system() { - fn sys(_set_1: QuerySet<(Query<&mut A>,)>, _set_2: QuerySet<(Query<&mut A>, Query<&B>)>) {} + fn sys( + _set_1: QuerySet<(QueryState<&mut A>,)>, + _set_2: QuerySet<(QueryState<&mut A>, QueryState<&B>)>, + ) { + } let mut world = World::default(); run_system(&mut world, sys); @@ -520,8 +524,11 @@ mod tests { world.insert_resource(A(42)); world.spawn().insert(B(7)); - let mut system_state: SystemState<(Res, Query<&B>, QuerySet<(Query<&C>, Query<&D>)>)> = - SystemState::new(&mut world); + let mut system_state: SystemState<( + Res, + Query<&B>, + QuerySet<(QueryState<&C>, QueryState<&D>)>, + )> = SystemState::new(&mut world); let (a, query, _) = system_state.get(&world); assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!( @@ -651,11 +658,10 @@ mod tests { println!("{} {}", a1.0, a2.0); } } - - fn query_set(mut queries: QuerySet<(Query<&mut A>,Query<&A>)>, e: Res) { - { - let q2 = queries.q0_mut(); + fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { + { + let mut q2 = queries.q0(); let mut iter2 = q2.iter_mut(); let mut b = iter2.next().unwrap(); @@ -668,12 +674,11 @@ mod tests { } { - let q1 = queries.q1(); let mut iter = q1.iter(); let a = &*iter.next().unwrap(); - let q2 = queries.q0_mut(); + let mut q2 = queries.q0(); let mut iter2 = q2.iter_mut(); let mut b = iter2.next().unwrap(); @@ -681,8 +686,7 @@ mod tests { b.0 = a.0; } { - - let q2 = queries.q0_mut(); + let mut q2 = queries.q0(); let mut b = q2.get_mut(*e).unwrap(); let q1 = queries.q1(); @@ -696,7 +700,7 @@ mod tests { let q1 = queries.q1(); let a = q1.get(*e).unwrap(); - let q2 = queries.q0_mut(); + let mut q2 = queries.q0(); let mut b = q2.get_mut(*e).unwrap(); // this should fail to compile (but currently doesn't) b.0 = a.0 @@ -708,7 +712,7 @@ mod tests { run_system(&mut world, system); } - /// this test exists to show that read-only world-only queries can return data that lives as long as 'world + /// this test exists to show that read-only world-only queries can return data that lives as long as 'world #[test] fn long_life_test() { struct Holder<'w> { diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 0e3fb1b4e2004..e4979e27b73c3 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -158,6 +158,17 @@ where } } + /// Returns an [`Iterator`] over the query results. + #[inline] + pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> { + // SAFE: system runs without conflicts with other systems. + // same-system queries have runtime borrow checks when they conflict + unsafe { + self.state + .iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick) + } + } + /// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition. /// This can only be called for read-only queries /// @@ -181,17 +192,6 @@ where } } - /// Returns an [`Iterator`] over the query results. - #[inline] - pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> { - // SAFE: system runs without conflicts with other systems. - // same-system queries have runtime borrow checks when they conflict - unsafe { - self.state - .iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick) - } - } - /// Iterates over all possible combinations of `K` query results without repetition. /// /// The returned value is not an `Iterator`, because that would lead to aliasing of mutable references. @@ -285,7 +285,7 @@ where /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot /// be chained like a normal [`Iterator`]. #[inline] - pub fn for_each_mut(&'s mut self, f: impl FnMut(>::Item)) { + pub fn for_each_mut(&mut self, f: impl FnMut(>::Item)) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict unsafe { @@ -328,10 +328,10 @@ where /// Runs `f` on each query result in parallel using the given task pool. #[inline] pub fn par_for_each_mut( - &'s mut self, + &mut self, task_pool: &TaskPool, batch_size: usize, - f: impl Fn(>::Item) + Send + Sync + Clone, + f: impl Fn(>::Item) + Send + Sync + Clone, ) { // SAFE: system runs without conflicts with other systems. same-system queries have runtime // borrow checks when they conflict @@ -351,7 +351,10 @@ where /// /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries. #[inline] - pub fn get(&'s self, entity: Entity) -> Result<>::Item, QueryEntityError> + pub fn get( + &'s self, + entity: Entity, + ) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { @@ -406,7 +409,10 @@ where /// entity does not have the given component type or if the given component type does not match /// this query. #[inline] - pub fn get_component(&self, entity: Entity) -> Result<&T, QueryComponentError> { + pub fn get_component( + &self, + entity: Entity, + ) -> Result<&'w T, QueryComponentError> { let world = self.world; let entity_ref = world .get_entity(entity) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 98e9145ec63d7..bd1ae8409c1ca 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -146,7 +146,8 @@ where fn default_config() {} } -impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's> for QueryState +impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's> + for QueryState where F::Fetch: FilterFetch, { @@ -184,7 +185,13 @@ fn assert_component_access_compatibility( query_type, filter_type, system_name, accesses); } -pub struct QuerySet(T); +pub struct QuerySet<'w, 's, T> { + query_states: &'s T, + world: &'w World, + last_change_tick: u32, + change_tick: u32, +} + pub struct QuerySetState(T); impl_query_set!(); @@ -228,7 +235,7 @@ impl<'w, T: Component> Res<'w, T> { self.ticks .is_changed(self.last_change_tick, self.change_tick) } - + pub fn into_inner(self) -> &'w T { self.value } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index f3f63ca40a6f3..1ac86b41a4ae5 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -4,9 +4,10 @@ use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, + prelude::QueryState, query::Added, reflect::ReflectComponent, - system::{Query, QuerySet, Res}, + system::{QuerySet, Res}, }; use bevy_math::{Mat4, Vec2, Vec3}; use bevy_reflect::{Reflect, ReflectDeserialize}; @@ -70,8 +71,8 @@ pub fn camera_system( mut window_created_events: EventReader, windows: Res, mut queries: QuerySet<( - Query<(Entity, &mut Camera, &mut T)>, - Query>, + QueryState<(Entity, &mut Camera, &mut T)>, + QueryState>, )>, ) { let mut changed_window_ids = Vec::new(); @@ -99,7 +100,7 @@ pub fn camera_system( for entity in &mut queries.q1().iter() { added_cameras.push(entity); } - for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() { + for (entity, mut camera, mut camera_projection) in queries.q0().iter_mut() { if let Some(window) = windows.get(camera.window) { if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index 29d2cdf19d4c4..b8d42eb0605ae 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -9,8 +9,9 @@ use bevy_core::cast_slice; use bevy_ecs::{ entity::Entity, event::EventReader, + prelude::QueryState, query::{Changed, With}, - system::{Local, Query, QuerySet, Res}, + system::{Local, QuerySet, Res}, world::Mut, }; use bevy_math::*; @@ -512,8 +513,8 @@ pub fn mesh_resource_provider_system( meshes: Res>, mut mesh_events: EventReader>, mut queries: QuerySet<( - Query<&mut RenderPipelines, With>>, - Query<(Entity, &Handle, &mut RenderPipelines), Changed>>, + QueryState<&mut RenderPipelines, With>>, + QueryState<(Entity, &Handle, &mut RenderPipelines), Changed>>, )>, ) { let mut changed_meshes = HashSet::default(); @@ -573,7 +574,7 @@ pub fn mesh_resource_provider_system( if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) { for entity in mesh_entities.entities.iter() { - if let Ok(render_pipelines) = queries.q0_mut().get_mut(*entity) { + if let Ok(render_pipelines) = queries.q0().get_mut(*entity) { update_entity_mesh( render_resource_context, mesh, @@ -587,7 +588,7 @@ pub fn mesh_resource_provider_system( } // handover buffers to pipeline - for (entity, handle, render_pipelines) in queries.q1_mut().iter_mut() { + for (entity, handle, render_pipelines) in queries.q1().iter_mut() { let mesh_entities = state .mesh_entities .entry(handle.clone_weak()) diff --git a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs index e0a66246376c9..453ffa6faac9e 100644 --- a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs @@ -13,10 +13,9 @@ use bevy_app::EventReader; use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId}; use bevy_ecs::{ entity::Entity, + prelude::QueryState, query::{Changed, Or, With}, - system::{ - BoxedSystem, ConfigurableSystem, Local, Query, QuerySet, RemovedComponents, Res, ResMut, - }, + system::{BoxedSystem, ConfigurableSystem, Local, QuerySet, RemovedComponents, Res, ResMut}, world::World, }; use bevy_utils::HashMap; @@ -437,8 +436,11 @@ fn render_resources_node_system( render_resource_context: Res>, removed: RemovedComponents, mut queries: QuerySet<( - Query<(Entity, &T, &Visible, &mut RenderPipelines), Or<(Changed, Changed)>>, - Query<(Entity, &T, &Visible, &mut RenderPipelines)>, + QueryState< + (Entity, &T, &Visible, &mut RenderPipelines), + Or<(Changed, Changed)>, + >, + QueryState<(Entity, &T, &Visible, &mut RenderPipelines)>, )>, ) { let state = state.deref_mut(); @@ -446,7 +448,7 @@ fn render_resources_node_system( let render_resource_context = &**render_resource_context; uniform_buffer_arrays.begin_update(); // initialize uniform buffer arrays using the first RenderResources - if let Some((_, first, _, _)) = queries.q0_mut().iter_mut().next() { + if let Some((_, first, _, _)) = queries.q0().iter_mut().next() { uniform_buffer_arrays.initialize(first, render_resource_context); } @@ -456,8 +458,7 @@ fn render_resources_node_system( // handle entities that were waiting for texture loads on the last update for entity in std::mem::take(&mut *entities_waiting_for_textures) { - if let Ok((entity, uniforms, _visible, mut render_pipelines)) = - queries.q1_mut().get_mut(entity) + if let Ok((entity, uniforms, _visible, mut render_pipelines)) = queries.q1().get_mut(entity) { if !setup_uniform_texture_resources::( uniforms, @@ -469,7 +470,7 @@ fn render_resources_node_system( } } - for (entity, uniforms, visible, mut render_pipelines) in queries.q0_mut().iter_mut() { + for (entity, uniforms, visible, mut render_pipelines) in queries.q0().iter_mut() { if !visible.is_visible { continue; } @@ -498,8 +499,7 @@ fn render_resources_node_system( // if the buffer array was resized, write all entities to the new buffer, otherwise // only write changes if resized { - for (entity, uniforms, visible, mut render_pipelines) in - queries.q1_mut().iter_mut() + for (entity, uniforms, visible, mut render_pipelines) in queries.q1().iter_mut() { if !visible.is_visible { continue; @@ -515,8 +515,7 @@ fn render_resources_node_system( ); } } else { - for (entity, uniforms, visible, mut render_pipelines) in - queries.q0_mut().iter_mut() + for (entity, uniforms, visible, mut render_pipelines) in queries.q0().iter_mut() { if !visible.is_visible { continue; @@ -621,8 +620,8 @@ fn asset_render_resources_node_system( render_resource_context: Res>, removed_handles: RemovedComponents>, mut queries: QuerySet<( - Query<(&Handle, &mut RenderPipelines), Changed>>, - Query<&mut RenderPipelines, With>>, + QueryState<(&Handle, &mut RenderPipelines), Changed>>, + QueryState<&mut RenderPipelines, With>>, )>, ) { let state = state.deref_mut(); @@ -752,7 +751,7 @@ fn asset_render_resources_node_system( // update removed entity asset mapping for entity in removed_handles.iter() { - if let Ok(mut render_pipelines) = queries.q1_mut().get_mut(entity) { + if let Ok(mut render_pipelines) = queries.q1().get_mut(entity) { render_pipelines .bindings .remove_asset_with_type(TypeId::of::()) @@ -760,7 +759,7 @@ fn asset_render_resources_node_system( } // update changed entity asset mapping - for (asset_handle, mut render_pipelines) in queries.q0_mut().iter_mut() { + for (asset_handle, mut render_pipelines) in queries.q0().iter_mut() { render_pipelines .bindings .remove_asset_with_type(TypeId::of::()); diff --git a/crates/bevy_render/src/wireframe/mod.rs b/crates/bevy_render/src/wireframe/mod.rs index d98eb165abe46..fc958163238f2 100644 --- a/crates/bevy_render/src/wireframe/mod.rs +++ b/crates/bevy_render/src/wireframe/mod.rs @@ -8,9 +8,9 @@ use crate::{ use bevy_app::prelude::*; use bevy_asset::{Assets, Handle, HandleUntyped}; use bevy_ecs::{ - query::With, + query::{QueryState, With}, reflect::ReflectComponent, - system::{Query, QuerySet, Res}, + system::{QuerySet, Res}, world::Mut, }; use bevy_reflect::{Reflect, TypeUuid}; @@ -62,8 +62,8 @@ pub fn draw_wireframes_system( meshes: Res>, wireframe_config: Res, mut query: QuerySet<( - Query<(&mut Draw, &mut RenderPipelines, &Handle, &Visible)>, - Query<(&mut Draw, &mut RenderPipelines, &Handle, &Visible), With>, + QueryState<(&mut Draw, &mut RenderPipelines, &Handle, &Visible)>, + QueryState<(&mut Draw, &mut RenderPipelines, &Handle, &Visible), With>, )>, ) { let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): ( @@ -123,8 +123,8 @@ pub fn draw_wireframes_system( }; if wireframe_config.global { - query.q0_mut().iter_mut().for_each(iterator); + query.q0().iter_mut().for_each(iterator); } else { - query.q1_mut().iter_mut().for_each(iterator); + query.q1().iter_mut().for_each(iterator); } } diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 890f57bbf1d6d..88dc89575c381 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -2,7 +2,7 @@ use bevy_asset::Assets; use bevy_ecs::{ bundle::Bundle, entity::Entity, - query::{Changed, With, Without}, + query::{Changed, QueryState, With, Without}, system::{Local, Query, QuerySet, Res, ResMut}, }; use bevy_math::{Size, Vec3}; @@ -136,12 +136,12 @@ pub fn text2d_system( mut font_atlas_set_storage: ResMut>, mut text_pipeline: ResMut, mut text_queries: QuerySet<( - Query, Changed)>, - Query<(&Text, &mut Text2dSize), With>, + QueryState, Changed)>, + QueryState<(&Text, &mut Text2dSize), With>, )>, ) { // Adds all entities where the text or the style has changed to the local queue - for entity in text_queries.q0_mut().iter_mut() { + for entity in text_queries.q0().iter_mut() { queued_text.entities.push(entity); } @@ -157,7 +157,7 @@ pub fn text2d_system( // Computes all text in the local queue let mut new_queue = Vec::new(); - let query = text_queries.q1_mut(); + let mut query = text_queries.q1(); for entity in queued_text.entities.drain(..) { if let Ok((text, mut calculated_size)) = query.get_mut(entity) { match text_pipeline.queue_text( diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 3e5c91c9eae01..c59230b4b323a 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -2,7 +2,7 @@ use crate::{CalculatedSize, Node, Style, Val}; use bevy_asset::Assets; use bevy_ecs::{ entity::Entity, - query::{Changed, Or, With, Without}, + query::{Changed, Or, QueryState, With, Without}, system::{Local, Query, QuerySet, Res, ResMut}, }; use bevy_math::Size; @@ -53,9 +53,9 @@ pub fn text_system( mut font_atlas_set_storage: ResMut>, mut text_pipeline: ResMut, mut text_queries: QuerySet<( - Query, Changed